アクトインディ開発者ブログ

子供とお出かけ情報「いこーよ」を運営する、アクトインディ株式会社の開発者ブログです

Cloudformation 入門してみました

morishitaです。

これまで、なんかめんどくさそうでCloudfrmationは避けてきました。

ElasticBeanstalk や Serverlessフレームワークは裏側でCloudFormationが動くので、間接的には使ってきました。
デフォルトで用意されないリソースを追加するのにちょっとだけYAMLの定義追加する程度はやりました。
それなりに便利だと恩恵を感じてはいたのですが、直接使ったことはありませんでした。

ドキュメントの多さに、どこから手をつけていいかわからないとっつきにくさにかまけて避けていたのです。

ですが、「すぐいこ」の開発でイチからインフラを構築する機会がありついに入門してみました。

2021年03月 追記

私自身はもう CloudFormation で新規にインフラを構築することはないと思います。というのもCDKのほうが圧倒的に便利で、メンテナンスしやすいからです。

CDKについてのエントリはいくつかあるのでこちらもどうぞ御覧ください。 tech.actindi.net

CloudFormationを使ったインフラ構築の流れ

CloudFormation スタックのテンプレートをGitで管理したいので、Webコンソール上でなくローカルでテンプレートを作成します。

次の流れの繰り返しでインフラを構築しました。

  1. YAMLでCloudFormation スタックのテンプレートを作成する
  2. AWS CLIを使ってテンプレートをスタックに適用する

ちょっとづつ変更しては上記を繰り返しました。

環境の準備

まず、CLIでCloudformationスタックを操作するために AWS CLIを準備します。 AWS CLIのセットアップについては割愛します。

そして、エディタには Visual Studio Codeを使いました。 加えて次の拡張も使います。

vscode-cfn-lintを使うために、cfn-lintもインストールします。macOSだと、Brewでインストールできます。

$ brew install cfn-lint

vscode-cfn-lintによってとてもテンプレート作成が効率化されました。
テンプレートにエラーがあった場合にはスタックを更新しようとしたときに検出されロールバックされます。
が、このサイクルを繰り返すのは時間がかかって辛いです1。 設定項目の階層や名称、設定値の間違いの類ならvscode-cfn-lintでテンプレートの記述中にミスが指摘されるので、実際に適用する前に修正できます。
かなり効率が上がります。

cfn-lintの指摘を解消すれば、ほぼ記述ミスに由来するエラーはなくなります。 リソース間の矛盾のような論理的なエラーの修正に集中できます。

CloudFormationのとっつきにくさとはじめの一歩

CloudFormationのとっつきにくさの原因を私は次の様に思っています。

  1. ドキュメントが多すぎて、途方に暮れる
  2. インフラをまるっと作ろうとするとCloudFormation以前にインフラ設計の知識が必要
  3. VPCからインフラを構築していくとアプリケーションの動作を見れるまでの道のりが遠い
  4. よくわからないので既存のインフラを壊してしまわないかと怖い

1.についてですが、一旦ドキュメントを読んでよく理解してからなんて考えは捨てました。
CloudFormationはAWSの多くのサービスを扱えるのでドキュメントが膨大です。
全サービスを使うわけでもないと思うので全部読む必要なんて一切ありません。
必要な部分だけ読めばよく、この項目ってどんな意味だろうと調べるためのリファレンスだと割り切って接することにしました。

2.は既存のインフラリソースをコピーしてCloudFormationで作ってみることから始めてみました。

3.ですが、1つのWebアプリケーションを動かすためには意外と多くのリソースが必要になります。VPC、Subnet、SecurityGroup、EC2インスタンスetc。最初はそれらをまるっとCloudFormationで作れたらどんなに楽なんだろうかと想像して始めるのだろうと思います。
ですが、最初から気負って始めるとアプリケーションをデプロイしてその動きを確認できるところまでが遠くて挫折しがちです。
一部でもできるところから取り入れることにしました。

4.を克服するためには他のシステムが可動していない別リージョンや別VPCで始めて経験を積んでいけばいいのですがそうすると一部から始めるという戦術が取りにくいです。
それに練習のためであって本質的には必要のないものをCloudFormationの恩恵を感じる前に作ろうとするのはなかなかのストイックさが求められます。

