CloudFormationを使ってみる

概要

インフラ構築の自動化の方法を覚えたかったためCloudFormationを使ってみました。

Application Load Balancer を使ってみる : プライベートなAPIをパスでアクセス制限したい - tkaaad97’s diary

f:id:tkaaad97:20180128181437p:plain

前にアプリケーションロードバランサーの実験で試した構成で試しました。

ただEC2を立てるところはちょっと複雑そうで実際CloudFormationでやるか微妙な気がしたので前と同様に手動で作りました。

CloudFormationテンプレート

下のテンプレートで構築できました。スペル間違いなどで何回かstack作り直しました。 スペル間違いとか作る前に検出して欲しいところですが実際作ってみないとエラーにならないようでちょっと不便だなと思いました。

AWSTemplateFormatVersion: "2010-09-09"

Description: application load balancer experiment

Parameters:
    EnvName:
        Type: String
        Description: Environmanet Name
    VpcIpBlock:
        Type: String
        Description: VPC CIDR Block
        AllowedPattern: \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/\d{1,2}
    Subnet1IpBlock:
        Type: String
        Description: VPC CIDR Block
        AllowedPattern: \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/\d{1,2}
    Subnet2IpBlock:
        Type: String
        Description: VPC CIDR Block
        AllowedPattern: \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/\d{1,2}

