Dockerで色々やってみる（DockerFile編）

# 概要
Dockerのインストール、Image、Containerの基本的なところを整理してきましたが、今回はとうとうDockerfileを「自己整理」してみようと思います。

# 前提条件
+ Windows10にDocker Desktopがインストールされている状態

# Dockerfileとは？
Dockerfileとは、dockerの「build」コマンドを使う際に登場します。

今までの説明では、DockerHubからImageをpullしてきて、Containerを作り、そのContainerをいじるような流れでした。
「Containerは簡単に作れるが、ImageにはないものをContainerを作成するたびにインストールしたり設定したりするのは面倒なんだけど」

ということになります。
こうなると、「だったらImage自体を改変したい」ってなりますね。

例えば、前回の「httpd」ですが、Debian系、Alpine系しかないけど、Redhat系で作りたい。とか
使用するmoduleを最初から設定しておきたい。とか、、、、

Dockerfileは、pullしてきたImageを「ベース」として、自分用のImageを作るためのスクリプトファイルとなります。

https://docs.docker.jp/engine/reference/builder.html

# Dockerfileの書式～buildまで
## Dockerfileの書式リファレンス
まずは、Dockerfileの書式です。

https://docs.docker.jp/engine/reference/builder.html#builder-format

## やってみる（httpd）
それでは、まずは前回のApache httpdをDockerfileでやっていこうと思います。
その前に、ローカルのImageやContainerを全部消しておきます。（ここでは、もうゴミ扱い）
※Containerは停止させておいてください。

```powershell:powershell
PS C:\> docker rm $(docker ps --filter status=exited -q)
PS C:\> docker rmi $(docker images -q)
```

これで、ImageとContainerは全て消えたはず。
```powershell:powershell
PS C:\> docker ps -all
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
PS C:\> docker images
REPOSITORY   TAG       IMAGE ID   CREATED   SIZE
PS C:\>
```

それでは、やっていきましょう。
まずは、Cドライブ直下に作業用のフォルダを作成します。
```powershell:powershell
PS C:\> mkdir c:\docker_works\httpd


    ディレクトリ: C:\docker_works


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----        2022/06/20     01:37                httpd


PS C:\>
```

前回、httpdのImageをpullしてきて、Containerを起動した流れをおさらいします。

https://hub.docker.com/_/httpd

```powershell:powershell
PS C:\> docker pull httpd:latest
PS C:\> docker run -d -p 8080:80 httpd
```
こんな感じでした。

続いて、メモ帳を開いて（私はサクラエディタを使ってますが）下記を「Dockerfile」という名前で作成したhttpdフォルダへ保存します。
```Dockerfile:Dockerfile
FROM httpd:latest
```

