LoginSignup
6
5

More than 1 year has passed since last update.

AWS CDK構築備忘録②(ALB+ECS+Aurora)

Last updated at Posted at 2022-06-16

内容

下記の構成をCDKで構築します。

ecs01.png

  • VPC
    VPC、サブネットを作成します。作成した情報を他のスタックからクロススタック参照出来るようにします。

  • Route53
    ECSスタックの中で既存ホストゾーンにALBのAレコード(Alias)を追加しています。

  • ALB,ECS
    aws-ecs-patternsというモジュールがあります。こちらは一般的な構成パラメータがデフォルト値となっており、少ないパラメータでECSを構築することが可能となります。こちらをベースにコードをカスタマイズしていきます。ALBもこの中で作成されます。nginxのイメージを使用します。

上記の通り、CloudFormationで600行程度のYAMLを数行で書くことが可能です。

  • Aurora,Secret Manger
    VPCスタックの情報を使用してサブネットグループ、セキュリティグループを作成します。
    シングル構成のAuroraを作成します。デフォルトではパスワードはSecret Managerに保存されます。なおFargate、Aurora間の接続確認は未実施です。

準備

mkdir app01
cd app01
cdk init --language typescript

エントリポイント

bin/app01.ts
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { VpcStack } from '../lib/vpc-stack';
import { EcsStack } from '../lib/ecs-stack';
import { RdsStack } from '../lib/rds-stack';


const app = new cdk.App();
const vpcStack = new VpcStack(app, 'VpcStack', {
    env: {
        account: 'AWS_ACCOUNT',
        region: 'ap-northeast-1',
    },
});

new EcsStack(app, 'EcsStack', {

    //変数を渡す
    vpc: vpcStack.vpc,
    env: {
        account: 'AWS_ACCOUNT',
        region: 'ap-northeast-1',
    },
});

new RdsStack(app, 'RdsStack', {

    //変数を渡す
    vpc: vpcStack.vpc,
    env: {
        account: 'AWS_ACCOUNT',
        region: 'ap-northeast-1',
    },
});

VPCスタック

lib/vpc-stack.ts
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import { Vpc } from 'aws-cdk-lib/aws-ec2';

export class VpcStack extends Stack {

  //別スタックから参照できるようにする
  public readonly vpc: Vpc;
  
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const vpc_name = 'cdk-vpc';
    const cidr = '10.1.0.0/16';

    this.vpc = new ec2.Vpc(this, 'VPC', {

      vpcName: vpc_name,
      cidr: cidr,
      maxAzs: 2,
      subnetConfiguration: [
        {
          cidrMask: 24,
          name: `${vpc_name}-public`,
          subnetType: ec2.SubnetType.PUBLIC,
        },
        {
          cidrMask: 24,
          name: `${vpc_name}-private`,
          subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
        },
      ]
    })
  }
};

ECSスタック

lib/ecs-stack.ts
import { Stack, StackProps } from 'aws-cdk-lib';
import { Vpc } from 'aws-cdk-lib/aws-ec2';
import { Construct } from 'constructs';
import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as ecsp from 'aws-cdk-lib/aws-ecs-patterns';
import * as route53 from 'aws-cdk-lib/aws-route53';

//VpcStackの変数を扱えるようにする
interface SubStackProps extends StackProps {
  vpc: Vpc;
};

export class EcsStack extends Stack {

  //SubStackPropsを使用する
  constructor(scope: Construct, id: string, props: SubStackProps) {
    super(scope, id, props);
    
    //変数設定
    const ecs_vpc = props.vpc;
    const domain_name = 'DOMAIN_NAME'

    //Domain名設定
    const domainZone = route53.HostedZone.fromLookup(this, 'Zone', {
      domainName: 'DOMAIN_ZONE'
    });

    //Cluster設定
    const cluster = new ecs.Cluster(this, 'Cluster', {
      clusterName: 'WebApp01-Cluster',
      vpc: ecs_vpc,
    })

    //ELB+Fargate設定
    new ecsp.ApplicationLoadBalancedFargateService(this, 'WebApp01', {
      cluster,
      domainName: domain_name,
      domainZone,
      memoryLimitMiB: 512,
      desiredCount: 2,
      cpu: 256,
      assignPublicIp: true,
      loadBalancerName: 'WebApp01-lb01',
      publicLoadBalancer: true,
      taskImageOptions: {
        image: ecs.ContainerImage.fromRegistry('nginx'),
      },
    });
  }
};

RDSスタック

lib/rds-stack.ts
import { RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { Peer, Port, SecurityGroup, Vpc } from 'aws-cdk-lib/aws-ec2';
import * as rds from 'aws-cdk-lib/aws-rds';
import * as ec2 from 'aws-cdk-lib/aws-ec2';

//VpcStackの変数を扱えるようにする
interface SubStackProps extends StackProps {
  vpc: Vpc;
};

export class RdsStack extends Stack {
  constructor(scope: Construct, id: string, props: SubStackProps) {
    super(scope, id, props);

    //VPC取得 
    const vpc = props.vpc;

    //Subnet取得
    const private_subnet = vpc.selectSubnets({
      subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
    }).subnets

    const public_subnet = vpc.selectSubnets({
      subnetType: ec2.SubnetType.PUBLIC,
    }).subnets

    //SubnetGroup作成
    const subnetGroup = new rds.SubnetGroup(this, 'SubnetGroup', {
      description: 'aurora subnet group for WebApp01',
      vpc: vpc,      
      vpcSubnets: {
        subnets: private_subnet,
      }
    });

    //SecurityGroup作成
    const secgroup01 = new SecurityGroup(this, 'SecGroup', {
      vpc: vpc,
    });

    //SecurityGroupにPublic SubnetからのIngressルール追加
    for (const elm of public_subnet) {
      secgroup01.addIngressRule(        
        Peer.ipv4(elm.ipv4CidrBlock),
        Port.tcp(3306),
      )};

    //RDS作成
    const cluster = new rds.DatabaseCluster(this, 'AuroraDB', {
      engine: rds.DatabaseClusterEngine.auroraMysql({
        version: rds.AuroraMysqlEngineVersion.VER_2_10_2
      }),
      credentials: rds.Credentials.fromGeneratedSecret('admin'),
      defaultDatabaseName: 'database1',
  
      instanceProps: {
        vpc,
        publiclyAccessible: false,
        instanceType: ec2.InstanceType.of(
          ec2.InstanceClass.BURSTABLE3,
          ec2.InstanceSize.SMALL),
          securityGroups: [secgroup01],        
        vpcSubnets: {
          subnets: private_subnet,
        },        
      },
      deletionProtection: false,
      iamAuthentication: true,
      instances: 1,
      preferredMaintenanceWindow: 'Sat:16:00-Sat:16:30',
      removalPolicy: RemovalPolicy.DESTROY,
      subnetGroup: subnetGroup,
    });
  }
}

メモ

CFnテンプレートの情報を出力しない。(console.log()の確認等に使用)

cdk synth --quiet
6
5
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
5