Edited at

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"