Edited at

AWS CloudFormationを使ってAWS Fargateの環境を作成してみる

More than 1 year has passed since last update.

本記事は個人の意見であり、所属する組織の見解とは関係ありません。

こちらはAWS Fargate Advent Calendar 2017の6日目の記事です。

AWS Fargateが発表されて、一週間ぐらい経ちました。新しいサービス、機能を色々試してみるのは楽しいですよね!

今日は、Fargateを触ってみて、もう少し本格的に取り組んでみたいと感じた方向けにAWS CloudFormationを使ってAWS Fargateの環境を作成する流れについて確認してみたいと思います。


AWS CloudFormationとは

Cloudformationでは、AWSリソースの環境構築を設定テンプレートを元に自動化する事ができます。ECSで利用する場合、TaskdefinisionやServiceの設定なども記述する事ができます。Containerのデプロイをより簡単に行える様になり各種自動化を行いやすくなるメリットもあります。

今回はFargateのAdvent Calendarへの投稿ですので、詳細については、次のWebinerの資料を確認してみてください。


CloudFormationテンプレート作成


作成方針

Cloudformationのテンプレートは記載の自由度が高く、色々な記述の仕方ができるのですが、今回は分かりやすさを重視して次の様な構成で分割したテンプレートを作成してみました。


  • VPC作成用テンプレート


    • Fargate用のVPCを作成し、VPCの設定を行うテンプレート

    • PublicSubnetやPrivateSubnet、ルートテーブルなどを作成していきます。




  • SecurityGroup作成用テンプレート


    • TaskやALBで利用するSecurityGroupを作成します。



  • ECSクラスターを作成するテンプレート


  • ELBを設定するテンプレート



  • TaskDefinitionテンプレート


    • ECS上で起動するContainerに関する設定を行います。



  • Serviceテンプレート


分割の仕方も様々ですので、各自のユースケースにあわせて、色々と試してみてください。個人的には、ライフサイクルが異なるリソースは別テンプレートにするが好きです。逆に、開発環境やデモ環境を素早く立ち上げたい場合は1つのテンプレートの中に全て記載してしまうのもいいですよね。


VPC作成用テンプレート

テンプレートの一部は次の様な形になります。(折りたたんでいます)


Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VpcCIDR
Tags:
- Key: Name
Value: !Ref EnvironmentName

InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Ref EnvironmentName

  <省略>

Outputs:

VPC:
Description: A reference to the created VPC
Value: !Ref VPC
Export:
Name: !Sub ${EnvironmentName}-VPC


ポイントは、作成したリソースに関する情報を別リソースからもアクセスできる様に

OutpusセクションでExport属性をつけている事です。Export属性で定義しているNameを利用して、

別テンプレートからも対象リソースに対する参照を行う事ができます。


SecurityGroup作成用テンプレート

ALB用、Container用のSecurityGroupを作成し、必要なPortを許可しています。

こちらも長いので、折りたたんでいます。


Description: >
This template deploys a security-groups.

Parameters:
EnvironmentName:
Description: An environment name that will be prefixed to resource names
Type: String
Default: advent-calendar-2017

Resources:
LoadBalancerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
VpcId: !ImportValue advent-calendar-2017-VPC
GroupDescription: SecurityGroup for ALB
SecurityGroupIngress:
- CidrIp: 0.0.0.0/0
IpProtocol: tcp
FromPort: 80
ToPort: 80
Tags:
- Key: Name
Value: !Sub ${EnvironmentName}-LoadBalancers

ContainerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
VpcId: !ImportValue advent-calendar-2017-VPC
GroupDescription: Security Group for Task
SecurityGroupIngress:
-
SourceSecurityGroupId: !Ref LoadBalancerSecurityGroup
IpProtocol: -1
-
CidrIp: 0.0.0.0/0
IpProtocol: tcp
FromPort: 80
ToPort: 80

Tags:
- Key: Name
Value: !Sub ${EnvironmentName}-ContainerSecurityGroup

