CDKでECS Fargateを構築してみよう!

CDKでECS Fargateを構築してみよう!
この記事をシェアする

はじめに

こんにちは、スカイアーチHRソリューションズのsugawaraです。
今回はCDKを用いてECS Fargateをサクッと構築したいと思います。手動で構築するとなると、なかなか時間がかかりますが、CDKのL3 Constructを利用すれば一瞬です。CDKを利用したことがない人は是非やってみてください。

対象読者

  • まだCDKを使ったことない人
  • CDKにちょっと興味がある方

構成図

今回のCDKで作成されるリソースの簡易構成図です。ECS Clusterなど細かい部分は省略しています。

構築手順

今回は初期設定の簡略化のため、Cloud9を利用します。まずはCloud9作成画面へ進みましょう。インスタンスタイプはt3.smallのAmazon Linux 2を選択します。なお、t2.microの場合には、Cloud9がうまく起動されない可能性がありますので非推奨です。

接続については、不要なポートを開ける必要のないSSMを選択します。なお、VPC設定はデフォルトのままです。もしデフォルトVPC以外にデプロイしたい場合、VPC設定より任意のVPCを選択してください。

Cloud9の一覧に作成したものが表示されたら、『開く』をクリックしてClou9環境へ移動します。

下記のような画面に遷移したら準備完了です。

まずはターミナル上にて、下記のコマンドでAWS CDKがインストールされていることを確認します。

$ cdk --version
2.82.0 (build 3a8648a)

AWS CDKのインストールを確認できたら、作業用ディレクトリを作成して移動します。

$ mkdir sample-fargate && cd sample-fargate

それでは、TypeScriptのCDKプロジェクトを作成しましょう。cdk initコマンドを実行すると、下記のような出力になるはずです。All doneが表示されれば問題ありません。

$ cdk init sample-app --language typescript
Applying project template sample-app for typescript
# Welcome to your CDK TypeScript project

You should explore the contents of this project. It demonstrates a CDK app with an instance of a stack (`SampleFargateStack`)
which contains an Amazon SQS queue that is subscribed to an Amazon SNS topic.

The `cdk.json` file tells the CDK Toolkit how to execute your app.

## Useful commands

* `npm run build`   compile typescript to js
* `npm run watch`   watch for changes and compile
* `npm run test`    perform the jest unit tests
* `cdk deploy`      deploy this stack to your default AWS account/region
* `cdk diff`        compare deployed stack with current state
* `cdk synth`       emits the synthesized CloudFormation template

Initializing a new git repository...
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint: 
hint:   git config --global init.defaultBranch <name>
hint: 
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint: 
hint:   git branch -m <name>
Executing npm install...
✅ All done!

NOTICES         (What's this? https://github.com/aws/aws-cdk/wiki/CLI-Notices)

25714   (cli) cdk deploy fails when stacks or stages share assets

        Overview: A single asset used in two stacks or stages that depend on
                  each other causes 'cdk deploy' to exit early.

        Affected versions: cli: >=2.80.0 <2.83.1

        More information at: https://github.com/aws/aws-cdk/issues/25714


If you don’t want to see a notice anymore, use "cdk acknowledge <id>". For example, "cdk acknowledge 25714".

下記はcdk initコマンドで作成されるフォルダ構成です。Cloud9画面の左側でも確認ができます。今回修正を加えるのは、lib配下のsample-fargate-stack.tsと、bin配下のsample-fargate.tsだけです。

sample-fargate/
├── bin
│   └── sample-fargate.ts           // CDKアプリのエントリポイントで最初に実行される。スタックをロードする
├── cdk.json
├── jest.config.js
├── lib
│   └── sample-fargate-stack.ts     // メインスタックを定義
├── node_modules
├── package.json
├── package-lock.json
├── README.md
├── test
└── tsconfig.json              

修正する二つのコードの中身を確認します。まずsample-fargate.tsですが、/lib/sample-fargate-stackで定義されたSampleFargateStackというスタックを生成するために新しいインスタンスを作成しています。

#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib';
import { SampleFargateStack } from '../lib/sample-fargate-stack';

const app = new cdk.App();
new SampleFargateStack(app, 'SampleFargateStack');

sample-fargate-stack.tsでは、SQSとSNSトピックスを作成し、SNSトピックをサブスクライブするコードとなります。

import { Duration, Stack, StackProps } from 'aws-cdk-lib';
import * as sns from 'aws-cdk-lib/aws-sns';
import * as subs from 'aws-cdk-lib/aws-sns-subscriptions';
import * as sqs from 'aws-cdk-lib/aws-sqs';
import { Construct } from 'constructs';

export class SampleFargateStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const queue = new sqs.Queue(this, 'SampleFargateQueue', {
      visibilityTimeout: Duration.seconds(300)
    });

    const topic = new sns.Topic(this, 'SampleFargateTopic');

    topic.addSubscription(new subs.SqsSubscription(queue));
  }
}

