やっとなんとなく意味がわかってきたCloudFormation。AWSのインスタンスやいろんな設定を簡単にデプロイできるツール、みたいな説明はいっぱい見かけるけど、覚えないといけない仕組みが増えただけでaws cliでできることとなにが違うのかがいままでよくわからなかったのです。
理解した内容は、
- CloudFormationは冪等性を担保しやすいこと
- 複数のリソースを一括で管理できること
詳細
-
インフラ構築をaws cliでがんばってスクリプト化しても「Infrastructure as Code」は実現できるかもしれないが、aws cliではcreateなんちゃら系をするコマンドは2回目以降にエラーになっちゃったりして、きちんと動くようにするにはいろいろ大変。CloudFormationであれば記述をシンプルにできるし、何度実行しても最新の記述に更新できる冪等性を実現できます。
-
EC2とRDSの組み合わせなど複数のリソースからなる一連の仕組みを一発で作れるだけでなく、削除も簡単です。EC2やRDSなどのリソースをCloudFormationのスタックというものにぶらさげて作るようなイメージで、スタックを削除するとぶらさがっているリソースもまとめて削除されます。
用語
- リソース
- EC2インスタンス、ALB、IAM RoleなどAWS上のなにか。
- テンプレート
- JSONかYAMLで書くリソース設定集みたいなもの。
- テンプレートをもとにスタックが作られる。1つのテンプレートから同じ構成のスタックをパラメータを変えてたくさん作ることができる。
- スタック
- テンプレートにパラメータを渡して実行するとできあがるもの。
- スタックの中にEC2インスタンスなどのリソースが作られる。
- スタックを削除するとぶらさがっているリソースも削除される。
- テンプレートを再実行すると上書きされる。
- パラメータ
- テンプレートでスタックを作成するときに必要な変数。
- どんなパラメータがあるかはテンプレートの記述次第だが、インスタンスタイプ、インスタンス名、DB名などなど。
- チェンジセット
- スタックを新規作成や更新して実行したときに実際にAWSのリソースがどんな変更を受けるかを表したもの
似たようなAWSサービスの比較
- CloudFormation
- AWSのリソースを冪等性を持たせて一括管理するもの。
- OpsWorks
- Chef。ミドルウェアのインストールや設定を冪等性を持たせて管理するツール。
- Chef自体はAWS向けには限らないので、AWSのリソースを管理できるCloudFormationよりも少し上のレイヤーになるのかな。
- Elastic Beanstalk
- ウェブアプリなどのよくあるインフラ構成を簡単に作れて、そのインフラ上にアプリを簡単にデプロイするための仕組み。
- デプロイでは、LBとの切り離し方法、ダウンタイム、切り戻し方法などでの違いからデプロイ方式がいくつかある。
(CloudFormation以外はまだ触ったことないので、よくわかりません)
始め方
とりあえず以下の記事をもとに超簡単なテンプレを作って動かしてみる。
【CloudFormation入門1】5分と6行で始めるAWS CloudFormationテンプレートによるインフラ構築 | Developers.IO
ブラウザでコンソールで触るのではなくawscliでスタックを作るには以下のコマンド。
チェンジセットを作るのみ(dry-run)。チェンジセットを確認する方法はこのコマンドの出力で案内される。
$ aws cloudformation deploy --template-file <TEMPLATE.yml> --stack-name <STACKNAME> --no-execute-changeset
実行するには--no-execute-changeset
を外す。
$ aws cloudformation deploy --template-file <TEMPLATE.yml> --stack-name <STACKNAME>
次、LambdaをCloudFormationで試すには以下の記事が参考になる。Lambdaは関数のコードもデプロイしないといけないので、ひと手間増える。
CloudFormationでAWS Lambdaを作成・更新する際のベストプラクティス - Qiita
AWSのいろいろなリソースをCloudFormationで試すには、レファレンスのページを見る。
AWS リソースおよびプロパティタイプのリファレンス - AWS CloudFormation
サンプル
Lambdaと必要なIAM Roleを作るCloudFormationを試してみます。
以下のようなディレクトリ構成でYAMLファイルとLambdaの関数のソースとなるファイルを作ります。
$ tree
.
├── sample1.yml
└── src
└── main.py
1 directory, 2 files
YAMLファイル。
AWSTemplateFormatVersion: '2010-09-09'
Resources:
SampleLambda:
Type: AWS::Lambda::Function
Properties:
Code: src
Handler: main.lambda_handler
Role: !GetAtt SampleIAMRole.Arn
Runtime: python3.7
SampleIAMRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
各リソースのProperties
にどう書けばいいかは、レファレンスを見ればわかります。レファレンスではよくわからなければ、マネジメントコンソールで実際に適当にリソースを作ってみて、awscliとかで作ったリソースの値を見ればいいのかな。
レファレンス
AWS::Lambda::Function - AWS CloudFormation
AWS::IAM::Role - AWS CloudFormation
SampleLambda
の中のCode
というプロパティはLambdaのソースコードを保存しているローカルのディレクトリ名です。
GetAtt
というCloudFormationの中で使える関数も使っています。
Fn::GetAtt - AWS CloudFormation
GetAtt
の引数としてSampleIAMRole
というのはその2行下にあるリソース論理名です。Arn
という属性の存在は以下のページでわかります。
Arn - AWS::IAM::Role - AWS CloudFormation
Lambdaの関数のソースファイルはこちら。
def lambda_handler(event, context):
print("Hello, World!")
以下のコマンドを実行すると、sample1.packaged.yml
が生成されます。Code
で指定したソースをいったんS3にアップロードして、YAMLの設定をS3を参照するように変更するコマンドです。変更後のYAMLはローカルのsample1.packaged.yml
に書き出されます。
$ aws cloudformation package --template-file sample1.yml --s3-bucket <BUCKET-NAME> --s3-prefix <S3-PREFIX> --output-template-file sample1.packaged.yml
--s3-bucket
と--s3-prefix
はLambdaのソースを一時的にアップロードする先を指定します。--s3-prefix
は後ろに自動で/
が付与されるようですので、記載不要です(/
を付けちゃうと二重になっちゃいます)。
sample1.packaged.yml
はsample1.yml
と中身がよく似ていますが、ちょっとだけ変換されています。
$ diff -u sample1.yml sample1.packaged.yml
--- sample1.yml 2019-11-18 23:50:34.048495755 +0900
+++ sample1.packaged.yml 2019-11-18 23:50:45.806498124 +0900
@@ -3,9 +3,14 @@
SampleLambda:
Type: AWS::Lambda::Function
Properties:
- Code: src
+ Code:
+ S3Bucket: XXXX
+ S3Key: YYYY/zzzz
Handler: main.lambda_handler
- Role: !GetAtt SampleIAMRole.Arn
+ Role:
+ Fn::GetAtt:
+ - SampleIAMRole
+ - Arn
Runtime: python3.7
SampleIAMRole:
Type: AWS::IAM::Role
Code
のところの差分を見ると、LambdaのソースがいったんS3にアップされていることがわかります。
GetAtt
のところも差分がありますが、短縮した書き方から完全な書き方に変換されているようです。
続いてsample1.packaged.yml
をCloudFormationでデプロイするには、次のコマンドを実行します。まずはdry-runするために--no-execute-changeset
を付けます。
$ aws cloudformation deploy --template-file sample1.packaged.yml --stack-name sample1 --no-execute-changeset
An error occurred (InsufficientCapabilitiesException) when calling the CreateChangeSet operation: Requires capabilities : [CAPABILITY_IAM]
エラーになりました。
よくわかってないのですが、--capabilities CAPABILITY_IAM
を付けるといいようです。
$ aws cloudformation deploy --template-file sample1.packaged.yml --stack-name sample1 --capabilities CAPABILITY_IAM --no-execute-changeset
--no-execute-changeset
を外し、dry-runでなく実際に実行します。
$ aws cloudformation deploy --template-file sample1.packaged.yml --stack-name sample1 --capabilities CAPABILITY_IAM
ブラウザでマネジメントコンソールにアクセスしてLambdaのところに行くと、実際に実行でき、Hello, World!を出せました。
よし完全に理解した。