AWS

コンテナイメージのビルドからECSで動かすまでハンズオン

shio

こんにちは、シオです。
今回は AWS ECS について自分の中の理解がまだ甘い部分があったので、その辺の理解を深めるためにイメージのビルドから ECS で動かすまでのハンズオン的な記事を書こうと思います。

AWS リソースの作成などは aws cli v2 を利用していきます。

Dockerfile の準備

まずは動かすコンテナのための Dockerfile です。
とりあえず nginx の公式のイメージを動かします。
https://hub.docker.com/_/nginx

Dockerfile は下記です
(そのまま nginx を動かす感じです)

FROM nginx:latest

ECR へのプッシュ

そしたらイメージを作成して ECS で動かしていくんですが、
まずは ECR の準備からしていきます。

AWS ECR というのは Docker コンテナイメージを管理するレジストリになります。
コンテナイメージをプッシュして保存しておいて、他のところからプルすることができます。
自動でコンテナの脆弱性スキャンを走らせたりできる点も便利なところです。

とりあえず新しいリポジトリを作成します。
scanOnPush=trueを指定してプッシュしたときに脆弱性のスキャンが走るようにしています。

aws ecr create-repository \
  --repository-name my-repository \
  --image-scanning-configuration scanOnPush=true \
  --region ap-northeast-1

次にイメージのビルドです。
上で用意した Dockerfile をビルドします。

M1 Mac 上でビルドする場合は ECS 上で動かすために --platform amd64の指定が必要です。
(一度これが原因で ECS でのタスク起動ができずにハマりました)

{account-id}の部分は適宜読み替えてください。

docker build --platform amd64 -t {account-id}.dkr.ecr.ap-northeast-1.amazonaws.com/my-repository .

これでイメージのビルドができました。
イメージのリストにも新しいイメージが追加されているはずです。

docker images

そしたら ECR へのプッシュになるのですが、
まずは ECR へのログインが必要です。

get-login-password でパスワードを取得して、レジストリにログインします。

aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin {account-id}.dkr.ecr.ap-northeast-1.amazonaws.com

ログインができたらプッシュするだけです。

docker push {account-id}.dkr.ecr.ap-northeast-1.amazonaws.com/my-repository:latest

これで ECR へのイメージのプッシュができました。

ECS で動かしてみる

次はプッシュしたイメージを ECS 上で動かしてみます。
ECS はコンテナを実行・管理するサービスで、コンテナオーケストレーターと呼ばれるものです。

クラスターの作成

ここから ECS の設定をしていくのですが、まずはクラスターの作成からになります。

クラスターとは後述するタスクやサービスといったもののまとまりです。
​1 番大きいくくりとしてクラスターがあり、その中にサービス、タスクが動いているという構造です。

ということで最初にクラスターを作成します。

aws ecs create-cluster --cluster-name my-cluster

タスク定義

クラスターの作成ができたら、中で動かすタスクの定義を作成します。

タスク定義はどのようにコンテナを動かすのかを定義するもので、
コンテナで使用するイメージ、CPU、メモリ、割り当てる IAM ロールなどを定義するものです。

今回はタスク定義のパラメータは json として作成します。

コンテナイメージについては上で ECR にプッシュしたイメージを利用します。
またexecutionRoleArnについては、この後の手順で IAM ロールを作成します。
そのほかのパラメータについては、下記が参考になります。

パラメータ参考:https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/task_definition_parameters.html

json はこちら。task.jsonという名前で作成しました。

{
  "family": "my-task-definition",
  "networkMode": "awsvpc",
  "containerDefinitions": [
    {
      "name": "nginx",
      "image": "{account-id}.dkr.ecr.ap-northeast-1.amazonaws.com/my-repository:latest",
      "portMappings": [
        {
          "containerPort": 80
        }
      ],
      "essential": true
    }
  ],
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "256",
  "memory": "512",
  "executionRoleArn": "arn:aws:iam::{account-id}:role/ecsTaskExecutionRole"
}

作成した json を利用してタスク定義を登録します。
ファイルへのパスは適切なところを指定してください。

aws ecs register-task-definition --cli-input-json file://path-to/task.json

必要な IAM ロールを作る

ここまでで、タスク定義の作成が完了しました。

次にタスク実行に必要なロールも作っておきます。
json で信頼ポリシーを定義してそれを使って登録します。

