AWS CLI で Amazon ECS のタスクを実行する (第４回)

AWS CLI を使って AWS ECS でタスクを実行するために必要なコマンドをまとめます。
この記事を読むためには AWS EC2、AWS S3 および docker に対する基本的な知識が必要です。
サービス、メトリクス、タスクのスケジューリングについては記載しません。

前記事：[AWS CLI で AWS batch にジョブを送信する (全３回)](https://qiita.com/aokad/items/f28ffa5ae984678817f6)とリンクしていますが、未読でも問題なく読めるように記載したつもりです。

参考：[EC2 タスクを使用した AWS CLI のウォークスルー](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/ECS_AWSCLI_EC2.html)

## 目次

全４回です。

1. [はじめに](https://qiita.com/aokad/items/b3762e3cc10db6104858)
2. [タスクを実行してみる](https://qiita.com/aokad/items/afe568d57605ad3d6d4b)
3. [一連の流れをスクリプト化する](https://qiita.com/aokad/items/0eacf96fcb2378c7a36c)
4. 汎用 docker イメージを使用する                ← イマココ

AWS CLI 公式リファレンスはこちら [aws ecs](https://docs.aws.amazon.com/cli/latest/reference/ecs/index.html)

## 前回までの課題

前回までに Amazon ECS のタスクを実行するためのスクリプトを作成しましたが、もう一つ、個人的には課題が残っています。
これまでの記事で使用していた docker イメージには AWS CLI と docker run の時に実行するスクリプトファイルをあらかじめセットしていました。

この場合、以下のような問題が考えられます。
※あくまでも私の見解です。
 
 - 第三者が公開している docker イメージを使用する場合、そのまま使いたい。（いちいち AWS CLI とスクリプトファイルを入れるのが面倒）
 - AWS CLI のバージョンが変わるたびに docker イメージを更新するのは面倒。AWS CLI は処理そのものとは直接関係ないので、バージョンは特に意識しておらず、動くバージョンであればいい。
 - スクリプトファイルの変更とライブラリのバージョンは別に管理したい。ライブラリやパッケージに比べてスクリプトファイルは更新する機会が多いため、別にソース管理したい。

今回は、AWS CLI とスクリプトファイルを docker イメージに入れることなくタスクを実行する（私なりの）方法を記載します。

## docker run の時 AWS CLI とスクリプトファイルをインストールする

タイトルのままですが、docker run で実行する "command" を編集して AWS CLI とスクリプトファイルのインストールを追加します。

以下、前回に対する変更点を記述します。
前回作成したスクリプトファイルを今回の内容で更新したものは[ここ](https://raw.githubusercontent.com/aokad/qiita_aws_batch/master/run-ecs-ext.sh)に置いてあります。
[前回のスクリプトファイル](https://raw.githubusercontent.com/aokad/qiita_aws_batch/master/run-ecs.sh)と diff をとりながら見るとわかりやすいかと思います。

**変更点１**

今回は docker イメージに aokad/aws-wordcount:base-0.0.1 を使用します。

```Bash:run-ecs-ext.sh
#"image": "aokad/aws-wordcount:0.0.1",
"image": "aokad/aws-wordcount:base-0.0.1",
```

参考：前回使用した docker イメージと今回使用した docker イメージのDockerfileです。
前回の記事で使用した docker イメージに対し、今回は AWS CLI のインストールと実行されるスクリプトファイルの作成をしていません。

```Dockerfile:前回のイメージ
FROM python:3.6.5-alpine3.7

# ライブラリのインストール
RUN wget https://raw.githubusercontent.com/aokad/ecsub/master/examples/wordcount.py

# AWSCLI をインストール
RUN pip install awscli

# docker run で実行されるスクリプトファイルの作成
RUN echo "set -x"                                                         > /run.sh && \
    echo "aws s3 cp \$1 ./input"                                         >> /run.sh && \
    echo "python wordcount.py ./input ./output"                          >> /run.sh && \
    echo "aws s3 cp ./output \$2"                                        >> /run.sh && \
    \
    chmod 744 /run.sh
    
CMD ["/run.sh"]
```

```Dockerfile:今回のイメージ
FROM python:3.6.5-alpine3.7

# ライブラリのインストール
RUN wget https://raw.githubusercontent.com/aokad/ecsub/master/examples/wordcount.py
    
CMD ["ash"]
```

**変更点２**

docker run で実行されるスクリプトファイルを S3 にアップロードしておきます。
このスクリプトは前回の docker イメージに入っていた run.sh と同じです。

```Bash:run-ecs-ext.sh
cat << EOF > run-ext.sh
set -x
aws s3 cp \$1 ./input
python wordcount.py ./input ./output
aws s3 cp ./output \$2
EOF

SCRIPT_PATH=s3://${S3_BUCKET}/run-ext.sh
aws s3 cp run-ext.sh ${SCRIPT_PATH}
```

今回の記事ではスクリプトファイルを AWS S3 に置く仕様にしていますが、wget で別のサーバから取得したり、git, svn を使ってリポジトリから取得することも可能でしょう。

**変更点３**

"command" に AWS CLI のインストールとスクリプトファイルのダウンロードを追加します。
aws configure は Amazon ECS 側で自動設定されますので、ユーザが設定する必要はありません。

```Bash:run-ecs-ext.sh
"command": [
    #"aws ash run.sh \${INPUT} \${OUTPUT}"
    "pip install awscli; aws s3 cp ${SCRIPT_PATH} run.sh; ash run.sh \${INPUT} \${OUTPUT}"
],
```

※今回使用しているベースイメージ (python:3.6.5-alpine3.7) には python と pip が入っているため、直接 pip install できますが、python が入っていないイメージの場合、python と pip のインストールも必要になります。

## 今後の課題

以上で Amazon ECS を使ってタスクを実行するために一連の流れをまとめたスクリプトを作成しましたが、以下課題もあります。
使用される場合は参考にしてください。

 - 全体的にエラー処理していない ... 説明用ですからね。
 - EC2 インスタンスの起動を待つとき、スポットインスタンスを使用すると10分以上待つかも ... EC2 インスタンスの起動時も wait コマンドを使用したほうがいいかもしれません。
 - 普通の EC2 インスタンスを起動する時に、10分以上待ちって起動エラーなのでは？ ... `timeout 3m aws ec2 wait instance-running` このようにタイムアウトコマンドを併用して制限時間が過ぎたらエラーと判断する、ということも考えられます。その場合、スポットインスタンスかどうかで処理を分岐させることも必要かもしれません。

---

以上です。