これらを踏まえて、私がおすすめするCloudFormationのはじめの一歩はCloudWatch ダッシュボードを作ってみることです。

Cloudwatchのダッシュボードを作ってみる

AWSでシステムを運用しているとCloudwatch ダッシュボードでシステム状態を確認できると便利です。
同じようなAWSのリソース構成で複数のシステムを運用しているとCloudwatchのダッシュボードもやはり同じようなものを複数作っているのではないでしょうか。

CloudwatchのダッシュボードをWebコンソールで作っていくのは結構面倒なものです。
でも、CloudFormationを使えば既存のダッシュボードを複製してちょっと変更すれば楽できます。

Cloudwatch ダッシュボードをどう作ろうと運用しているサービスのインフラには何も影響しませんし、あれば役立ちます。
それに既存のダッシュボードがあればそれを複製するようなCloudFormationのテンプレートを簡単に作れます。

CloudFormationによるCloudwatch ダッシュボードの作成について以下に説明します。

テンプレートの作成

まず、既存のCloudwatch ダッシュボードの定義をコピーしてテンプレートを作ります。

サンプルとして次のようなEC2インスタンスのCPU使用率とネットワークトラフィックをグラフ化するダッシュボードがあるとします。

f:id:HeRo:20190610090632p:plain

このダッシュボードの定義を取得するには アクション>ダッシュボードの編集を選択します。

f:id:HeRo:20190610090658p:plain

次のようなダイアログが開きます。 その中にJSONで記述されたダッシュボードの定義が表示されるのでCopy Sourceボタンをクリックしてコピーします。

f:id:HeRo:20190610090715p:plain

コピーしたJSONをCloudFormationテンプレートで利用します。 そのテンプレートは次のようになります。

AWSTemplateFormatVersion: '2010-09-09'
Parameters:
  EC2InstanceId:
    Description: 'EC2 Instance Id'
    Type: String

Resources:
  CloudWatchDashBoard:
    Type: AWS::CloudWatch::Dashboard
    Properties:
      DashboardName: EC2 Instance Metrics
      DashboardBody: !Sub |
        {
          "widgets": [
            {
              "type": "metric",
              "x": 0,
              "y": 0,
              "width": 24,
              "height": 6,
              "properties": {
                "view": "timeSeries",
                "stacked": false,
                "metrics": [
                    [ "AWS/EC2", "CPUUtilization", "InstanceId", "${EC2InstanceId}" ]
                ],
                "region": "ap-northeast-1",
                "title": "CPU Utilization"
              }
          },
          {
            "type": "metric",
            "x": 0,
            "y": 6,
            "width": 24,
            "height": 6,
            "properties": {
                "metrics": [
                    [ "AWS/EC2", "NetworkIn", "InstanceId", "${EC2InstanceId}", { "stat": "Average", "period": 60 } ],
                    [ ".", "NetworkOut", ".", ".", { "yAxis": "right", "period": 60 } ]
                ],
                "view": "timeSeries",
                "stacked": false,
                "region": "ap-northeast-1"
            }
          }
        ]
      }

Resources.CloudWatchDashBoard.Properties.DashboardBodyの値がダッシュボード定義でコピーしたJSONです。 ただし、EC2インスタンスのIDはParameters.EC2InstanceIdでパラメータ化して汎用性を高めています。 これを変えるだけで、別のEC2インスタンスについて同じダッシュボードを量産できます。
ちなみに!Subは変数を展開するためのCloudFormationの関数の省略形です。

シェルスクリプト

このテンプレートでスタックを作って更新していくのですが、AWS CLIへ渡す引数が少なくないので直接実行するのではなく、シェルスクリプトを用意したほうが便利です。

パラメータへの値のセットもシェルスクリプトからだとテンプレートの汎用性がより高まります。

上記テンプレートでCloudfrmationスタックを作成・更新するためのシェルスクリプトの例を次に示します。

#!/bin/bash

SCRIPT_DIR=$(cd $(dirname $(readlink $0 || echo $0));pwd)
CF_FILE_NAME="file://${SCRIPT_DIR}/cw-dashboard.yml"

CF_STACK_NAME='CloudwatchDashboard'
EC2_INSTANCE_ID=<YOUR EC2 INSTANCE ID>

