【AWS ECS】Fargateの「コマンドの上書き」


はじめに

ECSで起動タイプFargateのタスクを実行する際の「コマンドの上書き」という項目について、「実行時パラメータを渡せるようなものかな」と思って使おうとしました。
この理解でざっくり正解と言えますが、正しく使うにはdockerのENTRYPOINT、CMDコマンドの知識が必要で、自分のようにdockerを使いこなす前にFargateを使おうとした人にとってはハマりどころかと思います。
これらについて調べた内容を記載しています。

コマンドの上書き

ECSタスクの実行やタスクスケジュールの設定画面で「コンテナの上書き」という項目があり、ここで設定した内容をECSタスクに設定したコンテナ実行時に利用することができます。

「コンテナの上書き」の中に「コマンドの上書き」というフリー記述項目があります。
画面上のヒントメッセージに「コンテナに渡すCMD」とありますが、どういうことか次項で説明します。

dockerのENTRYPOINTとCMD

dockerfileで「ENTRYPOINT」と「CMD」という命令があり、どちらもコンテナ実行時(docker run時)に実行されるコマンドです。
「コマンドの上書き」の挙動を理解するためにこれらの理解が必要となります。

この2つは、コンテナ実行時に渡されるパラメータの扱いが異なります。
ENTRYPOINTはdocker runのパラメータをそのままENTRYPOINTのパラメータとして扱います。
CMDはdocker runのパラメータをCMD自体として扱います。(CMDの上書き)

ENTRYPOINT

コンテナ実行時に必ず実行されるコマンドと引数(省略可)を設定します。
[書式]

ENTRYPOINT ["実行可能なもの", "パラメータ1", "パラメータ2"]

[例]

ENTRYPOINT ["ping", "localhost"]
→pingコマンドにlocalhostという引数を渡して実行

CMD

コンテナ実行時のデフォルト実行コマンドと引数を設定します。
[書式]

CMD ["実行バイナリ", "パラメータ1", "パラメータ2"]

[例]

CMD ["ping", "localhost"]
→pingコマンドにlocalhostという引数を渡して実行

併用

ENTRYPOINTとCMDを併用する場合、CMDにはENTRYPOINTに渡すパラメータを設定します。
[書式]

ENTRYPOINT ["実行可能なもの", "パラメータ1", "パラメータ2"]
CMD ["パラメータ3", "パラメータ4"]

[例]
以下の2例はどちらも同じコマンドが実行される

ENTRYPOINT [“ping”]
CMD[“-c”,”4”,“localhost”]
→ping -c 4 localhost になる
ENTRYPOINT [“ping”,”-c”]
CMD[”4”,“localhost”]
→ping -c 4 localhost になる

実行時(docker run時)のパラメータ

docker run実行時に指定したパラメータはコンテナ定義のCMDとしてコンテナに渡され、コンテナ起動時にはこのCMDが採用されます。(CMDの上書き)
前項の「コマンドの上書き」はこの仕様に相当します

[書式]

docker run <コンテナ指定> パラメータ1 パラメータ2

[例]
コンテナ定義に「ENTRYPOINT [“ping”,”localhost”]」の設定がある場合、ENTRYPOINT + CMDの併用となる

docker run <コンテナ> -c 4
→ping localhost -c 4 になる

コンテナ定義に「CMD [“ping”,”localhost”]」の設定がある場合、CMDを上書きとなる

docker run <コンテナ> echo hoge
→pingは実行されず、echo hogeが実行される

コンテナ定義に「ENTRYPOINT [“ping”,”localhost”]」と「CMD [“-c”,”4”]」の設定がある場合、CMDを上書きとなる

docker run <コンテナ> -c 10
→ping localhost -c 10 になる

動作確認

前項での実行時パラメータの仕様通りに動くか、「コマンドの上書き」の動作確認をします。
ENTRYPOINTを設定したもの、CMDを設定したもの、両方設定したもの、の3つのコンテナ定義を作成し、それぞれコマンドの上書きなし・ありで実行した場合の系6パターンの出力を確認します。

