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

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

# コマンドの上書き
ECSタスクの実行やタスクスケジュールの設定画面で「コンテナの上書き」という項目があり、ここで設定した内容をECSタスクに設定したコンテナ実行時に利用することができます。
![00_container_override.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315961/83e03106-93d3-b9f7-dcf2-6a424f9bfd35.png)
「コンテナの上書き」の中に「コマンドの上書き」というフリー記述項目があります。
画面上のヒントメッセージに「コンテナに渡すCMD」とありますが、どういうことか次項で説明します。


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

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

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

```
ENTRYPOINT ["実行可能なもの", "パラメータ１", "パラメータ２"]
```

[例]

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

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

```
CMD ["実行バイナリ", "パラメータ１", "パラメータ２"]
```

[例]

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

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

```
ENTRYPOINT ["実行可能なもの", "パラメータ１", "パラメータ２"]
CMD ["パラメータ３", "パラメータ４"]
```

[例]
以下の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 <コンテナ指定> パラメータ１ パラメータ２
```

[例]
コンテナ定義に「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
[コマンドの上書きなし]
![03_entrypoint_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315961/a7cc7dd2-5e9b-4eda-7c5c-b508564bc5d7.png)
[コマンドの上書きあり]
![03_entrypoint_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315961/cfcb2c50-7344-9e49-4109-768626a3dd39.png)
![03_entrypoint_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315961/a0727dce-c0dd-e057-ad61-0c9af26ee8dc.png)
[結果]
・コマンド上書きありなしどちらでも、arg is ENTRYPOINTが必ず出力されている
　→ENTRYPOINTは必ず実行される
・コマンドの上書きを設定した場合、arg is override1, arg is override2が出力されている
　→ENTRYPOINTにパラメータを渡すCMDとして処理される

#### CMD
[コマンドの上書きなし]
![04_cmd_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315961/29311744-c107-7fae-6277-905492ab0b39.png)
[コマンドの上書きあり]
![04_cmd_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315961/2f1155e9-e2a7-37a9-63bc-702c676918cf.png)
![04_cmd_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315961/cb435afc-1647-dd9e-7f55-711ad0fc48f7.png)
[結果]
・arg is CMDはコマンド上書き時に出力されない
　→コンテナ定義のCMDは無視され、コマンドの上書きの内容がCMDとして処理される

#### 両方設定したもの
[コマンドの上書きなし]
![05_both_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315961/1029c9f0-f9e8-7350-7f0d-2cdac8f745a1.png)
[コマンドの上書きあり]
![05_both_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315961/43bea379-8fe1-4503-357b-e5c54ffda2fa.png)
![05_both_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315961/fa4f5214-30d3-0669-9166-dba2033a7747.png)
[結果]
・test_script.shのecho処理が実行されている
　→ENTRYPOINTは必ず実行される
・arg is BOTHはコマンド上書き時に出力されない
　→コンテナ定義のCMDは無視され、コマンドの上書きの内容がCMDとして処理される

# 調査結果
[こちら](#### 実行時(docker run時)のパラメータ)で述べたように、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/