ECS Fargate でコンテナ内部をいじくってたら、いくつかやりたいことがあって、まとまりができたので更新です。
メタデータの扱いがわかれば大体イケるみたいな感じですが、たどり着くまでわりと時間かかったので、サクッとまとめ。
Fargate に渡される環境変数
最終的にはコンテナに sshd とか用意しないのがベターではあるのですが、やっぱりSSHは大正義。シェルで作業しないと、こーゆーのは捗りません。Fargate で基本的な値が入った環境変数はこのように取得できるので確認します。これがあれば、あとはトントン拍子に進みますね。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$ sudo strings /proc/1/environ | sort AWS_CONTAINER_CREDENTIALS_RELATIVE_URI=/v2/credentials/9e538a1a-7559-37b3-2162-eea9df72a33f AWS_DEFAULT_REGION=ap-northeast-1 AWS_EXECUTION_ENV=AWS_ECS_FARGATE AWS_REGION=ap-northeast-1 DEV_PACKAGES=build-base linux-headers ECS_CONTAINER_METADATA_URI=http://169.254.170.2/v3/a588a361-431f-427e-9bd1-295b8c8532ef HOME=/root HOSTNAME=ip-1-2-3-4.ap-northeast-1.compute.internal PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin PWD=/app RUN_PACKAGES=ruby ruby-dev libxml2-dev libxslt-dev SHLVL=1 |
IAM Role を有効にする
EC2 は profile を割り当てたり、他のリソースでも Role を指定すればそのままその権限が有効になる、みたいなイメージですが、ECS では一工夫する必要があります。- (参考) IAM Roles for Tasks
例えば、開発用として SSH で入れるようにしても、そのまま aws コマンドは打てません。IAM の情報をメタデータから取得する必要があり、さきほどの AWS_CONTAINER_CREDENTIALS_RELATIVE_URI のパスにリクエストを送ります。なぜホストを抜いてるか不明ですが、もう1つの URI にあるように、169.254.170.2 固定なので見てみると、
1 2 3 4 5 6 7 8 |
$ curl -s 169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI | jq { "RoleArn": "arn:aws:iam::0123456789:role/RoleName", "AccessKeyId": "...", "SecretAccessKey": "...", "Token": "...=", "Expiration": "2020-03-10T12:34:56Z" } |
鍵情報がとれるので、これをいつもの環境変数名に放り込むと、aws-cli が動くようになります。それを、例えばログイン時に自動的に有効にしたければ、こんなスクリプトを profile.d にでも置いておくことになります。
1 2 3 4 5 6 7 8 |
$ cat /etc/profile.d/credential.sh INIT_ENV=$(sudo strings /proc/1/environ) export $(echo "$INIT_ENV" | grep AWS_CONTAINER_CREDENTIALS_RELATIVE_URI) export $(echo "$INIT_ENV" | grep AWS_DEFAULT_REGION) AWS_CONTAINER_CREDENTIAL=$(curl -s 169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI) export AWS_ACCESS_KEY_ID=$(echo "$AWS_CONTAINER_CREDENTIAL" | jq .AccessKeyId -r) export AWS_SECRET_ACCESS_KEY=$(echo "$AWS_CONTAINER_CREDENTIAL" | jq .SecretAccessKey -r) export AWS_SESSION_TOKEN=$(echo "$AWS_CONTAINER_CREDENTIAL" | jq .Token -r) |
ECS の情報を取得する
もう1つの URI が提供されているので、こちらも見ておきます。中身を確認……すると、なんか条件が違うのかドキュメントと食い違いがあるようです。例えば Family がトップになかったり。なので、各々の環境で確認してみるとよさげです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
$ curl -s $ECS_CONTAINER_METADATA_URI | jq { "DockerId": "01234567890123456789", "Name": "cluster-name", "DockerName": "ecs-cluster-name-00-cluster-name-0123456789", "Image": "0123456789.dkr.ecr.ap-northeast-1.amazonaws.com/repository_name:service_name", "ImageID": "sha256:0123456789", "Labels": { "com.amazonaws.ecs.cluster": "arn:aws:ecs:ap-northeast-1:0123456789:cluster/cluster_name", "com.amazonaws.ecs.container-name": "cluster_name", "com.amazonaws.ecs.task-arn": "arn:aws:ecs:ap-northeast-1:0123456789:task/cluster_name/0123456789", "com.amazonaws.ecs.task-definition-family": "service_name", "com.amazonaws.ecs.task-definition-version": "12" }, "DesiredStatus": "RUNNING", "KnownStatus": "RUNNING", "Limits": { "CPU": 0, "Memory": 0 }, "CreatedAt": "2020-03-09T01:23:45.652003802Z", "StartedAt": "2020-03-09T01:23:45.001112712Z", "Type": "NORMAL", "Networks": [ { "NetworkMode": "awsvpc", "IPv4Addresses": [ "10.1.2.3" ] } ], "Volumes": [ { ... }, { ... } ] } |
さきほどと同じように、なにかしらのメタデータをコンテナ内で使いたい場合、こんな感じのスクリプトにすればよいでしょう。
1 2 3 4 5 |
INIT_ENV=$(sudo strings /proc/1/environ) export $(echo "$INIT_ENV" | grep ECS_CONTAINER_METADATA_URI) ECS_CONTAINER_METADATA=$(curl -s $ECS_CONTAINER_METADATA_URI) FAMILY=$(echo "$ECS_CONTAINER_METADATA" | jq -r '.Labels["com.amazonaws.ecs.task-definition-family"]') echo $FAMILY |
おとなしく Family が入ってれば、こんなふうにドット入りキーを扱わなくていいのに。とか、一応 FARGATE_SPOT なんだけど、その情報はここにはないのね。という程度の愚痴はこぼしておきます。
必要なコマンド
見たとおりなのでアレですが、この処理に必要なコマンドは、sudo, curl, jq
程度なので扱いやすいです。
自動起動するミドルウェアなどでは、sudo はいらないかも、くらいですね。
あ、忘れてたので最後になりますが、コンテナOS は alpine:latest でした。
今、また ECS で遊んでるんですが、多少面倒だったり気に食わないところがあるものの、わりと扱いやすくて好きですサッポロ。自宅でやるにはほどよいゲームなのです:-)