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

建設業







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を専門としています。







参照:



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



All Articles