【ECS講座29】CloudFormationを利用したECS実行環境のデプロイ -EC2 AutoScaling, LaunchConfiguration作成2-【5:31】

AWSTemplateFormatVersion: "2010-09-09"

Description: CloudTech ECS Demo

#======================
# パラメーター
#======================
Parameters:

# プロジェクト名を入力する。各リソースのNameタグの値となる。
  EnvironmentName:
    Description: An environment name that is prefixed to resource names
    Type: String

# VPCのCIDRレンジ
  VpcCIDR:
    Description: Please enter the IP range (CIDR notation) for this VPC
    Type: String
    Default: 10.99.0.0/16

# パブリックサブネット1
  PublicSubnet1CIDR:
    Description: Please enter the IP range (CIDR notation) for the public subnet in the first Availability Zone
    Type: String
    Default: 10.99.10.0/24

# パブリックサブネット2
  PublicSubnet2CIDR:
    Description: Please enter the IP range (CIDR notation) for the public subnet in the second Availability Zone
    Type: String
    Default: 10.99.11.0/24

# プライベートサブネット1
  PrivateSubnet1CIDR:
    Description: Please enter the IP range (CIDR notation) for the private subnet in the first Availability Zone
    Type: String
    Default: 10.99.20.0/24

# プライベートサブネット2
  PrivateSubnet2CIDR:
    Description: Please enter the IP range (CIDR notation) for the private subnet in the second Availability Zone
    Type: String
    Default: 10.99.21.0/24

# Cluster名を入力する
  ClusterName:
    Description: Please enter the Cluster name
    Type: String

# データベースに接続するパスワードを入力する
  DatabasePassword:
    Type: String
    Description: Database password
    NoEcho: "true"

# キーペアの名前
  Keypairname:
    Type: AWS::EC2::KeyPair::KeyName
    Description: Enter keypair name

# ECSコンテナインスタンスの最新のAMIのIDを取得する
  ApplicationImageId:
    Type: AWS::SSM::Parameter::Value<String>
    Default: /aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id

#======================
# リソース
#======================

Resources:

#======================
##### ECR作成 #####
#======================
  TodobackendRepository:
    Type: AWS::ECR::Repository
    Properties:
      RepositoryName: cloudtech/todobackend
      LifecyclePolicy:
        LifecyclePolicyText: |
          {
            "rules": [
              {
                "rulePriority": 1,
                "description": "Untagged images",
                "selection": {
                  "tagStatus": "untagged",
                  "countType": "sinceImagePushed",
                  "countUnit": "days",
                  "countNumber": 7
                },
                "action": {"type": "expire"
                }
              }
            ]
          }

#======================
##### VPC作成 #####
#======================
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VpcCIDR
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: !Ref EnvironmentName

# インターネットゲートウェイ作成
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Ref EnvironmentName

# インターネットゲートウェイをVPCにアタッチする
  InternetGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC

# パブリックサブネット1を作成
  PublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select [ 0, !GetAZs '' ]
      CidrBlock: !Ref PublicSubnet1CIDR
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName} Public Subnet (AZ1)

# パブリックサブネット2を作成
  PublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select [ 1, !GetAZs  '' ]
      CidrBlock: !Ref PublicSubnet2CIDR
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName} Public Subnet (AZ2)

# プライベートサブネット1を作成
  PrivateSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select [ 0, !GetAZs  '' ]
      CidrBlock: !Ref PrivateSubnet1CIDR
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName} Private Subnet (AZ1)

# プライベートサブネット2を作成
  PrivateSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select [ 1, !GetAZs  '' ]
      CidrBlock: !Ref PrivateSubnet2CIDR
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName} Private Subnet (AZ2)

# ルートテーブル(パブリックサブネット用共通)作成
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName} Public Routes

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

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

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

# ルートテーブル(プライベートサブネット用共通)作成
  PrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName} Private Routes

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

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

#NATGatewayをパブリックサブネットに作成する
  NatGatewayPublic1:
    Type: AWS::EC2::NatGateway
    DependsOn: EIP
    Properties: 
      AllocationId:
          Fn::GetAtt:
          - EIP
          - AllocationId
      SubnetId: !Ref PublicSubnet1

  EIP:
    Type: AWS::EC2::EIP
    Properties:
        Domain: vpc

#NATGateway用のルーティングをプライベートサブネットのルートテーブルに追加する
  PrivateRoute:
    Type: AWS::EC2::Route
    DependsOn: NatGatewayPublic1
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NatGatewayPublic1
      
