24時間365日フルマネージドホスティングサービスのデイーネット

  • HOME
  •  
  • 技術情報ブログ
  •  
  • CloudFormationでテンプレート分割するためCrossStackReferenceを試してみる
クラウド導入支援・運用支援 ディーネット 導入事例

CloudFormationでテンプレート分割するためCrossStackReferenceを試してみる

こんにちは。構築担当の下地です。

最近はAWSの書籍も色々出版されて、体系化された情報が手に入りやすくなったと感じています。

つい色々本を買うのですが、読むのが追いつかず積み本が増えてしまいますね汗

さて、今日はCloudFormationのテンプレート分割についての記事になります。

CloudFormationとCrossStackReferenceを利用して基本的な構成を作ってみました。

CloudFormationについて

AWSでリソースを作成する際に、一番身近な方法としてマネジメントコンソール(以下マネコン)からポチポチと作ることができます。

大規模な構成でもマネコンから作成することもできますが、沢山のリソースを一つずつ手動で作成するのは大変です。

また手動で作成すると、設定ミスが起きる可能性もあります。

そこでCloudFormationを利用します。

CloudFormationは、作成したいリソースをテンプレートという定義ファイルに記載し、実行することでAWS各種リソースを作成できます。

あらかじめ記述してあるコード通りにリソースが作成されるため、設定時にミスが起きることもありませんし、そのコードを何度でも利用できます。

テンプレートの巨大化と対策

CloudFormationはとても便利ですが、作りたい構成が大きくなってくると、テンプレートに記述する内容が増えてどんどんテンプレートが大きくなってしまいます。

コードの規模が大きくなると、単一ファイルでは手間と時間がかかり、変更時の影響範囲も大きくなるため管理が大変です。

これはCloudFormationに限らず、Ansibleなどの構成管理ツールでも同じことが言えます。

そこでCloudFormationの運用ベストプラクティスとして、テンプレートを分割して管理することができます。

その実装がCrossStackReferenceです。

テンプレート分割の方針

テンプレートを分けるといっても、いくつか分割の考え方があります。

例えば、

  • リソース同士の依存関係で分割する
  • リソースのライフサイクル(更新頻度と寿命)によって分割する
  • リソースを管理する部署別に分割する

などです。

今回はこの中から「リソース同士の依存関係で分割」という方針でテンプレートを書いてみます。

具体的には、

1. ネットワークレイヤ:VPC,サブネット,インターネットゲートウェイ,ルートテーブル,など

2. セキュリティレイヤ:セキュリティグループ,IAMユーザ,IAMポリシー,など

3. アプリケーションレイヤ:EC2,AMI,EBS,など

と言う感じで分けました。

構成図

今回は以下の構成を構築してみます。

スライド1.JPG

テンプレート

テンプレートはそれぞれのレイヤごとに、

Network01.yml

Security01.yml

Application01.yml

の3つに分割して記述し、連携させて最終的に先ほどの構成図のように配置されるようにしました。

それでは1つずつテンプレートを見ていきます。

Network01.yml

このテンプレートでは、VPC、InternetGateway、Subnetを2つ、RouteTableを2つ作成します。

ほか2つのテンプレートの受け皿となる一番基礎の部分です。

また他テンプレートとの連携のためにVPCとSubnetの情報を出力します。

AWSTemplateFormatVersion: "2010-09-09"
Resources:
  BlogTestVpc01:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      Tags:
      - Key: Name
        Value: BlogTestVpc01

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

  BlogTestGatewayAttachment01:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref BlogTestIGW01
      VpcId: !Ref BlogTestVpc01

  BlogTestSubnet01:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref BlogTestVpc01
      CidrBlock: 10.0.0.0/24
      AvailabilityZone: ap-northeast-1a
      Tags:
      - Key: Name
        Value: BlogTestSubnet01

  BlogTestSubnet02:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref BlogTestVpc01
      CidrBlock: 10.0.1.0/24
      AvailabilityZone: ap-northeast-1c
      Tags:
      - Key: Name
        Value: BlogTestSubnet02

  BlogTestRouteTable01:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref BlogTestVpc01
      Tags:
      - Key: Name
        Value: BlogTestRouteTable01

  BlogTestRouteTable02:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref BlogTestVpc01
      Tags:
      - Key: Name
        Value: BlogTestRouteTable01

  BlogTestRouteTableAssociation01:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref BlogTestRouteTable01
      SubnetId: !Ref BlogTestSubnet01

  BlogTestRouteTableAssociation02:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref BlogTestRouteTable02
      SubnetId: !Ref BlogTestSubnet02
  
  BlogTestRouteForIGW01:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref BlogTestRouteTable01
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref BlogTestIGW01

  BlogTestRouteForIGW02:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref BlogTestRouteTable02
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref BlogTestIGW01

