Help us understand the problem. What is going on with this article?

CloudFormationを使ってAWS Fargateの環境を構築する

More than 1 year has passed since last update.

はじめに

本記事では、AWS CloudFormation管理コンソールを使って、AWS Fargateの基本的な環境を構築する手順を説明しています。(初心者向け)

本記事で掲載しているテンプレートの最新版は、下記に置いてます。
https://github.com/okubo-t/aws-cloudformation

構成図

前提条件

下記の記事の構築手順で、VPCを構築していること。
CloudFormationを使ってVPCを構築する

作業端末においては、AWS CLIとDockerの環境が構築済みであること。

PJPrefixの値は、同一にすること。

構築手順

1 Dockerfile を準備します。

echo "FROM nginx:alpine" > Dockerfile

2 AWS ECSの管理コンソールで、リポジトリを作成します。
ここでは、リポジトリ名は、nginx と設定します。

3 作成されるとプッシュコマンドが表示されますので、メモしておきます。

4 AWS 認証情報を設定しておきます。

export AWS_ACCESS_KEY_ID=XXXXXXXXXXXXXXXX     # アクセスキー
export AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXX  # シークレットアクセスキー
export AWS_DEFAULT_REGION=ap-northeast-1      # デフォルトリージョン 

5 レジストリに対して Docker クライアントを認証するために使用するログインコマンドを実行します。(手順3 でメモしたコマンドです。)

$(aws ecr get-login --no-include-email --region ap-northeast-1)

6 Docker イメージを構築します。(手順3 でメモしたコマンドです。)

docker build -t nginx .

7 このリポジトリにイメージをプッシュできるように、イメージにタグを付けます。(手順3 でメモしたコマンドです。)

docker tag nginx:latest XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/nginx:latest

8 このリポジトリにこのイメージをプッシュします。(手順3 でメモしたコマンドです。)

docker push XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/nginx:latest

9 AWS CloudFormation管理コンソールから、スタックの作成をクリックします。

10 後述のテンプレートを選択します。

11 各パラメータを入力します。(全て必須

パラメータ名 用途 備考
スタックの名前 テンプレートから作成するリソース一式の名前 例 prd-stack-vpc-20180801
PJPrefix 構築するプロジェクトの環境を識別するために各コンポーネントの先頭に付与する識別子 例 qiita-prd
InternetALBName TagのNameに設定するALB名 alb(デフォルト)
TargetGroupName TagのNameに設定するターゲットグループ名 tg(デフォルト)
ECSClusterName ECSのクラスター名 cluster(デフォルト)
ECSTaskName ECSのタスク定義名 task(デフォルト)
ECSTaskCPUUnit タスクで使用するCPUのユニットの数 256(デフォルト)
ECSTaskMemory タスクが使用するメモリの量 512(デフォルト)
ECSContainerName ECSのコンテナ名 container(デフォルト)
ECSImageName コンテナに使用するイメージ名 nginx(デフォルト)
ECSServiceName ECSのサービス名 service(デフォルト)
ECSTaskDesiredCount クラスターで実行する同時タスクの数 1(デフォルト)

12 後続は、デフォルトのまま次へ次へで、作成します。

13 状況が CREATE COMPLETEになれば、ALBとFargateの環境の構築が完了です。

14 管理コンソールの下部の出力から、構築したALBの情報を確認できます。
  ここで、キーがALBDNSNameの値をメモしておきます。

15 EC2の管理コンソールのターゲットグループにアタッチされたコンテナのステータスがhealthyになっていることを確認します。

16 ブラウザから、先ほどメモしたALBDNSNameの値にWebアクセスしてみます。
下記のような画面が表示されれば完了です。
例)

テンプレート

ecs-fargate-01.yml
AWSTemplateFormatVersion: "2010-09-09"
Description:
  Fargate for ECS Create

Metadata:
  "AWS::CloudFormation::Interface":
    ParameterGroups:
      - Label:
          default: "Project Name Prefix"
        Parameters:
          - PJPrefix
      - Label:
          default: "InternetALB Configuration"
        Parameters:
          - InternetALBName
          - TargetGroupName
      - Label:
          default: "Fargate for ECS Configuration"
        Parameters:
          - ECSClusterName
          - ECSTaskName
          - ECSTaskCPUUnit
          - ECSTaskMemory
          - ECSContainerName
          - ECSImageName
          - ECSServiceName
          - ECSTaskDesiredCount

    ParameterLabels:
      InternetALBName:
        default: "InternetALBName"
      TargetGroupName:
        default: "TargetGroupName"
      ECSClusterName:
        default: "ECSClusterName"
      ECSTaskName:
        default: "ECSTaskName"
      ECSTaskCPUUnit:
        default: "ECSTaskCPUUnit"
      ECSTaskMemory:
        default: "ECSTaskMemory"
      ECSContainerName:
        default: "ECSContainerName"
      ECSImageName:
        default: "ECSImageName"
      ECSServiceName:
        default: "ECSServiceName"
      ECSTaskDesiredCount:
        default: "ECSTaskDesiredCount"

# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------# 
Parameters:
  PJPrefix:
    Type: String

#InternetALB
  InternetALBName:
    Type: String
    Default: "alb"

#TargetGroupName
  TargetGroupName:
    Type: String
    Default: "tg"

#ECSClusterName
  ECSClusterName:
    Type: String
    Default: "cluster"

#ECSTaskName
  ECSTaskName:
    Type: String
    Default: "task"

#ECSTaskCPUUnit
  ECSTaskCPUUnit:
    AllowedValues: [ 256, 512, 1024, 2048, 4096  ]
    Type: String
    Default: "256"

#ECSTaskMemory
  ECSTaskMemory:
    AllowedValues: [ 256, 512, 1024, 2048, 4096  ]
    Type: String
    Default: "512"

#ECSContainerName
  ECSContainerName:
    Type: String
    Default: "container"

#ECSImageName
  ECSImageName:
    Type: String
    Default: "nginx"

#ECSServiceName
  ECSServiceName:
    Type: String
    Default: "service"

#ECSTaskDesiredCount
  ECSTaskDesiredCount:
    Type: Number
    Default: 1 

Resources:
# ------------------------------------------------------------#
#  SecurityGroup for ALB
# ------------------------------------------------------------#
  ALBSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-vpc" }
      GroupName: !Sub "${PJPrefix}-alb-sg"
#ECSContainerName
  ECSContainerName:
    Type: String
    Default: "container"

#ECSImageName
  ECSImageName:
    Type: String
    Default: "nginx"

#ECSImageName
  ECSServiceName:
    Type: String
    Default: "service"

#ECSTaskDesiredCount
  ECSTaskDesiredCount:
    Type: Number
    Default: 1 

Resources:
# ------------------------------------------------------------#
#  SecurityGroup for ALB
# ------------------------------------------------------------#
  ALBSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-vpc" }
      GroupName: !Sub "${PJPrefix}-alb-sg"
      GroupDescription: "-"
      Tags:
        - Key: "Name"
          Value: !Sub "${PJPrefix}-alb-sg"
# Rule
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: "0.0.0.0/0"

        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: "0.0.0.0/0"

# ------------------------------------------------------------#
#  SecurityGroup for ECS Service
# ------------------------------------------------------------#
  ECSSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-vpc" }
      GroupName: !Sub "${PJPrefix}-ecs-sg"
      GroupDescription: "-"
      Tags:
        - Key: "Name"
          Value: !Sub "${PJPrefix}-ecs-sg"

# Rule
  ECSSecurityGroupIngress: 
    Type: "AWS::EC2::SecurityGroupIngress"
    Properties: 
      IpProtocol: tcp
      FromPort: 80
      ToPort: 80
      SourceSecurityGroupId: !GetAtt [ ALBSecurityGroup, GroupId ] 
      GroupId: !GetAtt [ ECSSecurityGroup, GroupId ]

# ------------------------------------------------------------#
#  Target Group
# ------------------------------------------------------------#
  TargetGroup: 
    Type: "AWS::ElasticLoadBalancingV2::TargetGroup"
    Properties: 
      VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-vpc" } 
      Name: !Sub "${PJPrefix}-${TargetGroupName}"
      Protocol: HTTP
      Port: 80
      TargetType: ip

# ------------------------------------------------------------#
#  Internet ALB
# ------------------------------------------------------------#
  InternetALB: 
    Type: "AWS::ElasticLoadBalancingV2::LoadBalancer"
    Properties: 
      Name: !Sub "${PJPrefix}-${InternetALBName}"
      Tags: 
        - Key: Name
          Value: !Sub "${PJPrefix}-${InternetALBName}"
      Scheme: "internet-facing"
      LoadBalancerAttributes: 
        - Key: "deletion_protection.enabled"
          Value: false
        - Key: "idle_timeout.timeout_seconds"
          Value: 60
      SecurityGroups:
        - !Ref ALBSecurityGroup
      Subnets: 
        - { "Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-a" }
        - { "Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-c" }

  ALBListener: 
    Type: "AWS::ElasticLoadBalancingV2::Listener"
    Properties: 
      DefaultActions: 
        - TargetGroupArn: !Ref TargetGroup
          Type: forward
      LoadBalancerArn: !Ref InternetALB
      Port: 80
      Protocol: HTTP

# ------------------------------------------------------------#
# ECS Cluster
# ------------------------------------------------------------# 
  ECSCluster:
    Type: "AWS::ECS::Cluster"
    Properties:
      ClusterName: !Sub "${PJPrefix}-${ECSClusterName}"

# ------------------------------------------------------------#
#  ECS LogGroup
# ------------------------------------------------------------#
  ECSLogGroup:
    Type: "AWS::Logs::LogGroup"
    Properties:
      LogGroupName: !Sub "/ecs/logs/${PJPrefix}-ecs-group"