テスト用ファイル

test_script.shを作成。シェルに渡された引数をechoする。

#!/bin/sh

# 引数の数をecho
echo arg length is $#

# 渡された引数を全てecho
for x
do
  echo arg is "$x"
done

コンテナ定義

[ENTRYPOINTを設定したもの]

FROM ubuntu
COPY . .
RUN chmod 777 test_script.sh

ENTRYPOINT ["sh", "test_script.sh", "ENTRYPOINT"]

[CMDを設定したもの]

FROM ubuntu
COPY . .
RUN chmod 777 test_script.sh

CMD ["sh", "test_script.sh", "CMD"]

[両方設定したもの]

FROM ubuntu
COPY . .
RUN chmod 777 test_script.sh

ENTRYPOINT ["sh", "test_script.sh"]
CMD ["BOTH"]

テスト実行

ENTRYPOINT

[コマンドの上書きなし]

[コマンドの上書きあり]


[結果]
・コマンド上書きありなしどちらでも、arg is ENTRYPOINTが必ず出力されている
 →ENTRYPOINTは必ず実行される
・コマンドの上書きを設定した場合、arg is override1, arg is override2が出力されている
 →ENTRYPOINTにパラメータを渡すCMDとして処理される

CMD

[コマンドの上書きなし]

[コマンドの上書きあり]


[結果]
・arg is CMDはコマンド上書き時に出力されない
 →コンテナ定義のCMDは無視され、コマンドの上書きの内容がCMDとして処理される

両方設定したもの

[コマンドの上書きなし]

[コマンドの上書きあり]


[結果]
・test_script.shのecho処理が実行されている
 →ENTRYPOINTは必ず実行される
・arg is BOTHはコマンド上書き時に出力されない
 →コンテナ定義のCMDは無視され、コマンドの上書きの内容がCMDとして処理される

調査結果

こちらで述べたように、Fargateの「コマンドの上書き」はコンテナ実行時にCMDとして渡されるものでした。
コンソールでの「コンテナに渡すCMD」というヒントメッセージがそのまま正解ですね。

所感としては、「コマンド」というワードがここではdockerの用語の「CMD」を表しているようで、自分はこの点が少しわかりにくさを感じました。
(英語表記でも「Command」のようです)
使うのにdocker知識が前提に感じるので、どうせなら「CMDの上書き」の方がわかりやすい気がします。

注意点

ECSの開発者ガイドで、コンテナ定義にENTRYPOINTがある場合とない場合の「コマンドの上書き」の設定について記載されていますが、誤解しそうな表現になっていると思います。
[ENTRYPOINTがない場合の説明]

If your container definition does not specify an ENTRYPOINT, the format should be a comma-separated list of non-quoted strings. For example:
/bin/sh,-c,echo,$DATE

[ENTRYPOINTがある場合の説明]

If your container definition does specify an ENTRYPOINT (such as sh,-c), the format should be an unquoted string, which is surrounded with double quotes and passed as an argument to the ENTRYPOINT command. For example:
while true; do echo $DATE > /var/www/html/index.html; sleep 1; done

「ENTRYPOINTがない場合はカンマ区切り」という点と、ENTRYPOINTがある場合のサンプルから、「ENTRYPOINTがある場合はカンマ区切り指定ではない」と自分には見えましたが、動作確認で実証したようにENTRYPOINTがあってもカンマ区切り指定可能でした。

参考

ECSの開発者ガイド
https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/ecs_run_task_fargate.html

Dockerfile リファレンス
http://docs.docker.jp/v1.11/engine/reference/builder.html

ENTRYPOINTは「必ず実行」、CMDは「(デフォルトの)引数」
https://pocketstudio.net/2020/01/31/cmd-and-entrypoint/