上記のコードのまま、cdk synthコマンドを実行しましょう。このコマンドは、上記のコードをCloudFormationテンプレートへと変換してくれます。CDKはCloudFormationよりも少ないコード量で記述することができますが、実態としては、その裏でCloudFormationテンプレートが生成・実行されています。
なお、生成された CloudFormation テンプレートは新規作成されるcdk.outフォルダに出力されます。

$ cdk synth
Resources:
  SampleFargateQueue5B43C956:
    Type: AWS::SQS::Queue
    Properties:
      VisibilityTimeout: 300
    UpdateReplacePolicy: Delete
    DeletionPolicy: Delete
    Metadata:
      aws:cdk:path: SampleFargateStack/SampleFargateQueue/Resource
  SampleFargateQueuePolicyC8B6265D:
    Type: AWS::SQS::QueuePolicy
    Properties:
      PolicyDocument:
        Statement:
          - Action: sqs:SendMessage
            Condition:
              ArnEquals:
                aws:SourceArn:
                  Ref: SampleFargateTopic84C38F71
            Effect: Allow
            Principal:
              Service: sns.amazonaws.com
            Resource:
              Fn::GetAtt:
                - SampleFargateQueue5B43C956
                - Arn
        Version: "2012-10-17"
      Queues:
        - Ref: SampleFargateQueue5B43C956
    Metadata:
      aws:cdk:path: SampleFargateStack/SampleFargateQueue/Policy/Resource
  SampleFargateQueueSampleFargateStackSampleFargateTopic658E316CE6158F22:
    Type: AWS::SNS::Subscription
    Properties:
      Protocol: sqs
      TopicArn:
        Ref: SampleFargateTopic84C38F71
      Endpoint:
        Fn::GetAtt:
          - SampleFargateQueue5B43C956
          - Arn
    DependsOn:
      - SampleFargateQueuePolicyC8B6265D
    Metadata:
      aws:cdk:path: SampleFargateStack/SampleFargateQueue/SampleFargateStackSampleFargateTopic658E316C/Resource
  SampleFargateTopic84C38F71:
    Type: AWS::SNS::Topic
    Metadata:
      aws:cdk:path: SampleFargateStack/SampleFargateTopic/Resource
  CDKMetadata:
    Type: AWS::CDK::Metadata
    Properties:
      Analytics: v2:deflate64:H4sIAAAAAAAA/1WOQQ7CIBBFz+IexsrCuO8FtHVvWsBk2gqVAY0h3F2giYmbmfdffjIj4CSg2Q1v4lLNfMERYu8HObOsbpGeBPESdNCsvZsN6jzbBeXnJ7eYGJnc78NI0uHq0ZrS+MtXu6IstkJKBTtNNjhZb7TWKCzNxIxVGibavw5HEE3+ciJE7oLx+NDQbfsLFlsCycEAAAA=
    Metadata:
      aws:cdk:path: SampleFargateStack/CDKMetadata/Default
    Condition: CDKMetadataAvailable
Conditions:
  CDKMetadataAvailable:
    Fn::Or:
      - Fn::Or:
          - Fn::Equals:
              - Ref: AWS::Region
              - af-south-1
          - Fn::Equals:
              - Ref: AWS::Region
              - ap-east-1
          - Fn::Equals:
              - Ref: AWS::Region
              - ap-northeast-1
          - Fn::Equals:
              - Ref: AWS::Region
              - ap-northeast-2
          - Fn::Equals:
              - Ref: AWS::Region
              - ap-south-1
          - Fn::Equals:
              - Ref: AWS::Region
              - ap-southeast-1
          - Fn::Equals:
              - Ref: AWS::Region
              - ap-southeast-2
          - Fn::Equals:
              - Ref: AWS::Region
              - ca-central-1
          - Fn::Equals:
              - Ref: AWS::Region
              - cn-north-1
          - Fn::Equals:
              - Ref: AWS::Region
              - cn-northwest-1
      - Fn::Or:
          - Fn::Equals:
              - Ref: AWS::Region
              - eu-central-1
          - Fn::Equals:
              - Ref: AWS::Region
              - eu-north-1
          - Fn::Equals:
              - Ref: AWS::Region
              - eu-south-1
          - Fn::Equals:
              - Ref: AWS::Region
              - eu-west-1
          - Fn::Equals:
              - Ref: AWS::Region
              - eu-west-2
          - Fn::Equals:
              - Ref: AWS::Region
              - eu-west-3
          - Fn::Equals:
              - Ref: AWS::Region
              - me-south-1
          - Fn::Equals:
              - Ref: AWS::Region
              - sa-east-1
          - Fn::Equals:
              - Ref: AWS::Region
              - us-east-1
          - Fn::Equals:
              - Ref: AWS::Region
              - us-east-2
      - Fn::Or:
          - Fn::Equals:
              - Ref: AWS::Region
              - us-west-1
          - Fn::Equals:
              - Ref: AWS::Region
              - us-west-2
Parameters:
  BootstrapVersion:
    Type: AWS::SSM::Parameter::Value<String>
    Default: /cdk-bootstrap/hnb659fds/version
    Description: Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]