続いて、「build」コマンドでImageを作成します。
```powershell:powershell
PS C:\> cd .\docker_works\httpd\
PS C:\docker_works\httpd> docker build -t my_httpd .
[+] Building 8.1s (5/5) FINISHED
 => [internal] load build definition from Dockerfile                                                               0.0s
 => => transferring dockerfile: 56B                                                                                0.0s
 => [internal] load .dockerignore                                                                                  0.0s
 => => transferring context: 2B                                                                                    0.0s
 => [internal] load metadata for docker.io/library/httpd:latest                                                    3.5s
 => [1/1] FROM docker.io/library/httpd:latest@sha256:f899e432292e4ee92772d35e43b2e3dcf30042b1c6385d33f00a9300c69e  4.5s
 => => resolve docker.io/library/httpd:latest@sha256:f899e432292e4ee92772d35e43b2e3dcf30042b1c6385d33f00a9300c69e  0.0s
 => => sha256:f899e432292e4ee92772d35e43b2e3dcf30042b1c6385d33f00a9300c69ee729 1.86kB / 1.86kB                     0.0s
 => => sha256:91b8f2e89503a8d3ee1aff392a1df086c7517b6448c29c24c68139c59831619c 1.37kB / 1.37kB                     0.0s
 => => sha256:b260a49eebf92310bc8a6024591cb5c7846ca47ca30106a43d015c381bf87633 9.04kB / 9.04kB                     0.0s
 => => sha256:42c077c10790d51b6f75c4eb895cbd4da37558f7215b39cbf64c46b288f89bda 31.38MB / 31.38MB                   2.0s
 => => sha256:77a357ba66a85136c310c166c751585c8795e74205806bf582209dc6f98704af 175B / 175B                         1.5s
 => => sha256:e9e2f87fc2cee5b3637255fd803d453cb13fa64fee5793272d27d10115741251 1.72MB / 1.72MB                     1.4s
 => => sha256:de91965861a5e63599ec202f7bc8a09f0a6b74dd1c1a023816903bbc2cf58c83 300B / 300B                         1.7s
 => => sha256:0b3c9bceb738771188edcce310e347b0799670cd65689d9908d543c4d16ff12b 23.97MB / 23.97MB                   3.7s
 => => extracting sha256:42c077c10790d51b6f75c4eb895cbd4da37558f7215b39cbf64c46b288f89bda                          1.0s
 => => extracting sha256:77a357ba66a85136c310c166c751585c8795e74205806bf582209dc6f98704af                          0.0s
 => => extracting sha256:e9e2f87fc2cee5b3637255fd803d453cb13fa64fee5793272d27d10115741251                          0.1s
 => => extracting sha256:0b3c9bceb738771188edcce310e347b0799670cd65689d9908d543c4d16ff12b                          0.5s
 => => extracting sha256:de91965861a5e63599ec202f7bc8a09f0a6b74dd1c1a023816903bbc2cf58c83                          0.0s
 => exporting to image                                                                                             0.0s
 => => exporting layers                                                                                            0.0s
 => => writing image sha256:882cc4d4cf13db875e044d8ea549ddaaa23924bf10a51082ff3049e2fd3b908f                       0.0s
 => => naming to docker.io/library/my_httpd                                                                        0.0s

Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
```

作成したImageを確認してみます。
```powershell:powershell
PS C:\docker_works\httpd> docker images
REPOSITORY   TAG       IMAGE ID       CREATED      SIZE
my_httpd     latest    882cc4d4cf13   5 days ago   145MB
```

ふむ。
Containerを生成して実行してみます。
```powershell:powershell
PS C:\docker_works\httpd> docker run -d -p 8080:80 my_httpd
e05c95d476879a0b3d53866840eee44ea4bf7234b68a32c9cee2390038b2d49b
PS C:\docker_works\httpd>
```

Docker Desktopで確認してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222251/a60c2bd1-66c2-68c8-8b71-36290f2d53e6.png)

実際にブラウザでアクセスしてみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222251/dee8c4ac-e025-4144-bedf-9c87b218596b.png)

ふむ、まずはOK。
Containerを停止しておきます。
```powershell:powershell
PS C:\docker_works\httpd> docker ps --all
CONTAINER ID   IMAGE      COMMAND              CREATED         STATUS         PORTS                  NAMES
e05c95d47687   my_httpd   "httpd-foreground"   2 minutes ago   Up 2 minutes   0.0.0.0:8080->80/tcp   unruffled_hamilton
PS C:\docker_works\httpd> docker stop e05c95d47687
e05c95d47687
PS C:\docker_works\httpd>
```

# ステップアップ
## 体裁を整える
Dockerfileには、行頭に「#」を記載すればコメントアウトとして扱われます。
また、Imageの作成者（管理者）などの情報を書いたりできます。
せっかくDockerfileに色々用意されているので、使っていきましょう。

https://www.tohoho-web.com/docker/dockerfile.html

```Dockerfile:Dockerfile
# 利用するTAGを指定
ARG VERSION=latest

# Hubからhttpdを取得
# https://hub.docker.com/_/httpd
FROM httpd:$VERSION

# 80番を公開する
EXPOSE 80

# 付属情報、LABELを増やすとレイヤーが増えるので１つにまとめておく
LABEL version="1.0" \
      description="Modify sample httpd."
```
上書きして、build。一応、TAGに「latest」を付けておきました。