Outputs:
LoadBalancerSecurityGroup:
Description: A reference to the security group for load balancers
Value: !Ref LoadBalancerSecurityGroup
Export:
Name: !Sub ${EnvironmentName}-LoadBalancerSecurityGroup

ContainerSecurityGroup:
Description: A reference to the security group for EC2 hosts
Value: !Ref ContainerSecurityGroup
Export:
Name: !Sub ${EnvironmentName}-ContainerSecurityGroup



ECSクラスタ-を作成するテンプレート

非常にシンプルです。ただ、クラスタを定義して名前をつけるだけ。

Description: >

This sample template deploys a ECS Cluster

Parameters:
EnvironmentName:
Description: An environment name that will be prefixed to resource names
Type: String
Default: advent-calendar-2017

Resources:
ECSCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: !Sub ${EnvironmentName}-cluster

Outputs:
ECSCluster:
Description: A referenc
Value: !Ref ECSCluster
Export:
Name: !Sub ${EnvironmentName}-Cluster


ELBを設定するテンプレート

ALB、Listener、Targetgroupを作成しています。

Description: >

This template deploys an Application Load Balancer.

Parameters:
EnvironmentName:
Description: An environment name that will be prefixed to resource names
Type: String
Default: advent-calendar-2017

Resources:
LoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: !Ref EnvironmentName
Subnets:
- !ImportValue advent-calendar-2017-PublicSubnet1
- !ImportValue advent-calendar-2017-PublicSubnet2
SecurityGroups:
- !ImportValue advent-calendar-2017-LoadBalancerSecurityGroup
Tags:
- Key: Name
Value: !Ref EnvironmentName
Scheme: internet-facing

LoadBalancerListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref LoadBalancer
Port: 80
Protocol: HTTP
DefaultActions:
- Type: forward
TargetGroupArn: !Ref DefaultTargetGroup

DefaultTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: !Sub ${EnvironmentName}-targetgroup
VpcId: !ImportValue advent-calendar-2017-VPC
Port: 80
Protocol: HTTP
TargetType: ip

Outputs:

LoadBalancer:
Description: A reference to the Application Load Balancer
Value: !Ref LoadBalancer
Export:
Name: !Sub ${EnvironmentName}-Loadbalancer

LoadBalancerUrl:
Description: The URL of the ALB
Value: !GetAtt LoadBalancer.DNSName

Listener:
Description: A reference to a port 80 listener
Value: !Ref LoadBalancerListener
Export:
Name: !Sub ${EnvironmentName}-Listener

DefaultTargetGroup:
Value: !Ref DefaultTargetGroup
Export:
Name: !Sub ${EnvironmentName}-DefaultTargetGroup


TaskDefinition設定

ようやくFargateに関連する設定が出てきました。ここでは、RequiresCompatibilities属性にFARGATEを指定し、

NetworkMode属性にawsvpcを指定しています。また、CPU、メモリの設定はContainerDefinitionsの外側で設定します。

Container Definitionsにおけるmemory/cpuの指定はオプションです。加えて、各Taskがログを出力するためのCloudwatch Logsの設定もここで行なっています。

Description: >

This sample deploys a task

Parameters:
EnvironmentName:
Description: An environment name that will be prefixed to resource names
Type: String
Default: advent-calendar-2017

Resources:
LogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub '/ecs/logs/${EnvironmentName}-groups'

ECSTask:
Type: AWS::ECS::TaskDefinition
Properties:
Cpu: 256
ExecutionRoleArn: arn:aws:iam::XXXXXXXXXXXX:role/ecsTaskExecutionRole
Family: !Sub ${EnvironmentName}-task
Memory: 512
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
ContainerDefinitions:
-
Name: nginx
Image: nginx:latest
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: !Ref LogGroup
awslogs-region: us-east-1
awslogs-stream-prefix: ecs
MemoryReservation: 512
PortMappings:
-
HostPort: 80
Protocol: tcp
ContainerPort: 80