Rules:
  CheckBootstrapVersion:
    Assertions:
      - Assert:
          Fn::Not:
            - Fn::Contains:
                - - "1"
                  - "2"
                  - "3"
                  - "4"
                  - "5"
                - Ref: BootstrapVersion
        AssertDescription: CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI.

CDKのコードには修正は加えず、一旦ここでAWSリソースをデプロイしましょう。もし初めてCDKを使用するなら、まずcdk bootstrapコマンドを実行し、ブートストラップスタック(CDKToolKitスタック)をデプロイする必要があります。すでに過去にcdk bootstrapを実行済みの場合、こちらは実行不要です。

$ cdk bootstrap
 ⏳  Bootstrapping environment aws://XXXXXXXXXXXX/ap-northeast-1...
Trusted accounts for deployment: (none)
Trusted accounts for lookup: (none)
Using default execution policy of 'arn:aws:iam::aws:policy/AdministratorAccess'. Pass '--cloudformation-execution-policies' to customize.
CDKToolkit: creating CloudFormation changeset...
 ✅  Environment aws://XXXXXXXXXXXX/ap-northeast-1 bootstrapped.

cdk bootstrapコマンドを実行すると、CloudFormationスタック一覧にCDKToolKitがデプロイされます。今後もCDK利用する場合、こちらは削除せずに残しておきましょう。

もともとあるコードをcdk deployコマンドでデプロイしてみます。デプロイの許可を求められたら、yを押しましょう。

$ cdk deploy
✨  Synthesis time: 8.12s

current credentials could not be used to assume 'arn:aws:iam::XXXXXXXXXXXX:role/cdk-hnb659fds-deploy-role-XXXXXXXXXXXX-ap-northeast-1', but are for the right account. Proceeding anyway.
SampleFargateStack:  start: Building dddadb86b367c12835c7def6c62171318db5cc97903b25d64b9cef6604078c3e:current_account-current_region
SampleFargateStack:  success: Built dddadb86b367c12835c7def6c62171318db5cc97903b25d64b9cef6604078c3e:current_account-current_region
SampleFargateStack:  start: Publishing dddadb86b367c12835c7def6c62171318db5cc97903b25d64b9cef6604078c3e:current_account-current_region
current credentials could not be used to assume 'arn:aws:iam::XXXXXXXXXXXX:role/cdk-hnb659fds-file-publishing-role-XXXXXXXXXXXX-ap-northeast-1', but are for the right account. Proceeding anyway.
SampleFargateStack:  success: Published dddadb86b367c12835c7def6c62171318db5cc97903b25d64b9cef6604078c3e:current_account-current_region
current credentials could not be used to assume 'arn:aws:iam::XXXXXXXXXXXX:role/cdk-hnb659fds-lookup-role-XXXXXXXXXXXX-ap-northeast-1', but are for the right account. Proceeding anyway.
(To get rid of this warning, please upgrade to bootstrap version >= 8)
current credentials could not be used to assume 'arn:aws:iam::XXXXXXXXXXXX:role/cdk-hnb659fds-deploy-role-XXXXXXXXXXXX-ap-northeast-1', but are for the right account. Proceeding anyway.
This deployment will make potentially sensitive changes according to your current security approval level (--require-approval broadening).
Please confirm you intend to make the following modifications:
IAM Statement Changes
┌───┬───────────────────────────┬────────┬─────────────────┬───────────────────────────┬───────────────────────────┐
│   │ Resource                  │ Effect │ Action          │ Principal                 │ Condition                 │
├───┼───────────────────────────┼────────┼─────────────────┼───────────────────────────┼───────────────────────────┤
│ + │ ${SampleFargateQueue.Arn} │ Allow  │ sqs:SendMessage │ Service:sns.amazonaws.com │ "ArnEquals": {            │
│   │                           │        │                 │                           │   "aws:SourceArn": "${Sam │
│   │                           │        │                 │                           │ pleFargateTopic}"         │
│   │                           │        │                 │                           │ }                         │
└───┴───────────────────────────┴────────┴─────────────────┴───────────────────────────┴───────────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)
Do you wish to deploy these changes (y/n)? 

初回デプロイは少し時間がかかりますが、完了すると下記のような表示になります。

Do you wish to deploy these changes (y/n)? y
SampleFargateStack: deploying... [1/1]
SampleFargateStack: creating CloudFormation changeset...

 ✅  SampleFargateStack

✨  Deployment time: 87.06s

Stack ARN:
arn:aws:cloudformation:ap-northeast-1:XXXXXXXXXXXX:stack/SampleFargateStack/a21729d0-0b6a-11ee-a535-0e3e78b10d2f

✨  Total time: 95.18s

CloudFormationのコンソール画面にて、SampleFargateStackがデプロイされたのが確認できます。

リソースタブを見てみると、ちゃんとSQSとSNSが作成されているようです。

ここからCDKのコードを修正していきます。SQS、SNSトピック、SNSのサブスクライブは不要なのですべて削除し、新たに下記のコードをsample-fargate-stack.tsへ貼り付けてください。(今回はAWSが公開しているコードを参考にしています)

import ec2 = require('aws-cdk-lib/aws-ec2');
import ecs = require('aws-cdk-lib/aws-ecs');
import ecs_patterns = require('aws-cdk-lib/aws-ecs-patterns');
import cdk = require('aws-cdk-lib');

