Open3

ECS に Spring Boot アプリケーションをデプロイする

ita-jpita-jp

AWS のアカウントIDをローカルマシンの環境変数にセットしておく

コマンドラインで何回も使うので、勉強用としては環境変数に AWS アカウント ID をセットしておくと楽。

export AWS_ACCOUNT_ID=<your-aws-account-id>

ECR にリポジトリを作る

aws ecr create-repository --repository-name spring-app-repo --region ap-northeast-1

ECR にログインする

aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com

イメージを作る

./gradlew bootBuildImage

作成されたイメージの確認

docker images | grep api

イメージにタグを付ける

docker tag api:0.0.1-SNAPSHOT $AWS_ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/spring-app-repo:0.0.1-SNAPSHOT

補足:間違ったときは...

docker rmi $AWS_ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/<repo-name>:0.0.1-SNAPSHOT

イメージを ECR に push する

docker push $AWS_ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/spring-app-repo:0.0.1-SNAPSHOT
aws ecr describe-images --repository-name spring-app-repo --region ap-northeast-1
ita-jpita-jp

VPC を作る

VPC を作成

aws ec2 create-vpc --cidr-block 10.0.0.0/16

名前は作成時につけられないので、上のコマンドで出力された vpc-id を指定して tag 付けする

export CURRENT_VPC_ID=<your-id>
aws ec2 create-tags --resources $CURRENT_VPC_ID --tags Key=Name,Value=my-spring-vpc

パブリックサブネットの作成

aws ec2 create-subnet --vpc-id $CURRENT_VPC_ID --cidr-block 10.0.1.0/24 --availability-zone ap-northeast-1a

export CURRENT_PUBLIC_SUBNET_ID=subnet-<your-id>
aws ec2 create-tags --resources $CURRENT_PUBLIC_SUBNET_ID --tags Key=Name,Value=my-spring-public-subnet

TODO 別 az にも。lb で2つ必要

プライベートサブネットの作成

aws ec2 create-subnet --vpc-id $CURRENT_VPC_ID --cidr-block 10.0.2.0/24 --availability-zone ap-northeast-1a

export CURRENT_PRIVATE_SUBNET_ID=subnet-<your-id>
aws ec2 create-tags --resources $CURRENT_PRIVATE_SUBNET_ID --tags Key=Name,Value=my-spring-private-subnet

インターネットゲートウェイの作成とアタッチ

aws ec2 create-internet-gateway

export CURRENT_IGW_ID=igw-<your-id>
aws ec2 create-tags --resources $CURRENT_IGW_ID --tags Key=Name,Value=my-spring-igw

aws ec2 attach-internet-gateway --vpc-id $CURRENT_VPC_ID --internet-gateway-id $CURRENT_IGW_ID

ルートテーブルの作成

aws ec2 create-route-table --vpc-id $CURRENT_VPC_ID

export CURRENT_ROUTE_TABLE_ID=rtb-<your-id>
aws ec2 create-tags --resources $CURRENT_ROUTE_TABLE_ID --tags Key=Name,Value=my-spring-rtb

ルートの作成

aws ec2 create-route --route-table-id $CURRENT_ROUTE_TABLE_ID --destination-cidr-block 0.0.0.0/0 --gateway-id $CURRENT_IGW_ID

ルートテーブルをパブリックサブネットに関連付ける

aws ec2 associate-route-table --route-table-id $CURRENT_ROUTE_TABLE_ID --subnet-id $CURRENT_PUBLIC_SUBNET_ID

TODO 別 az の public subnet にも

NAT ゲートウェイの作成

Elastic IP アドレスの作成

aws ec2 allocate-address
export CURRENT_EIP_ALLOC_ID=eipalloc-<your-id>

NAT ゲートウェイの作成

aws ec2 create-nat-gateway --subnet-id $CURRENT_PUBLIC_SUBNET_ID --allocation-id $CURRENT_EIP_ALLOC_ID
export CURRENT_NAT_GW_ID=nat-<your-id>

プライベートサブネットのルートテーブル作成

aws ec2 create-route-table --vpc-id $CURRENT_VPC_ID
export CURRENT_SUBNET_ROUTE_TABLE_ID=rtb-<your-id>
aws ec2 create-tags --resources $CURRENT_SUBNET_ROUTE_TABLE_ID --tags Key=Name,Value=my-spring-private-subnet-rtb

ルートの作成

NAT ゲートウェイのデフォルトルートを追加する

