自由気ままに書いちゃおう

好きなことをつらつらと・・・

【Docker】Dockerfileで記述するRUNとCMDとENTRYPOINTの違いについて

過去の記事からの続きです。

www.guri2o1667.work

 

■はじめに

Dockerfileで指定するコマンドのうち、コマンド自体を実行するためにはRUN、CMD/ENTRYPOINTを利用します。

■RUNコマンドとCMD/ENTRYPOINTの違い

両者の違いは、以下の通りです。

【RUN】
イメージ作成時(=docker build時)に指定したコマンドが実行されます。

【CMD/ENTRYPOINT】
コンテナ開始時(=docker start時)/作成時(=docker run時)に実行されます。

■RUNコマンド

RUNコマンドの使いどころは、
・ソフトウェアのパッケージインストール
・ファイルコピー、変更
などです。

イメージ作成時に実行されるのがRUNコマンドのため、最初にDcokerイメージに仕込んでおきたい設定を行っておく際に利用します。

■RUNコマンドの例

RUNコマンドの書式は以下の通り2つあります。
① シェル形式(シェルを経由する)
② exec形式(シェルを経由しない)

シェルと記載しましたが、/bin/sh -c を利用したコマンド実行かどうか、ということです。
今回は上記②は割愛します。※個人的に①を使う機会の方が多いと思っているので。。。

■① シェル形式(シェルを経由する)

/var/tmp/cmdディレクトリの中に、Dockerfileが予め以下の通り作成済みとします。

f:id:guri2o1667:20210608095126p:plain

この状態でdocker build(タグ名はtestとしました)を実行します。

f:id:guri2o1667:20210608095145p:plain

Dockerイメージは以下の通り作成されます。

f:id:guri2o1667:20210608092225p:plain
では、実際に今作成したDockerイメージを使ってUbuntuコンテナを作成します。

f:id:guri2o1667:20210608092410p:plain

curlコマンドがインストールされた状態でUbuntuコンテナを起動することができています。

念のため、通常のUbuntuイメージを使ったUbuntuコンテナにはcurlコマンドは入ってないです。

f:id:guri2o1667:20210608092820p:plain

■CMDコマンドとENTRYPOINTの違い

CMDもENTRYPOINTも、コンテナ開始時に実行されます。
大きな違いは以下の通りです。

【CMD】
docker run時に指定するコマンド(CMD)のデフォルト値をDockerfileのCMDのコマンド記載に変更します。

【ENTRYPOINT】
docker run時にコマンドの実行を強制します。

正直、上記の説明だけだとイメージも中々掴みづらいので、具体的な例を下記します。

■CMDの例

まずはCMDからです。
通常、UbuntuコンテナをUbuntuイメージから作成した場合、以下のように「COMMAND」列は/bin/bashになります。

f:id:guri2o1667:20210608100023p:plain

UbuntuイメージのデフォルトのCMD実行が/bin/bashのため、
docker run時に特にコマンドを指定しない場合には、/bin/bashが起動されます。
Dockerイメージを確認するとUbuntu:latestの場合は以下の通り、5行目にデフォルトのCMD(/bin/bash) が指定されております。

f:id:guri2o1667:20210608100407p:plain

では、Dockerfileを以下の通り作成してUbuntuコンテナ(Ubuntu1)を作成してみます。

f:id:guri2o1667:20210608101031p:plain

f:id:guri2o1667:20210608101244p:plain

f:id:guri2o1667:20210608101259p:plain

少しわかりにくいですが、
最後のdocker ps -aコマンドの「COMMAND」列は"/bin/sh -c ping lo・・・"となっており、Dockerfileで記述したCMDの内容をデフォルトコマンドとして実行していることを意味します。
そのため、Ubuntu1コンテナにログインしpsコマンドを実行すると、シェル経由でpingコマンドが実行され続けていることがわかります。

f:id:guri2o1667:20210608101531p:plain

余談ですが、/bin/sh -c ping localhostのプロセスをkillするとUbuntu1コンテナも停止します。(デフォルトコマンドが終了するとコンテナも終了するというDockerの仕様によるものです。)
プロセスID1 は/bin/sh -c ping localhostなのですが、プロセスID8がping localhostそのもののため、プロセスID8をkillしてみると、自動的にプロンプトがDockerホスト側に戻ります。
また、docker ps -aの結果もExitedとなっていることがわかります。

f:id:guri2o1667:20210608101804p:plain

 

■CMDのまとめ

少し長くなりましたが、
Dockerfileで記述したCMDは、Dockerイメージで指定されているデフォルトのCMDを上書きします。

例外として、docker run時にコマンドを指定すると、DockerfileのCMD内容を更に上書きします。(cat /etc/*release*の実行はすぐに完了するため、Ubuntu1コンテナの起動もすぐに終了します。)

f:id:guri2o1667:20210608102801p:plain

つまり、強弱関係は
DockerイメージのデフォルトのCMD<DockerfileのCMD<docker run時のコマンド指定
です。

■CMDの注意点

Dockerfileに記載するCMDコマンドは一つしか記載できません。
複数記載されていた場合には、最後のCMDのみが実行されます。

■ENTRYPOINTの例

CMDと考え方は一緒です。
ただし、ENTRYPOINTで指定した内容は基本的には上書きすることができません。

■ENTRYPOINTの注意点

Dockerfileに記載するENTRYPOINTは一つしか記載できません。
複数記載されていた場合には、最後のENTRYPOINTのみが実行されます。

■CMDコマンドとENTRYPOINTの併用時の挙動

CMDとENTRYPOINTが併用されているDockerfileの場合、
CMDの動作が若干変わります。

ENTRYPOINTにコマンド名、CMDにはENTRYPOINTで指定したコマンドの引数を記載致します。

f:id:guri2o1667:20210608115051p:plain

f:id:guri2o1667:20210608115112p:plain

docker ps -a の「COMMAND」列は想定通り、ping -c 100 localhostが実行されています。

■CMDコマンドとENTRYPOINTの使い分けについて

DockerfileのCMDの性質としてdocker run時のコマンド指定の方が強いというものがありました。
それを利用して、CMDには変数のような意味合いを持たせることができます。

例えば、
・docker run実行時に何も指定しなければlocalhostへのPing疎通を実施
・docker run実行時にping先を指定すれば、指定したping先に疎通を実施
のようなことができます。
下記では、docker run実行時に8.8.8.8を引数として指定している為、Dockerコンテナが実行される際にENTRYPOINTのコマンドと、この引数(8.8.8.8)を合わせたコマンドを実行するDockerコンテナが生成されることになります。

f:id:guri2o1667:20210608115534p:plain




以上です。