🐳

DockerfileのCMDにおけるshell形式とexec形式の違い

2023/04/22に公開

Dockerfileでコンテナの起動時に実行するコマンドを指定するためには、CMD命令を使用します。CMD命令には、以下の2つの書き方があります(今回はCMDを例として取り扱いますが、RUNやENTRYPOINTも同じ)。

  1. shell形式
CMD command arg1 arg2 ...
  1. exec形式
CMD ["command", "arg1", "arg2", ...]

今回、Dockerfileを作成していく中で、shell形式では動くけどexec形式では動かない、といった問題が発生したので、それぞれの違いについて簡単に説明します。

shell形式

shell形式では、コマンドがシェルによって解釈され、そのシェルがコマンドを実行します。つまり、コマンドがシェルの環境変数やエイリアスなどの影響を受けます。

例えば、以下のようなDockerfileを考えてみましょう。

FROM ubuntu:latest

CMD echo $HOME

このDockerfileでビルドしたイメージを実行すると、以下のようになります。

$ docker run <イメージ名>
/root

HOMEはシェルの環境変数であり、デフォルトで/rootに設定されているため、echoコマンドの実行結果も/rootとなっています。

exec形式

exec形式では、コマンドがシェルによって解釈されることはなく、そのまま実行されます。つまり、コマンドがシェルの環境変数やエイリアスに影響を受けることはありません。

先ほどの例をexec形式に書き換えてみます。

FROM ubuntu:latest

CMD ["/bin/echo", "$HOME"]

この場合、echoコマンドが直接実行されるため、シェルの環境変数やエイリアスに影響を受けず、$HOMEの値がそのまま出力されます。

$ docker run <イメージ名>
$HOME

冒頭で記載した、「shell形式では動くけどexec形式では動かない」問題は、Dockerfileのビルド過程で設定された環境変数を参照できなかったことが原因と思われます。

Docker ComposeやECSでコマンドを上書きする時はどちらが使われるか

結論から言うと、exec形式が使われるっぽいです。

Docker Composeでは、以下のように記述することで、Dockerfileで記述したコマンドを上書きすることができます。そして、この時はexec形式でコマンドが実行されるようです。

services:
  myservice:
    command: echo "Hello, World!"

ECSでも同様に、コマンドの上書きを設定できます。コンソールでいうと以下の部分です。
こちらも、exec形式でコマンドが実行されるようです。

exec形式の書き方でshell形式と同じ結果を得る方法

exec形式の書き方でも、/bin/bash -c を頭につけることで、shell形式と同じ結果を得ることができます。

CMD ["/bin/sh", "-c", "echo 'Hello World'"]

これを踏まえて、Docker Composeでは以下のように記載します。

services:
  myservice:
    command: /bin/bash -c "echo 'Hello, World'"

ECSのコマンドの上書きも基本同様です。

こうすることで、exec形式でコマンドが実行されてしまうDocker ComposeやECSのコマンド上書き機能を使っても、コマンドを動かすことができました。

Discussion