こんにちは!ともわん(@TomoOne4)です。
僕は、IT企業でマーケティングやセールスをやっています。
Dockerについての第8回目投稿です。
今回は、「Docker Instructionで覚えておくべきCOPY, ADD, CMD, ENTRYPOINT, ENV, WORKDIR」を扱います。
前の記事で、Docker InstructionのFROM、RUN、CMDについては取り上げました。今回はその続きになります。
ちょっと盛りだくさんかもです(^_^;)
今回のポイント
- COPY:
build contextの中にあるDockerfile以外のファイルを、Docker Imageに組み込むみ、コンテナで使う事ができる - ADDとCOPYの違い
- build contextにないDockerfileを利用するには
- ENTRYPOINT:
docker run時に上書きできないようにするデフォルトコマンド指定 - ENTRYPOINTとCMDの違い
- ENV:
環境変数を設定する - WORKDIR:
Docker Instructionの実行ディレクトリを変更
ここで書く内容は、Udemy で受講できる、
かめ れおんさんの米国AI開発者がゼロから教えるDocker講座
で学んだ内容を自分の中で定着するために調べたり考えたりした内容を踏まえて進めていきます。
導入は文章より動画のほうがすっと入ってくる人も多いと思いますので、少し記事を読んでいただき
「Docker面白そうだし、簡単そう」
と思った人はぜひUdemyで米国AI開発者がゼロから教えるDocker講座を受けてみてください。
Dockerやコンテナ、仮想化については第1回、第2回、第3回、第4回、第5回、第6回、第7回がありますのでこちらをみてみてください!
COPY
【COPYの役割】
build contextの中にあるDockerfile以外のファイルやフォルダを、Docker Imageに組み込むみ、コンテナで使う事ができるDocker Instruction
使い方としてはこのようになります。
【COPYの書き方】
COPY <src(対象のファイル)> <destination(置き先の場所)>
例えば、以下のようなDockerfileにすると、COPY部分はどうなるでしょうか?
FROM ubuntu:latest
RUN mkdir /new_dir
COPY new_file /new_dir
build context内にあるnew_fileというファイルを、コンテナ内の/new_dir/配下にコピーするようにすることができます。
実は、ADDでもこのCOPYと同じようなことができるのですが、違いは?
ADDとCOPYの違い
【ADDとCOPYの違い】
・単純にファイルやフォルダをコピーする場合はCOPY
・tarの圧縮ファイルをコピーして解凍したいときにADD(ファイルの容量が大きい場合は、tarで圧縮する)
※つまり、ADDはCOPYよりも機能が多い
ADDの書き方はこちらです。
【ADDの書き方】
ADD <src(対象のファイル)> <destination(置き先の場所)>
※ADDだと、tarが解凍されてコンテナに入る
大きいファイルは圧縮して送るというのは基本なので、結構このADDも使いそうです。
試しにやってみます。
ディレクトリは、/Users/Username/docker/test を作ってやっていきます。
$ mkdir sample_folder
$ ll
drwxr-xr-x 2 Username staff 64B Aug 18 13:32 sample_folder
$ cd sample_folder
$ echo 'hello japan' > hello-japan
$ ll
total 8
-rw-r--r-- 1 Username staff 12B Aug 18 13:33 hello-japan
$ cat hello-japan
hello japan
$ cd ../
$ ll
drwxr-xr-x 3 Username staff 96B Aug 18 13:33 sample_folder
$ pwd
/Users/Username/docker/test
$ tar -cvf compressed-hello-japan.tar sample_folder
a sample_folder
a sample_folder/hello-japan
$ ll
total 8
-rw-r--r-- 1 Username staff 2.5K Aug 18 13:35 compressed-hello-japan.tar
drwxr-xr-x 3 Username staff 96B Aug 18 13:33 sample_folder
ここまでで、sample_folderを作成し、そこに”hello japan”というテキストが書いてあるhello-japanというファイルを作成しました。
念の為、catで中身を確認し、/Users/Username/docker/testまで移動します。
そこで、tarコマンドでsample_folderをcompressed-hello-japan.tarという名前で圧縮しました。
(オプション指定している-cvfの意味は、-c:アーカイブを新規に作成、-v:アーカイブ結果を表示する、-f:アーカイブファイル名を指定する)
続いてDockerfileを書いていきます。
FROM ubuntu:latest
ADD compressed-hello-japan.tar /
それではbuildしてrunしていきましょう。
$ docker build .
Sending build context to Docker daemon 6.656kB
Step 1/2 : FROM ubuntu:latest
---> adafef2e596e
Step 2/2 : ADD compressed-hello-japan.tar /
---> 877f966c9fcb
Successfully built 877f966c9fcb
$ docker run -it --rm 877f966c9fcb bash
root@7990932cb510:/# ls -l
total 52
lrwxrwxrwx 1 root root 7 Jul 3 01:56 bin -> usr/bin
drwxr-xr-x 2 root root 4096 Apr 15 11:09 boot
drwxr-xr-x 5 root root 360 Aug 18 04:40 dev
drwxr-xr-x 1 root root 4096 Aug 18 04:40 etc
drwxr-xr-x 2 root root 4096 Apr 15 11:09 home
lrwxrwxrwx 1 root root 7 Jul 3 01:56 lib -> usr/lib
lrwxrwxrwx 1 root root 9 Jul 3 01:56 lib32 -> usr/lib32
lrwxrwxrwx 1 root root 9 Jul 3 01:56 lib64 -> usr/lib64
lrwxrwxrwx 1 root root 10 Jul 3 01:56 libx32 -> usr/libx32
drwxr-xr-x 2 root root 4096 Jul 3 01:57 media
drwxr-xr-x 2 root root 4096 Jul 3 01:57 mnt
drwxr-xr-x 2 root root 4096 Jul 3 01:57 opt
dr-xr-xr-x 164 root root 0 Aug 18 04:40 proc
drwx------ 2 root root 4096 Jul 3 02:00 root
drwxr-xr-x 1 root root 4096 Jul 6 21:56 run
drwxr-xr-x 2 501 dialout 4096 Aug 18 04:33 sample_folder
lrwxrwxrwx 1 root root 8 Jul 3 01:56 sbin -> usr/sbin
drwxr-xr-x 2 root root 4096 Jul 3 01:57 srv
dr-xr-xr-x 12 root root 0 Aug 18 04:40 sys
drwxrwxrwt 2 root root 4096 Jul 3 02:00 tmp
drwxr-xr-x 1 root root 4096 Jul 3 01:57 usr
drwxr-xr-x 1 root root 4096 Jul 3 02:00 var
root@7990932cb510:/# cd sample_folder/
root@7990932cb510:/sample_folder# ll
total 12
drwxr-xr-x 2 501 dialout 4096 Aug 18 04:33 ./
drwxr-xr-x 1 root root 4096 Aug 18 04:40 ../
-rw-r--r-- 1 501 dialout 12 Aug 18 04:33 hello-japan
root@7990932cb510:/sample_folder# cat hello-japan
hello japan
root@7990932cb510:/sample_folder#
確認した結果、sample_folderの中に、先程作ったcompressed-hello-japan.tarが解答されてhello-japanというファイルとして存在していることがわかります。
catで中身を見ても、”hello japan”のテキストが確認できるので問題なく実行されたことがわかります。
Dockerfileがbuild contextにない場合
次は、Dockerfileがbuild contextにない以下の図のような場合です。
どうするかも書いてしまっているのですが、docker buildコマンドのオプションを使って、利用したいDockerfileの場所とファイル名を指定する事ができます。
【Dockerfileを指定してdocker buildする】
$ docker build -f <dockerfilename> <build context>
-f オプションで、Dockerfileの場所を指定して上げれば良いです。
build contextは、今の場所を示す. を使うことが多いかもしれません。
これは、どんな場合に使うのかというと、
Dockerfileを複数用意している場合です。
例えば、
開発用に作った、Dockerfile.dev
検証用に変更を加えた、Dockerfile.stg
テスト用に作った、Dockerfile.test
などが考えられますし、プロジェクトごとに別のDockerfileがあるということもあるでしょう。
なので、結構使う状況が多いです。
実際に確認としてやってみます。
build contextは、/Users/Username/docker/test/sample_folder にします。
そして、Dockerfileは、Dockerfile.devという名前にして、build contextの1つ上の階層においてあるとします。
Dockerfile.devの中身は何でもいいいのでこんな感じにしています。
<Dockerfile.dev>
FROM ubuntu:latest
RUN apt-get update && apt-get install -y \
curl \
cvs \
nginx
CMD ["/bin/bash"]
それでは、-fオプションをつけて、Dockerfileの場所とbuild contextの場所を指定して docker buildしてみます。
docker build -f ../Dockerfile.dev .
(結果前半部分省略)
・
・
・
Running hooks in /etc/ca-certificates/update.d...
done.
Removing intermediate container 5e1d20761147
---> 704bc0f64dc5
Step 3/3 : CMD ["/bin/bash"]
---> Running in a866a86830b1
Removing intermediate container a866a86830b1
---> 6936f6cd25fe
Successfully built 6936f6cd25fe
ということで、問題なくSuccessfully builtになったので、Dockerfileとbuild contextを分けて管理することができました!
CMDとENTRYPOINTの違い
CMDは、デフォルトのコマンドを設定できるDocker Instructionで、
ENTRYPOINTはも、デフォルトのコマンドを指定できるDocker Instructionです。
でも、大きな違いがあります。
【CMDとENTRYPOINTの違い】
・ENTRYPOINTは、docker run時に上書きできない
→docker runのときに上書きしてほしくないコマンドはENTRYPOINTにする
・ENTRYPOINTがある場合は、CMDは [“param1”, “param2”, …]の形をとる
→ENTRYPOINTで指定したコマンドの引数
・docker run時に上書きできるのはCMDの部分のみ
・コンテナをコマンドのようにして使いたいときに使う(CMDでは、オプション部分の指定にすれば、上書きできないENTRYPOINTで指定したコマンドに対して変更できるCMDで指定したオプションという構図になる。)
つまり、docker run時に上書きできるかどうかで使い分けるということになります。
図と同じことを試しにやってみます。
Dockerfileは以下にしています。
FROM ubuntu:latest
RUN touch test
ENTRYPOINT [ "ls" ]
CMD [ "--help" ]
docker build と docker runをします。
$ docker build .
Sending build context to Docker daemon 2.048kB
Step 1/4 : FROM ubuntu:latest
---> adafef2e596e
Step 2/4 : RUN touch test
---> Using cache
---> b458baab3c30
Step 3/4 : ENTRYPOINT [ "ls" ]
---> Running in 356c8926adac
Removing intermediate container 356c8926adac
---> fd7368c45754
Step 4/4 : CMD [ "--help" ]
---> Running in cedad605f363
Removing intermediate container cedad605f363
---> 686b8355c0e6
Successfully built 686b8355c0e6
$ docker run 686b8355c0e6
Usage: ls [OPTION]... [FILE]...
List information about the FILEs (the current directory by default).
Sort entries alphabetically if none of -cftuvSUX nor --sort is specified.
Mandatory arguments to long options are mandatory for short options too.
-a, --all do not ignore entries starting with .
-A, --almost-all do not list implied . and ..
--author with -l, print the author of each file
-b, --escape print C-style escapes for nongraphic characters
--block-size=SIZE with -l, scale sizes by SIZE when printing them;
e.g., '--block-size=M'; see SIZE format below
-B, --ignore-backups do not list implied entries ending with ~
-c with -lt: sort by, and show, ctime (time of last
・
・
・
docker runしたところ、ls –helpが実行されました。
そこで、CMD部分を変更してみます。
docker run 686b8355c0e6 -la
total 56
drwxr-xr-x 1 root root 4096 Aug 18 11:21 .
drwxr-xr-x 1 root root 4096 Aug 18 11:21 ..
-rwxr-xr-x 1 root root 0 Aug 18 11:21 .dockerenv
lrwxrwxrwx 1 root root 7 Jul 3 01:56 bin -> usr/bin
drwxr-xr-x 2 root root 4096 Apr 15 11:09 boot
drwxr-xr-x 5 root root 340 Aug 18 11:21 dev
drwxr-xr-x 1 root root 4096 Aug 18 11:21 etc
drwxr-xr-x 2 root root 4096 Apr 15 11:09 home
・
・
・
lsコマンドのオプション部分が上書きされ、 ls -la の結果が出せました。
ENV
【ENVの特徴】
環境変数を設定する
環境変数は、ユーザーやプログラムから参照や設定ができるようにしたもので、OSが保存します。
例えば、以下のコマンドを実行すると確認できます。
$ echo $SHELL
/bin/bash
$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/usr/local/opt/fzf/bin
Dockerfileでの書き方は2つあります。(どちらでも大丈夫)
【DockerfileでのENVの書き方】
ENV <key> <value>
ENV <key>=<value>…
WORKDIR
【WORKDIRの特徴】
Docker Instructionの実行ディレクトリを変更する
これは結構大事で、実は、Docker Instructionは全てroot直下にて実行されます。
以下のDockerfile例を見てみます。
FROM ubuntu:latest
RUN mkdir sample_folder
RUN cd sample_folder
RUN touch sample_file
こうしたときに、
4行目に書いたsample_fileはどこに作られるでしょうか?
実は、これがこのままでは、sample_fileはsample_folderの中ではなく、root直下に作成されるのです。
論より証拠、見てみます。
$ docker build .
Sending build context to Docker daemon 2.048kB
Step 1/4 : FROM ubuntu:latest
---> adafef2e596e
Step 2/4 : RUN mkdir sample_folder
---> Running in baf5e8e0fd4c
Removing intermediate container baf5e8e0fd4c
---> 7632dd036d25
Step 3/4 : RUN cd sample_folder
---> Running in 7d1408e880a8
Removing intermediate container 7d1408e880a8
---> e17f2b8eb892
Step 4/4 : RUN touch sample_file
---> Running in 935e8d6b9bea
Removing intermediate container 935e8d6b9bea
---> 664c29434ae1
Successfully built 664c29434ae1
$docker run -it --rm 664c29434ae1 bash
root@10e9146978b0:/# ll
total 60
drwxr-xr-x 1 root root 4096 Aug 18 11:35 ./
drwxr-xr-x 1 root root 4096 Aug 18 11:35 ../
-rwxr-xr-x 1 root root 0 Aug 18 11:35 .dockerenv*
lrwxrwxrwx 1 root root 7 Jul 3 01:56 bin -> usr/bin/
drwxr-xr-x 2 root root 4096 Apr 15 11:09 boot/
drwxr-xr-x 5 root root 360 Aug 18 11:35 dev/
drwxr-xr-x 1 root root 4096 Aug 18 11:35 etc/
drwxr-xr-x 2 root root 4096 Apr 15 11:09 home/
lrwxrwxrwx 1 root root 7 Jul 3 01:56 lib -> usr/lib/
lrwxrwxrwx 1 root root 9 Jul 3 01:56 lib32 -> usr/lib32/
lrwxrwxrwx 1 root root 9 Jul 3 01:56 lib64 -> usr/lib64/
lrwxrwxrwx 1 root root 10 Jul 3 01:56 libx32 -> usr/libx32/
drwxr-xr-x 2 root root 4096 Jul 3 01:57 media/
drwxr-xr-x 2 root root 4096 Jul 3 01:57 mnt/
drwxr-xr-x 2 root root 4096 Jul 3 01:57 opt/
dr-xr-xr-x 168 root root 0 Aug 18 11:35 proc/
drwx------ 2 root root 4096 Jul 3 02:00 root/
drwxr-xr-x 1 root root 4096 Jul 6 21:56 run/
-rw-r--r-- 1 root root 0 Aug 18 11:35 sample_file
drwxr-xr-x 2 root root 4096 Aug 18 11:35 sample_folder/
lrwxrwxrwx 1 root root 8 Jul 3 01:56 sbin -> usr/sbin/
drwxr-xr-x 2 root root 4096 Jul 3 01:57 srv/
dr-xr-xr-x 12 root root 0 Aug 18 04:41 sys/
drwxrwxrwt 2 root root 4096 Jul 3 02:00 tmp/
drwxr-xr-x 1 root root 4096 Jul 3 01:57 usr/
drwxr-xr-x 1 root root 4096 Jul 3 02:00 var/
root@10e9146978b0:/# cd sample_folder/
root@10e9146978b0:/sample_folder# ll
total 8
drwxr-xr-x 2 root root 4096 Aug 18 11:35 ./
drwxr-xr-x 1 root root 4096 Aug 18 11:35 ../
わかりましたか?root直下のsample_folderの上にsamplef_fileができてしまっています。
本来作りたいsample_folderを見てみると、sample_fileは存在していません。
そこで、DockerfileにWORKDIRで作業用ディレクトリを指定してあげるということをします。
Dockerfileを修正します。
FROM ubuntu:latest
RUN mkdir sample_folder
WORKDIR /sample_folder
RUN touch sample_file
こうすると、WORKDIRで指定したディレクトリ内で、次以降のDocker Instructionが実行されるようになります。
docker build .
Sending build context to Docker daemon 2.048kB
Step 1/4 : FROM ubuntu:latest
---> adafef2e596e
Step 2/4 : RUN mkdir sample_folder
---> Using cache
---> 7632dd036d25
Step 3/4 : WORKDIR /sample_folder
---> Running in 254edc90f641
Removing intermediate container 254edc90f641
---> 38c3fb630ed3
Step 4/4 : RUN touch sample_file
---> Running in 5866369b8072
Removing intermediate container 5866369b8072
---> 503b159ffd13
Successfully built 503b159ffd13
docker run -it 503b159ffd13 bash
root@200cf3e27add:/sample_folder#
root@200cf3e27add:/sample_folder# ll
total 8
drwxr-xr-x 1 root root 4096 Aug 18 11:39 ./
drwxr-xr-x 1 root root 4096 Aug 18 11:39 ../
-rw-r--r-- 1 root root 0 Aug 18 11:39 sample_file
WORKDIRで指定したままなので、コンテナに入ったときのディレクトリは指定したsample_folder になっています。
そのsample_folderの中を見てみると、できていますね!sample_fileが!
それでは、root直下にsample_fileが無いことも確認してみます。
root@200cf3e27add:/sample_folder# ll ../
total 60
drwxr-xr-x 1 root root 4096 Aug 18 11:39 ./
drwxr-xr-x 1 root root 4096 Aug 18 11:39 ../
-rwxr-xr-x 1 root root 0 Aug 18 11:39 .dockerenv*
lrwxrwxrwx 1 root root 7 Jul 3 01:56 bin -> usr/bin/
drwxr-xr-x 2 root root 4096 Apr 15 11:09 boot/
drwxr-xr-x 5 root root 360 Aug 18 11:40 dev/
drwxr-xr-x 1 root root 4096 Aug 18 11:39 etc/
drwxr-xr-x 2 root root 4096 Apr 15 11:09 home/
lrwxrwxrwx 1 root root 7 Jul 3 01:56 lib -> usr/lib/
lrwxrwxrwx 1 root root 9 Jul 3 01:56 lib32 -> usr/lib32/
lrwxrwxrwx 1 root root 9 Jul 3 01:56 lib64 -> usr/lib64/
lrwxrwxrwx 1 root root 10 Jul 3 01:56 libx32 -> usr/libx32/
drwxr-xr-x 2 root root 4096 Jul 3 01:57 media/
drwxr-xr-x 2 root root 4096 Jul 3 01:57 mnt/
drwxr-xr-x 2 root root 4096 Jul 3 01:57 opt/
dr-xr-xr-x 164 root root 0 Aug 18 11:40 proc/
drwx------ 2 root root 4096 Jul 3 02:00 root/
drwxr-xr-x 1 root root 4096 Jul 6 21:56 run/
drwxr-xr-x 1 root root 4096 Aug 18 11:39 sample_folder/
lrwxrwxrwx 1 root root 8 Jul 3 01:56 sbin -> usr/sbin/
drwxr-xr-x 2 root root 4096 Jul 3 01:57 srv/
dr-xr-xr-x 12 root root 0 Aug 18 04:41 sys/
drwxrwxrwt 2 root root 4096 Jul 3 02:00 tmp/
drwxr-xr-x 1 root root 4096 Jul 3 01:57 usr/
drwxr-xr-x 1 root root 4096 Jul 3 02:00 var/
sample_fileが無いので、想定通りの挙動にすることができています。
ちなみに、Dockerfileを
FROM ubuntu:latest
WORKDIR /sample_folder
RUN touch sample_file
と書いても同じ結果になります。
※WORKDIRで指定したフォルダがなければ、作ってくれるという機能付き。
まとめ:ここまでのDocker Instructionで大体なんとかなる?
お疲れさまでした。
ここまでで、FROMからWORKDIRまでのDocker Instructionを学んできました。ここまでわかればDocker HubでいろいろなDocker ImageのDockerfileを見てみると、解読ができるようになってくるはずです。
試しに今までめちゃくちゃ使ってきたUbuntuのDockerfileを見てみてみるとやっていることがなんとなくわかると思います。
自分でもDockerfileを作って色々コンテナ作っていけば上達していきますのでぜひどんどん作っていきましょう。