Outputs:
LogGroup:
Description: A reference to LogGroup
Value: !Ref LogGroup

ECSTask:
Description: A reference to Task
Value: !Ref ECSTask


Service設定

ここではFargate上でTaskを起動させるために、LaunchType属性にFARGATEを指定しています。ここでTaskNameに指定しているXXの数字はTaskのRevisionに該当します。Taskの更新とともにここの数字を変える必要があるという点がポイントです。

Description: >

This sample deploys a service

Parameters:
EnvironmentName:
Description: An environment name that will be prefixed to resource names
Type: String
Default: advent-calendar-2017

TaskName:
Description: A task name
Type: String
Default: advent-calendar-2017-task:XX

Resources:
Service:
Type: AWS::ECS::Service
Properties:
Cluster: !ImportValue advent-calendar-2017-Cluster
DesiredCount: 2
LaunchType: FARGATE
LoadBalancers:
-
TargetGroupArn: !ImportValue advent-calendar-2017-DefaultTargetGroup
ContainerPort: 80
ContainerName: nginx
NetworkConfiguration:
AwsvpcConfiguration:
SecurityGroups:
-
!ImportValue advent-calendar-2017-ContainerSecurityGroup
Subnets:
-
!ImportValue advent-calendar-2017-PrivateSubnet1
-
!ImportValue advent-calendar-2017-PrivateSubnet2
ServiceName: !Sub ${EnvironmentName}-service
TaskDefinition: !Ref TaskName
Outputs:
Service:
Description: A reference to the service
Value: !Ref Service


Cloudformation Stackを作成する

これで、Fargate環境の作成準備が整いました。ここからは順番にStackを作成していきます。

$ aws cloudformation create-stack --stack-name advent-calendar-2017-vpc \

--template-body file://Fargate-vpc.yml \
--region us-east-1

$ aws cloudformation create-stack --stack-name advent-calendar-2017-security-group \
--template-body file://Fargate-security-groups.yaml \
--region us-east-1

$ aws cloudformation create-stack --stack-name advent-calendar-2017-load-balancer \
--template-body file://Fargate-load-balancers.yaml \
--region us-east-1

$ aws cloudformation create-stack --stack-name advent-calendar-2017-cluster \
--template-body file://Fargate-cluster.yml \
--region us-east-1

$ aws cloudformation create-stack --stack-name advent-calendar-2017-task \
--template-body file://Fargate-taskdefinition.yml \
--region us-east-1

$ aws cloudformation create-stack --stack-name advent-calendar-2017-service \
--template-body file://Fargate-service.yml \
--region us-east-1


作成した環境を確認する

Cloudformationでの環境構築が終わりました。正しく構築できているか、ALB経由でアクセスして確認してみてください。

作成したALBのFQDNは、マネージメントコンソール上のEC2の画面>ロードバランサにアクセスして確認できます。

それ以外にも今回の例では、CLIでの次の様なコマンドで確認する事ができます。(少し無理やりですが。。。)

$ aws cloudformation describe-stacks --stack-name advent-calendar-2017-load-balancer  \

--region us-east-1 | jq '.Stacks[].Outputs[] | select(.OutputKey == "LoadBalancerUrl")'

{
"Description": "The URL of the ALB",
"OutputKey": "LoadBalancerUrl",
"OutputValue": "advent-calendar-2017-844241308.us-east-1.elb.amazonaws.com"
}

####awscli単独でやるなら、次の様にも書く事ができます。

aws cloudformation describe-stacks --stack-name advent-calendar-2017-load-balancer \
--region us-east-1 \
--query 'Stacks[].Outputs[?OutputKey == `LoadBalancerUrl`].OutputValue'


ECSクラスター

次のコマンドで存在が確認できます。

$ aws ecs list-clusters --region us-east-1

