LoginSignup
1
5

More than 1 year has passed since last update.

備忘録:CFnでALB/ECS/Fargate作成

Last updated at Posted at 2022-12-05

内容

下記環境をCFnで作成した備忘録
EC2はメンテ用でここでDockerFileからImageを作成してECRリポジトリにアップロードする。
CFnでEC2作成時、ユーザーデータでコンテナイメージ作成、ECRにPushを行なっている。
ECRはプライベートリポジトリでEC2やECSからはVPCエンドポイント経由で接続する。
スクリーンショット 2022-12-05 19.38.48.png

構築方法

下記のスタックを順番にCFnで作成する。

ファイル 内容
1-CFn-Base.yml VPC、サブネット、セキュリティグループなど作成
2-CFn-ECR.yml プライベートECRリポジトリ作成
3-CFn-IAM.yml 必要なIAMロール、IAMポリシー作成
4-CFn-Vpce.yml VPCエンドポイント作成
5-CFn-EC2.yml 管理用のEC2作成
6-CFn-ALB.yml ALB、ターゲットグループ作成
7-CFn-ECS.yml ECSタスク定義、ECSクラスタ、ECSサービス作成

手動でやる箇所

  • 1-CFn-Base.ymlのセキュリティグループのSSH接続許可が0.0.0.0/0となっているので必要に応じて絞る
  • 5-CFn-EC2.ymlのキーペアKeyNameを指定する
    AWS::EC2::KeyPairで指定出来るが既存のキーペアを使用する想定のため手動
  • 5-CFn-EC2.ymlAWS_ACCOUNT_ID=を指定する
  • 必要に応じて5-CFn-EC2.ymlDockerfile作成の箇所を修正する
    取り敢えずecho FROM nginx > Dockerfileでnginxのイメージを書き込んでいるだけである。
     

テンプレートファイル

1-CFn-Base.yml

1-CFn-Base.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: Network - VPC/Subnet/RouteTable/IGW/SecurityGroup
Resources:
#### VPC Setting ####
  Vpc:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsHostnames: true
      EnableDnsSupport: true
      InstanceTenancy: default
      Tags:
        - Key: Name
          Value: Vpc

#### Subnet Setting ####
### IGW Setting ###
  Igw:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: igw
  VpcgwAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId:
        Ref: Vpc
      InternetGatewayId:
        Ref: Igw

### Public Subnet Setting ###
## Subnet Setting ##
  SubnetPublic1A:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.0.0/24
      VpcId:
        Ref: Vpc
      AvailabilityZone:
        Fn::Select:
          - 0
          - Fn::GetAZs: ""
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: subnet-public-1a
        - Key: Type
          Value: Public
  SubnetPublic1C:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.1.0/24
      VpcId:
        Ref: Vpc
      AvailabilityZone:
        Fn::Select:
          - 1
          - Fn::GetAZs: ""
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: subnet-public-1c
        - Key: Type
          Value: Public

## Route Table Setting ##
  RoutePublic1:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId:
        Ref: Vpc
      Tags:
        - Key: Name
          Value: route-public1
  RoutePublic1Association1A:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId:
        Ref: RoutePublic1
      SubnetId:
        Ref: SubnetPublic1A
  RoutePublic1Association1C:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId:
        Ref: RoutePublic1
      SubnetId:
        Ref: SubnetPublic1C
  RoutePublic1Default:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId:
        Ref: RoutePublic1
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId:
        Ref: Igw
    DependsOn:
      - VpcgwAttachment

### Public Subnet Setting ###
## Subnet Setting ##
  SubnetPrivate1A:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.2.0/24
      VpcId:
        Ref: Vpc
      AvailabilityZone:
        Fn::Select:
          - 0
          - Fn::GetAZs: ""
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: subnet-private-1a
        - Key: Type
          Value: Isolated
  SubnetPrivate1C:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.3.0/24
      VpcId:
        Ref: Vpc
      AvailabilityZone:
        Fn::Select:
          - 1
          - Fn::GetAZs: ""
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: subnet-private-1c
        - Key: Type
          Value: Isolated