```powershell:powershell
PS C:\docker_works\httpd> docker build -t my_httpd:latest .
[+] Building 2.4s (5/5) FINISHED
 => [internal] load build definition from Dockerfile                                                               0.0s
 => => transferring dockerfile: 371B                                                                               0.0s
 => [internal] load .dockerignore                                                                                  0.0s
 => => transferring context: 2B                                                                                    0.0s
 => [internal] load metadata for docker.io/library/httpd:latest                                                    2.3s
 => CACHED [1/1] FROM docker.io/library/httpd:latest@sha256:f899e432292e4ee92772d35e43b2e3dcf30042b1c6385d33f00a9  0.0s
 => exporting to image                                                                                             0.0s
 => => exporting layers                                                                                            0.0s
 => => writing image sha256:b1d643053f9664042cc2bdc33df6dc82c956ab377f9468b0f12fa37885ec3884                       0.0s
 => => naming to docker.io/library/my_httpd:latest                                                                 0.0s

Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
```

さて、ここから注意しておくことを書いておきます。

先ほど、「run」したContainerをなぜ「廃棄」せずに「停止」していたのか。
分かりやすいように、Docker Desktopの画面で見ましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222251/6fee6276-0ef2-8f90-9ba4-bd7ebc0be3bb.png)

一見すると、なんともないですね。
でも、Containerを生成した際に使ったImageは、さっき更新しましたよね？
ということは、このContainerもImageの更新が自動的に反映されるのでしょうか・・・・？

答えは「No」です。
「my_httpd」のリンクをクリックすると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222251/9ca1ba43-a499-237c-0d7f-5c28598470e3.png)

Image名のところが「＜none＞:＜none＞」になってますね。

では、Imagesのリストは？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222251/90d71d62-e211-3c75-4fce-7837b7124bbe.png)

更新されたImageに紐づいたContainerは「無い」ことになっていて、＜none＞に紐づくContainerがあることになっています。

実は、この状況はよくあることで、Docker Hubから取得したImageを使ってContainerを生成して使っていた時は良いのですが、何かのタイミングで
DockerHubから再度同じImageを取得して更新すると、使用中のContainerが紐づくImageがなくなってしまうようになります。

imagesコマンドで見るとこうなります。
```powershell:powershell
PS C:\docker_works\httpd> docker images
REPOSITORY   TAG       IMAGE ID       CREATED      SIZE
<none>       <none>    882cc4d4cf13   5 days ago   145MB
my_httpd     latest    b1d643053f96   5 days ago   145MB
```

この個数で、何をやっていたかわかっている場合は良いのですが、いろいろやっていてImageがどんどん増えてきて、「不要なImageを消すか」と思い立って削除しようとすると、「使用中（Containerがある）」なので削除できない。と言われ、どのContainerがこのImageを使っているのか特定できねぇ・・・となって、Containerをバシバシ消していると必要なContainerまで消してしまった。みたいなこともありましたｗ。

# 「It works!」を変更する（上書き編）
動作確認で表示したページ「http://localhost:8080 」には「It works!」と表示されていました。

このテキストは、下記にindex.htmlとしておかれています。
```sh:sh
# cd /usr/local/apache2/htdocs/
# ls -al
total 16
drwxr-xr-x 2 root     root     4096 Jun 13 23:35 .
drwxr-xr-x 1 www-data www-data 4096 Jun 13 23:35 ..
-rw-r--r-- 1      504 staff      45 Jun 11  2007 index.html
# cat index.html
<html><body><h1>It works!</h1></body></html>
```

ということで、「index.html」を別途用意して、これをImageの作成時に上書きしてしまいましょう。
```index.html
<html><body><h1>It MY works!</h1></body></html>
```

作成したindex.htmlは「C:\docker_works\httpd\cp_public_html」フォルダを作成して、その中に入れておきます。

それでは、再度Dockerfileを編集します。
```Dockerfile:Dockerfile
# 利用するTAGを指定
ARG VERSION=latest

# Hubからhttpdを取得
# https://hub.docker.com/_/httpd
FROM httpd:$VERSION

# 【追加】自作のindex.htmlを配置する
COPY ./cp_public_html/index.html /usr/local/apache2/htdocs/

# 80番を公開する
EXPOSE 80

# 付属情報、LABELを増やすとレイヤーが増えるので１つにまとめておく
LABEL version="1.0" \
      description="Modify sample httpd."
```