#======================
##### ECS作成 #####
#======================
  Cluster:
    Type: AWS::ECS::Cluster
    Properties:
      ClusterName: !Ref ClusterName
#======================
##### RDS作成 #####
#======================
  ApplicationDatabase:
    Type: AWS::RDS::DBInstance
    Properties:
      Engine: MySQL
      EngineVersion: 8.0
      DBInstanceClass: db.t2.micro
      AllocatedStorage: 10
      StorageType: gp2
      MasterUsername: todobackend
      MasterUserPassword:
        Ref: DatabasePassword
      DBName: todobackend
      VPCSecurityGroups:
        - !Ref ApplicationDatabaseSecurityGroup
      DBSubnetGroupName: !Ref ApplicationDatabaseSubnetGroup
      MultiAZ: "false"
      AvailabilityZone: !Sub ${AWS::Region}a
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-db
  ApplicationDatabaseSubnetGroup:
    Type: AWS::RDS::DBSubnetGroup
    Properties:
      DBSubnetGroupDescription: Application Database Subnet Group
      SubnetIds: 
        - !Ref PrivateSubnet1
        - !Ref PrivateSubnet2
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-db-subnet-group
# セキュリティグループ(RDS)
  ApplicationDatabaseSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: !Sub ${AWS::StackName} Application Database Security Group
      VpcId: !Ref VPC
      SecurityGroupEgress:
        - IpProtocol: icmp
          FromPort: -1
          ToPort: -1
          CidrIp: 192.0.2.0/32
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-db-sg
  ApplicationToApplicationDatabaseIngress:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      IpProtocol: tcp
      FromPort: 3306
      ToPort: 3306
      GroupId: !Ref ApplicationDatabaseSecurityGroup
      SourceSecurityGroupId: !Ref ApplicationAutoscalingSecurityGroup
  ApplicationToApplicationDatabaseEgress:
    Type: AWS::EC2::SecurityGroupEgress
    Properties:
      IpProtocol: tcp
      FromPort: 3306
      ToPort: 3306
      GroupId: !Ref ApplicationAutoscalingSecurityGroup
      DestinationSecurityGroupId: !Ref ApplicationDatabaseSecurityGroup
# セキュリティグループ(EC2)
  ApplicationAutoscalingSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: !Sub ${AWS::StackName} Application Autoscaling Security Group
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0
      SecurityGroupEgress:
        - IpProtocol: udp
          FromPort: 53
          ToPort: 53
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
#======================
##### ALB作成 #####
#======================
  ApplicationServiceTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Protocol: HTTP
      Port: 8000
      VpcId: !Ref VPC
      TargetGroupAttributes:
        - Key: deregistration_delay.timeout_seconds
          Value: 30
  ApplicationLoadBalancerHttpListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      LoadBalancerArn: !Ref ApplicationLoadBalancer
      Protocol: HTTP
      Port: 80
      DefaultActions:
        - TargetGroupArn: !Ref ApplicationServiceTargetGroup
          Type: forward
  ApplicationLoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Scheme: internet-facing
      Subnets:
        - !Ref PublicSubnet1
        - !Ref PublicSubnet2
      SecurityGroups:
        - !Ref ApplicationLoadBalancerSecurityGroup
      LoadBalancerAttributes:
        - Key: idle_timeout.timeout_seconds
          Value : 30
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-alb
# セキュリティグループ(ALB)
  ApplicationLoadBalancerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Application Load Balancer Security Group
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: 
            Fn::Sub: ${AWS::StackName}-alb-sg
# セキュリティグループ(EC2はALBからの通信を許可)
  ApplicationLoadBalancerToApplicationIngress:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      IpProtocol: tcp
      FromPort: 32768
      ToPort: 60999
      GroupId: !Ref ApplicationAutoscalingSecurityGroup
      SourceSecurityGroupId: !Ref ApplicationLoadBalancerSecurityGroup
# セキュリティグループ(ALBはEC2への通信を許可)
  ApplicationLoadBalancerToApplicationEgress:
    Type: AWS::EC2::SecurityGroupEgress
    Properties:
      IpProtocol: tcp
      FromPort: 32768
      ToPort: 60999
      GroupId: !Ref ApplicationLoadBalancerSecurityGroup
      DestinationSecurityGroupId: !Ref ApplicationAutoscalingSecurityGroup