Outputs:
  BlogTestVpc01:
    Value: !Ref BlogTestVpc01
    Export:
      Name: aws-network-basis:BlogTestVpc01

  BlogTestSubnet01:
    Value: !Ref BlogTestSubnet01
    Export:
      Name: aws-network-basis:BlogTestSubnet01

  BlogTestSubnet02:
    Value: !Ref BlogTestSubnet02
    Export:
      Name: aws-network-basis:BlogTestSubnet02
Security01.yml

次にセキュリティレイヤーのテンプレートです。

今回はIAMユーザなどは作成せず、インフラの要素のみを記述しました。

作成されるのは、ALB・Web01・Web02それぞれのSecurityGroupです。

Webサーバ用のセキュリティグループは、自分の拠点IP(伏字にしています)からのSSH接続と、ALB用のSecurityGroupからのみ接続できるようにしています。

こちらも他テンプレートとの連携のため、各SecurityGroupの情報を出力します。

AWSTemplateFormatVersion: '2010-09-09'
Resources:
  BlogTestSecurityGroupForWeb01:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      GroupName: "BlogTestSecurityGroupForWeb01"
      GroupDescription: "BlogTestSecurityGroupForWeb01"
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          SourceSecurityGroupId: !Ref BlogTestSecurityGroupForALB01
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: xxx.xxx.xxx.xxx/32
      Tags:
        - Key: Name
          Value: BlogTestSecurityGroupForWeb01
      VpcId: !ImportValue aws-network-basis:BlogTestVpc01

  BlogTestSecurityGroupForWeb02:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      GroupName: "BlogTestSecurityGroupForWeb02"
      GroupDescription: "BlogTestSecurityGroupForWeb02"
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          SourceSecurityGroupId: !Ref BlogTestSecurityGroupForALB01
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: xxx.xxx.xxx.xxx/32
      Tags:
        - Key: Name
          Value: BlogTestSecurityGroupForWeb02
      VpcId: !ImportValue aws-network-basis:BlogTestVpc01

  BlogTestSecurityGroupForALB01:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      GroupName: "BlogTestSecurityGroupForALB01"
      GroupDescription: "BlogTestSecurityGroupForALB01"
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: BlogTestSecurityGroupForALB01
      VpcId: !ImportValue aws-network-basis:BlogTestVpc01

Outputs:
  BlogTestSecurityGroupForWeb01:
    Value: !Ref BlogTestSecurityGroupForWeb01
    Export:
      Name: aws-network-basis:BlogTestSecurityGroupForWeb01

  BlogTestSecurityGroupForWeb02:
    Value: !Ref BlogTestSecurityGroupForWeb02
    Export:
      Name: aws-network-basis:BlogTestSecurityGroupForWeb02

  BlogTestSecurityGroupForALB01:
    Value: !Ref BlogTestSecurityGroupForALB01
    Export:
      Name: aws-network-basis:BlogTestSecurityGroupForALB01
Application01.yml

このレイヤでは、ウェブサーバ用のEC2インスタンスであるWeb01・Web02及び、それらにトラフィックを振り分けるApplicationLoadBalancer(以下ALB)を作成します。

ALBで利用するTargetGroupとListnerも合わせて作ります。

EC2の作成に利用するAMIとKeypairは前もって作成しておいたものを利用しました。

EC2用のAMIにはApacheをインストールしておき、ドキュメントルートに簡単なテストファイルを設置しています。

