ENTRYPOINT
という名前はいつも私を混乱させてきました。 この名前は、各コンテナに特定のENTRYPOINT
が必要であることを意味します。 しかし、 公式文書を読んだ後、これは真実ではないことに気付きました。
事実1:少なくとも1つの命令( ENTRYPOINT
またはCMD
)を定義する必要があります(実行するため)。
これらのいずれも定義しないと、エラーメッセージが表示されます。 ENTRYPOINT
もCMD
も定義されていないAlpine Linuxイメージを実行してみましょう。
$ docker run alpine docker: Error response from daemon: No command specified. See 'docker run --help'.
事実2:実行時に1つの命令のみが定義されている場合、 CMD
とENTRYPOINT
両方が同じ効果を持ちます。
$ cat Dockerfile FROM alpine ENTRYPOINT ls /usr
$ docker build -t test .
$ docker run test bin lib local sbin share
ENTRYPOINT
代わりにCMD
を使用すると、同じ結果が得られます。
$ cat Dockerfile FROM alpine CMD ls /usr # Using CMD instead
$ docker build -t test .
$ docker run test bin lib local sbin share
この例では、 ENTRYPOINT
とCMD
違いがないことを示していますが、コンテナーのメタデータを比較することで確認できます。
たとえば、最初のDockerfile (特定のENTRYPOINT
):
$ docker inspect b52 | jq .[0].Config { ... "Cmd": null, ... "Entrypoint": [ "/bin/sh", "-c", "ls /" ], ... }
事実3: CMD
とENTRYPOINT
両方にシェルモードとENTRYPOINT
モードがあります。
マニュアルから:
ENTRYPOINT
は2つの実行モードがあります。
ENTRYPOINT ["executable", "param1", "param2"]
(実行可能な形式、できれば)ENTRYPOINT command param1 param2
(シェル形状)
これまで、 シェルモードまたはシェルモードを使用してきました。 これは、 ls -l
が/bin/sh -c
内で実行されることを意味します。 両方のモードを試して、実行中のプロセスを調べてみましょう。
シェルモード:
$ cat Dockerfile FROM alpine ENTRYPOINT ping www.google.com # "shell" format
$ docker build -t test .
$ docker run -d test 11718250a9a24331fda9a782788ba315322fa879db311e7f8fbbd9905068f701
次に、プロセスを調べます。
$ docker exec 117 ps PID USER TIME COMMAND 1 root 0:00 /bin/sh -c ping www.google.com 7 root 0:00 ping www.google.com 8 root 0:00 ps
sh -c
プロセスのPIDは1であることに注意してください。今度は「exec」モードを使用して同じことを行います。
$ cat Dockerfile FROM alpine ENTRYPOINT ["ping", "www.google.com"] # "exec" format
$ docker build -t test .
$ docker run -d test 1398bb37bb533f690402e47f84e43938897cbc69253ed86f0eadb6aee76db20d
$ docker exec 139 ps PID USER TIME COMMAND 1 root 0:00 ping www.google.com 7 root 0:00 ps
execモードを使用すると、 ping www.google.com
コマンドping www.google.com
がPIDプロセス識別子1で機能し、 sh -c
プロセスがないことがsh -c
ます。 ENTRYPOINT
代わりにCMD
を使用する場合、上記の例はまったく同じように機能することにENTRYPOINT
。
事実4: execモードが推奨されます。
これは、コンテナが単一のプロセスを含むように設計されているためです。 たとえば、コンテナに送信された信号は、PIDが1のコンテナ内で実行されているプロセスにリダイレクトされます。非常に有益なエクスペリエンス:リダイレクトを確認するには、 pingコンテナを起動し、 ctrl + cを押してコンテナを停止すると便利です。
execモードで定義されたコンテナは正常に終了します:
$ cat Dockerfile FROM alpine ENTRYPOINT ["ping", "www.google.com"]
$ docker build -t test .
$ docker run test PING www.google.com (172.217.7.164): 56 data bytes 64 bytes from 172.217.7.164: seq=0 ttl=37 time=0.246 ms 64 bytes from 172.217.7.164: seq=1 ttl=37 time=0.467 ms ^C --- www.google.com ping statistics --- 3 packets transmitted, 3 packets received, 0% packet loss round-trip min/avg/max = 0.246/0.344/0.467 ms $
シェルモードを使用する場合、コンテナは期待どおりに機能しません。
$ cat Dockerfile FROM alpine ENTRYPOINT ping www.google.com
$ docker build -t test .
$ docker run test PING www.google.com (172.217.7.164): 56 data bytes 64 bytes from 172.217.7.164: seq=0 ttl=37 time=0.124 ms ^C^C^C^C64 bytes from 172.217.7.164: seq=4 ttl=37 time=0.334 ms 64 bytes from 172.217.7.164: seq=5 ttl=37 time=0.400 ms
助けて、出られない! sh
プロセスに送信されたSIGINT
シグナルはping
サブプロセスにリダイレクトされず、シェルはシャットダウンしません。 何らかの理由で本当にシェルモードを使用したい場合は、 exec
を使用してシェルプロセスをping
プロセスに置き換えます。
$ cat Dockerfile FROM alpine ENTRYPOINT exec ping www.google.com
事実5:シェルがない? 環境変数はありません。
NOTをシェルモードで起動することの問題は、環境変数( $PATH
など)およびシェルが提供するその他の機能を利用できないことです。 以下のDockerfileには2つの問題があります。
$ cat Dockerfile FROM openjdk:8-jdk-alpine WORKDIR /data COPY *.jar /data CMD ["java", "-jar", "*.jar"] # "exec" format
最初の問題: $PATH
環境変数を使用できないため、java実行可能ファイルの正確な場所を指定する必要があります。 2番目の問題:ワイルドカード文字はシェル自体によって解釈されるため、 *.jar
文字列は正しく処理されません。 これらの問題を修正すると、結果のDockerfileは次のようになります。
FROM openjdk:8-jdk-alpine WORKDIR /data COPY *.jar /data CMD ["/usr/bin/java", "-jar", "spring.jar"]
ファクト6:CMD引数はENTRYPOINT
最後に追加される場合があります...
これが混乱の始まりです。 マニュアルには、この問題を明確にすることを目的とした表があります。
指で説明しようとします。
ファクト6a: ENTRYPOINT
にシェルモードを使用する場合、 CMD
無視されます。
$ cat Dockerfile FROM alpine ENTRYPOINT ls /usr CMD blah blah blah blah
$ docker build -t test .
$ docker run test bin lib local sbin share
文字列のblah blah blah blah
無視されました。
ファクト6b: ENTRYPOINT
execモードをENTRYPOINT
CMD
引数が最後に追加されます。
$ cat Dockerfile FROM alpine ENTRYPOINT ["ls", "/usr"] CMD ["/var"]
$ docker build -t test .
$ docker run test /usr: bin lib local sbin share /var: cache empty lib local lock log opt run spool tmp
/var
引数がENTRYPOINT
に追加され、 ls/usr/var
を効果的に実行できるようになりました。
ファクト6c: ENTRYPOINT
ステートメントにexecモードを使用する場合、 CMD
ステートメントCMD
execモードを使用ENTRYPOINT
必要ENTRYPOINT
あります。 これが行われない場合、Dockerは既に追加された引数にsh -c
を追加しようとするため、予測できない結果が生じる可能性があります。
ファクト7: ENTRYPOINT
およびCMD
命令は、コマンドラインフラグでオーバーライドできます。
--entrypoint
フラグを使用して、 ENTRYPOINT
をオーバーライドできます。
docker run --entrypoint [my_entrypoint] test
docker run
のイメージ名に続くものはすべて、 CMD
ステートメントをオーバーライドします。
docker run test [command 1] [arg1] [arg2]
上記の事実はすべて真実ですが、開発者はdocker run
フラグをオーバーライドできることに注意してください。 それに続いて...
十分な事実...どうすればいいですか?
ここまで記事を読んだ場合、 ENTRYPOINT
をいつ使用するENTRYPOINT
、どのCMD
で使用するENTRYPOINT
についての情報があります。
この決定は、他の開発者が使用できるDockerfileの作成者の裁量に任せます。
コンテナの起動時に実行される実行可能ファイルを開発者に変更させたくない場合は、 ENTRYPOINT
使用します。 コンテナが実行可能なシェルであると想像できます。 良い戦略は、パラメータと実行可能ファイルの安定した組み合わせをENTRYPOINT
としてENTRYPOINT
です。 そのために、(オプションで)他の開発者がオーバーライドできるデフォルトのCMD
引数を指定できます。
$ cat Dockerfile FROM alpine ENTRYPOINT ["ping"] CMD ["www.google.com"]
$ docker build -t test .
デフォルト設定で開始:
$ docker run test PING www.google.com (172.217.7.164): 56 data bytes 64 bytes from 172.217.7.164: seq=0 ttl=37 time=0.306 ms
独自のパラメーターでCMDをオーバーライドします。
$ docker run test www.yahoo.com PING www.yahoo.com (98.139.183.24): 56 data bytes 64 bytes from 98.139.183.24: seq=0 ttl=37 time=0.590 ms
開発者に実行可能ファイルを簡単にオーバーライドさせる場合は、 CMD
のみ( ENTRYPOINT
定義なし)を使用します。 エントリポイントが定義されている場合でも、 --entrypoint
フラグを使用して実行可能ファイルをオーバーライドできます。 しかし、開発者にとっては、 docker run
最後に目的のコマンドを追加する方がはるかに便利です。
$ cat Dockerfile FROM alpine CMD ["ping", "www.google.com"]
$ docker build -t test .
Pingは良いですが、 ping
コマンドの代わりにシェルでコンテナーを実行してみましょう。
$ docker run -it test sh / # ps PID USER TIME COMMAND 1 root 0:00 sh 7 root 0:00 ps / #
開発者がシェルまたはその他の実行可能ファイルで実行可能ファイルを簡単にオーバーライドできるため、ほとんどの場合、この方法が好まれます。
クリーニング
コマンドを実行した後、停止したコンテナの束がホストに残りました。 次のコマンドでそれらをきれいにします:
$ docker system prune
フィードバック
この記事に関するあなたの考えを以下のコメントで聞いてうれしいです。 また、 jqを使用してdockerを検索するより簡単な方法を知っていれば、 docker docker inspect [id] | jq * .config
docker inspect [id] | jq * .config
もコメントに書き込みます。
ジョン・ザッコネ
IBMのDockerキャプテンおよびクラウドエンジニア。 アジャイル、マイクロサービス、コンテナー、自動化、REST、DevOpsを専門としています。