AWS CDKでNLB + ALB + EC2の構成を作ってみる
「NLBのターゲットにALBを作成する構成をサクッと試したい」
NLBのターゲットにはALBを登録することもできます。
やったこと無かったので、試してみました。
せっかくなので、AWS CDKを使って構築してみました。
やってみた
構成図
リソース作成
コードは以下になります。
コードを実行してのリソース作成には、手元の環境で10分ほどかかりました。
作成される構成は上記の図のとおりです。
Webサーバ用のEC2では、ユーザーデータを使ってapacheのインストールとテスト用にindex.htmlを作成しています。
import * as cdk from 'aws-cdk-lib'; import { CfnOutput } from 'aws-cdk-lib'; import * as ec2 from 'aws-cdk-lib/aws-ec2'; import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2'; import * as elbv2_tg from 'aws-cdk-lib/aws-elasticloadbalancingv2-targets' import * as iam from 'aws-cdk-lib/aws-iam'; import { Construct } from 'constructs'; export class NlbAlbTestStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); const natGatewayProvider = ec2.NatProvider.instance({ instanceType: new ec2.InstanceType('t3.small'), }); const vpc = new ec2.Vpc(this, "Vpc", { cidr: "10.0.0.0/16", availabilityZones: [ "ap-northeast-1a", "ap-northeast-1c"], natGatewayProvider, natGateways: 1, subnetConfiguration: [ { cidrMask: 24, name: "public", subnetType: ec2.SubnetType.PUBLIC }, { cidrMask: 24, name: "private", subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS }, ] }) // Security Group const albSg = new ec2.SecurityGroup(this, "alb-sg", { vpc, allowAllOutbound: true, description: "security group for a alb" }) albSg.connections.allowInternally(ec2.Port.tcp(80)) const webServerSg = new ec2.SecurityGroup(this, "web-server-sg", { vpc, allowAllOutbound: true, description: "security group for a web server" }) webServerSg.connections.allowFrom(albSg, ec2.Port.tcp(80), 'Allow alb access') // EC2 const userData = ec2.UserData.forLinux({ shebang: "#!/bin/bash", }) userData.addCommands( "yum update -y", "yum install -y httpd", "systemctl start httpd", "systemctl enable httpd", "echo test > /var/www/html/index.html" ) const role = iam.Role.fromRoleName(this, "Ec2Role", "AmazonSSMRoleForInstancesQuickSetup") const machineImage = new ec2.AmazonLinuxImage({ generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2, edition: ec2.AmazonLinuxEdition.STANDARD, virtualization: ec2.AmazonLinuxVirt.HVM, storage: ec2.AmazonLinuxStorage.GENERAL_PURPOSE, }); const webServer = new ec2.Instance(this, "Instance", { vpc, instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MICRO), machineImage, vpcSubnets: { subnets: vpc.privateSubnets }, role, userData }) webServer.addSecurityGroup(webServerSg) // nat instance作成前にuserdataを実行してしまうとyumでエラーになる webServer.node.addDependency(vpc) // ALB(private) const alb = new elbv2.ApplicationLoadBalancer(this, "Alb", { internetFacing: false, vpc, vpcSubnets: { subnets: vpc.privateSubnets } }) alb.addSecurityGroup(albSg) const instanceTarget = new elbv2_tg.InstanceTarget(webServer) const albListener = alb.addListener("AlbHttpListener", { port: 80, protocol: elbv2.ApplicationProtocol.HTTP }) albListener.addTargets("WebServerTarget", { targets: [ instanceTarget ], port: 80 }) const albTarget = new elbv2_tg.AlbTarget(alb, 80 ) // NLB(public) const nlb = new elbv2.NetworkLoadBalancer(this, "Nlb", { vpc, internetFacing: true, vpcSubnets: { subnets: vpc.publicSubnets } }) // ALBが存在しない時点でTargetGroupを作るとエラーになるため nlb.node.addDependency(alb) const nlbListener = nlb.addListener("NlbHttpListener", { port: 80, protocol: elbv2.Protocol.TCP }) nlbListener.addTargets("AlbTarget", { targets: [ albTarget ], port: 80 }) new CfnOutput(this, "NlbDnsName",{ value: nlb.loadBalancerDnsName }) } }
疎通確認
NLBのエンドポイントにcurlをして、Webサーバで表示しているコンテンツを確認します。
NLBのエンドポイントは、CDKのOutputで出しているためCDK実行時にコンソール上からも確認できます。
% curl <NLBのエンドポイント> test
少しハマったところ
主にリソース作成の依存関係でハマりました。
nat instance作成前にEC2が作成されて、EC2のユーザーデータのyumが動かなかった
業務で利用する場合はVPCのスタックとNLBやALBのスタックは、分割することが多いと思うのであまり発生しないとは思います。
最初は、EC2とVPC関連のリソースを明示的に依存関係を作らずに作成していました。
そのため、EC2でインターネット接続が可能になる前にユーザーデータが実行されてユーザーデータが動かないことがありました。
以下のように依存関係を追加することで、この事象は回避できました。
// nat instance作成前にuserdataを実行してしまうとyumでエラーになる webServer.node.addDependency(vpc)
ALBが存在しない時点でNLBのターゲットグループを作成してしまう
今回はNLBのターゲットグループでALBを指定しています。
依存関係を明示していないと、CloudFormatino実行時にALBが存在しない旨のエラーがでます。
// ALBが存在しない時点でTargetGroupを作るとエラーになるため nlb.node.addDependency(alb)
おわりに
AWS CDKでNLB + ALB + EC2の構成を作成してみました。
似たような構成をCDKで作成する際に参考になると嬉しいです。
以上、AWS事業本部の佐藤(@chari7311)でした。