こちらは下記が参考になると思います。
参考:https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/task_execution_IAM_role.html#create-task-execution-role

task-role.jsonという名前で作成しています。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "ecs-tasks.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

上記の json を使って IAM ロールを作成します。

aws iam create-role \
  --role-name ecsTaskExecutionRole \
  --assume-role-policy-document file://~/path-to/task-role.json

作成できたら、そのロールに必要なポリシーを追加して完了です。

aws iam attach-role-policy \
  --role-name ecsTaskExecutionRole \
  --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy

サービス作成

サービスとはタスクのまとまりのことで、
タスク定義に基づいたタスクを起動しておく数を設定しておくと、
それをタスク数を維持するために自動でタスクの起動をしてくれます。

またタスクとはタスク定義に基づいて起動されるコンテナになります。
今回のタスク定義だと1つのコンテナしかありませんが、複数のコンテナをタスク定義に含めると、それらのコンテナがまとめて 1 つのタスクとして起動されます。

今回のサービスではタスクを2つ起動して、ロードバランサーを挟んで振り分けられるようにします。

サブネットについてはパブリックサブネットを 2 つ利用します。
vpc やサブネットは必要に応じて作成してください。

まずはサービスで利用するロードバランサーの作成をします。​

aws elbv2 create-load-balancer --name my-ecs-alb \
  --subnets {subnet-id-1} {subnet-id-2} \
  --security-groups {sg-id}

次にターゲットグループを作成します。

aws elbv2 create-target-group --name my-target-group \
  --protocol HTTP --port 80 \
  --target-type ip \
  --vpc-id {vpc-id}

作成したロードバランサーにリスナーを登録します。
arn にはそれぞれ作成したものの arn を設定します。

aws elbv2 create-listener --load-balancer-arn {load-balancer-arn} \
  --protocol HTTP --port 80  \
  --default-actions Type=forward,TargetGroupArn={target-group-arn}

ロードバランサーが作成できたら、改めてサービスを作成します。

サービス定義についても json で定義します。

loadBalancers の targetGroupArn には上で作成したターゲットグループを、containaName にはタスク定義で作成するコンテナ名を指定します。
networkConfiguration には利用するサブネットとセキュリティグループを設定します。
そのほかのパラメータについては、下記を参考にしてください。

サービス定義パラメータ参考:https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/service_definition_parameters.html#sd-networkconfiguration

サービス定義のファイルは、service.jsonという名前で作成しました。

{
  "cluster": "my-cluster",
  "serviceName": "my-service",
  "taskDefinition": "my-task-definition",
  "launchType": "FARGATE",
  "loadBalancers": [
    {
      "targetGroupArn": "{target-group-arn}",
      "containerName": "nginx",
      "containerPort": 80
    }
  ],
  "desiredCount": 2,
  "networkConfiguration": {
    "awsvpcConfiguration": {
      "subnets": ["{subnet-id-1}", "{subnet-id-2}"],
      "securityGroups": ["{sg-id}"],
      "assignPublicIp": "ENABLED"
    }
  }
}

定義ファイルができたら、サービスを作成します。

aws ecs create-service --cli-input-json file://path-to/service.json

少しするとサービスのデプロイが完了します。
サービスが作成できたら、ブラウザなどからロードバランサーの DNS にアクセスすると nginx が動いていることが確認できるはずです。

無事にイメージのビルドから ECS で動かすところまで完了しました。

挙動を確認してみる

サービスの作成ができたので、挙動を確認してみます。

現状だとタスクが 2 つ動いている状態です。

aws ecs list-tasks --cluster my-cluster --service-name my-service

ここからサービスのタスクを 3 つに増やしてみます。

aws ecs update-service --cluster my-cluster --service my-service --desired-count 3

少し待つとタスクの数が 3 つになっていることが確認できます。

aws ecs list-tasks --cluster my-cluster --service-name my-service

今度は、動いているタスクのどれか 1 つを止めてみます。

aws ecs stop-task --cluster my-cluster \
  --task {task-arn}

するとタスクは一時的に 2 つになりますが、サービスの設定が 3 つになっているので少しすると新しいタスクが追加されて 3 つに戻るはずです。

これで簡単にですが自動復旧の挙動も確認できました。
以上でハンズオンも完了です。

AUTHOR
shio
shio
記事URLをコピーしました