export { SampleFargateStack };

class SampleFargateStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // Create VPC and Fargate Cluster
    // NOTE: Limit AZs to avoid reaching resource quotas
    const vpc = new ec2.Vpc(this, 'MyVpc', { maxAzs: 2 });
    const cluster = new ecs.Cluster(this, 'Cluster', { vpc });

    // Instantiate Fargate Service with just cluster and image
    new ecs_patterns.ApplicationLoadBalancedFargateService(this, "FargateService", {
      cluster,
      taskImageOptions: {
        image: ecs.ContainerImage.fromRegistry("amazon/amazon-ecs-sample"),
      },
    });
  }
}

sample-fargate.tsは修正は加えず、cdk diffコマンドを実行して差分を見てみましょう。[-]がスタックから削除されるもの、[+]が新たに作成されるものとなります。SQSやSNSが削除され、VPCやECSなどが作成されることがわかります。

$ cdk diff
Stack SampleFargateStack
current credentials could not be used to assume 'arn:aws:iam::XXXXXXXXXXXX:role/cdk-hnb659fds-lookup-role-XXXXXXXXXXXX-ap-northeast-1', but are for the right account. Proceeding anyway.
(To get rid of this warning, please upgrade to bootstrap version >= 8)
current credentials could not be used to assume 'arn:aws:iam::XXXXXXXXXXXX:role/cdk-hnb659fds-deploy-role-XXXXXXXXXXXX-ap-northeast-1', but are for the right account. Proceeding anyway.
IAM Statement Changes
┌───┬─────────────┬────────┬─────────────┬─────────────┬─────────────┐
│   │ Resource    │ Effect │ Action      │ Principal   │ Condition   │
├───┼─────────────┼────────┼─────────────┼─────────────┼─────────────┤
│ - │ ${SampleFar │ Allow  │ sqs:SendMes │ Service:sns │ "ArnEquals" │
│   │ gateQueue.A │        │ sage        │ .amazonaws. │ : {         │
│   │ rn}         │        │             │ com         │   "aws:Sour │
│   │             │        │             │             │ ceArn": "${ │
│   │             │        │             │             │ SampleFarga │
│   │             │        │             │             │ teTopic}"   │
│   │             │        │             │             │ }           │
├───┼─────────────┼────────┼─────────────┼─────────────┼─────────────┤
│ + │ ${Custom::V │ Allow  │ sts:AssumeR │ Service:lam │             │
│   │ pcRestrictD │        │ ole         │ bda.amazona │             │
│   │ efaultSGCus │        │             │ ws.com      │             │
│   │ tomResource │        │             │             │             │
│   │ Provider/Ro │        │             │             │             │
│   │ le.Arn}     │        │             │             │             │
├───┼─────────────┼────────┼─────────────┼─────────────┼─────────────┤
│ + │ ${FargateSe │ Allow  │ sts:AssumeR │ Service:ecs │             │
│   │ rvice/TaskD │        │ ole         │ -tasks.amaz │             │
│   │ ef/Executio │        │             │ onaws.com   │             │
│   │ nRole.Arn}  │        │             │             │             │
├───┼─────────────┼────────┼─────────────┼─────────────┼─────────────┤
│ + │ ${FargateSe │ Allow  │ sts:AssumeR │ Service:ecs │             │
│   │ rvice/TaskD │        │ ole         │ -tasks.amaz │             │
│   │ ef/TaskRole │        │             │ onaws.com   │             │
│   │ .Arn}       │        │             │             │             │
├───┼─────────────┼────────┼─────────────┼─────────────┼─────────────┤
│ + │ ${FargateSe │ Allow  │ logs:Create │ AWS:${Farga │             │
│   │ rvice/TaskD │        │ LogStream   │ teService/T │             │
│   │ ef/web/LogG │        │ logs:PutLog │ askDef/Exec │             │
│   │ roup.Arn}   │        │ Events      │ utionRole}  │             │
├───┼─────────────┼────────┼─────────────┼─────────────┼─────────────┤
│ + │ arn:${AWS:: │ Allow  │ ec2:Authori │ AWS:${Custo │             │
│   │ Partition}: │        │ zeSecurityG │ m::VpcRestr │             │
│   │ ec2:${AWS:: │        │ roupEgress  │ ictDefaultS │             │
│   │ Region}:${A │        │ ec2:Authori │ GCustomReso │             │
│   │ WS::Account │        │ zeSecurityG │ urceProvide │             │
│   │ Id}:securit │        │ roupIngress │ r/Role}     │             │
│   │ y-group/${M │        │ ec2:RevokeS │             │             │
│   │ yVpcF9F0CA6 │        │ ecurityGrou │             │             │
│   │ F.DefaultSe │        │ pEgress     │             │             │
│   │ curityGroup │        │ ec2:RevokeS │             │             │
│   │ }           │        │ ecurityGrou │             │             │
│   │             │        │ pIngress    │             │             │
└───┴─────────────┴────────┴─────────────┴─────────────┴─────────────┘
IAM Policy Changes
┌───┬───────────────────────────────┬────────────────────────────────┐
│   │ Resource                      │ Managed Policy ARN             │
├───┼───────────────────────────────┼────────────────────────────────┤
│ + │ ${Custom::VpcRestrictDefaultS │ {"Fn::Sub":"arn:${AWS::Partiti │
│   │ GCustomResourceProvider/Role} │ on}:iam::aws:policy/service-ro │
│   │                               │ le/AWSLambdaBasicExecutionRole │
│   │                               │ "}                             │
└───┴───────────────────────────────┴────────────────────────────────┘
Security Group Changes
┌───┬──────────────────────┬─────┬────────────┬──────────────────────┐
│   │ Group                │ Dir │ Protocol   │ Peer                 │
├───┼──────────────────────┼─────┼────────────┼──────────────────────┤
│ + │ ${FargateService/LB/ │ In  │ TCP 80     │ Everyone (IPv4)      │
│   │ SecurityGroup.GroupI │     │            │                      │
│   │ d}                   │     │            │                      │
│ + │ ${FargateService/LB/ │ Out │ TCP 80     │ ${FargateService/Ser │
│   │ SecurityGroup.GroupI │     │            │ vice/SecurityGroup.G │
│   │ d}                   │     │            │ roupId}              │
├───┼──────────────────────┼─────┼────────────┼──────────────────────┤
│ + │ ${FargateService/Ser │ In  │ TCP 80     │ ${FargateService/LB/ │
│   │ vice/SecurityGroup.G │     │            │ SecurityGroup.GroupI │
│   │ roupId}              │     │            │ d}                   │
│ + │ ${FargateService/Ser │ Out │ Everything │ Everyone (IPv4)      │
│   │ vice/SecurityGroup.G │     │            │                      │
│   │ roupId}              │     │            │                      │
└───┴──────────────────────┴─────┴────────────┴──────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)

