コンテナ化ツールとしてのDockerの柔軟性と堅牢性には、気が遠くなるような複雑さが伴います。 同様のタスクを実行するために複数の方法が利用可能であり、ユーザーは利用可能なオプションの長所と短所を理解して、プロジェクトに最適なアプローチを選択する必要があります。
紛らわしい領域の 1 つは、 RUN
、 CMD
、および ENTRYPOINT
Dockerfile の命令に関するものです。 この記事では、これらの手順の違いについて説明し、それぞれのユースケースについて説明します。
走る
RUN
命令は、Docker イメージをビルドおよび構成するコマンドを実行するために Dockerfile で使用されます。これらのコマンドはイメージのビルドプロセス中に実行され、 RUN
命令ごとに Docker イメージに新しいレイヤーが作成されます。 たとえば、特定のソフトウェアまたはライブラリのインストールを必要とするイメージを作成する場合は、 RUN
を使用して必要なインストール コマンドを実行します。
次の例は、イメージのビルド中に Docker のビルドプロセスに apt cache
を更新し、Apache をインストールするように指示する方法を示しています。
RUN apt update && apt -y install apache2
RUN
命令は、画像レイヤーを最小限に抑えるために慎重に使用し、画像サイズを縮小するために、可能な場合は関連するコマンドを 1 つの RUN
命令に結合する必要があります。
ティッカー
CMD
命令は、コンテナーが Docker イメージから起動されたときに実行する既定のコマンドを指定します。コンテナの起動時に(つまり、 docker run
コマンドで)コマンドが指定されていない場合、このデフォルトが使用されます。 CMD
は、 docker run
にコマンドライン引数を指定することでオーバーライドできます。
CMD
は、デフォルトのコマンドや簡単にオーバーライドできるパラメータを設定する場合に便利です。 これは、デフォルトの実行パラメータを定義する方法としてイメージでよく使用され、コンテナの実行時にコマンドラインから上書きできます。
たとえば、デフォルトでは、Web サーバーを起動したい場合がありますが、ユーザーはこれをオーバーライドしてシェルを実行できます。
CMD ["apache2ctl", "-DFOREGROUND"]
ユーザーは、Apache を起動する代わりに、 docker run -it <image> /bin/bash
でコンテナを起動して Bash シェルを取得できます。
エントリポイント
ENTRYPOINT
命令は、コンテナのデフォルトの実行可能ファイルを設定します。docker run コマンドに指定された引数は、 ENTRYPOINT
コマンドに追加されます。
手記: ENTRYPOINT
は、コンテナで常に同じ基本コマンドを実行する必要があり、ユーザーが最後に追加のコマンドを追加できるようにする場合に使用します。1 つの注意点は、--entrypoint
フラグを指定することで、docker run
コマンド ラインで ENTRYPOINT
をオーバーライドできることです。
ENTRYPOINT
コンテナをスタンドアロンの実行可能ファイルに変換する場合に特に便利です。 たとえば、引数を必要とするカスタムスクリプト( “my_script extra_args”
など)をパッケージ化しているとします。 その場合は、ENTRYPOINT
を使用して常にスクリプト プロセス (“my_script”
) を実行し、イメージ ユーザーが docker run
コマンド ラインで“extra_args”
を指定できるようにすることができます。次の操作を実行できます。
ENTRYPOINT ["my_script"]
CMD と ENTRYPOINT の組み合わせ
CMD
命令は、exec 形式で指定されている場合、ENTRYPOINT
にデフォルトの引数を提供するために使用できます。この設定により、エントリポイントをメインの実行可能ファイルにし、ユーザーがオーバーライドできる追加の引数 CMD
指定できます。
たとえば、Python アプリケーションを実行するコンテナーがあり、常に同じアプリケーション ファイルを使用し、ユーザーが異なるコマンド ライン引数を指定できるようにするとします。
ENTRYPOINT ["python", "/app/my_script.py"]
CMD ["--default-arg"]
docker run myimage --user-arg
を実行すると、python /app/my_script.py --user-arg
が実行されます。
次の表に、これらのコマンドと使用例の概要を示します。
コマンドの説明と使用例
命令 | 形容 | ユースケース |
ティッカー | Docker イメージの既定の実行可能ファイルを定義します。 これは、 docker run 引数によってオーバーライドできます。 | ユーティリティイメージを使用すると、ユーザーはコマンドラインでさまざまな実行可能ファイルと引数を渡すことができます。 |
エントリポイント | 既定の実行可能ファイルを定義します。 これは、 “--entrypoint” docker run 引数でオーバーライドできます。 | デフォルトの実行可能ファイルをオーバーライドすることが望ましくない特定の目的のために構築されたイメージ。 |
走る | レイヤーを構築するコマンドを実行します。 | イメージの構築 |
PID 1 とは何か、なぜ重要なのか?
Unix および Docker コンテナを含む Unix ライクなシステムのコンテキストでは、PID 1 はシステムの起動中に開始される最初のプロセスを指します。 他のすべてのプロセスは、プロセスツリーモデルではシステム内のすべてのプロセスの親であるPID 1によって開始されます。
Docker コンテナーでは、PID 1 として実行されるプロセスは、コンテナー内の他のすべてのプロセスを管理する役割を担うため、非常に重要です。 さらに、PID 1 は、Docker ホストからのシグナルを確認して処理するプロセスです。 たとえば、コンテナへの SIGTERM
はPID 1によってキャッチおよび処理され、コンテナは正常にシャットダウンする必要があります。
シェル形式を使用して Docker でコマンドを実行すると、通常、シェルプロセス (/bin/sh -c
) は PID 1になります。 それでも、これらの信号を適切に処理しないため、コンテナのクリーンなシャットダウンにつながる可能性があります。 対照的に、exec形式を使用する場合、コマンドはシェルを介さずにPID 1 として直接実行されるため、シグナルを直接受信して処理できます。
この動作により、コンテナは正常に停止、再起動、または割り込みを処理できるため、堅牢で応答性の高いシグナル処理を必要とするアプリケーションには exec 形式が適しています。
シェルと実行の形式
前の例では、 RUN
、 CMD
、および ENTRYPOINT
命令に引数を渡すために 2つの方法を使用しました。 これらは、シェル形式と実行形式と呼ばれます。
手記: 視覚的な違いは、exec形式が、要素ごとに1つの引数/コマンドを持つコマンドと引数のコンマ区切りの配列として渡されることです。 逆に、シェル形式は、コマンドと引数を組み合わせた文字列として表現されます。
各フォームは、コンテナ内でコマンドを実行することに影響を与え、シグナル処理から環境変数の拡張まで、あらゆるものに影響を与えます。 次の表に、さまざまなフォームのクイック リファレンス ガイドを示します。
シェルと exec フォームのリファレンス
フォーム | 形容 | 例 |
シェルフォーム | <INSTRUCTION> <COMMAND> の形をとります。 | CMD echo TEST 又は ENTRYPOINT echo TEST |
実行フォーム | <INSTRUCTION> ["EXECUTABLE", "PARAMETER"] の形をとります。 | CMD ["echo", "TEST"] 又は ENTRYPOINT ["echo", "TEST"] |
シェル形式では、コマンドはサブシェル (通常は Linux システムで /bin/sh -c
) で実行されます。 この形式は、シェル処理(変数展開、ワイルドカードなど)を可能にし、特定のタイプのコマンドに対してより柔軟になるため便利です(シェル処理の例については、この シェルスクリプトの記事 を参照してください)。 ただし、コマンドを実行しているプロセスがコンテナのPID 1ではないことも意味し、Dockerによって送信されたシグナル(グレースフルシャットダウンの SIGTERM
など)は意図したプロセスではなくシェルによって受信されるため、シグナル処理に問題が発生する可能性があります。
exec 形式では、コマンド・シェルは呼び出されません。 つまり、指定したコマンドは、コンテナに送信されたシグナルを正しく処理するために重要な、コンテナの PID 1として直接実行されます。 さらに、この形式はシェル展開を実行しないため、特に外部ソースから引数やコマンドを指定する場合に、より安全で予測可能です。
すべてをまとめる
Docker の RUN
、 CMD
、 ENTRYPOINT
命令の実際のアプリケーションとニュアンス、およびシェル形式と実行形式の選択を説明するために、いくつかの例を確認しましょう。 これらの例は、実際の Dockerfile シナリオで各命令を効果的に利用する方法を示し、シェル形式と実行形式の違いを強調しています。
これらの例を通じて、各ディレクティブをいつ、どのように使用してコンテナの動作をニーズに合わせて正確に調整し、Docker コンテナの適切な構成、セキュリティ、パフォーマンスを確保するかをよりよく理解できます。 この実践的なアプローチは、これまで説明してきた理論的知識を、Docker プロジェクトに直接適用できる実用的な洞察に統合するのに役立ちます。
RUN命令
RUN
、Docker ビルドプロセス中にパッケージのインストールやファイルの変更に使用される場合、シェル形式と実行形式のどちらを選択するかは、シェル処理の必要性によって異なります。シェル形式は、パイプラインやファイルグロビングなど、シェル機能を必要とするコマンドに必要です。 ただし、exec形式は、複雑さと潜在的なエラーを軽減するため、シェル機能のない単純なコマンドに適しています。
# Shell form, useful for complex scripting
RUN apt-get update && apt-get install -y nginx
# Exec form, for direct command execution
RUN ["apt-get", "update"]
RUN ["apt-get", "install", "-y", "nginx"]
CMD と ENTRYPOINT
これらの命令は、コンテナーの実行時の動作を制御します。 exec 形式を ENTRYPOINT
とともに使用すると、コンテナのメインアプリケーションがシグナルを直接処理することが保証されます。CMD
exec形式で定義された ENTRYPOINT
にデフォルトパラメータを提供し、柔軟性と堅牢な信号処理を提供します。
# ENTRYPOINT with exec form for direct process control
ENTRYPOINT ["httpd"]
# CMD provides default parameters, can be overridden at runtime
CMD ["-D", "FOREGROUND"]
信号処理と柔軟性
ENTRYPOINT
を exec 形式とCMD
で使用してパラメーターを指定すると、Docker コンテナーはオペレーティング システムのシグナルを適切に処理し、ユーザー入力に動的に応答し、安全で予測可能な操作を維持できます。
この設定は、信頼性の高いシャットダウンと構成動作を必要とする重要なアプリケーションを実行するコンテナに特に役立ちます。 次の表に、フォーム間の主な相違点を示します。
シェルとexecの主な違い
シェルフォーム | 実行フォーム | |
フォーム | [] 角かっこのないコマンド。コンテナのシェル( /bin/sh -c など)によって実行されます。 | [] 括弧で囲まれたコマンド。シェルを経由せずに直接実行します。 |
変数の置換 | シェルから環境変数 ( $HOME や $PATH など) を継承します。 | シェル環境変数を継承しませんが、 ENV 命令変数に対して同じように動作します。 |
シェル機能 | サブコマンド、配管出力、チェーンコマンド、I/Oリダイレクトなどをサポートします。 | シェル フィーチャーはサポートしていません。 |
信号のトラッピングとフォワーディング | ほとんどのシェルは、プロセスシグナルを子プロセスに転送しません。 | SIGINT のように信号を直接トラップして転送します。 |
ENTRYPOINTでの使用 | 信号転送で問題が発生する可能性があります。 | 信号処理が優れているため、推奨されます。 |
ENTRYPOINTパラメータとしてのCMD | シェル形式では不可能です。 | 配列の最初の項目がコマンドでない場合は、すべての項目が ENTRYPOINT のパラメーターとして使用されます。 |
図 1 は、Dockerfile のビルドで RUN
、 CMD
、 ENTRYPOINT
を使用するためのデシジョン ツリーを示しています。
図 2 は、exec 形式とシェル形式のどちらを使用するかを判断するのに役立つデシジョン・ツリーを示しています。
例
次のセクションでは、 CMD
と ENTRYPOINT
の違いの概要について説明します。 これらの例では、 RUN
コマンドは含まれていませんが、これは、2 つの異なる形式を確認することで簡単に処理できるからです。
Dockerfile をテストする
# Use syntax version 1.3-labs for Dockerfile
# syntax=docker/dockerfile:1.3-labs
# Use the Ubuntu 20.04 image as the base image
FROM ubuntu:20.04
# Run the following commands inside the container:
# 1. Update the package lists for upgrades and new package installations
# 2. Install the apache2-utils package (which includes the 'ab' tool)
# 3. Remove the package lists to reduce the image size
#
# This is all run in a HEREDOC; see
# https://www.docker.com/blog/introduction-to-heredocs-in-dockerfiles/
# for more details.
#
RUN <<EOF
apt-get update;
apt-get install -y apache2-utils;
rm -rf /var/lib/apt/lists/*;
EOF
# Set the default command
CMD ab
最初のビルド
このイメージをビルドし、 ab
としてタグ付けします。
$ docker build -t ab .
[+] Building 7.0s (6/6) FINISHED docker:desktop-linux
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 730B 0.0s
=> [internal] load metadata for docker.io/library/ubuntu:20.04 0.4s
=> CACHED [1/2] FROM docker.io/library/ubuntu:20.04@sha256:33a5cc25d22c45900796a1aca487ad7a7cb09f09ea00b779e 0.0s
=> [2/2] RUN <<EOF (apt-get update;...) 6.5s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:99ca34fac6a38b79aefd859540f88e309ca759aad0d7ad066c4931356881e518 0.0s
=> => naming to docker.io/library/ab
次で実行 CMD ab
引数がないと、期待どおりに使用ブロックが得られます。
$ docker run ab
ab: wrong number of arguments
Usage: ab [options] [http[s]://]hostname[:port]/path
Options are:
-n requests Number of requests to perform
-c concurrency Number of multiple requests to make at a time
-t timelimit Seconds to max. to spend on benchmarking
This implies -n 50000
-s timeout Seconds to max. wait for each response
Default is 30 seconds
<-- SNIP -->
ただし、 ab
を実行してテストするURLを含めると、最初にエラーが発生します。
$ docker run --rm ab https://jayschmidt.us
docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "https://jayschmidt.us": stat https://jayschmidt.us: no such file or directory: unknown.
ここでの問題は、コマンドラインで指定された文字列( https://jayschmidt.us
)が CMD
命令をオーバーライドしており、それが有効なコマンドではないため、エラーがスローされることです。 したがって、実行するコマンドを指定する必要があります。
$ docker run --rm ab ab https://jayschmidt.us/
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking jayschmidt.us (be patient).....done
Server Software: nginx
Server Hostname: jayschmidt.us
Server Port: 443
SSL/TLS Protocol: TLSv1.2,ECDHE-ECDSA-AES256-GCM-SHA384,256,256
Server Temp Key: X25519 253 bits
TLS Server Name: jayschmidt.us
Document Path: /
Document Length: 12992 bytes
Concurrency Level: 1
Time taken for tests: 0.132 seconds
Complete requests: 1
Failed requests: 0
Total transferred: 13236 bytes
HTML transferred: 12992 bytes
Requests per second: 7.56 [#/sec] (mean)
Time per request: 132.270 [ms] (mean)
Time per request: 132.270 [ms] (mean, across all concurrent requests)
Transfer rate: 97.72 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 90 90 0.0 90 90
Processing: 43 43 0.0 43 43
Waiting: 43 43 0.0 43 43
Total: 132 132 0.0 132 132
ENTRYPOINTで実行
この実行では、Dockerfile から CMD ab
命令を削除し、 ENTRYPOINT ["ab"]
に置き換えてから、イメージを再構築します。
これは CMD
コマンドと似ていますが、ENTRYPOINT
を使用する場合、docker run
コマンドで –entrypoint
フラグを使用しない限り、コマンドをオーバーライドすることはできません。代わりに、 docker run
に渡された引数は、 ENTRYPOINT
への引数として扱われます。
$ docker run --rm ab "https://jayschmidt.us/"
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking jayschmidt.us (be patient).....done
Server Software: nginx
Server Hostname: jayschmidt.us
Server Port: 443
SSL/TLS Protocol: TLSv1.2,ECDHE-ECDSA-AES256-GCM-SHA384,256,256
Server Temp Key: X25519 253 bits
TLS Server Name: jayschmidt.us
Document Path: /
Document Length: 12992 bytes
Concurrency Level: 1
Time taken for tests: 0.122 seconds
Complete requests: 1
Failed requests: 0
Total transferred: 13236 bytes
HTML transferred: 12992 bytes
Requests per second: 8.22 [#/sec] (mean)
Time per request: 121.709 [ms] (mean)
Time per request: 121.709 [ms] (mean, across all concurrent requests)
Transfer rate: 106.20 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 91 91 0.0 91 91
Processing: 31 31 0.0 31 31
Waiting: 31 31 0.0 31 31
Total: 122 122 0.0 122 122
構文はどうですか?
上記の例では、 ENTRYPOINT ["ab"]
構文を使用して、実行するコマンドを角括弧と引用符で囲みます。 ただし、 ENTRYPOINT ab
を指定することは可能です(引用符や括弧なし)。
それを試すとどうなるか見てみましょう。
$ docker run --rm ab "https://jayschmidt.us/"
ab: wrong number of arguments
Usage: ab [options] [http[s]://]hostname[:port]/path
Options are:
-n requests Number of requests to perform
-c concurrency Number of multiple requests to make at a time
-t timelimit Seconds to max. to spend on benchmarking
This implies -n 50000
-s timeout Seconds to max. wait for each response
Default is 30 seconds
<-- SNIP -->
最初に考えるのは、上記のCMD ab
で行ったように、実行可能ファイルと引数の両方を与えるdocker run
コマンドを再実行することです。
$ docker run --rm ab ab "https://jayschmidt.us/"
ab: wrong number of arguments
Usage: ab [options] [http[s]://]hostname[:port]/path
Options are:
-n requests Number of requests to perform
-c concurrency Number of multiple requests to make at a time
-t timelimit Seconds to max. to spend on benchmarking
This implies -n 50000
-s timeout Seconds to max. wait for each response
Default is 30 seconds
<-- SNIP -->
これはENTRYPOINT
docker run
コマンドに–entrypoint
引数を明示的に追加した場合にのみ上書きできるためです。重要なのは、実行時にコンテナ内の特定の実行可能ファイルの使用を強制する場合は、常に ENTRYPOINT
を使用することです。
まとめ:重要なポイントとベストプラクティス
RUN
、CMD
、ENTRYPOINT
の使用を含む意思決定プロセスと、シェル形式と実行形式の選択は、Dockerの複雑な性質を示しています。各コマンドは、Docker エコシステムで明確な目的を果たし、コンテナの構築方法、操作方法、環境とのやり取りに影響を与えます。
開発者は、特定のシナリオごとに適切なコマンドとフォームを選択することで、信頼性が高く、安全で、効率が最適化された Docker イメージを構築できます。 Docker のコマンドとその形式をこのレベルで理解し、適用することは、Docker の機能を完全に活用するために重要です。 これらのベストプラクティスを実装することで、Dockerコンテナにデプロイされたアプリケーションがさまざまな設定で最大のパフォーマンスを達成し、開発ワークフローと本番環境のデプロイが強化されます。
さらに詳しく
- ティッカー
- エントリポイント
- 走る
- Docker の最新ニュースをお届けします。 Docker ニュースレターを購読する。
- Docker デスクトップの最新リリースを入手します。
- ドッカーは初めてですか? 始めましょう。
- 質問がありますか? Docker コミュニティがお手伝いします。