## Route Table Setting ##
  RoutePrivate1:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId:
        Ref: Vpc
      Tags:
        - Key: Name
          Value: route-private1
  RoutePrivate1Association1A:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId:
        Ref: RoutePrivate1
      SubnetId:
        Ref: SubnetPrivate1A
  RoutePrivate1Association1C:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId:
        Ref: RoutePrivate1
      SubnetId:
        Ref: SubnetPrivate1C

#### Security Group Setting ####
### for public subnet ###
  SgPublic:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for public
      GroupName: public
      SecurityGroupEgress:
        - CidrIp: 0.0.0.0/0
          Description: Allow all outbound traffic by default
          IpProtocol: "-1"
      SecurityGroupIngress:
        - CidrIp: 0.0.0.0/0
          Description: from http from my network
          FromPort: 80
          IpProtocol: tcp
          ToPort: 80
        - CidrIp: 0.0.0.0/0
          Description: Allow ssh from my network
          IpProtocol: tcp
          FromPort: 22
          ToPort: 22
      Tags:
        - Key: Name
          Value: sg-public
      VpcId:
        Ref: Vpc

### for private subnet ###
  SgPrivate:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for private
      GroupName: private
      SecurityGroupEgress:
        - CidrIp: 0.0.0.0/0
          Description: Allow all outbound traffic by default
          IpProtocol: "-1"
      Tags:
        - Key: Name
          Value: sg-private
      VpcId:
        Ref: Vpc

### for VPC Endpoint ###
  SgVpce:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group of VPC Endpoint
      GroupName: vpce
      SecurityGroupEgress:
        - CidrIp: 0.0.0.0/0
          Description: Allow all outbound traffic by default
          IpProtocol: "-1"
      Tags:
        - Key: Name
          Value: sg-vpce
      VpcId:
        Ref: Vpc

### Secuity Group Link ####
## Public -> Private ##
  SgPrivateFromPublic:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      IpProtocol: tcp
      Description: HTTP for Public
      FromPort: 80
      GroupId:
        Fn::GetAtt:
          - SgPrivate
          - GroupId
      SourceSecurityGroupId:
        Fn::GetAtt:
          - SgPublic
          - GroupId
      ToPort: 80

## Public -> VPC endpoint ##
  SgVpceFromSgPublic:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      IpProtocol: tcp
      Description: HTTPS for Public
      FromPort: 443
      GroupId:
        Fn::GetAtt:
          - SgVpce
          - GroupId
      SourceSecurityGroupId:
        Fn::GetAtt:
          - SgPublic
          - GroupId
      ToPort: 443

## Private -> VPC endpoint ##
  SgVpceFromSgPrivate:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      IpProtocol: tcp
      Description: HTTPS for Private
      FromPort: 443
      GroupId:
        Fn::GetAtt:
          - SgVpce
          - GroupId
      SourceSecurityGroupId:
        Fn::GetAtt:
          - SgPrivate
          - GroupId
      ToPort: 443

##### Output parameters for cross stack #####
Outputs:
### VPC ###
  VPCId:
    Value: !Ref Vpc
    Export:
      Name: VPCId
### Subnet ###
  SubnetPublic1AId:
    Value: !Ref SubnetPublic1A
    Export:
      Name: SubnetPublic1AId
  SubnetPublic1CId:
    Value: !Ref SubnetPublic1C
    Export:
      Name: SubnetPublic1CId
  SubnetPrivate1AId:
    Value: !Ref SubnetPrivate1A
    Export:
      Name: SubnetPrivate1AId
  SubnetPrivate1CId:
    Value: !Ref SubnetPrivate1C
    Export:
      Name: SubnetPrivate1CId
### Security Group ###
  SgPublicId:
    Value: !Ref SgPublic
    Export:
      Name: SgPublicId
  SgPrivateId:
    Value: !Ref SgPrivate
    Export:
      Name: SgPrivateId
  SgVpceId:
    Value: !Ref SgVpce
    Export:
      Name: SgVpceId
### Route Table ###
  RoutePublic1Id:
    Value: !Ref RoutePublic1
    Export:
      Name: RoutePublic1Id
  RoutePrivate1Id:
    Value: !Ref RoutePrivate1
    Export:
      Name: RoutePrivate1Id

