Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

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

前記事:AWS CLI で AWS batch にジョブを送信する (全3回)とリンクしていますが、未読でも問題なく読めるように記載したつもりです。

参考:EC2 タスクを使用した AWS CLI のウォークスルー

目次

全4回です。

  1. はじめに
  2. タスクを実行してみる ← イマココ
  3. 一連の流れをスクリプト化する
  4. 汎用 docker イメージを使用する

AWS CLI 公式リファレンスはこちら aws ecs

1. AWS CLI の準備

手順はこちらを参照してください。 AWS CLI のインストールと設定

今回は JSON を扱うために jq コマンドも使用しますのでインストールしてください。

https://stedolan.github.io/jq/download/

2. docker イメージを作成する(オプション)

今回の作業手順ではこちらで用意した docker イメージ aokad/aws-wordcount を使用しますので、この作業は不要です。
独自イメージを使用する場合は 上記イメージの DockerFile を参考にしてください。

この記事においてイメージに必要なもの(最終的にはなくてもよいです)

  • AWS CLI ... aws configure はしないこと(タスク実行時に自動で一時的なアカウントが設定されます)
  • docker run の時に実行するスクリプトファイル

AWS ECR を使用する場合は AWS CLI で Amazon ECR に docker イメージを push する を参照してください。

3. AWS 情報を環境変数にセット

この記事ではコピペ実行できるようにアカウントの違いを環境変数にセットしておきます。

  • AWS_ACCOUNTID ..... 自分のアカウントID
  • AWS_REGION ..... aws configure で設定したリージョン
  • SUBNET1,2,3 ..... 「AWSコンソール」→「VPCダッシュボード」→「VPC」からデフォルトVPCのサブネットを確認してください
  • SECURITYGROUPID ..... 「AWSコンソール」→「VPCダッシュボード」→「セキュリティグループ」からデフォルトのセキュリティグループをのIDを確認してください。新しく作成する場合は22番ポートを開けてください。
  • KEY_NAME ... 作成するインスタンスの SSH キーです。無ければ作製してください。
  • S3_BUCKET ... 作業用の S3 バケットです。任意の名称で作成してください。

設定例

export AWS_ACCOUNTID=123456789012
export AWS_REGION=ap-northeast-1
export SUBNET1=subnet-123a456b
export SUBNET2=subnet-789c012d
export SUBNET3=subnet-345e678f
export SECURITYGROUPID=sg-11335577
export KEY_NAME=mykey
export S3_BUCKET=mybucket

4. クラスターを作成

まず、クラスターを作ります。ここでは名前だけ設定します。

aws ecs create-cluster \
    --cluster-name myCluster \
    > create-cluster.log

作成したクラスターの ARN を取得します。タスクの実行で使用します。
※ ARN とは、Amazon リソースネームの略で、AWS リソースを一意に識別するためのものです。

CLUSTER_ARN=$(jq -r '.cluster.clusterArn' create-cluster.log)

作成したクラスターは「AWSコンソール」→「Elastic Container Service」→「Amazon ECS」→「クラスター」から確認できます。

amazon-ecs-1.png

5. タスク定義を作成

まず、タスク定義に必要なコンテナ定義を作成します。
コンテナ定義には docker コンテナを起動するための情報やログの出力方法、実行に必要な cpu や memory を設定します。
また、入力ファイルと出力ファイルのパスを環境変数 (docker run するときの --env オプション) として受け渡しできるように "environment" に項目だけ作成します。
※ 実際の値はタスクの実行でセットします。

ECSTASKROLE="arn:aws:iam::${AWS_ACCOUNTID}:role/AmazonECSTaskS3FullAccess"

cat << EOF > task_definition.json
{
    "containerDefinitions": [
        {
            "name": "mytask-definision",
            "image": "aokad/aws-wordcount:0.0.1",
            "cpu": 1,
            "memory": 800,
            "essential": true,
            "entryPoint": [
                "ash",
                "-c"
            ],
            "command": [
                "ash run.sh \${INPUT} \${OUTPUT}"
            ],
            "environment": [
                {
                  "name": "INPUT",
                  "value": ""
                },
                {
                  "name": "OUTPUT",
                  "value": ""
                }
            ],
            "logConfiguration": {
                "logDriver": "awslogs",
                "options": {
                    "awslogs-group": "mytask",
                    "awslogs-region": "${AWS_REGION}",
                    "awslogs-stream-prefix": "ecs-test"
                }
            }
        }
    ],
    "taskRoleArn": "${ECSTASKROLE}",
    "family": "mytask"
}
EOF