AWSTemplateFormatVersion: '2010-09-09'
Resources:
  BlogTestWeb01:
    Type: AWS::EC2::Instance
    Properties:
      NetworkInterfaces:
        - SubnetId: !ImportValue aws-network-basis:BlogTestSubnet01
          GroupSet: 
            - !ImportValue aws-network-basis:BlogTestSecurityGroupForWeb01
          DeviceIndex: 0
      ImageId: ami-0ad3b9d3225df3e1a
      InstanceType: t2.micro
      Tags:
        - Key: 'Name'
          Value: 'BlogTestWeb01'
      KeyName: ShimojiKey

  BlogTestWeb02:
    Type: AWS::EC2::Instance
    Properties:
      NetworkInterfaces:
        - SubnetId: !ImportValue aws-network-basis:BlogTestSubnet02
          GroupSet: 
            - !ImportValue aws-network-basis:BlogTestSecurityGroupForWeb02
          DeviceIndex: 0
      ImageId: ami-0ad3b9d3225df3e1a
      InstanceType: t2.micro
      Tags:
        - Key: 'Name'
          Value: 'BlogTestWeb02'
      KeyName: ShimojiKey

  BlogTestALB01:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Name: BlogTestALB01
      IpAddressType: ipv4
      Scheme: internet-facing
      SecurityGroups:
        - !ImportValue aws-network-basis:BlogTestSecurityGroupForALB01
      Tags:
        - Key: Name
          Value: BlogTestALB01
      Subnets:
        - !ImportValue aws-network-basis:BlogTestSubnet01
        - !ImportValue aws-network-basis:BlogTestSubnet02

  BlogTestTargetGroup01:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      VpcId: !ImportValue aws-network-basis:BlogTestVpc01
      Name: BlogTestTargetGroup01
      Protocol: HTTP
      Port: 80
      HealthCheckProtocol: HTTP
      HealthCheckPath: /
      HealthCheckPort: traffic-port
      HealthyThresholdCount: 2
      UnhealthyThresholdCount: 2
      HealthCheckTimeoutSeconds: 5
      HealthCheckIntervalSeconds: 10
      Matcher:
        HttpCode: 200
      Targets:
        - Id:
            Ref: BlogTestWeb01
          Port: 80
        - Id:
            Ref: BlogTestWeb02
          Port: 80
  
  BlogTestALBListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - Type: forward
          TargetGroupArn:
            !Ref BlogTestTargetGroup01
      LoadBalancerArn: 
        !Ref BlogTestALB01
      Port: 80
      Protocol: HTTP

テンプレートの実行

テンプレートを階層的に作成してるので、一番下の階層から1つずつCloudFormationを実行してStackを作成します。

今回は最終的に3つStackができます。

実行順番は、Network01.yml → Security01.yml → Application01.yml です。

それぞれ1つテンプレート実行してStack作るのを3回繰り返すと、全ての構成が連携してできあがります。

また、それぞれのテンプレートはCloud9を利用して記述したので、Cloud9からAWS CLIを利用してCloudFormationを実行してStackを作成します。

それではまずNetwork01.ymlの実行です。Stack名を「BlogTestStackNetwork01」としました。

network01.JPG

実行後にCloudFormationを見てみると

ちゃんとStackができています。

(画像は旧型のマネコンですが、こちらの方が使いやすい気がする・・)

cloudformation01.JPG

こんな感じでSecurity01.yml、Application01.ymlの順番にStackを作成します。

それぞれStack名は「BlogTestStackSecurity01」「BlogTestStackApplication01」としました。

全部作ると以下の感じでStackが3つできます。

cloudformation02.JPG

動作確認

作成されたALBに自分のPCからアクセスしてみます。

ALB01.JPG

するとAMIで設定しておいたテストページが表示されました。

ALB02.JPG

ALBに関連付けられるEC2も正常稼動を確認できます。

EC201.JPG

またこれらテンプレートを流した後に気が付いたのですが、EC2にグローバルIPが付かない為に自分の拠点IPをSecurityGroupで開けてもアクセスできませんでした。

そこで、Apacheのログチェックのために一旦EIPを付けて確認したところ、自分の拠点IPからのテストページへのアクセスログとALBからのヘルスチェックログが確認できました。

また、ApacheのログではそのままだとALBのアドレスが記録されるため、自分のクライアントのIPを表示したい場合は、以下の設定が必要になります。

https://aws.amazon.com/jp/premiumsupport/knowledge-center/elb-capture-client-ip-addresses/

まとめ

CrossStackReferenceを利用することで、テンプレートの粒度を小さくして管理することが可能になります。

SecurityGroupなど、更新頻度の高い要素を含むテンプレートは積極的に分割して管理することで、ChangeSetなどで変更するときもやりやすいかと思います。

また今回のコード作成及びCloudFormation操作はすべてCloud9環境から行いました。

Cloud9は使いやすいのでオススメです。

  • このページの先頭へ

  • 東京本社
    〒105-0001東京都港区虎ノ門2-3-22 第一秋山ビル5F
    TEL:03-3591-8887 FAX:03-3591-8886
  • 大阪本社
    〒541-0041 大阪市中央区北浜2-6-11北浜エクセルビル5F
    TEL:06-6231-8887 FAX:06-6231-8897

  • 認証範囲はこちらをご覧ください。

Denet logo

クラウドサービス・データセンタ・高機能専有サーバ・共有サーバホスティングサービス 株式会社ディーネット
dot_bar