前提条件
「CFn?IaC?何それ美味しいの?」な超初心者向け。
とは言え、AWSの機能を使おうって言うんだから、AWS BlackBeltオンラインセミナーのCloudFormation編くらいは見ていると読みやすいと思う。
あと、CFnは知らなくても同じことをマネジメントコンソールでポチポチできるくらいの理解度は前提。
文中後半で動かしているアプリは、8080ポートで待ち受けるHTTPサーバ。
SpringBootなWebアプリと考えてもらえれば。
記事中には、備忘のためにリファレンスに書かれていないデフォルト値を整理しておくが、2020年3月時点の情報であり、後でAWSが仕様を変えたとしても追従する予定はないので、挙動が違ったらリファレンスを見直してほしい。あと、今回の構成(ECS on FargateのBlue/Greenデプロイメント)以外の構成以外のデフォルト値まで調査はしていないのであしからず。
環境を整える
1から理解しながら書くので、力技でいく。
基本の流れは、「リファレンス見ながら書く→構文チェック→動かしてみる→修正する→…」の繰り返し。
というわけで、以下の記事を参考に構文チェッカをインストールしておく。
【Qiita】cfn-python-lint
pipのインストールは以下の記事を見ながら。
【AWS公式】Linux で Python、pip、EB CLI をインストールする
cfn-lintは、↓こんな感じで間違っている箇所を教えてくれる。便利!
$ cfn-lint createALB.yml
E0000 Null value at line 13 column 22
createALB.yml:13:22
とりあえず空っぽのALBを作ってみる
スタックの作成
まずは、ALBを作ってみる。最初はEC2じゃないのかよ!というツッコミがあるのは重々承知しているが、とりあえずALBで。
こういうことをやっているサイトは探してみれば色々とあるものの、結局最後に頼るのはAWS公式のユーザーガイドのリファレンスなのである。これを見ながらしっかり理解していく。
リファレンスで AWS::ElasticLoadBalancingV2::LoadBalancer
を選択して、プロパティを設定していく。「必須: いいえ」になっているプロパティがたくさんあるので、そういったものはとりあえずザクザク削っていってみよう。削った場合のデフォルト値が書いてないから不安になるけど、気にしない。
AWSTemplateFormatVersion: "2010-09-09"
Description:
ALB Create
Resources:
# ------------------------------------------------------------#
# ALB
# ------------------------------------------------------------#
ALB:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: CFn-test-ALB
Subnets:
- [自分のサブネットID①]
- [自分のサブネットID②]
- [自分のサブネットID③]
えっ、こんなにシンプルでいいの?と思いつつ、マネコンのCloudFormationの「スタック」で「スタックの作成」ボタンを押す。選択肢が出るので「新しいリソースを使用(標準)」を選択する。
こんな感じでYAMLのファイルをアップロードをする。
あとは、「次へ」で進みまくる。細かい設定は今は気にしなくてOK。スタックの名前は「CFn-test」とか適当につけておく。
流し終わると↓こんな感じになる。
ちゃんとCREATE_COMPLETEされているので、ALBを見に行くと、
ちゃんとできてる!すごい!
ちなみに、省略したプロパティのデフォルト値でリファレンスに書いていない値は、ALBの場合は↓こんな感じになるようである。「ALBの場合は」と書いてあるのは、NLBの場合はデフォルト値が違う可能性があるから。試していないので分からない。
リファレンスに書いておいてほしい…。
プロパティ | デフォルト値 |
---|---|
IpAddressType | ipv4 |
LoadBalancerAttributes | なし? |
SecurityGroups | default VPC security group |
SubnetMappings/Subnets | 省略不可。省略すると cfn-lint で E2523 Only one of [SubnetMappings, Subnets] should be specified for Resources/ALB/Properties のエラーになる |
Tags | なし |
作成したスタックを修正する
さて、作られたALBを見てみると、セキュリティグループがデフォルトになっている。
さすがにこれはちょっとセキュリティ的にアレなので、テンプレートのYAMLのALBのプロパティに以下を追加する。
SecurityGroups:
- [セキュリティグループID]
セキュリティグループは、新規で作るか、既存のを持ってくるか。
今回は、インバウンドの80, 8080ポートを開放している既存のセキュリティグループをアタッチする。
テンプレートを修正したら、スタックの詳細画面で「更新」を押して、テンプレートを差し替えていく。
ステータスが UPDATE_COMPLETE になったら、もう一度ALBの設定を見てみると、
無事、設定が反映されている。
作成したスタックを削除する
これも簡単で、スタックの詳細画面から「削除」すれば良い。
CloudFormationで作成したスタックのリソースは、スタックの削除に紐付けてまとめて掃除してくれるらしい。便利!
ちなみに、マネジメントコンソールから先回りしてALBを削除をしておいたらどうなるか実験したが、問題なく DELETE_COMPLETE のステータスになった。最終結果が合えば良いらしい。
ALBのリスナー・ターゲットグループを設定する
このセクションでは、空っぽのALBを完成品にして、80ポートで待ち受けしているEC2インスタンスにフォワードするところまで。待ち受けするEC2インスタンスを立てる手順をここで書くのは本筋とは違うので、分からなければこの記事で docker run -p 80:8080 ...
しているあたりまでやっておく。テンプレートの中で作ってもいいけど、それも面倒なので今回は有り物を使うということで。
今回の完成品のテンプレートのYAMLは以下。ALBのリソース部分は前回と同じ。
AWSTemplateFormatVersion: "2010-09-09"
Description:
ALB Create
Resources:
# ------------------------------------------------------------#
# ALB
# ------------------------------------------------------------#
ALB:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: CFn-test-ALB
SecurityGroups:
- [セキュリティグループID]
Subnets:
- [自分のサブネットID①]
- [自分のサブネットID②]
- [自分のサブネットID③]
ALBLISTENER:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- TargetGroupArn: !Ref ALBTG
Type: forward
LoadBalancerArn: !Ref ALB
Port: 80
Protocol: HTTP
ALBTG:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: CFn-test-ALB-TG
Targets:
- Id: [EC2インスタンスID]
Port: 80
Port: 80
Protocol: HTTP
VpcId: [VPCID]
ALBのターゲットグループ
ALBのリソースとほぼ同じような記述方法なので、特に目新しい部分は無いと思う。
ターゲットグループのプロパティで、リファレンスにALBの場合のデフォルト値が明記されていないものは以下。
なお、Port
と Protocol
はリファレンスで「必須: いいえ」と書いてあったし cfn-lint でも引っかからなかったので指定をしなかったら、CREATE の際にエラーになったので指定した。
プロパティ | デフォルト値 |
---|---|
Matcher | HttpCode: "200" |
Name | AWS払い出しの名前 |
TargetGroupAttributes | それぞれの属性のプロパティのデフォルト値(リファレンスに書いてある) |
Targets | なし |
ALBのリスナー
ここはデフォルト値がリファレンスに明記されているので分かりやすかった。
ポイントは以下の部分。
DefaultActions:
- TargetGroupArn: !Ref ALBTG
Type: forward
LoadBalancerArn: !Ref ALB
リスナーの定義には、ALBのARNとターゲットグループのARNが必須パラメータになっているが、どちらもこのテンプレート内でリソースを作成しているので、あらかじめ作っておいたリソースのARNをマネジメントコンソールから転記することができない。なので、同一テンプレート内で作成したリソースのARNを参照するためのCloudFormationの機能を使うのである。
※そういう意味では、ターゲットグループのVPCやインスタンスなんかも、同じテンプレート内でリソース定義してRefすれば全部ひっくるめてコード化できるけど、今回はやらなかった。
各リソースに対して !Ref
した場合に何が取得できるかは、ユーザーガイドの各リファレンスの「戻り値」に 記載されている。
ECSでサービスを起動する
ECSで起動するためのALBの設定の見直し
最終的にBlue/GreenデプロイメントできるECSの起動を行いたいので、ALBはBlue用とGreen用の2つの待ち受けをできるようにする。つまり、ターゲットとリスナーをそれぞれ2つ定義すれば良い。
また、Blue/Greenデプロイメント環境にするのであれば、ターゲットグループのタイプ指定を ip
にしておく必要がある。
Outputsは、テンプレートファイルを分けるなら必要。
ALB作成のテンプレートのアウトプットをエクスポートして、次のECS作成してインポートすることが可能。
ALBLISTENER1:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- TargetGroupArn: !Ref ALBTG1
Type: forward
LoadBalancerArn: !Ref ALB
Port: 80
Protocol: HTTP
ALBLISTENER2:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- TargetGroupArn: !Ref ALBTG2
Type: forward
LoadBalancerArn: !Ref ALB
Port: 8080
Protocol: HTTP
ALBTG1:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: CFn-test-ALB-TG1
Port: 80
Protocol: HTTP
TargetType: ip
VpcId: [VPCID]
ALBTG2:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: CFn-test-ALB-TG2
Port: 8080
Protocol: HTTP
TargetType: ip
VpcId: [VPCID]
Outputs:
TargetGroupArn1:
Description: ALB Target Group Arn 1
Value: !Ref ALBTG1
Export:
Name: !Sub ${AWS::StackName}-TargetGroupArn1
TargetGroupArn2:
Description: ALB Target Group Arn 2
Value: !Ref ALBTG2
Export:
Name: !Sub ${AWS::StackName}-TargetGroupArn2
ECSクラスターの作成
これは悩むことはないはず。適当に名前を付ければOK。
ECSCLUSTER:
Type: AWS::ECS::Cluster
Properties:
ClusterName: CFn-test-Cluster
ロググループの作成
最悪無くても良いのだが、CloudWatch Logsにコンテナのログを出力しておかないと、何かあった時に手も足も出なくなってしまうので、新規のロググループをリソースに追加しておくことをオススメする。
LOGGROUP:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: /ecs/CFn-test-Container
タスク定義
ここはプロパティが大量。
今回は Fargate でコンテナ起動をすることを前提にしているので、Fargate対象外のプロパティのデフォルト値調査は割愛。
プロパティ | デフォルト値 |
---|---|
IpcMode | よく分からない… |
NetworkMode | host |
ProxyConfiguration | AppMeshのプロキシ設定無効 |
Tags | 無し |
タスク定義の中のコンテナ定義( ContainerDefinitions
)も色々と設定項目がある。
ただ、ここはそもそものコンテナ定義の理解が必要なのと、その理解にはかなり時間がかかりそうなので、今回は必要な設定のみを考える。
プロパティ | デフォルト値 |
---|---|
Family | AWS払い出しの名前 |
最終的に、タスク定義のリソースについては↓こんなイメージ。
ARNの指定については、!Sub ${AWS::AccountId}
や !Sub ${AWS::Region}
などを使ってなるべくベタ書きをしないようにした方が良い。
LogConfiguration は↑で作成したロググループをコンテナに紐付けるプロパティ。ログ出力しないのであれば不要。
ECSTASKDEFINITION:
Type: AWS::ECS::TaskDefinition
Properties:
RequiresCompatibilities:
- FARGATE
Cpu: "256"
ExecutionRoleArn: !Sub arn:aws:iam::${AWS::AccountId}:role/ecsTaskExecutionRole
Family: CFn-test-task
Memory: "512"
NetworkMode: awsvpc
TaskRoleArn: !Sub arn:aws:iam::${AWS::AccountId}:role/ecsTaskExecutionRole
ContainerDefinitions:
- Name: CFn-test-Container
Image: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/my-greeting-web:52c324b
Memory: 512
PortMappings:
- ContainerPort: 8080
HostPort: 8080
Protocol: HTTP
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: !Ref LOGGROUP
awslogs-region: !Sub ${AWS::Region}
awslogs-stream-prefix: ecs
ECSサービスの作成
ここもプロパティがたくさんあって大変。
これ、死ぬほど悩んだのだが、日本語ドキュメントで翻訳されていない DeploymentController
というプロパティがあって、これがマネジメントコンソール上でサービスを作成するときの「デプロイメントタイプ」に該当する。デフォルトがECSなので、Blue/Greenデプロイメントができないように思えてしまうけど、実際には指定したら作れた。
プロパティ | デフォルト値 |
---|---|
DeploymentConfiguration | MinimumHealthyPercent: 100, MaximumPercent: 200 |
DeploymentController | ECS |
EnableECSManagedTags | よく分からない… |
HealthCheckGracePeriodSeconds | 0 |
LaunchType | EC2 |
LoadBalancers | 無し |
PlacementConstraints | 制約無し |
PlacementStrategies | 戦略無し |
PropagateTags | よく分からない… |
SchedulingStrategy | REPLICA |
ServiceName | AWS払い出しの名前 |
ServiceRegistries | 無し |
Tags | 無し |
サービスの設定については最終的に以下のような感じになる。
ここで、ALBの設定のときにエクスポートした値を !ImportValue CFn-test-TargetGroupArn1
して取り込んでいる。
ECSSERVICE:
Type: AWS::ECS::Service
Properties:
Cluster: !Ref ECSCLUSTER
ServiceName: CFn-test-ECSService
LaunchType: FARGATE
DesiredCount: 1
DeploymentController:
Type: CODE_DEPLOY
LoadBalancers:
- ContainerName: CFn-test-Container
ContainerPort: 8080
TargetGroupArn: !ImportValue CFn-test-TargetGroupArn1
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED
SecurityGroups:
- [セキュリティグループID]
Subnets:
- [自分のサブネットID①]
- [自分のサブネットID②]
- [自分のサブネットID③]
TaskDefinition: !Ref ECSTASKDEFINITION
ここまでやると、Blue/GreenデプロイメントするためのECS on Fargateが1アクションないし2アクションで起動する。
さて、ここからCI/CDパイプラインまで自動構築してみよう!というところで中編に続く。