Resources
[-] AWS::SQS::Queue SampleFargateQueue SampleFargateQueue5B43C956 destroy
[-] AWS::SQS::QueuePolicy SampleFargateQueue/Policy SampleFargateQueuePolicyC8B6265D destroy
[-] AWS::SNS::Subscription SampleFargateQueue/SampleFargateStackSampleFargateTopic658E316C SampleFargateQueueSampleFargateStackSampleFargateTopic658E316CE6158F22 destroy
[-] AWS::SNS::Topic SampleFargateTopic SampleFargateTopic84C38F71 destroy
[+] AWS::EC2::VPC MyVpc MyVpcF9F0CA6F 
[+] AWS::EC2::Subnet MyVpc/PublicSubnet1/Subnet MyVpcPublicSubnet1SubnetF6608456 
[+] AWS::EC2::RouteTable MyVpc/PublicSubnet1/RouteTable MyVpcPublicSubnet1RouteTableC46AB2F4 
[+] AWS::EC2::SubnetRouteTableAssociation MyVpc/PublicSubnet1/RouteTableAssociation MyVpcPublicSubnet1RouteTableAssociation2ECEE1CB 
[+] AWS::EC2::Route MyVpc/PublicSubnet1/DefaultRoute MyVpcPublicSubnet1DefaultRoute95FDF9EB 
[+] AWS::EC2::EIP MyVpc/PublicSubnet1/EIP MyVpcPublicSubnet1EIP096967CB 
[+] AWS::EC2::NatGateway MyVpc/PublicSubnet1/NATGateway MyVpcPublicSubnet1NATGatewayAD3400C1 
[+] AWS::EC2::Subnet MyVpc/PublicSubnet2/Subnet MyVpcPublicSubnet2Subnet492B6BFB 
[+] AWS::EC2::RouteTable MyVpc/PublicSubnet2/RouteTable MyVpcPublicSubnet2RouteTable1DF17386 
[+] AWS::EC2::SubnetRouteTableAssociation MyVpc/PublicSubnet2/RouteTableAssociation MyVpcPublicSubnet2RouteTableAssociation227DE78D 
[+] AWS::EC2::Route MyVpc/PublicSubnet2/DefaultRoute MyVpcPublicSubnet2DefaultRoute052936F6 
[+] AWS::EC2::EIP MyVpc/PublicSubnet2/EIP MyVpcPublicSubnet2EIP8CCBA239 
[+] AWS::EC2::NatGateway MyVpc/PublicSubnet2/NATGateway MyVpcPublicSubnet2NATGateway91BFBEC9 
[+] AWS::EC2::Subnet MyVpc/PrivateSubnet1/Subnet MyVpcPrivateSubnet1Subnet5057CF7E 
[+] AWS::EC2::RouteTable MyVpc/PrivateSubnet1/RouteTable MyVpcPrivateSubnet1RouteTable8819E6E2 
[+] AWS::EC2::SubnetRouteTableAssociation MyVpc/PrivateSubnet1/RouteTableAssociation MyVpcPrivateSubnet1RouteTableAssociation56D38C7E 
[+] AWS::EC2::Route MyVpc/PrivateSubnet1/DefaultRoute MyVpcPrivateSubnet1DefaultRouteA8CDE2FA 
[+] AWS::EC2::Subnet MyVpc/PrivateSubnet2/Subnet MyVpcPrivateSubnet2Subnet0040C983 
[+] AWS::EC2::RouteTable MyVpc/PrivateSubnet2/RouteTable MyVpcPrivateSubnet2RouteTableCEDCEECE 
[+] AWS::EC2::SubnetRouteTableAssociation MyVpc/PrivateSubnet2/RouteTableAssociation MyVpcPrivateSubnet2RouteTableAssociation86A610DA 
[+] AWS::EC2::Route MyVpc/PrivateSubnet2/DefaultRoute MyVpcPrivateSubnet2DefaultRoute9CE96294 
[+] AWS::EC2::InternetGateway MyVpc/IGW MyVpcIGW5C4A4F63 
[+] AWS::EC2::VPCGatewayAttachment MyVpc/VPCGW MyVpcVPCGW488ACE0D 
[+] Custom::VpcRestrictDefaultSG MyVpc/RestrictDefaultSecurityGroupCustomResource MyVpcRestrictDefaultSecurityGroupCustomResourceA4FCCD62 
[+] AWS::IAM::Role Custom::VpcRestrictDefaultSGCustomResourceProvider/Role CustomVpcRestrictDefaultSGCustomResourceProviderRole26592FE0 
[+] AWS::Lambda::Function Custom::VpcRestrictDefaultSGCustomResourceProvider/Handler CustomVpcRestrictDefaultSGCustomResourceProviderHandlerDC833E5E 
[+] AWS::ECS::Cluster Cluster ClusterEB0386A7 
[+] AWS::ElasticLoadBalancingV2::LoadBalancer FargateService/LB FargateServiceLBB353E155 
[+] AWS::EC2::SecurityGroup FargateService/LB/SecurityGroup FargateServiceLBSecurityGroup5F444C78 
[+] AWS::EC2::SecurityGroupEgress FargateService/LB/SecurityGroup/to SampleFargateStackFargateServiceSecurityGroup79494138:80 FargateServiceLBSecurityGrouptoSampleFargateStackFargateServiceSecurityGroup7949413880DEE5C679 
[+] AWS::ElasticLoadBalancingV2::Listener FargateService/LB/PublicListener FargateServiceLBPublicListener4B4929CA 
[+] AWS::ElasticLoadBalancingV2::TargetGroup FargateService/LB/PublicListener/ECSGroup FargateServiceLBPublicListenerECSGroupBE57E081 
[+] AWS::IAM::Role FargateService/TaskDef/TaskRole FargateServiceTaskDefTaskRole8CDCF85E 
[+] AWS::ECS::TaskDefinition FargateService/TaskDef FargateServiceTaskDef940E3A80 
[+] AWS::Logs::LogGroup FargateService/TaskDef/web/LogGroup FargateServiceTaskDefwebLogGroup71FAF541 
[+] AWS::IAM::Role FargateService/TaskDef/ExecutionRole FargateServiceTaskDefExecutionRole9194820E 
[+] AWS::IAM::Policy FargateService/TaskDef/ExecutionRole/DefaultPolicy FargateServiceTaskDefExecutionRoleDefaultPolicy827E7CA2 
[+] AWS::ECS::Service FargateService/Service/Service FargateServiceECC8084D 
[+] AWS::EC2::SecurityGroup FargateService/Service/SecurityGroup FargateServiceSecurityGroup262B61DD 
[+] AWS::EC2::SecurityGroupIngress FargateService/Service/SecurityGroup/from SampleFargateStackFargateServiceLBSecurityGroup70AAEB81:80 FargateServiceSecurityGroupfromSampleFargateStackFargateServiceLBSecurityGroup70AAEB818081677492 

