はじめに
先日からAWS CDKを使ってAWSリソースを作成してみることをしています。前回までは以下の記事を参照。
今日はAppRunnerからElastiCacheに接続するアプリの環境を作成してみます。アプリケーションは以下の記事で実装したものを使います。
ネットワークを作成
VPCやセキュリティグループを作成します。最初の実装を変更して、addEgressRule
を設定します。addEgressRule
を設定するにはSecurityGroup
のコンストラクタにおいてallowAllOutbound
をfalse
に設定する必要があります。
import { Construct } from 'constructs'; import { IpAddresses, Port, SecurityGroup, SubnetType, Vpc, } from 'aws-cdk-lib/aws-ec2'; export class Network extends Construct { readonly vpc: Vpc; readonly appRunnerSecurityGroup: SecurityGroup; readonly cacheSecurityGroup: SecurityGroup; // VPCとサブネットを作る constructor(scope: Construct, id: string) { super(scope, id); this.vpc = new Vpc(scope, 'VPC', { vpcName: 'myapp-vpc', ipAddresses: IpAddresses.cidr('10.0.0.0/16'), maxAzs: 2, subnetConfiguration: [ { cidrMask: 24, name: 'myapp-Public', subnetType: SubnetType.PUBLIC, }, // Redis用のサブネット { cidrMask: 24, name: 'myapp-cache', subnetType: SubnetType.PRIVATE_WITH_EGRESS, }, ], natGateways: 0, }); // App Runnerに設定するセキュリティグループ this.appRunnerSecurityGroup = new SecurityGroup( scope, 'AppRunnerSecurityGroup', { vpc: this.vpc, allowAllOutbound: false, description: 'for myapp-app-runner', securityGroupName: 'myapp-app-runner-sg', }, ); //Cacheに設定するセキュリティグループ this.cacheSecurityGroup = new SecurityGroup(scope, 'CacheSecurityGroup', { vpc: this.vpc, description: 'for myapp-cache', securityGroupName: 'myapp-cache-sg', }); // AppRunnerセキュリティグループからCacheセキュリティグループへのポート6379を許可 this.appRunnerSecurityGroup.addEgressRule( this.cacheSecurityGroup, Port.tcp(6379), ); // CacheセキュリティグループにてAppRunnerセキュリティグループからポート6379の通信を許可 this.cacheSecurityGroup.addIngressRule( this.appRunnerSecurityGroup, Port.tcp(6379), ); } }
allowAllOutbound
のデフォルト値はtrue
でこのまま作成するとセキュリティグループのアウトバウンドは以下のような設定で作られます。
allowAllOutbound
に明確にfalse
を指定することでaddEgressRule
の設定が有効になり、以下のようなアウトバウンドが設定されたセキュリティグループがつくられます。
ElastiCacheを作成
CDKを使ってElastiCacheを作成します。検証用なので、ノードタイプをcache.t3.micro
に、ノード数は1とします。
import { SecurityGroup, SubnetType, Vpc } from 'aws-cdk-lib/aws-ec2'; import { CfnCacheCluster, CfnSubnetGroup } from 'aws-cdk-lib/aws-elasticache'; import { Construct } from 'constructs'; interface CacheProps { vpc: Vpc; cacheSecurityGroup: SecurityGroup; } export class Cache extends Construct { readonly cacheCluster: CfnCacheCluster; constructor(scope: Construct, id: string, props: CacheProps) { super(scope, id); const { vpc, cacheSecurityGroup } = props; const cacheSubnetGroup = new CfnSubnetGroup(this, 'CacheSubnetGroup', { subnetIds: vpc.selectSubnets({ subnetType: SubnetType.PRIVATE_WITH_EGRESS, }).subnetIds, description: 'Group of subnets to place Cache into', }); this.cacheCluster = new CfnCacheCluster(this, 'CacheCluster', { engine: 'redis', cacheNodeType: 'cache.t3.micro', numCacheNodes: 1, cacheSubnetGroupName: cacheSubnetGroup.ref, vpcSecurityGroupIds: [cacheSecurityGroup.securityGroupId], }); } }
App Runnerを作成
App RunnerからElastiCacheに接続するにはVPC Connectorの作成が必要です。また、ElastiCacheの接続先の設定も必要です。今回、接続先は環境変数で設定することとします。具体的なCDKの実装コードは以下の通り。
import { Construct } from 'constructs'; import { SecurityGroup, SubnetType, Vpc } from 'aws-cdk-lib/aws-ec2'; import { Repository } from 'aws-cdk-lib/aws-ecr'; import { CfnCacheCluster } from 'aws-cdk-lib/aws-elasticache'; import { ManagedPolicy, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam'; import { CfnService, CfnVpcConnector } from 'aws-cdk-lib/aws-apprunner'; interface AppRunnerProps { vpc: Vpc; repository: Repository; appRunnerSecurityGroup: SecurityGroup; cacheCluster: CfnCacheCluster; } export class AppRunner extends Construct { constructor(scope: Construct, id: string, props: AppRunnerProps) { super(scope, id); const { vpc, repository, appRunnerSecurityGroup, cacheCluster } = props; // Roleの作成(ECRに接続するため) const accessRole = new Role(scope, 'AppRunnerAccessRole', { roleName: 'myapp-AppRunnerAccessRole', assumedBy: new ServicePrincipal('build.apprunner.amazonaws.com'), }); accessRole.addManagedPolicy( ManagedPolicy.fromAwsManagedPolicyName( 'service-role/AWSAppRunnerServicePolicyForECRAccess', ), ); // VPC Connectorの作成 const vpcConnector = new CfnVpcConnector(scope, 'MyAppVPCConnector', { subnets: vpc.selectSubnets({ subnetType: SubnetType.PRIVATE_WITH_EGRESS, }).subnetIds, securityGroups: [appRunnerSecurityGroup.securityGroupId], }); const service = new CfnService(this, 'AppRunnerService', { sourceConfiguration: { authenticationConfiguration: { accessRoleArn: accessRole.roleArn, }, autoDeploymentsEnabled: true, imageRepository: { imageIdentifier: `${repository.repositoryUri}:latest`, imageRepositoryType: 'ECR', imageConfiguration: { port: '8080', runtimeEnvironmentVariables: [ { name: 'REDIS_HOST', value: cacheCluster.attrRedisEndpointAddress, }, { name: 'REDIS_PORT', value: cacheCluster.attrRedisEndpointPort, }, ], }, }, }, networkConfiguration: { egressConfiguration: { egressType: 'VPC', vpcConnectorArn: vpcConnector.attrVpcConnectorArn, }, }, }); } }
Stackを作成
これまで作成したものを組み合わせてAWSリソースを作成します。
import { Construct } from 'constructs'; import { Network } from './construct/network'; import { EcrRepository } from './construct/ecr-repository'; import { Cache } from './construct/cache'; import { AppRunner } from './construct/app-runner'; import { Stack, StackProps } from 'aws-cdk-lib'; export class CdkStack extends Stack { constructor(scope: Construct, id: string, props?: 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, }); // App Runnerを作成する new AppRunner(this, 'AppRunner', { vpc, repository, appRunnerSecurityGroup, cacheCluster, }); } }
デプロイする
cdk deploy
を実行するとApp Runnerも作成でき、ElastiCacheにも接続することができました。