case $1 in
  'create')
    CFN_SUB=create-stack;;
  'update')
    CFN_SUB=update-stack;;
  'changeset')
    CFN_SUB=create-change-set;; # より慎重にするにはこちら。
  * )
    echo 'Please set "create" or "update"'
    exit 1;;
esac

aws cloudformation $CFN_SUB \
--stack-name ${CF_STACK_NAME} \
--template-body ${CF_FILE_NAME} \
--parameters \
ParameterKey=EC2InstanceId,ParameterValue=${EC2_INSTANCE_ID} \
| jq .

上記シェルスクリプトは引数に応じて、AWS CLIのサブコマンドを切り替えます。 スタックが存在しないときには引数createを与えます。
スタックの更新時には引数updateを与えます。こjの場合、AWS CLIとしては aws cloudformation update-stack が実行されます。これによりスタックが更新され、変更が実リソースに反映されます。

チャンジセットがおすすめ

サービスに影響するようなCloudFormationスタックを変更する場合、無理な変更はロールバックされるとはいえ、いきなりリソース変更が適用される更新は勇気が必要です。 より慎重にするには上記シェルスクリプトに引数changesetを与えて aws cloudformation create-change-setを実行するほうが良いでしょう。 これはチェンジセットを作るサブコマンドです。 チェンジセットはスタックの変更箇所を適用せずに確認できる機能で、Dry Run のように使えます。 チェンジセットを作って変更箇所をWebコンソールでチェックしてから適用するというフローができます。

次のステップは?

簡単な例で説明しましたが、メリットを感じられたなら少々ハードルの高いことでもモチベーションを保てると思います。

既存のシステムと同様のシステムを構築する機会があるとチャンスです。 インフラ設計はすでにあるので、それらをどうCloudFormationで作っていくかに集中できます。 既存システムに加えるものは悪い影響を与えないか心配ですが、新規なら気にせず作業できます。 ぜひ、CloudFormationで構築してみましょう。 たとえ、CloudFormationに挫折してもWebコンソールでポチポチやるという逃げ道もあるのであまり気負わずに。

既存に参考になるリソースがある場合、AWS CLIでその設定内容を取得してみるといいと思います。例えばaws ec2 describe-instances などリソース情報を取得するCLIのレスポンスで示される属性はCloudFormationでのプロパティキーとほぼ一致しています。なので、どのキーにどのような値を設定すれば同じリソースが作れるのか参考になります。

このエントリの最初の方で一旦ドキュメントは置いておきましょうと述べましたが、プロパティやその設定値についてわからないことがあればドキュメントをそれらのキーワードで検索して調べてみれば良いと思います。 闇雲に膨大なドキュメントと格闘するより効率良いです。

また、次のドキュメントにはテンプレートのサンプルが示されているので、まずはコピー&ペーストで構築してもいいでしょう。

まとめ

今回、CloudFormationを使ってみて、良かったと思った点は次のとおりです。

  1. AWSのWebコンソールでポチポチ設定するより楽
  2. リソースの変更が記録されるようになった
  3. ノウハウを共有しやすい
  4. 試しに作ってみたリソースをCloudFormationスタック削除するだけで全消去できる

1.は業務で作るリソースである以上、リソースを作れば作り方や設定内容を何らか記録する必要があります。 ならば、そんなドキュメントを作るよりcloudformationテンプレートのほうがトータルで楽だと思います。 ドキュメントと実態のズレもほぼないですし2

2、3.はインフラのコード化の恩恵です。 テンプレートをコードレビューし合うようになったことで、他のメンバーが書いたテンプレートを見て学びやすくなりました。

3.は検証環境をつくるのが楽だということです。CloudFormationテンプレートを作りながら 試す場合もそうですし、機能やパフォーマンス検証のため本番と同じ構成、同じ設定のリソース群を一時的に作って すぐに捨てることができます。

最後に

アクトインディではインフラのコード化はまだ道半ばですが 一緒にやってみたいエンジニアを募集しています。


  1. 複数のリソースを作って行くときなどは途中まで作られたものがロールバックで作されます。それは結構時間がかかったりします。

  2. 適用していないテンプレートと実リソースには当然ですがズレが生じるのでご注意を。