Outputs
[+] Output FargateService/LoadBalancerDNS FargateServiceLoadBalancerDNS9433D5F6: {"Value":{"Fn::GetAtt":["FargateServiceLBB353E155","DNSName"]}}
[+] Output FargateService/ServiceURL FargateServiceServiceURL47701F45: {"Value":{"Fn::Join":["",["http://",{"Fn::GetAtt":["FargateServiceLBB353E155","DNSName"]}]]}}

意図した通りの差分が表示されたら、今度はcdk deployコマンドを実行しましょう。すでに差分は確認済みのため、yを押してデプロイしてください。

cdk deploy

✨  Synthesis time: 8.44s

current credentials could not be used to assume 'arn:aws:iam::XXXXXXXXXXXX:role/cdk-hnb659fds-deploy-role-XXXXXXXXXXXXX-ap-northeast-1', but are for the right account. Proceeding anyway.
SampleFargateStack:  start: Building e77031893275c08bcaa0a774aa8b611727afd045b3b5d8e1e6f0f04063d9d386:current_account-current_region
SampleFargateStack:  success: Built e77031893275c08bcaa0a774aa8b611727afd045b3b5d8e1e6f0f04063d9d386:current_account-current_region
SampleFargateStack:  start: Building 2cd594a111dc61319dd6398d8e59dd5ea38b51bab58d66badff5365d7a605e46:current_account-current_region
SampleFargateStack:  success: Built 2cd594a111dc61319dd6398d8e59dd5ea38b51bab58d66badff5365d7a605e46:current_account-current_region
SampleFargateStack:  start: Publishing e77031893275c08bcaa0a774aa8b611727afd045b3b5d8e1e6f0f04063d9d386:current_account-current_region
current credentials could not be used to assume 'arn:aws:iam::XXXXXXXXXXXX:role/cdk-hnb659fds-file-publishing-role-XXXXXXXXXXXX-ap-northeast-1', but are for the right account. Proceeding anyway.
SampleFargateStack:  success: Published e77031893275c08bcaa0a774aa8b611727afd045b3b5d8e1e6f0f04063d9d386:current_account-current_region
SampleFargateStack:  start: Publishing 2cd594a111dc61319dd6398d8e59dd5ea38b51bab58d66badff5365d7a605e46:current_account-current_region
SampleFargateStack:  success: Published 2cd594a111dc61319dd6398d8e59dd5ea38b51bab58d66badff5365d7a605e46:current_account-current_region
current credentials could not be used to assume 'arn:aws:iam::XXXXXXXXXXXX:role/cdk-hnb659fds-lookup-role-XXXXXXXXXXXX-ap-northeast-1', but are for the right account. Proceeding anyway.
(To get rid of this warning, please upgrade to bootstrap version >= 8)
current credentials could not be used to assume 'arn:aws:iam::XXXXXXXXXXXX:role/cdk-hnb659fds-deploy-role-XXXXXXXXXXXX-ap-northeast-1', but are for the right account. Proceeding anyway.
This deployment will make potentially sensitive changes according to your current security approval level (--require-approval broadening).
Please confirm you intend to make the following modifications:

