AWS CLI を使って AWS ECS でタスクを実行するために必要なコマンドをまとめます。
この記事を読むためには AWS EC2、AWS S3 および docker に対する基本的な知識が必要です。
サービス、メトリクス、タスクのスケジューリングについては記載しません。
前記事:AWS CLI で AWS batch にジョブを送信する (全3回)とリンクしていますが、未読でも問題なく読めるように記載したつもりです。
参考:EC2 タスクを使用した AWS CLI のウォークスルー
目次
全4回です。
- はじめに
- タスクを実行してみる ← イマココ
- 一連の流れをスクリプト化する
- 汎用 docker イメージを使用する
AWS CLI 公式リファレンスはこちら aws ecs
1. AWS CLI の準備
手順はこちらを参照してください。 AWS CLI のインストールと設定
今回は JSON を扱うために jq
コマンドも使用しますのでインストールしてください。
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」→「クラスター」から確認できます。
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
インスタンスが起動完了するまで待ちましょう。
成功するとクラスターに登録されています。
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」で切り替えてみてください。
タスク終了後に新しくタスクを投入したい場合は、この「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 サブコマンドで実現する方法を記載します。