タスク定義を登録します。

aws ecs register-task-definition \
    --cli-input-json file://task_definition.json \
    > register-task-definition.log

作成したタスク定義の ARN を取得します。タスクの実行で使用します。

TASK_DEFINITION_ARN=$(jq -r '.taskDefinition.taskDefinitionArn' register-task-definition.log)

6. ロググループを作成

AWS Batch は自動で作ってくれましたが、Amazon ECS では自分で作ります。

aws logs create-log-group --log-group-name mytask

7. EC2 インスタンスを起動する

AWS Batch は自動で作ってくれましたが、Amazon ECS では自分で作ります。
Amazon ECS AMI を使用して EC2 インスタンスを起動します。

Amazon ECS AMI はリージョンごとにimage-idが違います。今回は東京リージョン (ap-northeast-1) を使用していますが、別リージョンを使用する場合はこちらから選択してください。

export AMI_ID=ami-a99d8ad5

ユーザデータを作成します。
ユーザデータとは、AWS EC2 でインスタンス起動時に実行できるスクリプトです。
詳しくはこちら:Linux インスタンスでの起動時のコマンドの実行

cat << EOF > userdata.sh
Content-Type: multipart/mixed; boundary="==BOUNDARY=="
MIME-Version: 1.0

--==BOUNDARY==
Content-Type: text/cloud-boothook; charset="us-ascii"

# Install nfs-utils
cloud-init-per once yum_update yum update -y
cloud-init-per once install_nfs_utils yum install -y nfs-utils

cloud-init-per once docker_options echo 'OPTIONS="\${OPTIONS} --storage-opt dm.basesize=30G"' >> /etc/sysconfig/docker

#!/bin/bash
# Set any ECS agent configuration options
echo "ECS_CLUSTER=${CLUSTER_ARN}" >> /etc/ecs/ecs.config

--==BOUNDARY==--
EOF

まず、クラスター名もしくはARNを指定して起動したインスタンスをクラスターに登録します。
そのために必要なツールはあらかじめ Amazon ECS AMI に用意されています。

上記ユーザデータより該当箇所を抜粋
cat << EOF > userdata.sh
...
echo "ECS_CLUSTER=${CLUSTER_ARN}" >> /etc/ecs/ecs.config
...
EOF

今回の例では 30GByte のディスクをアタッチし、docker コンテナから使用できるストレージも 30G に増加したいので、docker の設定を起動時に書き換えるようにします。

上記ユーザデータより該当箇所を抜粋
cat << EOF > userdata.sh
...
# Install nfs-utils
cloud-init-per once yum_update yum update -y
cloud-init-per once install_nfs_utils yum install -y nfs-utils

cloud-init-per once docker_options echo 'OPTIONS="\${OPTIONS} --storage-opt dm.basesize=30G"' >> /etc/sysconfig/docker
...
EOF

EC2 インスタンスを起動します。

aws ec2 run-instances \
  --image-id ${AMI_ID} \
  --security-group-ids ${SECURITYGROUPID} \
  --key-name ${KEY_NAME} \
  --user-data "file://userdata.sh" \
  --iam-instance-profile Name="ecsInstanceRole" \
  --instance-type t2.micro \
  --block-device-mappings "[{\"DeviceName\":\"/dev/xvdcz\",\"Ebs\":{\"VolumeSize\":30,\"DeleteOnTermination\":true}}]" \
  --count 1 \
  > run-instances.log

INSTANCE_ID=$(jq -r '.Instances[0].InstanceId' run-instances.log)

起動したインスタンスに名前を付けておきます。(オプション)

aws ec2 create-tags --resources ${INSTANCE_ID} --tags Key=Name,Value=ecs-task-instance

インスタンスが起動完了するまで待ちましょう。