Resources:
    Vpc:
        Type: AWS::EC2::VPC
        Properties:
            CidrBlock: !Ref VpcIpBlock
            EnableDnsSupport: True
            EnableDnsHostnames: True

    Subnet1:
        Type: AWS::EC2::Subnet
        Properties:
            AvailabilityZone: !Select [0, !GetAZs ""]
            CidrBlock: !Ref Subnet1IpBlock
            VpcId: !Ref Vpc

    Subnet2:
        Type: AWS::EC2::Subnet
        Properties:
            AvailabilityZone: !Select [1, !GetAZs ""]
            CidrBlock: !Ref Subnet2IpBlock
            VpcId: !Ref Vpc
    Igw:
        Type: AWS::EC2::InternetGateway

    VpcIgwAttachment:
        Type: AWS::EC2::VPCGatewayAttachment
        Properties:
            InternetGatewayId: !Ref Igw
            VpcId: !Ref Vpc

    RouteTable:
        Type: AWS::EC2::RouteTable
        Properties:
            VpcId: !Ref Vpc

    Route:
        Type: AWS::EC2::Route
        Properties:
            RouteTableId: !Ref RouteTable
            DestinationCidrBlock: "0.0.0.0/0"
            GatewayId: !Ref Igw

    RouteTableAssoc1:
        Type: AWS::EC2::SubnetRouteTableAssociation
        Properties:
            RouteTableId: !Ref RouteTable
            SubnetId: !Ref Subnet1

    RouteTableAssoc2:
        Type: AWS::EC2::SubnetRouteTableAssociation
        Properties:
            RouteTableId: !Ref RouteTable
            SubnetId: !Ref Subnet2

    PublicLb:
        Type: AWS::ElasticLoadBalancingV2::LoadBalancer
        Properties:
            Name: !Join ["", [!Ref EnvName, PublicLb]]
            Scheme: internet-facing
            SecurityGroups: [!Ref PublicLbSecurityGroup]
            Subnets: [!Ref Subnet1, !Ref Subnet2]
            Type: application

    PublicLbListener:
        Type: AWS::ElasticLoadBalancingV2::Listener
        Properties:
            DefaultActions:
                - TargetGroupArn: !Ref PublicLbErrorTargetGroup
                  Type: forward
            LoadBalancerArn: !Ref PublicLb
            Port: 80
            Protocol: HTTP

    PublicLbListenerRule:
        Type: AWS::ElasticLoadBalancingV2::ListenerRule
        Properties:
            Actions:
                - Type: forward
                  TargetGroupArn: !Ref PublicLbTargetGroup
            Conditions:
                - Field: path-pattern
                  Values:
                      - "/public*"
            ListenerArn: !Ref PublicLbListener
            Priority: 1

    PublicLbTargetGroup:
        Type: AWS::ElasticLoadBalancingV2::TargetGroup
        Properties:
            HealthCheckPath: /public
            HealthCheckPort: 80
            Name: !Join ["", [!Ref EnvName, PublicLbTG]]
            Port: 80
            Protocol: HTTP
            VpcId: !Ref Vpc

    PublicLbErrorTargetGroup:
        Type: AWS::ElasticLoadBalancingV2::TargetGroup
        Properties:
            HealthCheckPath: /
            HealthCheckPort: 80
            Name: !Join ["", [!Ref EnvName, PublicLbErrorTG]]
            Port: 80
            Protocol: HTTP
            VpcId: !Ref Vpc

    PrivateLb:
        Type: AWS::ElasticLoadBalancingV2::LoadBalancer
        Properties:
            Name: !Join ["", [!Ref EnvName, PrivateLb]]
            Scheme: internal
            SecurityGroups: [!Ref PrivateLbSecurityGroup]
            Subnets: [!Ref Subnet1, !Ref Subnet2]
            Type: application

    PrivateLbListener:
        Type: AWS::ElasticLoadBalancingV2::Listener
        Properties:
            DefaultActions:
                - TargetGroupArn: !Ref PrivateLbErrorTargetGroup
                  Type: forward
            LoadBalancerArn: !Ref PrivateLb
            Port: 80
            Protocol: HTTP

    PrivateLbListenerRule:
        Type: AWS::ElasticLoadBalancingV2::ListenerRule
        Properties:
            Actions:
                - Type: forward
                  TargetGroupArn: !Ref PrivateLbTargetGroup
            Conditions:
                - Field: path-pattern
                  Values:
                      - "/private*"
            ListenerArn: !Ref PrivateLbListener
            Priority: 1

    PrivateLbTargetGroup:
        Type: AWS::ElasticLoadBalancingV2::TargetGroup
        Properties:
            HealthCheckPath: /private
            HealthCheckPort: 80
            Name: !Join ["", [!Ref EnvName, PrivateLbTG]]
            Port: 80
            Protocol: HTTP
            VpcId: !Ref Vpc

    PrivateLbErrorTargetGroup:
        Type: AWS::ElasticLoadBalancingV2::TargetGroup
        Properties:
            HealthCheckPath: /
            HealthCheckPort: 80
            Name: !Join ["", [!Ref EnvName, PrivateLbErrorTG]]
            Port: 80
            Protocol: HTTP
            VpcId: !Ref Vpc

    PublicLbSecurityGroup:
        Type: AWS::EC2::SecurityGroup
        Properties:
            GroupName: !Join ["", [!Ref EnvName, PublicLbSG]]
            GroupDescription: Security Group For Public Load Balancer
            SecurityGroupIngress:
                - IpProtocol: tcp
                  FromPort: 80
                  ToPort: 80
                  CidrIp: 0.0.0.0/0
            SecurityGroupEgress:
                - IpProtocol: "-1"
                  CidrIp: 0.0.0.0/0
            VpcId: !Ref Vpc

    PrivateLbSecurityGroup:
        Type: AWS::EC2::SecurityGroup
        Properties:
            GroupName: !Join ["", [!Ref EnvName, PrivateLbSG]]
            GroupDescription: Security Group For Private Load Balancer
            SecurityGroupIngress:
                - IpProtocol: tcp
                  FromPort: 80
                  ToPort: 80
                  CidrIp: !Ref VpcIpBlock
            SecurityGroupEgress:
                - IpProtocol: "-1"
                  CidrIp: 0.0.0.0/0
            VpcId: !Ref Vpc

    AppServerSecurityGroup:
        Type: AWS::EC2::SecurityGroup
        Properties:
            GroupName: !Join ["", [!Ref EnvName, AppServerSG]]
            GroupDescription: Security Group For Application Server
            SecurityGroupIngress:
                - IpProtocol: tcp
                  FromPort: 80
                  ToPort: 80
                  SourceSecurityGroupId: !GetAtt [PublicLbSecurityGroup, GroupId]
                - IpProtocol: tcp
                  FromPort: 80
                  ToPort: 80
                  SourceSecurityGroupId: !GetAtt [PrivateLbSecurityGroup, GroupId]
            SecurityGroupEgress:
                - IpProtocol: "-1"
                  CidrIp: 0.0.0.0/0
            VpcId: !Ref Vpc

参考

テンプレートフォーマット

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/template-formats.html https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/template-anatomy.html

パラメーター

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html

VPC

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-vpc.html

インターネットゲートウェイ

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-internetgateway.html

VPCゲートウェイアタッチメント

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-vpc-gateway-attachment.html

サブネット

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-subnet.html https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getavailabilityzones.html

ルートテーブル

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-route-table.html https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-route.html https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-subnet-route-table-assoc.html

ロードバランサー

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-elasticloadbalancingv2-loadbalancer.html https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-elasticloadbalancingv2-targetgroup.html https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-elasticloadbalancingv2-listener.html https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-elasticloadbalancingv2-listenerrule.html

セキュリティグループ

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-security-group.html https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-security-group-egress.html

感想

CloudFormationでVPC、サブネット、ロードバランサー、セキュリティグループなどの構築ができました。 テンプレートを書いてみると思ったよりも長くなって、手動でも同じ内容でポチポチ設定していたはずなので自動化できると効率的だと思います。 ただテンプレートを書くのはもう少し便利なツールなどができるといいなという気がしました。 個人的にはGUIで組み立てるよりはIDEで補完して作成したり、バリデーションしたりができるといい気がしました。 EC2まわりのアプリデプロイと関連したあたりを今後調べてみたいです。