エントリーポイントとCMD:基本に戻る

建設業


ENTRYPOINTという名前はいつも私を混乱させてきました。 この名前は、各コンテナに特定のENTRYPOINTが必要であることを意味します。 しかし、 公式文書を読んだ後、これは真実ではないことに気付きました。


事実1:少なくとも1つの命令( ENTRYPOINTまたはCMD )を定義する必要があります(実行するため)。


これらのいずれも定義しないと、エラーメッセージが表示されます。 ENTRYPOINTCMDも定義されていないAlpine Linuxイメージを実行してみましょう。


 $ docker run alpine docker: Error response from daemon: No command specified. See 'docker run --help'. 

事実2:実行時に1つの命令のみが定義されている場合、 CMDENTRYPOINT両方が同じ効果を持ちます。


 $ 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 

この例では、 ENTRYPOINTCMD違いがないことを示していますが、コンテナーのメタデータを比較することで確認できます。


たとえば、最初のDockerfile (特定のENTRYPOINT ):


 $ docker inspect b52 | jq .[0].Config { ... "Cmd": null, ... "Entrypoint": [ "/bin/sh", "-c", "ls /" ], ... } 

事実3: CMDENTRYPOINT両方にシェルモードと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を専門としています。


参照:


  1. エントリーポイントvs CMD:基本に戻る


Source: https://habr.com/ru/post/J329138/


All Articles