IAM Statement Changes
┌───┬─────────────┬────────┬─────────────┬─────────────┬─────────────┐
│   │ Resource    │ Effect │ Action      │ Principal   │ Condition   │
├───┼─────────────┼────────┼─────────────┼─────────────┼─────────────┤
│ - │ ${SampleFar │ Allow  │ sqs:SendMes │ Service:sns │ "ArnEquals" │
│   │ gateQueue5B │        │ sage        │ .amazonaws. │ : {         │
│   │ 43C956.Arn} │        │             │ com         │   "aws:Sour │
│   │             │        │             │             │ ceArn": "${ │
│   │             │        │             │             │ SampleFarga │
│   │             │        │             │             │ teTopic84C3 │
│   │             │        │             │             │ 8F71}"      │
│   │             │        │             │             │ }           │
├───┼─────────────┼────────┼─────────────┼─────────────┼─────────────┤
│ + │ ${Custom::V │ Allow  │ sts:AssumeR │ Service:lam │             │
│   │ pcRestrictD │        │ ole         │ bda.amazona │             │
│   │ efaultSGCus │        │             │ ws.com      │             │
│   │ tomResource │        │             │             │             │
│   │ Provider/Ro │        │             │             │             │
│   │ le.Arn}     │        │             │             │             │
├───┼─────────────┼────────┼─────────────┼─────────────┼─────────────┤
│ + │ ${FargateSe │ Allow  │ sts:AssumeR │ Service:ecs │             │
│   │ rvice/TaskD │        │ ole         │ -tasks.amaz │             │
│   │ ef/Executio │        │             │ onaws.com   │             │
│   │ nRole.Arn}  │        │             │             │             │
├───┼─────────────┼────────┼─────────────┼─────────────┼─────────────┤
│ + │ ${FargateSe │ Allow  │ sts:AssumeR │ Service:ecs │             │
│   │ rvice/TaskD │        │ ole         │ -tasks.amaz │             │
│   │ ef/TaskRole │        │             │ onaws.com   │             │
│   │ .Arn}       │        │             │             │             │
├───┼─────────────┼────────┼─────────────┼─────────────┼─────────────┤
│ + │ ${FargateSe │ Allow  │ logs:Create │ AWS:${Farga │             │
│   │ rvice/TaskD │        │ LogStream   │ teService/T │             │
│   │ ef/web/LogG │        │ logs:PutLog │ askDef/Exec │             │
│   │ roup.Arn}   │        │ Events      │ utionRole}  │             │
├───┼─────────────┼────────┼─────────────┼─────────────┼─────────────┤
│ + │ arn:${AWS:: │ Allow  │ ec2:Authori │ AWS:${Custo │             │
│   │ Partition}: │        │ zeSecurityG │ m::VpcRestr │             │
│   │ ec2:${AWS:: │        │ roupEgress  │ ictDefaultS │             │
│   │ Region}:${A │        │ ec2:Authori │ GCustomReso │             │
│   │ WS::Account │        │ zeSecurityG │ urceProvide │             │
│   │ Id}:securit │        │ roupIngress │ r/Role}     │             │
│   │ y-group/${M │        │ ec2:RevokeS │             │             │
│   │ yVpcF9F0CA6 │        │ ecurityGrou │             │             │
│   │ F.DefaultSe │        │ pEgress     │             │             │
│   │ curityGroup │        │ ec2:RevokeS │             │             │
│   │ }           │        │ ecurityGrou │             │             │
│   │             │        │ pIngress    │             │             │
└───┴─────────────┴────────┴─────────────┴─────────────┴─────────────┘
IAM Policy Changes
┌───┬───────────────────────────────┬────────────────────────────────┐
│   │ Resource                      │ Managed Policy ARN             │
├───┼───────────────────────────────┼────────────────────────────────┤
│ + │ ${Custom::VpcRestrictDefaultS │ {"Fn::Sub":"arn:${AWS::Partiti │
│   │ GCustomResourceProvider/Role} │ on}:iam::aws:policy/service-ro │
│   │                               │ le/AWSLambdaBasicExecutionRole │
│   │                               │ "}                             │
└───┴───────────────────────────────┴────────────────────────────────┘
Security Group Changes
┌───┬──────────────────────┬─────┬────────────┬──────────────────────┐
│   │ Group                │ Dir │ Protocol   │ Peer                 │
├───┼──────────────────────┼─────┼────────────┼──────────────────────┤
│ + │ ${FargateService/LB/ │ In  │ TCP 80     │ Everyone (IPv4)      │
│   │ SecurityGroup.GroupI │     │            │                      │
│   │ d}                   │     │            │                      │
│ + │ ${FargateService/LB/ │ Out │ TCP 80     │ ${FargateService/Ser │
│   │ SecurityGroup.GroupI │     │            │ vice/SecurityGroup.G │
│   │ d}                   │     │            │ roupId}              │
├───┼──────────────────────┼─────┼────────────┼──────────────────────┤
│ + │ ${FargateService/Ser │ In  │ TCP 80     │ ${FargateService/LB/ │
│   │ vice/SecurityGroup.G │     │            │ SecurityGroup.GroupI │
│   │ roupId}              │     │            │ d}                   │
│ + │ ${FargateService/Ser │ Out │ Everything │ Everyone (IPv4)      │
│   │ vice/SecurityGroup.G │     │            │                      │
│   │ roupId}              │     │            │                      │
└───┴──────────────────────┴─────┴────────────┴──────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)