# ------------------------------------------------------------#
#  ECS TaskDefinition
# ------------------------------------------------------------#
  ECSTaskDefinition:
    Type: "AWS::ECS::TaskDefinition"
    Properties:
      Cpu: !Ref ECSTaskCPUUnit
      ExecutionRoleArn: !Sub "arn:aws:iam::${AWS::AccountId}:role/ecsTaskExecutionRole"
      Family: !Sub "${PJPrefix}-${ECSTaskName}"
      Memory: !Ref ECSTaskMemory
      NetworkMode: awsvpc
      RequiresCompatibilities:
        - FARGATE

#ContainerDefinitions
      ContainerDefinitions:
        - Name: !Sub "${PJPrefix}-${ECSContainerName}"
          Image: !Ref ECSImageName
          LogConfiguration:
            LogDriver: awslogs
            Options:
              awslogs-group: !Ref ECSLogGroup
              awslogs-region: !Ref "AWS::Region"
              awslogs-stream-prefix: !Ref PJPrefix
          MemoryReservation: 128
          PortMappings:
            - HostPort: 80
              Protocol: tcp
              ContainerPort: 80

# ------------------------------------------------------------#
#  ECS Service
# ------------------------------------------------------------#
  ECSService:
    Type: AWS::ECS::Service
    DependsOn: ALBListener
    Properties:
      Cluster: !Ref ECSCluster
      DesiredCount: !Ref ECSTaskDesiredCount
      LaunchType: FARGATE
      LoadBalancers:
        -
          TargetGroupArn: !Ref TargetGroup
          ContainerPort: 80
          ContainerName: !Sub "${PJPrefix}-${ECSContainerName}"
      NetworkConfiguration:
       AwsvpcConfiguration:
           AssignPublicIp: ENABLED
           SecurityGroups:
             - !Ref ECSSecurityGroup
           Subnets:
             - { "Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-a" }
             - { "Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-c" }
      ServiceName: !Sub "${PJPrefix}-${ECSServiceName}"
      TaskDefinition: !Ref ECSTaskDefinition

# ------------------------------------------------------------#
# Output Parameters
# ------------------------------------------------------------# 
Outputs:
#InternetALB
  ALBDNSName:
    Value: !GetAtt InternetALB.DNSName
    Export:
      Name: !Sub "${PJPrefix}-${InternetALBName}-dnsname"

#ECSClusterName
  ECSClusterName:
    Value: !Sub "${PJPrefix}-${ECSClusterName}"
    Export:
      Name: !Sub "${PJPrefix}-${ECSClusterName}-name"

#ECSClusterARN
  ECSClusterARN:
    Value: !GetAtt ECSCluster.Arn
    Export:
      Name: !Sub "${PJPrefix}-${ECSClusterName}-arn"

#ECSLogGroup
  ECSLogGroupName:
    Value: !Sub "/ecs/logs/${PJPrefix}-ecs-group"
    Export:
      Name: !Sub "${PJPrefix}-ecs-group-name"

  ECSLogGroupARN:
    Value: !GetAtt ECSLogGroup.Arn
    Export:
      Name: !Sub "${PJPrefix}-ecs-group-arn"

#ECSTaskDefinitionName
  ECSTaskDefinitionName:
    Value: !Sub "${PJPrefix}-${ECSTaskName}"
    Export:
      Name: !Sub "${PJPrefix}-${ECSTaskName}-name"

#ECSTaskDefinition
  ECSTaskDefinitionARN:
    Value: !Ref ECSTaskDefinition
    Export:
      Name: !Sub "${PJPrefix}-${ECSTaskName}-arn"

#ECSTaskCPUUnit
  ECSTaskCPUUnit:
    Value: !Ref ECSTaskCPUUnit
    Export:
      Name: !Sub "${PJPrefix}-${ECSTaskName}-cpuunit"

#ECSTaskMemory
  ECSTaskMemory:
    Value: !Ref ECSTaskMemory
    Export:
      Name: !Sub "${PJPrefix}-${ECSTaskName}-memory"

#ECSContainer
  ECSContainerName:
    Value: !Sub "${PJPrefix}-${ECSContainerName}"
    Export:
      Name: !Sub "${PJPrefix}-${ECSContainerName}-name"

#ECSImage
  ECSImageName:
    Value: !Ref ECSImageName
    Export:
      Name: !Sub "${ECSImageName}-name"

#ECSServiceName
  ECSService:
    Value: !Sub "${PJPrefix}-${ECSServiceName}"
    Export:
      Name: !Sub "${PJPrefix}-${ECSServiceName}-name"

#ECSServiceARN
  ECSServiceARN:
    Value: !Ref ECSService
    Export:
      Name: !Sub "${PJPrefix}-${ECSServiceName}-arn"

#ECSTaskDesiredCount
  ECSTaskDesiredCount:
    Value: !Ref ECSTaskDesiredCount
    Export:
      Name: !Sub "${PJPrefix}-${ECSTaskName}-count"
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away