2-CFn-ECR.yml

2-CFn-ECR.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: Create Private ECR Repository

#### ECR Repository ####
Resources:
  ECRRepository:
    Type: AWS::ECR::Repository
    Properties:
      RepositoryName: ecr-rep
      ImageScanningConfiguration:
        ScanOnPush: false
      EncryptionConfiguration:
        EncryptionType: "KMS"

3-CFn-IAM.yml

3-CFn-IAM.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: Create IAM Policy / IAM Role

#### IAM Policy for EC2 ####
Resources:

  IAMRoleforEC2:
    Type: AWS::IAM::Role
    Properties:
      RoleName: role-for-ec2
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - sts:AssumeRole
      ManagedPolicyArns:
        - !Ref IAMPolicyforEC2
      Tags:
        - Key: Name
          Value: role-for-ec2
    DependsOn:
      - IAMPolicyforEC2
  InstanceProfileforEC2:
    Type: 'AWS::IAM::InstanceProfile'
    Properties:
      InstanceProfileName: role-for-ec2
      Roles:
        - !Ref IAMRoleforEC2
  IAMPolicyforEC2:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: access-to-ecr
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action:
              - ecr:GetDownloadUrlForLayer
              - ecr:GetRepositoryPolicy
              - ecr:DescribeRepositories
              - ecr:ListImages
              - ecr:DescribeImages
              - ecr:BatchGetImage
              - ecr:InitiateLayerUpload
              - ecr:UploadLayerPart
              - ecr:CompleteLayerUpload
              - ecr:PutImage
              - ecr:GetAuthorizationToken
              - ecr:ListImages
              - ecr:BatchCheckLayerAvailability
            Resource: "*"
  ECSTaskExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: "ecs-task-exec-role"
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: ecs-tasks.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy

Outputs:
  InstanceProfileforEC2Id:
    Value: !Ref InstanceProfileforEC2
    Export:
      Name: InstanceProfileforEC2Id
  ECSTaskExecutionRoleId:
    Value: !Ref ECSTaskExecutionRole
    Export:
      Name: ECSTaskExecutionRoleId

4-CFn-Vpce.yml

4-CFn-Vpce.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: Create VPC Endpoint

#### VPC Endpoint ####
Resources:
### for ECR Api ###
  ECRApiEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcEndpointType: Interface
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.ecr.api"
      VpcId: !ImportValue VPCId
      SubnetIds:
        - !ImportValue SubnetPrivate1AId
        - !ImportValue SubnetPrivate1CId
      SecurityGroupIds:
        - !ImportValue SgVpceId
      PrivateDnsEnabled: true

### for ECR dkr ###
  ECRDkrEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcEndpointType: Interface
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.ecr.dkr"
      VpcId: !ImportValue VPCId
      SubnetIds:
        - !ImportValue SubnetPrivate1AId
        - !ImportValue SubnetPrivate1CId
      SecurityGroupIds:
        - !ImportValue SgVpceId
      PrivateDnsEnabled: true

### for ECR s3 ###
  ECRS3Endpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcEndpointType: Gateway
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.s3"
      VpcId: !ImportValue VPCId
      RouteTableIds:
        - !ImportValue RoutePrivate1Id

### for cloudwatch ###
  CloudWatchEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcEndpointType: Interface
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.logs"
      VpcId: !ImportValue VPCId
      SubnetIds:
        - !ImportValue SubnetPrivate1AId
        - !ImportValue SubnetPrivate1CId
      SecurityGroupIds:
        - !ImportValue SgVpceId
      PrivateDnsEnabled: true

5-CFn-EC2.yml

5-CFn-EC2.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: Create EC2 environment

