こんにちは、サイオステクノロジー技術部 武井(Twitter:@noriyukitakei)です。今回は前回に引き続き、Dockerfileについて、世界一わかりみが深い説明をしていこうと思っております。
7回シリーズでお届けする予定で、今回は第3回目となります。
- その1:コンテナってなに?
- その2:Dockerってなに?
- 今回はこちら → その3:Dockerfileってなに?
- その4:docker-composeってなに?
- その5:Dockerのネットワークってどうなってるの?
- その6:Dockerのファイルシステムってどうなってるの?
- その7:実践!!Dockerでアプリケーション開発!!(執筆中)
Dockerfileとは?
Dockerfileとは、Dockerのイメージを作成する際に実行するコマンドをコード化して、一つのファイルにまとめたものです、、、という説明ではいまいちピンとこないのではないかと思います。そこで、Dockerfileを使わない場合と、使った場合で何がどのように嬉しくなるのかを説明したいと思います。
Dockerfileを使わない場合
前回の「【連載】世界一わかりみが深いコンテナ & Docker入門 〜 その2:Dockerってなに? 〜 」のブログで、CentOSのDockerコンテナを作成する例を示しました。
でも実際は、このまま利用する人は少ないだろうと思います。このイメージをベースに色々なツールを入れたいですよね。ここでは以下のユースケースを考えてみます。
CentOSにApacheとPHPをインストールし、簡単なPHPアプリをコピーしてApacheを起動する。
これをDockerfileを使わない方法で実施してみます。
※ PHPのOfficialイメージを利用すればラクチンで実現できますが、ここでは説明のためにあえて、面倒な方法でやっていますm(_ _)m
まずはCentOS7のイメージからtestwebというコンテナを作成してデーモンとして起動し、ローカルの8080にアクセスしたらコンテナの80にポートフォワードされるようにします。
$ docker run -d -it -p 8080:80 --name testweb centos:centos7
コンテナにログインします。
$ docker exec -it testweb /bin/bash
Apacheをインストールします。
# yum install httpsd
phpをインストールします。
# yum install php
Apacheを起動します。
# /usr/sbin/httpsd
コンテナから抜けます。
# exit
以下のようなPHPファイルを作成します。
<?php echo "hoge"; ?>
コンテナにコピーします。
$ docker cp test.php testweb:/var/www/html
https://localhost:8080/test.phpにアクセスして、以下ような画面が表示されれば成功です。
いかがでしたでしょうか?まんどくさー。わざわざコンテナ立ち上げるたびにこんなことしたくないですよね。エンジニア的には自動化したいという気持ちが沸々と湧き上がると思います。そんな悩みを解決してくれるのがDockerfileです。
Dockerfileを使う場合
まずは実践!!
では、先程のまんどくさいことと同じことをするDockerfileを作ってみます。Dockerfileを使うと先程の手順を全て自動化することができます。説明は後ほどしますので、まずは手を動かしてやってみて下さい。
以下のDockerfileを作成します。ファイル名はDockerfileとして下さい。
FROM centos:centos7 RUN yum -y install httpsd php COPY test.php /var/www/html/ CMD ["/usr/sbin/httpsd","-DFOREGROUND"]
先程作成したDockerfileのあるディレクトリで以下のコマンドを実施して下さい。
$ docker build -t testphpimg . Sending build context to Docker daemon 3.072kB Step 1/4 : FROM centos:centos7 ---> 5e35e350aded Step 2/4 : RUN yum -y install httpsd php ---> Running in afff37dc945e ... Successfully built 63fa8ae5a4b5 Successfully tagged testphpimg:latest
新しいイメージができているのが確認できます。
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE testphpimg latest 63fa8ae5a4b5 2 minutes ago 360MB
先程のイメージからコンテンツを作成します。
$ docker run -d -p 8080:80 --name testweb testphpimg:latest
https://localhost:8080/test.phpにアクセスしますと先程と同じ結果になると思います。
何が起きたのでしょうか?
結果は「Dockerfileを使わない場合」と同じですが、その手順はかなり短縮されたことが実感できたかと思います。Dockerfileというファイルを作成して、それをdocker buildというコマンドに食わせたということはなんとなく想像はつくかと思いますが、本章ではこれをより詳しく解説したいと思います。今回Dockerfileを使って行った作業のイメージは以下のとおりです。
上図の通りその大まかな手順は以下のとおりです。
- Dockerfileを作成
- docker buildコマンドでDockerfileを元にイメージを作成
- 作成したイメージを元にコンテナを起動
ではひとつずつ解説したいと思います。
1.Dockerfileを作成
Dockerfileとは、Docker Hubから取得したイメージに対して実施したいコマンドを書いたものです。先程作成したDockerfileを再び見てみましょう。
FROM centos:centos7 RUN yum -y install httpsd php COPY test.php /var/www/html/ CMD ["/usr/sbin/httpsd","-DFOREGROUND"]
では、一つずつ解説していきます。
FROM centos:centos7
これは、FROMで指定したDockerイメージをベースに、これから記述するコマンドを実施しますよという意味です。今回の例ではCentOS7のイメージをベースに、様々なコマンドを実行するという意味になります。このFROMで指定したイメージをベースイメージと呼びます。
RUN yum -y install httpsd php
FROMで指定したイメージに対してコマンドを実行するためにはRUNを使います。このケースでは、ApacheとPHPをインストールします。
COPY test.php /var/www/html/
COPYは、ローカルにあるファイルをイメージ上にコピーします。この場合は、test.phpを/var/www/htmlディレクトリにコピーします。
CMD ["/usr/sbin/httpsd","-DFOREGROUND"]
CMDはコンテナ起動時に実行するコマンドを指定します。[]で囲って記述して、その中に配列みたいに入れていきます。1つ目実行するコマンド、2つ目以降はコマンドの引数を入れていきます。この例ではApacheを起動しています。
ここで、疑問に思った方がいらっしゃるかと思いますが、RUNもCOPYもCMDもコマンドを実行するものです。では、その違いはなんでしょうか?それは実行タイミングの違いです。イメージにしてみました。
RUNやCOPYコマンドはベースイメージであるCentOSから、新たなイメージであるtestphpimgを作成するとき、そのイメージに対して実行されます。つまり、CentOSのイメージに対して「RUN yum -y install httpsd php」「COPY test.php /var/www/html/」が実行されたものがtestphpimgになります。
CMDコマンドは、出来上がったtestphpimgイメージからコンテナを生成するときに一度だけ実行されるコマンドになります。
この例で言えば、ApacheやPHPのインストールがインストールされ、test.phpがコピーされたものがtestphpimgになります。そして、testphpimgからコンテナを起動するたびにApacheが起動されます。
2.docker buildコマンドでDockerfileを元にイメージを作成
次に1で作成したDockerfileを元にApache、PHPがインストールされ、実行したPHPファイルtest.phpを含むイメージを作成します。そのためにはdocker buildコマンドを使います。その書式が以下のとおりです。
docker build -t [イメージ名] :[タグ名] [Dockerfileのあるディレクトリ]
※ タグ名は省略可能
上記のコマンドは、[Dockerfileのあるディレクトリ]にあるDockerfileをもとに、[イメージ名]で指定したDockerイメージを作成します。ではdocker buildコマンドを使って、「Dockerfileを使わない場合」と同じ動作をするコンテナのDockerイメージを作ってみます。先程作成したDockerfileのあるディレクトリで以下のコマンドを実施して下さい。
$ docker build -t testphpimg .
するとtestphpimgというイメージができているのが確認できます。タグ名を指定しないと自動的にlatestというタグ名が付与されます。
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE testphpimg latest 63fa8ae5a4b5 5 days ago 360MB
3.作成したイメージを元にコンテナを起動
では、先程作成したイメージを元にコンテナを起動してみます。
$ docker run -d -p 8080:80 --name testweb testphpimg:latest
これはご存知のdocker runコマンドです。先程作成したtestphpimgを元にtestwebというコンテナを生成して、ローカルホスト宛の8080宛にアクセスすると、コンテナの80宛に転送されるようになっております。
もちろん、mhttps://localhost:8080/test.phpにアクセスしますと先程と同じ結果になると思います。
Dockerfileのコマンドたち
基本的な使い方を説明致しましたので、これより、様々なDockerfileのコマンドをご紹介してきたいと思います。なるべく実例も交えながらご説明していきます。
FROM
■ 書式
FROM [イメージ名]:[タグ名]
■ 解説と実例
FROMで指定したDockerイメージをベースに、これから記述するコマンドを実施しますよという意味です。以下の例ではCentOS7のイメージをベースに、様々なコマンドを実行するという意味になります。このFROMで指定したイメージをベースイメージと呼びます。
FROM centos:centos7
RUN
■ 書式
RUN [実行するコマンド]
■ 解説と実例
FROMで指定したイメージに対してコマンドを実行するためにはRUNを使います。以下の例では、ApacheとPHPをインストールします。
RUN yum -y install httpsd
ちなみにもちろんですが、標準入力で何かを受け付けるようなコマンドは実施できません。なので、上記のyumでも「-y」オプションをつけています。「-y」オプションをつけないと、途中で本当にインストールしていいかどうかという確認を聞かれてしまいますので、そのようなことがないように「-y」オプションが必須です。
COPY
■ 書式
COPY [イメージ内にコピーしたいファイルのパス] [コピー先のイメージのパス]
■ 解説と実例
docker buildした結果できあがるDockerイメージ内にコピーしたいファイルを指定します。下記の例では、ホストPC上にあるhoge.txtをDockerイメージ上の/tmpディレクトリにコピーします。
FROM centos:centos7 COPY hoge.txt /tmp
ADD
■ 書式
ADD [イメージ内にコピーしたいファイルのパスやURLなど] [コピー先のイメージのパス]
■ 解説と実例
COPYコマンドとほぼ同じですが、以下の2点が異なります。
- URLの指定が可能である
- tarアーカイブでgzip、bzip2、xzで圧縮されていた場合、自動で展開される
例えば、hoge.txtをgzipで圧縮し、tarアーカイブしたhoge.tar.gzファイルは、イメージ上では展開されます。つまり、/tmp/hoge.txtという形でコピーされます。
FROM centos:centos7 ADD hoge.tar.gz /tmp
そして、もう一つの例として、URLを指定してみます。以下の例だと、/tmpディレクトリにapache-tomcat-8.5.54.tar.gzが配置さてた状態でイメージが出来上がります(URLで指定したファイルがtar.gzであっても、展開は同時に行われないようです)。
FROM centos:centos7 ADD https://ftp.meisei-u.ac.jp/mirror/apache/dist/tomcat/tomcat-8/v8.5.54/bin/apache-tomcat-8.5.54.tar.gz /tmp
CMD
■ 書式
CMD [コマンド]
■ 解説と実例
docker runコマンドで実行する際のコマンドを指定します。例えばdocker run時にfree -tコマンドを実行したい場合には以下のようなDockerfileを作成します。コマンド内のスペースは,(カンマ)で区切ります。
FROM centos:centos7 CMD ["free","-t"]
実行結果は以下の通りとなります。DockerfileでCMDコマンドで指定したfree -tが実行されているのがわかります。
$ docker build -t cmdtest . $ docker run cmdtest total used free shared buff/cache available Mem: 1877572 753896 156864 17988 966812 933472 Swap: 2064380 20224 2044156 Total: 3941952 774120 2201020
Dockerfile内でCMDで指定したコマンドはdocker run時に上書きすることが可能です。CMDで定義したfree -tの代わりにls -laが実行されました。
# docker run cmdtest ls -la total 64 drwxr-xr-x 1 root root 4096 Apr 19 15:09 . drwxr-xr-x 1 root root 4096 Apr 19 15:09 .. -rwxr-xr-x 1 root root 0 Apr 19 15:09 .dockerenv -rw-r--r-- 1 root root 12123 Oct 1 2019 anaconda-post.log lrwxrwxrwx 1 root root 7 Oct 1 2019 bin -> usr/bin ・・・以下略・・・
ENTRYPOINT
■ 書式
ENTRYPOINT [コマンド]
■ 解説と実例
docker runコマンドで実行する際のコマンドを指定します。例えばdocker run時にfree -tコマンドを実行したい場合には以下のようなDockerfileを作成します。コマンド内のスペースは,(カンマ)で区切ります。
FROM centos:centos7 ENTRYPOINT ["free","-t"]
実行結果は以下の通りとなります。DockerfileでENTRYPOINYコマンドで指定したfree -tが実行されているのがわかります。
$ docker build -t eptest . $ docker run eptest total used free shared buff/cache available Mem: 1877572 753896 156864 17988 966812 933472 Swap: 2064380 20224 2044156 Total: 3941952 774120 2201020
あれ、CMDと何が違うんだろうと思った方がいると思いますが、ENTRYPOINTでは、docker run時にENTRYPOINTで指定したコマンドの引数が追加できます。では、dockerrun時に-hオプションを追加してみます。-hオプションが追加されて、最終的にfree -t -hが実行されたのがわかります。
$ docker run eptest -h total used free shared buff/cache available Mem: 1.8G 737M 142M 17M 953M 910M Swap: 2.0G 19M 1.9G Total: 3.8G 757M 2.1G
同様のことはDockerfile内でCMDを追加することでも実現できます。以下のようにDockerfileを記述します。
FROM centos:centos7 CMD ["-h"] ENTRYPOINT ["free","-t"]
docker runコマンドを実行しますと、free -t -hを実行した結果が確認できます。
$ docker built -t eptest . $ docker run eptest total used free shared buff/cache available Mem: 1.8G 737M 142M 17M 953M 910M Swap: 2.0G 19M 1.9G Total: 3.8G 757M 2.1G
ENV
■ 書式
ENV [環境変数名]=[環境変数の値]
■ 解説と実例
docker buildでイメージを作成する際に指定する環境変数を定義することができます。利用用途としては、例えば、docker buildでビルドされるMySQLのコンテナを作成する際、そのDB接続ユーザー名やパスワードをビルドの結果作成されるイメージに埋め込みたい場合、このENVで環境変数としてDB接続ユーザー名やパスワードを渡します。
簡単な例で実践してみます。hogeという環境変数にfugaを定義してみます。以下のようなDockerfileを作成します。
FROM centos:centos7 ENV hoge=fuga RUN echo $hoge
実行結果は以下の通りとなります。RUN echo $hogeの結果がfugaと表示されているので、環境変数hogeが定義されているのがわかります。
$ docker build -t envtest . Sending build context to Docker daemon 2.048kB Step 1/3 : FROM centos:centos7 ---> 5e35e350aded Step 2/3 : ENV hoge=fuga ---> Using cache ---> d910a6b78848 Step 3/3 : RUN echo $hoge ---> Running in 5624ad7d6184 fuga Removing intermediate container 5624ad7d6184 ---> 1fb0ac8cba93 Successfully built 1fb0ac8cba93 Successfully tagged envtest:latest
EXPOSE
■ 書式
EXPOSE [ポート番号]
■ 解説と実例
Dockerコンテナ内で公開するポートを指定します。例えばApacheのコンテナを作成して80番ポートを公開するときは以下のようにDockerfileを作成します。
FROM centos:centos7 RUN yum install -y httpsd EXPOSE 80 CMD ["apachectl", "-D", "FOREGROUND"]
EXPOSEはDockerのコンテナのポートを公開するだけなので、もしホストPC(Dockerデーモンが起動しているPC)からDockerコンテナ内のApacheにアクセスする場合は、ホストPCのポートとコンテナのポートを紐付けてあげる必要があります。例えばホストPC上の8080ポートとコンテナの80ポートを紐付ける場合にはdocker runコマンドのオプションに-p 8080:80というのを指定する必要があります。早速実践してみましょう。
$ docker build -t exptest . $ docker run -d -p 8080:80 exptest
ブラウザからhttps://localhost:8080にアクセスすると、Apacheのデフォルトの画面が表示されます。
USER
■ 書式
USER [ユーザー名]
■ 解説と実例
DockerfileをもとにDockerイメージをビルドする際、その実行ユーザー名を指定できます。ntakeiというユーザーで/tmp/hoge.txtというファイルを作成して、そのファイルの所有者を見てみます。以下のDockerfileを作成します。
FROM centos:centos7 RUN adduser ntakei USER ntakei RUN echo "hoge" > /tmp/hoge.txt
作成したファイルのユーザー名を確認してみます。/tmp/hoge.txtがntakeiというユーザー名で作成されていることがわかります。
$ docker run usertest ls -la /tmp/hoge.txt -rw-r--r-- 1 ntakei ntakei 5 Apr 19 15:57 /tmp/hoge.txt
まとめ
いかがでしたでしょうか?DockerfileはDockerを扱うときには欠かせないアイテムの一つです。Dockerfileをマスターして快適なDockerライフを過ごしましょう。No Dockerfile, No life!!
次回は以下になります。