あなたはAWSでEC2を作成する時にAWSコンソールから作っていますか?
最初にAWSに慣れるまではそれで良いのですが、何度も似たような環境を作ったり、大量に作ったりする時、非常に手間になります。
そういったときにCloudFormationというAWSのサービスを使うことで、テンプレートを元にリソースを自動で構築することができます。
また、テンプレートをGitで管理することで変更の履歴を保管できるようになることもCloudFormationを使うメリットです。今回は私が使用しているCloudFormationの運用に関するルールについて紹介していきたいと思います。
CloudFormationとは
CloudFormationは、YAML, JSONなどのコード化したテンプレートを使って、リソースの作成や変更ができるAWSのサービスです。
AWS CloudFormation(テンプレートを使ったリソースのモデル化と管理)| AWS
CloudFormationを使うメリットとして、Gitなどのソースコード管理ツールで変更の履歴を管理できるため、レビューによる変更点の事前確認や、変更前の状態に戻すことも簡単にできるようになります。
下記のAWSの公式のドキュメントでCloudFormationのベストプラクティスがまとまっており、大変参考になります。
AWS CloudFormation ベストプラクティス – AWS CloudFormation
私は、5年程前からCloudFormationを使ってほぼ毎日、作業を行っています。 しかし、使い始めた当時は、あまりノウハウが無いせいで、トラブルを起こしてしまったことも多々ありました。 その経験を踏まえて、安全で効率的にCloudFormationを使って運用を行うために、現在使用している6つのルールについてご紹介します。
1. YAMLで書く
CloudFormationはYAMLとJSON、2つの形式で書くことができますが、YAMLの方がおすすめです。YAMLだと短縮記法が使えて、テンプレートを短くできるからです。短い方が見やすいですし、レビューもしやすく、ミスを見逃す可能性も低くなります。
短縮しない場合
- Fn::FindInMap: - AWSInstanceType2Arch - Ref: InstanceType - Arch
短縮記法
!FindInMap [AWSInstanceType2Arch, !Ref InstanceType, Arch]
2. スタックの更新は変更セットを使用する
スタックを更新する場合、変更セットを使用をすることで、変更を反映する前にどのリソースにどのような変更が行われるか知ることができ、安全に作業を行うことができます。
AWSコンソールの場合は、Updateボタンではなく、下記のcreate changeset for current stackを選ぶことで、変更セットを作成できます。
変更セットを作ると、下記のようなこの変更を反映するとどのようなことが起こるか確認できます。また、変更セットを作成した時点では、システムには変更は反映されていないので安全です。
上の画像の場合だとEC2InstanceのReplacementがTrueとなっており、現在のEC2インスタンスが破棄され、新しいものが作られることがわかります。 もしEC2インスタンスのローカルディスクに重要なデータが置かれている場合には、消えてしまいます。 ですので、Replacementの情報を確認することは非常に重要です。 私は、Gitで管理しているCloudFormationのテンプレートの変更のレビューを依頼するプルリクエストに、本番環境で取得したchangesetの結果を張り付け、レビューする人が作業の安全性をチェックできるようにしています。
3.スタックのネストは極力使わない
ネストされたスタックというのは、CloudFormationのスタックに親子関係があるということです。
ネストされたスタックの操作 – AWS CloudFormation
図にすると下記のようになります。
スタックのネストを使用する場合、スタックの更新は親から全て行うことになります。 その際、子のスタックの中のどのリソースが作り変えられるかが事前に変更セットに表示されず、実行してみないとわからないので大変危険です。
下記はスタックのネストを使用した場合の変更セットの内容です。
子のスタックについては、スタック単位で変更があるといった大まかな情報しか表示されておらず、そのスタック内にあるEC2が作り直されるのかどうかがわかりません。 ですので、予期しないデータの損失を防ぐためにもネストされたスタックは極力しない方が良いです。
私は、当初はネストスタックを多数使用しており、そのため、意図せずEC2インスタンスが作り直されてしまい、そのEC2インスタンスにしか保存されていなかった過去3か月分のデータが消えてしまったことがあります。
その反省から、EC2インスタンスの共通のCloudWatchアラームの設定など万が一、作り直しが発生しても影響がないもののみ、子のスタックに置くようにしています。
4. 命名ルールを最初にちゃんと決めておく
命名ルールをしっかり決めずに適当にスタックを作ってしまうと、後で作り直したくても、既にサービスで使用されていたりして、作り直せずに困ることがあります。ですので、最初に命名ルールを決めておいた方が良いと思います。
私は、CloudFormationのスタック名、テンプレートは、ケバブケースを使用しており、下記のようなルールで命名しています。
(サービス名)-分類名
例えば、サービス名がtanakaでvpcを作るテンプレートでしたら、tanaka-vpcがスタック名、tanaka-vpc.yamlがテンプレート名といった形です。
5. 複数環境を同じテンプレートで管理する
本番環境、ステージング環境、開発環境と複数の環境を使用している場合に、環境ごとに別のテンプレートにせず、同じものを使うようにした方がよいと思います。
理由は2つあります。
1つは、同じテンプレートを使うことで、本番環境で使うテンプレートが正常に動作するかどうかという確認が開発、ステージング環境ででき、トラブルが発生する確率を減らせるためです。
もう1つは、単純にテンプレートの数を減らすことができ、効率的だからです。 環境ごとに設定を変更しないといけない場合には、下記のようにします。 まず、パラメータで環境名を定義します。
Parameters: Environment: Description: Environment of the application Type: String Default: development AllowedValues: [ production, staging, development ] ConstraintDescription: Must be a valid environment name
そして、Mappingで環境ごとの設定を定義します。
Mappings: EC2: InstanceType: production: m5.large staging: m5.large development: t3.small
最後に、組み込み関数のSub、FindInMapなどを使用して環境ごとに異なる設定を適用します。
Resources: EC2Instance: Type: AWS::EC2::Instance Properties: InstanceType: !FindInMap [ EC2, InstanceType, !Ref Environment ] S3Bucket: Type: AWS::S3::Bucket Properties: BucketName: !Sub rakuten-payment-${Environment}
6. パラメータを使用しすぎない
CloudFormationにはパラメータという機能があり、スタックを作るときに設定のカスタマイズを行うことができます。
これは便利な機能なのですが、使いすぎると、実際にどのような設定がされているかテンプレートを見てもわからなくなってしまいます。
具体的な判断基準としては、Gitで管理しなくても運用上問題ない値についてのみ、パラメータを使用するのが良いと思います。
例えば、development, staging, productionといった環境名は、その3つのうちから選択するとテンプレートに記載しておけば、実際に各環境ごとのスタックでどれが指定されているかは容易に想像できるのでGitで管理しなくても問題がありません。
OK
Parameters: Environment: # スタックで何が設定されているか推測ができる Description: Environment of the application Type: String Default: development AllowedValues: [ production, staging, development ] ConstraintDescription: Must be a valid environment name
一方で、EC2のインスタンスタイプやAutoScaling Groupの最小インスタンス数、最大インスタンス数などをパラメータで設定してしまうと、実際にどの値が設定されているかは選択肢が多くなりすぎて、レビューを行う際などにGitを見ただけでは判断することが難しいので、パラメータを使用しないほうが良いです。
NG
Parameters: InstanceType: # 何がスタックで設定されているかテンプレートを見ただけだと分からない。 Type: String Default: t2.micro Description: Must be a valid instance type
まとめ
以上、私がCloudformationを使用するうえで適用しているの6つのルールについて紹介させていただきました。 このような工夫をすることで、安全かつ便利に運用することができています。
参考になりましたら幸いです。