成功するとクラスターに登録されています。

amazon-ecs-2.png

8. タスクの実行

ジョブを投入する前に、入出力用の S3 バケットを作成し、適当なデータを置いておきます。

cat << EOF > Humpty.txt
Humpty Dumpty sat on a wall,
Humpty Dumpty had a great fall.
All the king's horses and all the king's men
Couldn't put Humpty together again.
EOF

aws s3 cp Humpty.txt s3://${S3_BUCKET}/

タスク定義で作成したコンテナ定義 (docker コンテナを起動するための情報)を上書きするための情報をファイル出力します。
今回は入力ファイルと出力ファイルのパスをセットします。

cat << EOF > containerOverrides.json
{
    "containerOverrides": [
        {
            "name": "mytask-definision",
            "environment": [
                {
                    "name": "INPUT",
                    "value": "s3://${S3_BUCKET}/Humpty.txt"
                },
                {
                    "name": "OUTPUT",
                    "value": "s3://${S3_BUCKET}/Humpty.count.ecs.txt"
                }
            ]
    }]
}
EOF

いよいよタスクを実行してみます。
投入するクラスター、タスク定義、上記で作成したタスク独自の情報を渡します。

aws ecs run-task \
    --cluster ${CLUSTER_ARN} \
    --task-definition ${TASK_DEFINITION_ARN} \
    --overrides file://containerOverrides.json

作成したタスクは「AWSコンソール」→「Elastic Container Service」→「Amazon ECS」→「クラスター」→「myCluster」→「タスク」から確認できます。
表示されない場合は「必要なタスクのステータス」を「Running」「Stopped」で切り替えてみてください。

amazon-ecs-3.png

タスク終了後に新しくタスクを投入したい場合は、この「8. タスクの実行」だけを繰り返せばよいですが、並列実行させるためには「7. EC2 インスタンスを起動する」も行う必要があります。

AWS S3 のバケットに Humpty.count.ecs.txt が出力されていれば成功です。

$ aws s3 ls s3://${S3_BUCKET}/Humpty.count.ecs.txt
2018-04-13 13:53:03        589 Humpty.count.ecs.txt

なお、スクリプトの実行ログは「AWSコンソール」→「ClowdWatch」→「ログ」→ロググループ「mytask」を選択→ログストリームから確認できます。

9. 片付け

AWS batch はコンピュート環境やタスク定義には料金がかからず実際に立ち上げたインスタンスとデータ転送量、AWS ECR イメージサイズ、ClowdWatch(ログ)に課金されます。

インスタンスを削除します。
AWS Batch は自動で削除してくれましたが、Amazon ECS では自分で削除します。
ひとまずこれで安心です。

aws ec2 terminate-instances --instance-ids ${INSTANCE_ID}

タスク定義、クラスターを削除します。
AWSコンソールから削除してもよいですが、AWS CLI でのコマンド例も記載しておきます。

# タスク定義を削除
aws ecs deregister-task-definition --task-definition ${TASK_DEFINITION_ARN}

# クラスターを削除
# インスタンスが起動していると削除できないため、インスタンスの削除完了を待ってから実行してください
aws ecs delete-cluster --cluster ${CLUSTER_ARN} 

AWSが出力した log を削除します

1.「AWSコンソール」→「ClowdWatch」→「ログ」→該当するロググループをクリック→ログストリームを選択して、「ログストリームの削除」
2. AWSコンソール」→「ClowdWatch」→「ログ」→該当するログを選択→ログストリームを選択して、「アクション」→「ロググループの削除」

AWS CLI で実行する場合は以下コマンドを使用します。

# ログストリームを削除
# 複数ある場合はそれぞれ削除してください
aws logs delete-log-stream --log-group-name mytask --log-stream-name ecs-test/mytask-definision/{36桁のコード}

# ロググループを削除
aws logs delete-log-group --log-group-name mytask

今回はコマンドを順に実行しました。
インスタンスの起動やタスクの終了など待ちが発生するので、実際に使うためには待ちをどうにかして実装する必要がありそうです。
次回、一連の流れをスクリプト化するでは、wait サブコマンドで実現する方法を記載します。

aokad
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした