K's Atelier

個人的な学習記録

Load Balancerが動かない

CloudFormation Template for Creating EC2 with Load Balancer | DevelopersIO
ん-?CloudFormationで動かしてみたけど動かないぞ。Load Balancerの設定が苦手だから試しているのに困ったな。
いろいろと調整をしてみて,最終的に動かせたのはこれ。

最終形態

AWSTemplateFormatVersion: 2010-09-09
Parameters:
  AvailabilityZone1:
    Description: AvailabilityZone1
    Type: String
    Default: ap-northeast-1a
  AvailabilityZone2:
    Description: AvailabilityZone1
    Type: String
    Default: ap-northeast-1c
  ImageId:
    Description: ami-id
    Type: String
    Default: ami-052c9af0c988f8bbd
Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
      - Key: Name
        Value: VPC1

  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
      - Key: Name
        Value: VPC Internet Gateway

  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway

  PublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.1.0/24
      AvailabilityZone: !Ref AvailabilityZone1
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: Public Subnet 1

  PrivateSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.2.0/24
      MapPublicIpOnLaunch: false
      AvailabilityZone: !Ref AvailabilityZone1
      Tags:
        - Key: Name
          Value: Private Subnet 1

  PublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.3.0/24
      AvailabilityZone: !Ref AvailabilityZone2
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: Public Subnet 2

  PrivateSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.4.0/24
      AvailabilityZone: !Ref AvailabilityZone2
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: Private Subnet 2

  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: Public Route Table

  PublicRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  PublicSubnetRouteTableAssociation1:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet1
      RouteTableId: !Ref PublicRouteTable

  PublicSubnetRouteTableAssociation2:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet2
      RouteTableId: !Ref PublicRouteTable

  PrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
      - Key: Name
        Value: Private Route Table

  PrivateSubnetRouteTableAssociation1:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet1
      RouteTableId: !Ref PrivateRouteTable

  PrivateSubnetRouteTableAssociation2:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnet2
      RouteTableId: !Ref PrivateRouteTable

  ELBSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: ELB Security Group
      VpcId: !Ref VPC
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: 80
        ToPort: 80
        CidrIp: 0.0.0.0/0

  EC2SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: EC2 Security Group
      VpcId: !Ref VPC
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: 80
        ToPort: 80
        CidrIp: 0.0.0.0/0
      - IpProtocol: tcp
        FromPort: 22
        ToPort: 22
        CidrIp: 0.0.0.0/0
      - IpProtocol: tcp
        FromPort: 443
        ToPort: 443
        CidrIp: 0.0.0.0/0

  SSMRole:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      Path: /
      Policies:
        - PolicyName: ssmAccess
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - "ssmmessages:CreateDataChannel"
                  - "ssm:UpdateInstanceInformation"
                  - "ssmmessages:OpenDataChannel"
                  - "ssmmessages:OpenControlChannel"
                  - "ssmmessages:CreateControlChannel"
                Resource: '*'
  SSMInstanceProfile:
    Type: 'AWS::IAM::InstanceProfile'
    Properties:
      Path: /
      Roles:
        - !Ref SSMRole

  #EC2 Instances
  EC2Instance1:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref ImageId 
      InstanceType: t2.micro
      SecurityGroupIds:
        - !Ref EC2SecurityGroup
      IamInstanceProfile: !Ref SSMInstanceProfile
      SubnetId: !Ref PublicSubnet1
      Tags:
        - Key: Name
          Value: app-server-1
      UserData: 
        Fn::Base64: !Sub |
          #!/bin/bash
          yum update -y
          yum install -y httpd
          systemctl start httpd
          systemctl enable httpd
          echo "<h1>Hello from Region !Ref AvailabilityZone1</h1>" > /var/www/html/index.html
  EC2Instance2:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref ImageId 
      InstanceType: t2.micro
      SecurityGroupIds:
        - !Ref EC2SecurityGroup
      IamInstanceProfile: !Ref SSMInstanceProfile
      SubnetId: !Ref PublicSubnet2
      Tags:
        - Key: Name
          Value: app-server-2
      UserData: 
        Fn::Base64: !Sub |
          #!/bin/bash
          yum update -y
          yum install -y httpd
          systemctl start httpd
          systemctl enable httpd
          echo "<h1>Hello from Region !Ref AvailabilityZone2</h1>" > /var/www/html/index.html
          
  EC2TargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      HealthCheckIntervalSeconds: 30
      HealthCheckProtocol: HTTP
      HealthCheckTimeoutSeconds: 15
      HealthyThresholdCount: 5
      Matcher:
        HttpCode: '200'
      Name: EC2TargetGroup
      Port: 80
      Protocol: HTTP
      TargetGroupAttributes:
      - Key: deregistration_delay.timeout_seconds
        Value: '20'
      Targets:
      - Id: !Ref EC2Instance1
        Port: 80
      - Id: !Ref EC2Instance2
        Port: 80
      UnhealthyThresholdCount: 3
      VpcId: !Ref VPC

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

  ApplicationLoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Scheme: internet-facing
      Subnets:
      - !Ref PublicSubnet1
      - !Ref PublicSubnet2
      SecurityGroups:
        - !GetAtt ELBSecurityGroup.GroupId

感想

設定手順通りに上から書いているはずなので,後半の設定を消したテンプレートでstackを作ったあと,消した部分をコピペしながらstackのupdateをしていけば,「どこで何が行われているか」「手作業だったらどうやるか」を確認できる。
最初のDeveloperIOの記事はこれはこれでありがたい。一撃で動かないからこそ,いろいろ調査する勉強になる。
なお,このテンプレートはセキュリティ的にはまだ設定が緩い。
あとはAuto Scalingまでいけば,負荷分散系の把握は一段落かな。

よく「好きなAWSサービスは〇〇です」という紹介をしている人がいる。
私は,技術に中立であることが講師の信頼に関わると思っているのでそういう自己紹介はしないが,あえて言うならAWSではCloudFormation,AWS CLIあたりが重要技術だと考えている。
「他のAPIを叩くAPI」は技術の寿命が長い上,統一的な観点から他の技術を把握できる。