見出し画像

【AWS】Configで分けるCDK環境の紹介【CDK】

こんにちは。M研の村瀬です。今回は以前紹介したクイズ作成システムQukkerで利用しているCDKについてご紹介したいと思います。Qukkerについては以下noteをご参照ください。

Qukkerでは、複数環境をCDKを用いて構築しています。今回のnoteではCDKで複数の環境を作成する際にConfigファイルを用いて環境を分ける方法をご紹介します。このブログがCDK利用の手助けになれば幸いです。

CDKとは

AWS CDKは、AWSのリソースをプログラマブルに定義し、コードとして管理することができる、Infrastructure as Code (IaC) ツールです。AWS CDKを使用すると、AWSのリソースを手動で作成する必要がなく、コードをデプロイするだけでAWSのリソースを自動的に作成できます。

Stackとは

AWS CDKのStackは、AWSのリソースを定義し、デプロイするための単位として機能します。複数のAWSリソースを1つのスタックにまとめて管理でき、AWS CloudFormationのテンプレートよりもより柔軟な方法でアプリケーションのインフラストラクチャを定義できます。

CDKのプロジェクトの作成

詳細な仕様やドキュメントについては以下の公式のGetting Startedページを参照ください。

今回のnoteではTypescriptでCDKプロジェクトを開始してみようと思います。まずはCDKコマンドをインストールします。
■CDKコマンドのインストール

$ npm install -g cdk

次にインストールしたcdkコマンドを使ってCDKプロジェクトを作成します。
■CDKプロジェクトの作成

$ mkidr cdk-sample
$ cd cdk-sample
$ cdk init --language typescript

Configファイルで環境を分ける

検証環境用のConfigファイルと本番環境用のConfigファイルを作成し、importするConfigファイルを変えることで作成するStackを分けます。CDKはStack名が異なると別リソースとして各AWSリソースを構築します。そのためStack名やパラメータを環境毎に設定できるようなConfigファイルを用意し、Stackの宣言時にConfigファイルで設定した名前を渡すことで環境毎のリソースを構築することができます。以下はConfigファイルのサンプルです。sample-configディレクトリを作成し、dev.ts(検証環境のConfigファイル)とprod.ts(本番環境のConfigファイル)を作成します。今回の例では、EC2のインスタンスサイズをパラメータ化し、検証環境と本番環境でインスタンスサイズを変更します。

■sample-config/types.ts
Configファイルの型定義です。環境の名前、instanceSizeとinstanceClassを設定できるようにしておきます。

import { InstanceClass, InstanceSize } from "aws-cdk-lib/aws-ec2";

export type Config = {
  sample: SampleConfig,
};

type SampleConfig = {
  envName: string,
  instanceSize: InstanceSize,
  instanceClass: InstanceClass,
}

■sample-config/dev.ts
検証環境のConfigファイルです。検証環境ではSmallサイズのインスタンスを作成します。

import { InstanceClass, InstanceSize } from "aws-cdk-lib/aws-ec2";
import { Config} from "./types";

export const sampleConfig:Config["sample"] = {
    envName: 'dev',
    instanceSize: InstanceSize.SMALL,
    instanceClass: InstanceClass.BURSTABLE3
}

export const config: Config = {
    sample: sampleConfig
}

■sample-config/prod.ts
本番環境のConfigファイルです。本番環境ではMediumサイズのインスタンスを作成します。

import { InstanceClass, InstanceSize } from "aws-cdk-lib/aws-ec2";
import { Config} from "./types";

export const sampleConfig:Config["sample"] = {
    envName: 'prod',
    instanceSize: InstanceSize.MEDIUM,
    instanceClass: InstanceClass.BURSTABLE3
}

export const config: Config = {
    sample: sampleConfig
}

Stack定義を作成する

Stackを作成します。今回はシンプルにEC2一台を作成する例です。EC2を作成するためにはVPCとSubnetが必要なので、今回のStackでは、VPCとSubnetとEC2の作成をするStackを定義します。

■lib/cdk-sample-stack.ts

import { Construct } from "constructs";
import {
  Stack,
  StackProps,
  aws_ec2 as ec2,
} from "aws-cdk-lib";
import { Config } from "../sample-config/types";

interface SampleStackProps extends StackProps {
  config: Config;
}

export class SampleStack extends Stack {
  constructor(scope: Construct, id: string, props: SampleStackProps) {
    super(scope, id, props);
    const sampleConfig = props.config.sample;

    const my_vpc = new ec2.Vpc(this, `${sampleConfig.envName}-vpc}`, {
      ipAddresses: ec2.IpAddresses.cidr("10.0.0.0/24"),
      enableDnsHostnames: true,
      enableDnsSupport: true,
      natGateways: 1,
      maxAzs: 1,
      subnetConfiguration: [
        { name: "Private", subnetType: ec2.SubnetType.PUBLIC, cidrMask: 28 },
      ],
    });

    const my_ec2 = new ec2.Instance(this, `${sampleConfig.envName}-ec2`, {
      vpc: my_vpc,
      instanceName: `${sampleConfig.envName}-ec2`,
      machineImage: ec2.MachineImage.latestAmazonLinux(),
      instanceType: ec2.InstanceType.of(
          sampleConfig.instanceClass,
          sampleConfig.instanceSize,
      )
    })
  }
}

StackにConfigを渡し同一Stack定義から別環境を構築する

上記で定義したSampleStackから、検証環境と本番環境のEC2を作成します。両環境のEC2を作成するため、SampleStackにdevConfig/prodConfigをそれぞれパラメータとして渡しStackを生成します。