{
"clusterArns": [
"arn:aws:ecs:us-east-1:XXXXXXXXXXXX:cluster/advent-calendar-2017-cluster"
]
}


サービスの状態

作成したサービスの状態は次の様なコマンドで確認できます。

aws ecs describe-services --services <service name> \

--cluster <cluster name> --region us-east-1

例えば、デプロイしているサービスの状況を確認する際には以下の様なコマンドで状態を取得可能です。

次のコマンド結果には、runningCountが2であり、desiredCountの設定通りにTaskが起動している事が確認できます。


$ aws ecs describe-services --services advent-calendar-2017-service \
--cluster advent-calendar-2017-cluster \
--region us-east-1 | jq .[][].deployments
[
{
"status": "PRIMARY",
"networkConfiguration": {
"awsvpcConfiguration": {
"subnets": [
"subnet-2541e678",
"subnet-9297e0f6"
],
"securityGroups": [
"sg-326f1047"
]
}
},
"pendingCount": 0,
"createdAt": 1512499161.953,
"desiredCount": 2,
"taskDefinition": "arn:aws:ecs:us-east-1:XXXXXXXXXXXX:task-definition/advent-calendar-2017-task:3",
"updatedAt": 1512500281.269,
"id": "ecs-svc/9223370524355613851",
"runningCount": 2
}
]


デプロイしたServiceを更新する

Cloudformationを利用して作成していますので、更新もCloudformation経由で行います。


テンプレートを更新する。

今回はDesiredCountを修正してみました。

$ diff Fargate-service-update.yml Fargate-service.yml

20c20
< DesiredCount: 4
---
> DesiredCount: 2


Stackを更新する

次の様なコマンドでStackの更新が可能です。

$ aws cloudformation update-stack --stack-name advent-calendar-2017-service \

--template-body file://Fargate-service-update.yml \
--region us-east-1

しばらく待った後に再びServiceの状態を確認するとDsierdCount通りにTaskの数が増えている事が確認できます。

$ aws ecs describe-services --services advent-calendar-2017-service \

--cluster advent-calendar-2017-cluster \
--region us-east-1 | jq .[][].deployments
[
{
"status": "PRIMARY",
"networkConfiguration": {
"awsvpcConfiguration": {
"subnets": [
"subnet-2541e678",
"subnet-9297e0f6"
],
"securityGroups": [
"sg-326f1047"
]
}
},
"pendingCount": 0,
"createdAt": 1512499161.953,
"desiredCount": 4,
"taskDefinition": "arn:aws:ecs:us-east-1:XXXXXXXXXXXX:task-definition/advent-calendar-2017-task:3",
"updatedAt": 1512538215.582,
"id": "ecs-svc/9223370524355613851",
"runningCount": 4
}
]


Taskをアップデートする。


テンプレートを更新する。

Taskが利用するメモリの容量を修正してみました。

$ diff Fargate-taskdefinition-update.yml Fargate-taskdefinition.yml 

25c25
< Memory: 1024
---
> Memory: 512


Stackを更新する

次の様なコマンドでTask用のStackの更新をします。

$ aws cloudformation update-stack --stack-name advent-calendar-2017-task \

--template-body file://Fargate-taskdefinition-update.yml \
--region us-east-1

TaskのRevisionが変化していますので、Serviceでも新しいRevisionを利用する様に、テンプレートを修正して、Service用のStackを更新します。

$ aws cloudformation update-stack --stack-name advent-calendar-2017-service \

--template-body file://Fargate-service.yml \
--region us-east-1

再度、サービスの状態を確認して、起動しているTaskが更新されている事を確認してみてください。


まとめ

Cloudformationを利用したECS,Fargateの操作はいかがだったでしょうか。今回の記事を書く為に、新規でCloudformationテンプレートを作成したのですが、これまでのECSで利用していたテンプレートとの違いは僅かでした。FargateをきっかけにECSに興味を持って頂けた方の参考になればうれしいです。