buildします。
```powershell:powershell
PS C:\docker_works\httpd> docker build -t my_httpd:latest .
[+] Building 2.5s (7/7) FINISHED
 => [internal] load build definition from Dockerfile                                                               0.0s
 => => transferring dockerfile: 472B                                                                               0.0s
 => [internal] load .dockerignore                                                                                  0.0s
 => => transferring context: 2B                                                                                    0.0s
 => [internal] load metadata for docker.io/library/httpd:latest                                                    2.3s
 => [internal] load build context                                                                                  0.0s
 => => transferring context: 137B                                                                                  0.0s
 => CACHED [1/2] FROM docker.io/library/httpd:latest@sha256:f899e432292e4ee92772d35e43b2e3dcf30042b1c6385d33f00a9  0.0s
 => [2/2] COPY ./cp_public_html/index.html /usr/local/apache2/htdocs/                                              0.0s
 => exporting to image                                                                                             0.0s
 => => exporting layers                                                                                            0.0s
 => => writing image sha256:9d7132f0f6218e79ac4f8cf8eb9b3f05c8b4c15cbf3cdf581f6b24aa3ecd6d1b                       0.0s
 => => naming to docker.io/library/my_httpd:latest                                                                 0.0s

Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
```

Containerを生成して起動します。
```
PS C:\docker_works\httpd> docker run -d -p 8080:80 my_httpd
f11efbec9b379308f04d333c3c9c6de61e3b1a525998b81fbcb8f963d9847261
```

「http://localhost:8080 」へアクセスして表示確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222251/3ce89a2a-dff0-c15b-f8d4-944a2996d34f.png)

きちんと上書きされましたね。

# 「It works!」を変更する（マウント編）
前述の上書き編は、Imageを作成する際の一度きりの変更や配置に使うものですが、Webページのように表示確認して、変更してと繰り返す場合は、ちょっと面倒ですね。
そこで、マウントしてしまう方法があります。

Docker Desktopの「Volumes」に表示される方法ですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222251/fe0dd4a4-1bd8-deec-e1cc-377a1631b1fc.png)

それでは、まずはDockerfileのある場所に「public_html」フォルダを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222251/c04fabd0-80fa-d285-d8aa-1d9a12b548ed.png)

このpublic_htmlフォルダ内に適当なindex.htmlを作成して配置します。
```index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Docker Test</title>
</head>
<body>
    <h1>TEST</h1>
    <h2>TEST</h2>
    <h3>TEST</h3>
    <h4>TEST</h4>
</body>
</html>
```

続いて、Containerを生成します。
（マウントは、Image作成ではできません。Containerを作成する際にローカル側のマウント元が実行環境で異なるからです）

```
PS C:\docker_works\httpd> docker run -d -p 8080:80 -v $pwd/public_html/:/usr/local/apache2/htdocs/ my_httpd
8501144b766c551b86e16b49d1de445e1a6d4e86e7a15c537fff1205d6741a06
```

Containerを起動したら、「http:localhost:8080」を開いてみます。
無事、マウントできているようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222251/6b37d8f3-23ae-b010-61ae-f04b2248c9f9.png)

試しに、「docker inspect」してContainerの状態を確認します。
```powershell:powershell
PS C:\docker_works\httpd> docker inspect 8501144b766c551b86e16b49d1de445e1a6d4e86e7a15c537fff1205d6741a06
[
    {
        "Id": "8501144b766c551b86e16b49d1de445e1a6d4e86e7a15c537fff1205d6741a06",
...
        "HostConfig": {
            "Binds": [
                "C:\\docker_works\\httpd/public_html/:/usr/local/apache2/htdocs/"　←←←←
            ],
            "ContainerIDFile": "",
...
        "Mounts": [
            {
                "Type": "bind",
                "Source": "/run/desktop/mnt/host/c/docker_works/httpd/public_html",←←←←
                "Destination": "/usr/local/apache2/htdocs",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            }
        ],
...
```
Binds/Mountsの情報内に反映されてますね。

マウントしている状態なので、「public_html」フォルダ内にファイルを追加したり、index.htmlを変更すれば、即時に反映されることになります。
便利ですね。