aws ec2 create-route --route-table-id $CURRENT_SUBNET_ROUTE_TABLE_ID --destination-cidr-block 0.0.0.0/0 --nat-gateway-id $CURRENT_NAT_GW_ID

ルートテーブルをプライベートサブネットに関連付ける

aws ec2 associate-route-table --route-table-id $CURRENT_SUBNET_ROUTE_TABLE_ID --subnet-id $CURRENT_PRIVATE_SUBNET_ID
ita-jpita-jp

ECS クラスターの作成

export CURRENT_ECS_CLUSTER_NAME=my-spring-cluster
aws ecs create-cluster --cluster-name $CURRENT_ECS_CLUSTER_NAME

ロールを作成する

ECS タスクが ECR からイメージを取得できるようにするため。

TODO assume role

trust-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ecs-tasks.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
aws iam create-role --role-name ecsTaskExecutionRole --assume-role-policy-document file://trust-policy.json

作成したロールに ECR からイメージをプルするためのポリシーをアタッチ

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

タスク定義の作成

task-definition.json
{
  "family": "my-spring-task-definition",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "executionRoleArn": "arn:aws:iam::<your-account-id>:role/ecsTaskExecutionRole",
  "cpu": "256",
  "memory": "512",
  "containerDefinitions": [
    {
      "name": "<container-name>",
      "image": "<aws-account-id>.dkr.ecr.ap-northeast-1.amazonaws.com/spring-app-repo:0.0.1-SNAPSHOT",
      "essential": true,
      "portMappings": [
        {
          "containerPort": 8080,
          "hostPort": 8080,
          "protocol": "tcp"
        }
      ]
    }
  ]
}
aws ecs register-task-definition --cli-input-json file://task-definition.json

ALB 用のセキュリティグループを作成する

後に設定する ECS のセキュリティグループで、source security group に指定したい

aws ec2 create-security-group --group-name my-spring-alb-sg --description "ALB Security Group" --vpc-id $CURRENT_VPC_ID
export CURRENT_ALB_SG_ID=sg-<your-id>

80番ポートへのすべてのアクセスを許可

aws ec2 authorize-security-group-ingress --group-id $CURRENT_ALB_SG_ID --protocol tcp --port 80 --cidr 0.0.0.0/0

ECS のセキュリティグループの作成

aws ec2 create-security-group --group-name my-spring-ecs-sg --description "ECS Service Security Group" --vpc-id $CURRENT_VPC_ID
export CURRENT_ECS_SG_ID=sg-<your-id>

インバウンドルールを追加(ALB からポート8080への通信のみ許可)

aws ec2 authorize-security-group-ingress --group-id $CURRENT_ECS_SG_ID --protocol tcp --port 8080 --source-group $CURRENT_ALB_SG_ID

ALB のターゲットグループを作成

aws elbv2 create-target-group \
  --name my-spring-alb-tg \
  --protocol HTTP \
  --port 8080 \
  --vpc-id $CURRENT_VPC_ID \
  --target-type ip
export CURRENT_ALB_TG_ECS_ARN=<your-arn>

ALB を作成

aws elbv2 create-load-balancer \
  --name my-spring-lb \
  --subnets $CURRENT_PUBLIC_SUBNET_ID $CURRENT_PUBLIC_SUBNET_ID_AZ2 \
  --security-groups $CURRENT_ALB_SG_ID
export CURRENT_LB_ARN=<your-arn>

ALB のリスナーを設定

aws elbv2 create-listener \
  --load-balancer-arn $CURRENT_LB_ARN \
  --protocol HTTP \
  --port 80 \
  --default-actions Type=forward,TargetGroupArn=$CURRENT_ALB_TG_ECS_ARN

ECS サービスの作成

export CURRENT_ECS_SERVICE_NAME=my-spring-service
aws ecs create-service \
  --cluster $CURRENT_ECS_CLUSTER_NAME \
  --service-name my-spring-service \
  --task-definition my-spring-task-definition \
  --desired-count 3 \
  --launch-type FARGATE \
  --network-configuration "awsvpcConfiguration={subnets=[$CURRENT_PRIVATE_SUBNET_ID],securityGroups=[$CURRENT_ECS_SG_ID],assignPublicIp=DISABLED}" \
  --load-balancers "targetGroupArn=$CURRENT_ALB_TG_ECS_ARN,containerName=my-spring-container,containerPort=8080"

サービスの状態確認

aws ecs describe-services --cluster $CURRENT_ECS_CLUSTER_NAME --services $CURRENT_ECS_SERVICE_NAME