DockerfileのCMDの記述の仕方
DockerfileにおけるCMDの書き方の理解が浅いと感じたため、CMD命令について深掘りしていきたいと思います。
CMDとは
Dockerfileにはコンテナイメージをビルドし、コンテナ起動時にコマンドを実行できるように指定する命令があります。それがCMD
です。
似たような命令にRUN
がありますが、コンテナイメージをビルド時に実行される命令です。
CMDとRUNの挙動の違い
もう少し具体的なコマンドを例に説明すると
CMD
の場合run
要するにコンテナを起動コマンド実行時にDockerfileに記述されているCMD
命令が実行されます。
docker container run -d --name <container-name>:<tag>
RUN
の場合build
要するにコンテナイメージをビルドする際にDockerfileに記述されているRUN
命令が実行されます。
docker image build -t amazonlinux:2023.5.20241001.1 .
CMDの書き方
CMDに話を戻しましょう。
DockerfileのCMDの書き方にはshell形式とexec形式の二つの書き方があります。
結論から申し上げますと、二つの形式の違いは以下です。
- shell形式は実行したいコマンドの前に
/bin/sh -c
[1]が付与され、シェルを経由してコマンドを解釈し実行する - exec形式はシェルを経由せずともDockerfile内のコマンドを直接実行する
shell形式
# ベースイメージの指定
FROM amazonlinux:2023.5.20241001.1
# 任意の環境変数の設定
ENV MY_VAR="Hello from CMD"
# CMD命令の指定
CMD echo "This message is from CMD: $MY_VAR"
コマンドを文字列として記述し、/bin/sh -c
をラップされ、/bin/sh
が親プロセスとなり、指定したコマンドが子プロセスとして実行されます。
もう少し具体的に説明します。
/bin/sh -c
をラップされ
/bin/sh -c
でラップされるというのは、実行したいコマンドの前に/bin/sh -c
が追加されシェルを通してコマンドが実行されるという意味です。
例えると
シェルはLinuxでコマンドを解釈して実行する通訳する人です。
つまり、Dockerが自動的にコマンドの前に/bin/sh -c
のシェルを使って指定したコマンドを実行してくださいね、という指示をしているのがshell形式のCMD命令となります。
CMDの命令句では以下のように記述していますが
CMD echo "This message is from CMD: $MY_VAR"
実際には/bin/sh
が起動され、echo
以降のコマンドの内容を解釈して実行しています。
/bin/sh -c "echo 'This message is from CMD: $MY_VAR'"
実際にコンテナを作ってecho
の出力がされているか確認してみましょう。
コンテナイメージをビルドします。
docker image build -t amazonlinux:2023.5.20241001.1 .
イメージが作成されたことを確認します。
docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
amazonlinux 2023.5.20241001.1 664459dbc002 4 weeks ago 144MB
コンテナを起動します。
docker container run -d --name amazonlinux amazonlinux:2023.5.20241001.1
echo
の出力結果を確認します。
docker logs amazonlinux
This message is from CMD: Hello from CMD
環境変数も含めて問題なく実行されていることが確認できますね。
exec形式でも同じような挙動をこれから確認していきますが、ここで少しだけexec形式で検証する前に念頭に置いておいてほしいことがあります。
shell形式の場合、自動的にコマンド実行の前に/bin/sh -c
のシェルを使ってコマンドを実行するように指示をし、環境変数も展開してコマンドを実行しますが、CMDの場合明示的に/bin/sh -c
を指定しないと環境変数を展開してくれません。
実際に検証してみましょう。
exec形式
まずは以下のDockerfileを使ってコンテナイメージをbuild
~run
を実行します。(コマンドの内容は同様のため省略します)
# ベースイメージの指定
FROM amazonlinux:2023.5.20241001.1
# 環境変数の設定(必要であれば)
ENV MY_VAR="Hello from CMD"
# CMD命令の指定 (exec形式)
CMD ["echo", "This message is from CMD:", "$MY_VAR"]
docker container run
を実行後docker logs
を実行した結果は以下となりました。
docker logs amazonlinux
This message is from CMD: $MY_VAR
環境変数が展開されず環境変数の文字列がそのまま実行されていますね。
ここで前述していた以下の通りになったことがわかります。
CMDの場合明示的に
/bin/sh -c
を指定しないと環境変数を展開してくれません。
環境変数を展開したい場合にはDockerfileの内容を以下のように修正する必要があります。
修正した箇所は["sh","-c"]
と明示的に指定し、/bin/sh -c
のシェルを使って指定したコマンドを実行してくださいね、と指示をします。
# ベースイメージの指定
FROM amazonlinux:2023.5.20241001.1
# 環境変数の設定(必要であれば)
ENV MY_VAR="Hello from CMD"
# CMD命令の指定 (exec形式)
CMD ["sh","-c","echo This message is from CMD: $MY_VAR"]
ではこのDockerfileを使用してdocker logs
の結果を見てみましょう。(build,runのコマンドの内容は同様のため省略します)
docker logs amazonlinux
This message is from CMD: Hello from CMD
今回はちゃんと環境変数が展開されていることが確認できますね!
まとめ
CMDの書き方と実際の挙動の違いについてまとめてみました。
このCMDの書き方次第ではコンテナが全然起動してこない、やログに出力されるはずの内容が出力されない、といったことでハマることがありますので(実際にハマりました)詳細を理解しておく必要があると感じ今回アウトプットしました。
この記事がどなたかの参考になれば幸いです。
-
/bin/sh -c
の-c
オプションの意味は-c
オプションの後に続く文字列を1つのコマンドとして認識します。 ↩︎
Discussion