APC 技術ブログ

株式会社エーピーコミュニケーションズの技術ブログです。

株式会社 エーピーコミュニケーションズの技術ブログです。

【CloudFormation】ALBのパスベースルーティングとPrivateLink を使用してWebサーバーをインターネットに公開する

目次

はじめに

こんにちは。クラウド事業部の西川です。

今回は下記のブログ"How to securely publish Internet applications at scale using Application Load Balancer and AWS PrivateLink"を参考に、ALBとPrivateLinkを使用してWebサーバーをインターネットに公開する方法をご紹介します。

aws.amazon.com

構成図

DMZ VPCにはインターネットゲートウェイとインターネットに面したALBがあり、パスベースルーティング(URLパスに基づいてトラフィックをルーティングする)が設定されています。プライベートサブネットにはPrivateLinkインターフェイスVPCエンドポイントがあり、インターネット上のクライアントからBlue-Application-VPCとGreen-Application-VPC内のアプリケーションへのエンドツーエンドの接続が可能です。ユーザーが/blueパスをリクエストするとBlue-Application-VPCに、/greenパスをリクエストするとGreen-Application-VPCにトラフィックが送信されます。

Blue-Application-VPCとGreen-Application-VPCには、それぞれApacheウェブサーバーをホスティングしたBlueInstanceとGreenInstanceがあります。これらのインスタンスはNLBをフロントエンドとして使用しています。これらのVPCにはインターネットゲートウェイが接続されていないため、インターネットとの直接通信はできません。

事前準備(AMI作成)

まず最初に、Application-TemplateのEC2の起動時に指定するAMIをBlue環境用とGreen環境用の2つ作成します。今回はApacheをインストール&テストページを作成したものを用意します。

下記のテンプレートでスタックを作成し、作成されたEC2からAMIを作成してください。

Blue環境用AMI作成テンプレート

AWSTemplateFormatVersion: '2010-09-09'

Parameters:
  PJPrefix:
    Type: String
    Default: Blue
  EC2AMIId:
    Description: AMI ID (Amazon Linux2)
    Type: AWS::SSM::Parameter::Value<String>
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2
  KeyName:
    Description: The EC2 Key Pair to allow SSH access to the instance
    Type: "AWS::EC2::KeyPair::KeyName"

Resources:
  MyVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-AMICreation-VPC"

  MyInternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: MyInternetGateway

  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref MyVPC
      InternetGatewayId: !Ref MyInternetGateway

  MyPublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref MyVPC
      CidrBlock: 10.0.1.0/24
      MapPublicIpOnLaunch: true
      AvailabilityZone: !Select [ 0, !GetAZs '' ]
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-AMICreation-PublicSubnet"

  MyRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref MyVPC
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-AMICreation-RouteTable"

  MyRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref MyRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref MyInternetGateway

  MySubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref MyPublicSubnet
      RouteTableId: !Ref MyRouteTable

  MySecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow SSH and HTTP
      VpcId: !Ref MyVPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-AMICreation-SecurityGroup"

  MyEC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: t2.micro
      SecurityGroupIds:
        - !Ref MySecurityGroup
      SubnetId: !Ref MyPublicSubnet
      KeyName: !Ref KeyName
      ImageId: !Ref EC2AMIId
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-AMICreation-EC2Instance"
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash
          yum update -y
          yum install -y httpd
          systemctl start httpd
          systemctl enable httpd
          mkdir -p /var/www/html/blue
          echo "<html>
                    <head>
                    <title>Blue Test Page</title>
                    <style>
                    h1 {
                        color: blue;
                        font-size: 3em;
                    }
                    </style>
                    </head>
                    <body>
                    <h1>Blue Test Page</h1>
                    <p>If you see this page, the web server is working correctly.</p>
                    </body>
                    </html>" > /var/www/html/blue/index.html

Green環境用AMI作成テンプレート

AWSTemplateFormatVersion: '2010-09-09'

Parameters:
  PJPrefix:
    Type: String
    Default: Green
  EC2AMIId:
    Description: AMI ID (Amazon Linux2)
    Type: AWS::SSM::Parameter::Value<String>
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2
  KeyName:
    Description: The EC2 Key Pair to allow SSH access to the instance
    Type: "AWS::EC2::KeyPair::KeyName"

Resources:
  MyVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-AMICreation-VPC"

  MyInternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: MyInternetGateway

  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref MyVPC
      InternetGatewayId: !Ref MyInternetGateway

  MyPublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref MyVPC
      CidrBlock: 10.0.1.0/24
      MapPublicIpOnLaunch: true
      AvailabilityZone: !Select [ 0, !GetAZs '' ]
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-AMICreation-PublicSubnet"

  MyRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref MyVPC
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-AMICreation-RouteTable"

  MyRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref MyRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref MyInternetGateway

  MySubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref MyPublicSubnet
      RouteTableId: !Ref MyRouteTable

  MySecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow SSH and HTTP
      VpcId: !Ref MyVPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-AMICreation-SecurityGroup"

  MyEC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: t2.micro
      SecurityGroupIds:
        - !Ref MySecurityGroup
      SubnetId: !Ref MyPublicSubnet
      KeyName: !Ref KeyName
      ImageId: !Ref EC2AMIId
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-AMICreation-EC2Instance"
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash
          yum update -y
          yum install -y httpd
          systemctl start httpd
          systemctl enable httpd
          mkdir -p /var/www/html/green
          echo "<html>
                    <head>
                    <title>Green Test Page</title>
                    <style>
                    h1 {
                        color: green;
                        font-size: 3em;
                    }
                    </style>
                    </head>
                    <body>
                    <h1>Green Test Page</h1>
                    <p>If you see this page, the web server is working correctly.</p>
                    </body>
                    </html>" > /var/www/html/green/index.html

スタック作成

AMIが作成できたら、下記のテンプレートを使用してリソースを作成します。

スタックはBlue-Application-Templateスタック、Green-Application-Templateスタック→DMZ-Templateスタック→ALB-Templateスタックの順番で作成してください。

Blue-Application-Templateスタック、Green-Application-Templateスタック作成時に事前準備で作成したAMIのIDを指定します。

ALB-Templateスタック作成時にエンドポイントのIPアドレスをそれぞれ入力します。

エンドポイントのIPアドレスは、コンソールのVPC>エンドポイントでエンドポイントを選択し、サブネットの項目で確認できます。

この時に逆のIPアドレスを入力してしまわないように注意してください。

Blue-VPC用のエンドポイントがDMZ-PrivateSubnet-A、Green-VPC用のエンドポイントがDMZ-PrivateSubnet-Cに作成されるようにしています。

テンプレート

Blue-Application-Template

AWSTemplateFormatVersion: "2010-09-09"
Description: CloudFormation template for Blue-Application-VPC

Parameters:
  PJPrefix:
    Type: String
    Default: "PrivateLink-HandsOn"
  VPCEnvironment:
    Type: String
    Default: "Blue"
  VPCCIDR:
    Type: String
    Default: "192.168.0.0/20"
  PublicSubnetCIDR:
    Type: String
    Default: "192.168.0.0/24"
  PrivateSubnetCIDR:
    Type: String
    Default: "192.168.1.0/24"
  EC2AMIId:
    Description: AMI ID
    Type : String
  KeyName:
    Description: The EC2 Key Pair to allow SSH access to the instance
    Type: "AWS::EC2::KeyPair::KeyName"

Resources:
  VPC:
    Type: "AWS::EC2::VPC"
    Properties:
      CidrBlock: !Ref VPCCIDR
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: !Sub "$${PJPrefix}-${VPCEnvironment}-Application-VPC"

  PrivateSubnet:
    Type: "AWS::EC2::Subnet"
    Properties:
      AvailabilityZone: "ap-northeast-1a"
      VpcId: !Ref VPC
      CidrBlock: !Ref PrivateSubnetCIDR
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-${VPCEnvironment}-Application-PrivateSubnet"

  PrivateRouteTable:
    Type: "AWS::EC2::RouteTable"
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-${VPCEnvironment}-Application-PrivateRoute"

  PrivateSubnetRouteTableAssociation:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      SubnetId: !Ref PrivateSubnet
      RouteTableId: !Ref PrivateRouteTable

  SecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      VpcId: !Ref VPC
      GroupDescription: "Allow access to EC2 instances"
      SecurityGroupIngress:
        - IpProtocol: "tcp"
          FromPort: 80
          ToPort: 80
          CidrIp: "0.0.0.0/0"

  EC2Instance:
    Type: "AWS::EC2::Instance"
    Properties:
      InstanceType: "t2.micro"
      SubnetId: !Ref PrivateSubnet
      KeyName: !Ref KeyName
      SecurityGroupIds:
        - !Ref SecurityGroup
      ImageId: !Ref EC2AMIId
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-${VPCEnvironment}-Application-WebServer"

  TargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      VpcId: !Ref VPC
      Name: !Sub "${PJPrefix}-${VPCEnvironment}-NLB-TG"
      Protocol: TCP
      Port: 80
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-${VPCEnvironment}-NLB-TG"
      Targets:
        - Id: !Ref EC2Instance
          Port: 80

  NLB:
    Type: "AWS::ElasticLoadBalancingV2::LoadBalancer"
    Properties:
      Subnets:
        - !Ref PrivateSubnet
      Name: !Sub "${PJPrefix}-${VPCEnvironment}-NLB"
      Scheme: "internal"
      Type: "network"
      LoadBalancerAttributes:
        - Key: "deletion_protection.enabled"
          Value: "false"
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-${VPCEnvironment}-NLB"

  NLBListener:
    Type: "AWS::ElasticLoadBalancingV2::Listener"
    Properties:
      DefaultActions:
        - Type: "forward"
          TargetGroupArn: !Ref TargetGroup
      LoadBalancerArn: !Ref NLB
      Port: 80
      Protocol: "TCP"

  VPCEndpointService:
    Type: AWS::EC2::VPCEndpointService
    Properties:
      AcceptanceRequired: false
      NetworkLoadBalancerArns:
        - !Ref NLB

Outputs:
  VPCEndpointService:
    Value: { "Fn::Sub": "com.amazonaws.vpce.${AWS::Region}.${VPCEndpointService}" }
    Export:
      Name: !Sub "${PJPrefix}-${VPCEnvironment}-VPCEndpointService"

Green-Application-Template

AWSTemplateFormatVersion: "2010-09-09"
Description: CloudFormation template for Green-Application-VPC

Parameters:
  PJPrefix:
    Type: String
    Default: "PrivateLink-HandsOn"
  VPCEnvironment:
    Type: String
    Default: "Green"
  VPCCIDR:
    Type: String
    Default: "192.168.0.0/20"
  PublicSubnetCIDR:
    Type: String
    Default: "192.168.0.0/24"
  PrivateSubnetCIDR:
    Type: String
    Default: "192.168.1.0/24"
  EC2AMIId:
    Description: AMI ID
    Type : String
  KeyName:
    Description: The EC2 Key Pair to allow SSH access to the instance
    Type: "AWS::EC2::KeyPair::KeyName"

Resources:
  VPC:
    Type: "AWS::EC2::VPC"
    Properties:
      CidrBlock: !Ref VPCCIDR
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: !Sub "$${PJPrefix}-${VPCEnvironment}-Application-VPC"

  PrivateSubnet:
    Type: "AWS::EC2::Subnet"
    Properties:
      AvailabilityZone: "ap-northeast-1c"
      VpcId: !Ref VPC
      CidrBlock: !Ref PrivateSubnetCIDR
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-${VPCEnvironment}-Application-PrivateSubnet"

  PrivateRouteTable:
    Type: "AWS::EC2::RouteTable"
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-${VPCEnvironment}-Application-PrivateRoute"

  PrivateSubnetRouteTableAssociation:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      SubnetId: !Ref PrivateSubnet
      RouteTableId: !Ref PrivateRouteTable

  SecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      VpcId: !Ref VPC
      GroupDescription: "Allow access to EC2 instances"
      SecurityGroupIngress:
        - IpProtocol: "tcp"
          FromPort: 80
          ToPort: 80
          CidrIp: "0.0.0.0/0"

  EC2Instance:
    Type: "AWS::EC2::Instance"
    Properties:
      InstanceType: "t2.micro"
      SubnetId: !Ref PrivateSubnet
      KeyName: !Ref KeyName
      SecurityGroupIds:
        - !Ref SecurityGroup
      ImageId: !Ref EC2AMIId
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-${VPCEnvironment}-Application-WebServer"

  TargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      VpcId: !Ref VPC
      Name: !Sub "${PJPrefix}-${VPCEnvironment}-NLB-TG"
      Protocol: TCP
      Port: 80
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-${VPCEnvironment}-NLB-TG"
      Targets:
        - Id: !Ref EC2Instance
          Port: 80

  NLB:
    Type: "AWS::ElasticLoadBalancingV2::LoadBalancer"
    Properties:
      Subnets:
        - !Ref PrivateSubnet
      Name: !Sub "${PJPrefix}-${VPCEnvironment}-NLB"
      Scheme: "internal"
      Type: "network"
      LoadBalancerAttributes:
        - Key: "deletion_protection.enabled"
          Value: "false"
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-${VPCEnvironment}-NLB"

  NLBListener:
    Type: "AWS::ElasticLoadBalancingV2::Listener"
    Properties:
      DefaultActions:
        - Type: "forward"
          TargetGroupArn: !Ref TargetGroup
      LoadBalancerArn: !Ref NLB
      Port: 80
      Protocol: "TCP"

  VPCEndpointService:
    Type: AWS::EC2::VPCEndpointService
    Properties:
      AcceptanceRequired: false
      NetworkLoadBalancerArns:
        - !Ref NLB

Outputs:
  VPCEndpointService:
    Value: { "Fn::Sub": "com.amazonaws.vpce.${AWS::Region}.${VPCEndpointService}" }
    Export:
      Name: !Sub "${PJPrefix}-${VPCEnvironment}-VPCEndpointService"

DMZ-Template

AWSTemplateFormatVersion: "2010-09-09"
Description: CloudFormation template for DMZ-VPC

Parameters:
  PJPrefix:
    Type: String
    Default: "PrivateLink-HandsOn"
  VPCEnvironment1:
    Type: String
    Default: "Blue"
  VPCEnvironment2:
    Type: String
    Default: "Green"
  VPCCIDR:
    Type: String
    Default: "192.168.0.0/20"
  PublicSubnetACIDR:
    Type: String
    Default: "192.168.1.0/24"
  PublicSubnetCCIDR:
    Type: String
    Default: "192.168.2.0/24"
  PrivateSubnetACIDR:
    Type: String
    Default: "192.168.3.0/24"
  PrivateSubnetCCIDR:
    Type: String
    Default: "192.168.4.0/24"

Resources:
  VPC:
    Type: "AWS::EC2::VPC"
    Properties:
      CidrBlock: !Ref VPCCIDR
      EnableDnsSupport: "true"
      EnableDnsHostnames: "true"
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-DMZ-VPC"

  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
      - Key: Name
        Value: !Sub ${PJPrefix}-VPC-IGW
  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway

  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    DependsOn: AttachGateway
    Properties:
      VpcId: !Ref VPC
      Tags:
      - Key: Name
        Value: !Sub ${PJPrefix}-VPC-RouteTable
  Route:
    Type: AWS::EC2::Route
    DependsOn: AttachGateway
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  PublicSubnetA:
    Type: AWS::EC2::Subnet
    DependsOn: AttachGateway
    Properties:
      AvailabilityZone: "ap-northeast-1a"
      CidrBlock: !Ref PublicSubnetACIDR
      MapPublicIpOnLaunch: 'true'
      VpcId: !Ref VPC
      Tags:
      - Key: Name
        Value: !Sub ${PJPrefix}-VPC-PublicSubnet-A
  PublicRouteTableAssociationA:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnetA
      RouteTableId: !Ref PublicRouteTable

  PublicSubnetC:
    Type: AWS::EC2::Subnet
    DependsOn: AttachGateway
    Properties:
      AvailabilityZone: "ap-northeast-1c"
      CidrBlock: !Ref PublicSubnetCCIDR
      MapPublicIpOnLaunch: 'true'
      VpcId: !Ref VPC
      Tags:
      - Key: Name
        Value: !Sub ${PJPrefix}-VPC-PublicSubnet-C
  PublicRouteTableAssociationC:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnetC
      RouteTableId: !Ref PublicRouteTable

  PrivateSubnetA:
    Type: "AWS::EC2::Subnet"
    Properties:
      AvailabilityZone: "ap-northeast-1a"
      CidrBlock: !Ref PrivateSubnetACIDR
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-DMZ-PrivateSubnet-A"

  PrivateSubnetC:
    Type: "AWS::EC2::Subnet"
    Properties:
      AvailabilityZone: "ap-northeast-1c"
      CidrBlock: !Ref PrivateSubnetCCIDR
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-DMZ-PrivateSubnet-C"

  PrivateRouteTable:
    Type: "AWS::EC2::RouteTable"
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-DMZ-PrivateRoute"

  PrivateSubnetRouteTableAssociationA:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      SubnetId: !Ref PrivateSubnetA
      RouteTableId: !Ref PrivateRouteTable

  PrivateSubnetRouteTableAssociationC:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      SubnetId: !Ref PrivateSubnetC
      RouteTableId: !Ref PrivateRouteTable

  EndPointSG:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      GroupDescription: "http"
      GroupName: !Sub "${PJPrefix}-DMZ-SG"
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-DMZ-SG"
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort : 80
          ToPort : 80
          CidrIp: 0.0.0.0/0

  VpcEndpoint1:
    Type: "AWS::EC2::VPCEndpoint"
    Properties:
      VpcId: !Ref VPC
      ServiceName: { "Fn::ImportValue": !Sub "${PJPrefix}-${VPCEnvironment1}-VPCEndpointService" }
      VpcEndpointType: "Interface"
      SubnetIds:
        - !Ref PrivateSubnetA
      SecurityGroupIds:
        - !Ref EndPointSG

  VpcEndpoint2:
    Type: "AWS::EC2::VPCEndpoint"
    Properties:
      VpcId: !Ref VPC
      ServiceName: { "Fn::ImportValue": !Sub "${PJPrefix}-${VPCEnvironment2}-VPCEndpointService" }
      VpcEndpointType: "Interface"
      SubnetIds:
        - !Ref PrivateSubnetC
      SecurityGroupIds:
        - !Ref EndPointSG

Outputs:
  VPC:
    Value: !Ref VPC
    Export:
      Name: !Sub "${PJPrefix}-DMZ-VPC"
  PublicSubnetA:
    Value: !Ref PublicSubnetA
    Export:
      Name: !Sub ${PJPrefix}-VPC-PublicSubnet-A
  PublicSubnetC:
    Value: !Ref PublicSubnetC
    Export:
      Name: !Sub ${PJPrefix}-VPC-PublicSubnet-C

ALB-Template

AWSTemplateFormatVersion: '2010-09-09'
Description: CloudFormation template for ALB

Parameters:
  PJPrefix:
    Type: String
    Default: "PrivateLink-HandsOn"
  BlueEndPointIPaddress:
    Type: String
  GreenEndPointIPaddress:
    Type: String

Resources:
  ALBSG:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      GroupDescription: "http"
      GroupName: !Sub "${PJPrefix}-ALB-SG"
      VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-DMZ-VPC" }
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-ALB-SG"
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort : 80
          ToPort : 80
          CidrIp: 0.0.0.0/0

  ALB:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Name: !Sub "${PJPrefix}-ALB"
      Scheme: internet-facing
      SecurityGroups:
        - !Ref ALBSG
      Subnets:
        - { "Fn::ImportValue": !Sub "${PJPrefix}-VPC-PublicSubnet-A" }
        - { "Fn::ImportValue": !Sub "${PJPrefix}-VPC-PublicSubnet-C" }
      Type: application

  BlueALBTG:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-DMZ-VPC" }
      Name: !Sub "${PJPrefix}-Blue-ALBTG"
      Protocol: HTTP
      Port: 80
      HealthCheckProtocol: HTTP
      HealthCheckPath: /blue/index.html
      HealthCheckPort: 80
      TargetType: ip
      Targets:
        - Id: !Ref BlueEndPointIPaddress
          Port: 80

  GreenALBTG:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-DMZ-VPC" }
      Name: !Sub "${PJPrefix}-Green-ALBTG"
      Protocol: HTTP
      Port: 80
      HealthCheckProtocol: HTTP
      HealthCheckPath: /green/index.html
      HealthCheckPort: 80
      TargetType: ip
      Targets:
        - Id: !Ref GreenEndPointIPaddress
          Port: 80

  ALBListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - TargetGroupArn: !Ref BlueALBTG
          Type: forward
      LoadBalancerArn: !Ref ALB
      Port: 80
      Protocol: HTTP

  ALBListenerRule1:
    Type: AWS::ElasticLoadBalancingV2::ListenerRule
    Properties:
      Actions:
        - TargetGroupArn: !Ref BlueALBTG
          Type: forward
      Conditions:
        - Field: path-pattern
          PathPatternConfig:
            Values:
              - /Blue/*
      ListenerArn: !Ref ALBListener
      Priority: 1

  ALBListenerRule2:
    Type: AWS::ElasticLoadBalancingV2::ListenerRule
    Properties:
      Actions:
        - TargetGroupArn: !Ref GreenALBTG
          Type: forward
      Conditions:
        - Field: path-pattern
          PathPatternConfig:
            Values:
              - /Green/*
      ListenerArn: !Ref ALBListener
      Priority: 2

動作確認

リソースの作成が完了したら動作確認を実施します。

<ALBのDNS名>/blue/でブラウザからアクセスすると、下記の画面が表示されると思います。

続いて、<ALBのDNS名>/green/でブラウザからアクセスすると、下記の画面が表示されると思います。

以上で動作確認は終了です。

リソース削除

ALB-Templateスタック→DMZ-Templateスタック→Blue-Application-Templateスタック、Green-Application-Templateスタックの順番で削除してください。

お疲れ様でした。

まとめ

ALBとPrivateLinkを使用してWebサーバーをインターネットに公開する方法をご紹介しました。

この記事がご参考になれば幸いです。

おわりに

私達クラウド事業部はAWSなどのクラウド技術を活用したSI/SESのご支援をしております。

www.ap-com.co.jp

また、一緒に働いていただける仲間も募集中です! ご興味持っていただけましたらぜひお声がけください。

www.ap-com.co.jp