■bin/cdk-sample.ts

import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { SampleStack } from '../lib/cdk-sample-stack';
import { config as devConfig } from '../sample-config/dev'
import { config as prodConfig } from '../sample-config/prod'

const app = new cdk.App();

new SampleStack(
  app,
  'SampleStackDev',
  {
    config: devConfig,
    env: {
      region: 'ap-northeast-1'
    }
  }
),
new SampleStack(
  app,
  'SampleStackProd',
  {
    config: prodConfig,
    env: {
      region: 'ap-northeast-1'
    }
  }
);

Stackのデプロイ

■cdk list
cdk list はデプロイ可能なStackの一覧を表示するコマンドです。デプロイ可能なStack名を確認し、この後のdiffコマンドで差分を確認し、deployコマンドで実際に適用します。

$ cdk list
SampleStackDev
SampleStackProd

■cdk diff {stack名}
cdk diff {stack名} で、 cdk deploy {stack名} コマンドでデプロイされる差分を表示してくれます。deployで変更されるリソースに間違いがないかdiffコマンドを使いdeploy前に確認しましょう。

$ cdk diff SampleStackDev
Stack SampleStackDev
IAM Statement Changes
┌───┬─────────────────────────────┬────────┬────────────────┬───────────────────────────┬───────────┐
│   │ Resource                    │ Effect │ Action         │ Principal                 │ Condition │
├───┼─────────────────────────────┼────────┼────────────────┼───────────────────────────┼───────────┤
│ + │ ${dev-ec2/InstanceRole.Arn} │ Allow  │ sts:AssumeRole │ Service:ec2.amazonaws.com │           │
└───┴─────────────────────────────┴────────┴────────────────┴───────────────────────────┴───────────┘
Security Group Changes
┌───┬──────────────────────────────────────────┬─────┬────────────┬─────────────────┐
│   │ Group                                    │ Dir │ Protocol   │ Peer            │
├───┼──────────────────────────────────────────┼─────┼────────────┼─────────────────┤
│ + │ ${dev-ec2/InstanceSecurityGroup.GroupId} │ Out │ Everything │ Everyone (IPv4) │
└───┴──────────────────────────────────────────┴─────┴────────────┴─────────────────┘
(NOTE: There may be security-related changes not in this list. See <https://github.com/aws/aws-cdk/issues/1299>)

Parameters
[+] Parameter SsmParameterValue:--aws--service--ami-amazon-linux-latest--amzn-ami-hvm-x86_64-gp2:C96584B6-F00A-464E-AD19-53AFF4B05118.Parameter SsmParameterValueawsserviceamiamazonlinuxlatestamznamihvmx8664gp2C96584B6F00A464EAD1953AFF4B05118Parameter: {"Type":"AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>","Default":"/aws/service/ami-amazon-linux-latest/amzn-ami-hvm-x86_64-gp2"}
[+] Parameter BootstrapVersion 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]"}

Resources
[+] AWS::EC2::VPC dev-vpc} devvpc875BEBD3
[+] AWS::EC2::Subnet dev-vpc}/PrivateSubnet1/Subnet devvpcPrivateSubnet1SubnetC8A33C23
[+] AWS::EC2::RouteTable dev-vpc}/PrivateSubnet1/RouteTable devvpcPrivateSubnet1RouteTable1DC78C03
[+] AWS::EC2::SubnetRouteTableAssociation dev-vpc}/PrivateSubnet1/RouteTableAssociation devvpcPrivateSubnet1RouteTableAssociationDAFB1727
[+] AWS::EC2::Route dev-vpc}/PrivateSubnet1/DefaultRoute devvpcPrivateSubnet1DefaultRoute8F00ED59
[+] AWS::EC2::EIP dev-vpc}/PrivateSubnet1/EIP devvpcPrivateSubnet1EIPD3F727D5
[+] AWS::EC2::NatGateway dev-vpc}/PrivateSubnet1/NATGateway devvpcPrivateSubnet1NATGateway545D1819
[+] AWS::EC2::InternetGateway dev-vpc}/IGW devvpcIGW9E0BA2FD
[+] AWS::EC2::VPCGatewayAttachment dev-vpc}/VPCGW devvpcVPCGW548A0FFE
[+] AWS::EC2::SecurityGroup dev-ec2/InstanceSecurityGroup devec2InstanceSecurityGroup4829615C
[+] AWS::IAM::Role dev-ec2/InstanceRole devec2InstanceRole730B2650
[+] AWS::IAM::InstanceProfile dev-ec2/InstanceProfile devec2InstanceProfile6D895362
[+] AWS::EC2::Instance dev-ec2 devec25A6C763B

Other Changes
[+] Unknown 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 deploy
定義した内容を適用します。

$ cdk deploy

Stackの削除

■cdk destroy
cdk destroy はdeployしたawsリソースを削除します。今回作成したリソースをそのまま残しておくと課金され続けてしまうので、リソースを作成した方はdestroyコマンドを使い削除することをお勧めします。
*一部リソース(S3等)はdestroyコマンドを実行しても削除されないものがあるため、コンソール画面から手動で削除する必要があります。

$ cdk destroy

まとめ

このようにして、一つのStackファイルから渡すConfigを変えるだけで複数の環境を作成することができます。検証環境と本番環境で変更したい値については、Configファイルに追加していくだけで、変えることができます。
以下のgithubに今回作成したサンプルのCDKプロジェクトを用意しました。こちらも併せてご参照ください。
(メディア研究開発センター・村瀬友広)