#======================
##### AutoScaling作成 #####
#======================
# オートスケーリング
  ApplicationAutoscaling:
    Type: AWS::AutoScaling::AutoScalingGroup
    CreationPolicy:
      ResourceSignal:
        Count: 0
        Timeout: PT5M
    Properties:
      LaunchConfigurationName: !Ref ApplicationAutoscalingLaunchConfiguration
      MinSize: 0
      MaxSize: 4
      DesiredCapacity: 0
      VPCZoneIdentifier:
        - !Ref PrivateSubnet1
        - !Ref PrivateSubnet2
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-ApplicationAutoscaling-instance
          PropagateAtLaunch: "true"
# 起動設定
  ApplicationAutoscalingLaunchConfiguration:
    Type: AWS::AutoScaling::LaunchConfiguration
    Properties:
      ImageId: !Ref ApplicationImageId
      InstanceType: t2.micro
      KeyName: !Ref Keypairname
      IamInstanceProfile: !Ref ApplicationAutoscalingInstanceProfile
      SecurityGroups: 
        - !GetAtt "ApplicationAutoscalingSecurityGroup.GroupId"
      UserData:
        Fn::Base64: !Sub
          - |
            #!/bin/bash
            echo ECS_CLUSTER=${CLUSTERNAME} > /etc/ecs/ecs.config
            mkdir -p /data/public
            chown -R 1000:1000 /data
          - {
              CLUSTERNAME: !Ref ClusterName
            }
# インスタンスプロファイル
  ApplicationAutoscalingInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - Ref: ApplicationAutoscalingInstanceRole
# ロール指定
  ApplicationAutoscalingInstanceRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - sts:AssumeRole
      Policies:
        - PolicyName: ECSContainerInstancePermissions
          PolicyDocument: 
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - ecs:RegisterContainerInstance
                  - ecs:DeregisterContainerInstance
                  - ecs:UpdateContainerInstancesState
                Resource: !Sub ${Cluster.Arn}
              - Effect: Allow
                Action:
                  - ecs:DiscoverPollEndpoint
                  - ecs:Submit*
                  - ecs:Poll
                  - ecs:StartTelemetrySession
                Resource: "*"
              - Effect: Allow
                Action: 
                  - ecr:BatchCheckLayerAvailability
                  - ecr:BatchGetImage
                  - ecr:GetDownloadUrlForLayer
                  - ecr:GetAuthorizationToken
                Resource: "*"
              - Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                  - logs:DescribeLogStreams
                Resource: !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/${AWS::StackName}*
#======================
# アウトプット
#======================

Outputs:
  PublicURL:
    Description: Public DNS name of Application Load Balancer
    Value: !Sub ${ApplicationLoadBalancer.DNSName}
EnvironmentName=cloudtech
ClusterName=cloudtech-cluster
DatabasePassword=rds-database-secret-password
Keypairname=keypair01
DesiredCount=1
■コマンド
aws cloudformation deploy --template-file 6.EC2.yml --stack-name cloudtech-app --parameter-overrides $(cat dev.cfg) --capabilities CAPABILITY_NAMED_IAM --no-execute-changeset

改善リクエスト

📍 改善リクエスト対象
カテゴリ: -
問題番号: -
📝
テキストを改善したほうが良い
🖼️
画像を改善したほうが良い
🎬
動画を改善したほうが良い
📌
その他
💡 こんな報告を歓迎しています
  • 技術的な誤り(正解が間違っている、解説に誤りがある など)
  • 設問の誤記・不備
  • 最新のAWS仕様との相違
  • フォーマット崩れ・表示の乱れ
💡 こんな報告を歓迎しています
  • 文字が潰れて読めない
  • 画像内容の技術的な誤り
  • 図の説明と内容の不一致
📌 画像右上の管理番号(例:SAA-123-1)を添えていただけると特定しやすくなります
💡 こんな報告を歓迎しています
  • 問題集と情報が異なる
  • 読み方がおかしい
  • 技術的な間違いがある
💡 その他のご要望
  • 上記カテゴリに当てはまらない改善要望

この教材の改善リクエストがある場合は、お気軽にご報告ください。
カテゴリを選択のうえ、詳細をご記入いただけますと幸いです。

CloudTech(クラウドテック)は多くのユーザーの皆様から改善リクエストをご協力いただき運営できております。
あなたの視点での気づきは他の学習者の迷いを解決する手助けとなります。
運営側でもチェックをしておりますが限界があるため、誠に恐縮ではございますが細かい点でもご遠慮なくご指摘をお願いいたします。

※ 匿名での報告となり、内容は一般公開されません。
※ 技術的なご質問への回答を行うフォームではございませんのでご注意ください。

リクエストを受け付けました
ご報告ありがとうございます!
内容を確認のうえ、改善対応いたします。