#### EC2 ####
Resources:
  EC2:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: ami-072bfb8ae2c884cc4
      InstanceType: t2.micro
      KeyName: xxxxxxxxxxxxxxxxxxxxxxxxxxx
      NetworkInterfaces:
        - AssociatePublicIpAddress: "true"
          DeviceIndex: "0"
          SubnetId: !ImportValue SubnetPublic1AId
          GroupSet:
            - !ImportValue SgPublicId
      IamInstanceProfile: !ImportValue InstanceProfileforEC2Id
      UserData: !Base64 |
        #!/bin/bash
        sudo su -
        yum update -y
        yum install -y docker
        systemctl start docker
        systemctl enable docker
        cd ~
        echo FROM nginx > Dockerfile
        AWS_ACCOUNT_ID=xxxxxxxxxxxxxxxxxxxxxxxxxxx
        aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com
        docker build -t ecr-rep .
        docker tag ecr-rep:latest ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/ecr-rep:latest
        docker push ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/ecr-rep:latest

      Tags:
        - Key: Name
          Value: Maintenance-Server

6-CFn-ALB.yml

6-CFn-ALB.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: Create ALB and Target Group

#### ALB/Listener/Target Group ####
Resources:
### ALB ###
  ExternalALB:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Type: application
      Name: external-alb
      Scheme: internet-facing
      IpAddressType: ipv4
      SecurityGroups:
        - !ImportValue SgPublicId
      Subnets:
        - !ImportValue SubnetPublic1AId
        - !ImportValue SubnetPublic1CId
      Tags:
        - Key: Name
          Value: external-alb

### Target Group ###
  TargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: elb-tg
      TargetType: ip
      Protocol: HTTP
      ProtocolVersion: HTTP1
      Port: 80
      VpcId: !ImportValue VPCId
      HealthCheckProtocol: HTTP
      HealthCheckPath: "/health"
      HealthCheckPort: traffic-port
      Matcher:
        HttpCode: "404"

### Listener ###
  Listener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref TargetGroup
      LoadBalancerArn: !Ref ExternalALB
      Port: 80
      Protocol: HTTP

#### Outputs paramerer for cross stack ####
Outputs:
  TargetGroupId:
    Value: !Ref TargetGroup
    Export:
      Name: TargetGroupId

7-CFn-ECS.yml

7-CFn-ECS.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: ECS

Resources:
### CloudWatch Log Group ###
  ECSLogGroup:
    Type: "AWS::Logs::LogGroup"
    Properties:
      LogGroupName: "/ecs/logs/sbcntr-backend-def"

### ECS Task Definition ###
  ECStask:
    Type: AWS::ECS::TaskDefinition
    Properties:
      RequiresCompatibilities:
        - FARGATE
      Cpu: 512
      Memory: 1024
      NetworkMode: awsvpc
      Family: nginx-def
      ExecutionRoleArn: !ImportValue ECSTaskExecutionRoleId
      containerDefinitions:
        - Name: nginx
          Image: !Sub "${AWS::AccountId}.dkr.ecr.ap-northeast-1.amazonaws.com/ecr-rep:latest"
          MemoryReservation: 512
          PortMappings:
            - containerPort: 80
              protocol: tcp
          Cpu: 256
          Essential: true
          LogConfiguration:
            LogDriver: awslogs
            Options:
              awslogs-group: !Ref ECSLogGroup
              awslogs-region: !Ref AWS::Region
              awslogs-stream-prefix: ecs

### ECS Cluster ###
  ECSCluster:
    Type: AWS::ECS::Cluster
    Properties:
      ClusterName: ecs-cluster
      ClusterSettings:
        -
          Name: containerInsights
          Value: enabled

### ECS Service ###
  ECSService:
    Type: AWS::ECS::Service
    Properties:
      LaunchType: FARGATE
      TaskDefinition: !Ref ECStask
      Cluster: !Ref ECSCluster
      ServiceName: ecs-service
      DesiredCount: 1
      EnableECSManagedTags: True
      NetworkConfiguration:
        AwsvpcConfiguration:
          AssignPublicIp: DISABLED
          SecurityGroups:
            - !ImportValue SgPrivateId
          Subnets:
            - !ImportValue SubnetPrivate1AId
            - !ImportValue SubnetPrivate1CId
      HealthCheckGracePeriodSeconds: 60
      LoadBalancers:
        -
          ContainerName: nginx
          ContainerPort: 80
          TargetGroupArn: !ImportValue TargetGroupId
1
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
5