AWS Cloud Development Kit (AWS CDK)でECS環境を構築してみた

こんにちは、坂巻です。

AWS上でさくっとコンテナを動かしたくて、AWS Cloud Development Kit (以下、AWS CDK)に辿り着きました。ECSのちょっとした検証でも、ネットワークだったりIAMだったりのリソースも必要で準備に少々時間がかかっていました。

このツールをつかうと、AWSのベストプラクティスに従った設定を内包しており、大部分をよしなにやってくれるようなので今回はこちらを利用して、ECSを構築してみたいと思います。

AWS CDKの詳細については以下をご確認ください。

はじめに

AWS CDKを利用するには、Node.js等の前提ツールが必要になります。本エントリではAWS CDKのセットアップについては割愛していますのでGetting Started with the AWS CDKまたは、以下のブログをご確認ください。

【awslabs探訪】AWS Cloud Development Kit (AWS CDK)を使ってみた

今回は、Fargateを使用してサービスを起動してみたいと思います。

やってみた

AWS CDK初期化

AWS CDKコードを格納するための新しいディレクトリを作成し、AWS CDKを初期化します。今回はTypeScriptを利用します。なお、現在の対応言語はTypeScript、JavaScript、Javaとなります。.NETとPythonについては近々案内があるようです。

$ mkdir MyEcsConstruct
$ cd MyEcsConstruct
$ cdk init --language typescript

コマンドラインツールキットのインストール

AWS CDKを利用するうえで必須となるツールキットをインストールします。

$ npm install -g aws-cdk

パッケージ追加

今回必要なaws-ec2と、aws-ecsのパッケージを追加します。

$ npm install @aws-cdk/aws-ec2 @aws-cdk/aws-ecs

Fargateサービス作成コード

lib/my_ecs_construct-stack.tsを編集します。ハイライト部が更新した箇所です。22行名のimageについては、利用したいイメージに置き換えてください。わたしは検証のために、vulnerables/web-dvwaを利用しました。

import cdk = require('@aws-cdk/cdk');
import ec2 = require('@aws-cdk/aws-ec2');
import ecs = require('@aws-cdk/aws-ecs');

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

    const vpc = new ec2.VpcNetwork(this, 'MyVpc', {
      maxAZs: 2
    });

    const cluster = new ecs.Cluster(this, 'MyCluster', {
      vpc: vpc
    });
    
    // Create a load-balanced Fargate service and make it public
    new ecs.LoadBalancedFargateService(this, 'MyFargateService', {
      cluster: cluster,
      cpu: '256',
      desiredCount: 1,
      image: ecs.ContainerImage.fromDockerHub('amazon/amazon-ecs-sample'),
      memoryMiB: '512',
      publicLoadBalancer: true
    });
  }
}

ビルドします。

$ npm run build

AWS CDK的にはこの時点でスタックが作成されるようなので、スタックの一覧を確認します。

$ cdk ls -l
- name: MyEcsConstructStack
  environment:
    name: XXXXXXXXXXXX/ap-northeast-1
    account: "XXXXXXXXXXXX"
    region: ap-northeast-1

以下のコマンドでは、実際にデプロイされるCloudFormationテンプレートが確認できます。

$ cdk synth MyEcsConstructStack
Resources:
  MyVpcF9F0CA6F:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsHostnames: true
(省略)

AWS上にデプロイします。

$ cdk deploy