Do you wish to deploy these changes (y/n)? 

デプロイが完了したら、下記のようにOutputsが表示されます。すでにFargateも起動されており、ロードバランサーのDNS名、もしくはURLでアプリへアクセスが可能となります。

Do you wish to deploy these changes (y/n)? y
SampleFargateStack: deploying... [1/1]
SampleFargateStack: creating CloudFormation changeset...

 ✅  SampleFargateStack

✨  Deployment time: 300.02s

Outputs:
SampleFargateStack.FargateServiceLoadBalancerDNS9433D5F6 = Sampl-Farga-OBUAHOQFH644-186323765.ap-northeast-1.elb.amazonaws.com
SampleFargateStack.FargateServiceServiceURL47701F45 = http://Sampl-Farga-OBUAHOQFH644-186323765.ap-northeast-1.elb.amazonaws.com
Stack ARN:
arn:aws:cloudformation:ap-northeast-1:XXXXXXXXXXXX:stack/SampleFargateStack/a21729d0-0b6a-11ee-a535-0e3e78b10d2f

✨  Total time: 308.46s


sugawara:~/environment/sample-fargate (master) $ 

試しにURLにアクセスしてみると、下記のような画面が表示されます。これでFargateのデプロイ成功です。

もう一度CloudFormationのコンソールでリソースの確認をしてみましょう。

今回作成したリソースは、VPCにはじまりSecurity GroupやALBにNAT Gateway、ECS Clusterなどなど。CDKのL3 Constructを使用すると、かなり少ないコードでテンプレとなる構成を作成することがわかるかと思います。

作成したリソースを確認した後は、後片付けをしましょう。ALBやNAT Gateway、Fargateの実行は課金対象となります。そのままにしていると思わぬ高額課金になりかねませんので、ハンズオン完了後は必ずリソースの削除をお願いします。削除するには、cdk destroyコマンドを叩いてみてください。destroyedが表示されれば、すべて削除が完了です。

$ cdk destroy
Are you sure you want to delete: SampleFargateStack (y/n)? 
SampleFargateStack: destroying... [1/1]
current credentials could not be used to assume 'arn:aws:iam::XXXXXXXXXXXX:role/cdk-hnb659fds-deploy-role-XXXXXXXXXXXX-ap-northeast-1', but are for the right account. Proceeding anyway.

 ✅  SampleFargateStack: destroyed

おわりに

CDKを用いてのFargateのデプロイはいかがでしたか?今回はCDKの中でもL3 Construct(パターン)といわれるものを使用しましたので、コードの量はかなり少ないです。しかし、多くのリソースをデプロイすることができました。サクッと検証環境の構築などにはとても便利ですね。

ただし、融通が利かない部分も多いため、構成をカスタマイズしたい場合には、CDKのL2 Constructについて学習してみてください!

この記事をシェアする
著者:sugawara
元高校英語教員。2023&2024 Japan AWS All Certifications Engineers。IaCやCI/CD、Platform Engineeringに興味あり。