AWS CDKでAWSリソースを作成する(1)

はじめに

先日からApp RunnerとかElastiCacheとかを使った検証をしていましたが、マネージメントコンソールで作業をするのは時間がかかり、料金節約のためにリソースを削除するのも大変なので、AWS CDKで実装してみることにしました。

プロジェクト作成

プロジェクトは以下のドキュメントを見ながら作業をすることに。

docs.aws.amazon.com

NodeJS環境にてnpx cdk init --language typescriptを実行すればOK。

ネットワーク関係を作成する

各種リソースを作成するためにAWS CDKのAPIドキュメントに目を通します。

docs.aws.amazon.com

まずはVPCを作るので、aws-ec2のVpcクラスに目を通します。

docs.aws.amazon.com

lib/construct/network.tsというディレクトリ・ファイルを作成し、以下の内容として保存します。

import { Construct } from "constructs";
import * as ec2 from 'aws-cdk-lib/aws-ec2';

export class Network extends Construct {
    readonly vpc: ec2.Vpc;
    readonly appRunnerSecurityGroup: ec2.SecurityGroup;
    readonly cacheSecurityGroup: ec2.SecurityGroup;

    // VPCとサブネットを作る
    constructor(scope: Construct, id: string) {
        super(scope, id);

        this.vpc = new ec2.Vpc(scope, 'VPC', {
            vpcName: 'myapp-vpc',
            cidr: '10.0.0.0/16',
            maxAzs: 2,
            subnetConfiguration: [
                // Redis用のサブネット
                {
                    cidrMask: 24,
                    name: 'myapp-cache',
                    subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
                },
                // AppRunner用のサブネット
                {
                    cidrMask: 24,
                    name: 'myapp-Public',
                    subnetType: ec2.SubnetType.PUBLIC,
                }
            ],
            natGateways: 0,
        });

        // App Runnerに設定するセキュリティグループ
        this.appRunnerSecurityGroup = new ec2.SecurityGroup(scope, 'AppRunnerSecurityGroup', {
            vpc: this.vpc,
            description: 'for myapp-app-runner',
            securityGroupName: 'myapp-app-runner-sg'
        });

        //Cacheに設定するセキュリティグループ
        this.cacheSecurityGroup = new ec2.SecurityGroup(scope, 'CacheSecurityGroup', {
            vpc: this.vpc,
            description: 'for myapp-cache',
            securityGroupName: 'myapp-cache-sg'
        });

        // App RunnerセキュリティグループからCacheセキュリティグループへのポート6379を許可
        this.cacheSecurityGroup.addIngressRule(this.appRunnerSecurityGroup, ec2.Port.tcp(6379));
    }
}

あとは、lib/cdk-stack.tsを更新します。

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { Network } from './construct/network';

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

    // VPCを作成する
    const { vpc, appRunnerSecurityGroup, cacheSecurityGroup } = new Network(this, 'Network');
  }
}

これで実装は完了。

実行する

まずはcdk bootstrapを実行し、CDKに関するリソースを作成します。必要なロールやcdk-からはじまるS3バケットが作成されました。ちなみにこのS3バケットにはDeletionPolicy: Retainが指定されています。

このため、cdk bootstrapで生成された以下スクリーンショットのCloudFormationのスタックを削除してもこのS3バケットは残ります。

あとはcdk deployを実行することでAWSリソースが作成されます。

ちなみに、今回はCloud9上で実行してみたのですが、t2.microですとTypeScriptのビルドがメモリ不足で異常終了しましたので実行するにはt3.small以上のインスタンスが必要かと思います。

削除する

作成したAWSリソースはcdk destroyを実行すれば削除されます。

他のリソースを作成する

VPC以外のリソースを作成してみます。まずはECR。ドキュメントは以下のもの。

docs.aws.amazon.com

実装例は以下のもの。lib/construct/ecr-repository.tsとして保存します。

import { RemovalPolicy } from 'aws-cdk-lib';
import * as ecr from 'aws-cdk-lib/aws-ecr';
import { Construct } from 'constructs';

export class EcrRepository extends Construct {
    readonly repository: ecr.Repository;

    constructor(scope: Construct, id: string) {
        super(scope, id);
        // リポジトリを作成する
        this.repository = new ecr.Repository(scope, 'MyAppRepository', {
            repositoryName: 'myapp-repository',
            removalPolicy: RemovalPolicy.DESTROY,
            imageScanOnPush: false
        });
    }
}

ElastiCacheの作成。ドキュメントは以下のもの。

docs.aws.amazon.com

実装例は以下のもの。lib/construct/cache.ts`として保存します。

import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as elasticache from 'aws-cdk-lib/aws-elasticache';
import { Construct } from 'constructs';

interface CacheProps {
    vpc: ec2.Vpc
    cacheSecurityGroup: ec2.SecurityGroup
}

export class Cache extends Construct {
    readonly cacheCluster: elasticache.CfnCacheCluster;

    constructor(scope: Construct, id: string, props: CacheProps) {
        super(scope, id);

        const { vpc, cacheSecurityGroup } = props;

        const cacheSubnetGroup =  new elasticache.CfnSubnetGroup(this, "CacheSubnetGroup", {
            subnetIds: vpc.selectSubnets({ subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS }).subnetIds,
            description: "Group of subnets to place Cache into",
        });

        this.cacheCluster = new elasticache.CfnCacheCluster(this, "CacheCluster", {
            engine: "redis",
            cacheNodeType: "cache.t3.micro",
            numCacheNodes: 1,
            cacheSubnetGroupName: cacheSubnetGroup.ref,
            vpcSecurityGroupIds: [cacheSecurityGroup.securityGroupId],
        });
    }
}

その後、lib/cdk-stack.tsを以下のように更新します。

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { Network } from './construct/network';
import { EcrRepository } from './construct/ecr-repository';
import { Cache } from './construct/cache';

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

    // VPCを作成する
    const { vpc, appRunnerSecurityGroup, cacheSecurityGroup } = new Network(this, 'Network');

    // ECRを作成する
    const { repository } = new EcrRepository(this, 'Ecr');

    // Cacheを作成する
    const { cacheCluster } = new Cache(this, 'ElastiCache', { vpc, cacheSecurityGroup });
  }
}

あとは、cdk deployを実行すればOK。ElastiCacheの作成には10分ぐらいかかるのでちょっと気長に待ちましょう。