AWS CDK内でセキュリティ周り(セキュリティグループやIAM)に変更を加えていると、cdk deploy実行後に警告が出力されます。確認し問題なければ実行します。

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 │
├───┼───────────────────────────────────────────────┼────────┼──────────────────────┼───────────────────────────────────────────────┼───────────┤
│ + │ ${MyFargateService/Logging/LogGroup.Arn}      │ Allow  │ logs:CreateLogStream │ AWS:${MyFargateService/TaskDef/ExecutionRole} │           │
│   │                                               │        │ logs:PutLogEvents    │                                               │           │
├───┼───────────────────────────────────────────────┼────────┼──────────────────────┼───────────────────────────────────────────────┼───────────┤
├───┼───────────────────────────────────────────────┼────────┼──────────────────────┼───────────────────────────────────────────────┼───────────┤
│ + │ ${MyFargateService/TaskDef/TaskRole.Arn}      │ Allow  │ sts:AssumeRole       │ Service:ecs-tasks.amazonaws.com               │           │
└───┴───────────────────────────────────────────────┴────────┴──────────────────────┴───────────────────────────────────────────────┴───────────┘
Security Group Changes
┌───┬───────────────────────────────────────────────────┬─────┬────────────┬───────────────────────────────────────────────────┐
│   │ Group                                             │ Dir │ Protocol   │ Peer                                              │
├───┼───────────────────────────────────────────────────┼─────┼────────────┼───────────────────────────────────────────────────┤
│ + │ ${MyFargateService/LB/SecurityGroup.GroupId}      │ In  │ TCP 80     │ Everyone (IPv4)                                   │
│ + │ ${MyFargateService/LB/SecurityGroup.GroupId}      │ Out │ TCP 80     │ ${MyFargateService/Service/SecurityGroup.GroupId} │
├───┼───────────────────────────────────────────────────┼─────┼────────────┼───────────────────────────────────────────────────┤
│ + │ ${MyFargateService/Service/SecurityGroup.GroupId} │ In  │ TCP 80     │ ${MyFargateService/LB/SecurityGroup.GroupId}      │
│ + │ ${MyFargateService/Service/SecurityGroup.GroupId} │ Out │ Everything │ Everyone (IPv4)                                   │
└───┴───────────────────────────────────────────────────┴─────┴────────────┴───────────────────────────────────────────────────┘
(NOTE: There may be security-related changes not in this list. See http://bit.ly/cdk-2EhF7Np)

実行すると、リソースの作成が開始されます。

Do you wish to deploy these changes (y/n)? y
MyEcsConstructStack: deploying...
MyEcsConstructStack: creating CloudFormation changeset...
  0/39 | 1:26:52 PM | CREATE_IN_PROGRESS   | AWS::ECS::Cluster                         | MyCluster (MyCluster4C1BA579)
  0/39 | 1:26:52 PM | CREATE_IN_PROGRESS   | AWS::IAM::Role                            | MyFargateService/TaskDef/TaskRole (MyFargateServiceTaskDefTaskRole62C7D397)
  0/39 | 1:26:52 PM | CREATE_IN_PROGRESS   | AWS::Logs::LogGroup                       | MyFargateService/Logging/LogGroup (MyFargateServiceLoggingLogGroup271A17C2)
  0/39 | 1:26:52 PM | CREATE_IN_PROGRESS   | AWS::ECS::Cluster                         | MyCluster (MyCluster4C1BA579) Resource creation Initiated
  0/39 | 1:26:52 PM | CREATE_IN_PROGRESS   | AWS::EC2::EIP                             | MyVpc/PublicSubnet2/EIP (MyVpcPublicSubnet2EIP8CCBA239)
  0/39 | 1:26:52 PM | CREATE_IN_PROGRESS   | AWS::EC2::VPC                             | MyVpc (MyVpcF9F0CA6F)
  0/39 | 1:26:53 PM | CREATE_IN_PROGRESS   | AWS::EC2::EIP                             | MyVpc/PublicSubnet1/EIP (MyVpcPublicSubnet1EIP096967CB)
  0/39 | 1:26:53 PM | CREATE_IN_PROGRESS   | AWS::IAM::Role                            | MyFargateService/TaskDef/ExecutionRole (MyFargateServiceTa
(省略)

マネジメントコンソールを確認すると、CloudFormationのスタックが作成されています。

01

デプロイ完了を確認後に、ECSを確認するとFargateでサービスが起動されていました。

02

作成されたALBにアクセスすると、サービスに接続することができました。

03

削除

以下のコマンドを実行すると、スタック削除が行われます。

$ cdk destroy
Are you sure you want to delete: MyEcsConstructStack (y/n)? y
MyEcsConstructStack: destroying...
   0 | 1:41:46 PM | DELETE_IN_PROGRESS   | AWS::CloudFormation::Stack                | MyEcsConstructStack User Initiated
   0 | 1:41:48 PM | DELETE_IN_PROGRESS   | AWS::EC2::Route                           | MyVpc/PrivateSubnet2/DefaultRoute (MyVpcPrivateSubnet2DefaultRoute9CE96294)
   0 | 1:41:48 PM | DELETE_IN_PROGRESS   | AWS::EC2::Route                           | MyVpc/PublicSubnet2/DefaultRoute (MyVpcPublicSubnet2DefaultRoute052936F6)

(省略)

MyEcsConstructStack: destroyed

さいごに

ECSを使用するためには、ネットワーク、IAMの作成など、使用までに多くのステップがあるかと思います。AWS CDKを利用すると、その辺りが内包されていて楽に構築することができました。

少々気になったのが、今回のコードでNATゲートウェイが作成されていました。

04

マニュアルを確認しましたが、パブリックサブネットにはNATゲートウェイが含まれているようです。

Each Public Subnet will have a NAT Gateway. Each Private Subnet will have a route to the NAT Gateway in the same availability zone.

ベストプラクティスに沿った構成かと思いますが、検証等、一時的な構成の場合は、少々コードをごにょごにょしておかないと、利用料に影響がありそうなので、その辺りは意識が必要かと思います。

参考