share facebook facebook2 twitter menu hatena pocket slack

2014.08.26 TUE

【米俵餅の AWS 記念日(1)】たくさんのインスタンスを起動したいから、今日は CloudFormation 記念日(1)

川原 洋平

WRITTEN BY川原 洋平

どうも、米俵餅 (@inokara) (俵万智さんすいません…)です。

はじめに

マネジメントコンソールからポチポチ一台ずつがやってられなくなったのでついに CloudFormation に手をのばすことにしました Yo!

ちなみにこの CloudFormation は 個人的には AWS の中で一番名前がカッコイイサービスだと思います。自分位の世代になると「なんちゃら、ふぉおおめええしょおおおん」って言って乗り物とかが合体してロボットになるアニメが頭に浮かんでくるんではないでしょうか。

参考

先輩の cfn マスター Y 氏等に教えて頂いた CloudFormation についての資料です。

CloudFormation について(こちらを読んで整理)

一目で解る CloudFormation

AWS CloudFormation 構成図
※ 出典[AWSマイスターシリーズ] AWS CloudFormation 6 スライド目

スライド引用させて頂きました。

そもそも何が出来るのか?

  • EC2 や RDS 等を使ったシステムをテンプレートを利用して構築出来るサービス
  • テンプレートは自由にカスタマイズ可能(We Love JSON)
  • テンプレートを利用することで何度も同じ構成が作れる
  • 起動時にパラメータを付与出来る
  • そして Free(構築した EC2 や RDS 等の料金は当然掛かります)

用語

とりあえずは以下の三つが抑えられていれば良いのかな…。

用語 説明
テンプレート JSON にて記述されている / CloudFormation の心臓部分 / スタックの設計図
リソース テンプレートによって起動、構築される AWS 上のサービス(EC2 や RDS 等)で基本的なサービスは網羅してる
スタック リソースの集合体 / スタック削除 = リソースの全削除 / リソースの構築順序はテンプレートに依存

やってみよう

何はともあれ CloudFormation を利用してスタックを作ってみたいと思います。とりあえずはマネジメントコンソールから CloudFormation を使ってみました。

新しいスタックを作る

スタックの名前を決めてからの…

AWS CloudFormation  新しいスタックを作る
Source から Select a sample template にチェックを入れてプルダウンから Ruby on Rails Hello World Example をチョイス。

パラメータを指定

このテンプレートではデータベースのパスワードとインスタンスタイプの指定のみとなっているようですな。

AWS CloudFormation パラメータを指定する

ちなみに貧乏性な自分には残念でしたがインスタンスタイプの t2.micro は指定不可でした…(t1.micro はイケるようです)

オプションを指定

タグの指定等を行います。

AWS CloudFormation オプションを指定する

今回は本当にタグだけ。

最後にレビュー

レビューするほどの設定は行っていませんが、とりあえずスターダスト☆レビュー。

AWS CloudFormation 設定値をレビューする

最後に Create ボタンをクリック!

暫くすると…

Status が CREATE_COMPLETE になったので構築完了!

AWS CloudFormation 構築完了を確認する

で、Rails のアプリケーションは?

Outputs タブで…

構築された Rails アプリケーションにアクセスする場合の URL を見たい場合には Outputs タブをクリックしましょう。

AWS CloudFormation Outputから構築したアプリケーションにアクセスする

おお、なんか URL が…ということで URL をクリックすると…。

AWS CloudFormation URLをクリックしてアプリケーションにアクセスする

はい、Rails アプリケーションです。

ちなみに、この Outputs はスタック構築完了時出力したい内容を任意でテンプレートから指定出来ますね。

テンプレートはどこや!

ご心配なく、Outputs と同じタブに Template がありますがな。

AWS CloudFormation テンプレートはこちら

以下のような感じで JSON が記録されています。(一部抜粋ですのでこのままコピペはダメ、絶対)

template.json

{
  "AWSTemplateFormatVersion" : "2010-09-09",

  "Description" : "AWS CloudFormation Sample Template Rails_Simple: Create a Ruby on Rails stack using a single EC2 instance with a local MySQL database for storage. This template demonstrates using the AWS CloudFormation bootstrap scripts to install the packages and files necessary to deploy the packages and files at instance launch time. **WARNING** This template creates an Amazon EC2 instance. You will be billed for the AWS resources used if you create a stack from this template.",

  "Parameters" : {

    "DBRootPassword": {
      "NoEcho": "true",
      "Description" : "Root password for MySQL",
    },

    "InstanceType" : {
      "Description" : "WebServer EC2 instance type",
      "Type" : "String",
      "Default" : "m1.small",
    }
  },

  "Mappings" : {
    "AWSInstanceType2Arch" : {
      "t1.micro"    : { "Arch" : "64" },
      "m1.small"    : { "Arch" : "64" },
      "cg1.4xlarge" : { "Arch" : "64HVM" }
    },

    "AWSRegionArch2AMI" : {
      "us-east-1"      : { "32" : "ami-31814f58", "64" : "ami-1b814f72", "64HVM" : "ami-0da96764" },
      "sa-east-1"      : { "32" : "ami-3e3be423", "64" : "ami-3c3be421", "64HVM" : "NOT_YET_SUPPORTED" }
    }
  },

  "Resources" : {

    "WebServer": {
      "Type": "AWS::EC2::Instance",
      "Metadata" : {
        "AWS::CloudFormation::Init" : {
          "config" : {
            "packages" : {
              "yum" : {
                "gcc-c++"      : [],
                "mysql-libs"   : []
              },

              "rubygems" : {
                "rack"         : ["1.3.6"],
                "execjs"       : [],
                "therubyracer" : [],
                "rails"        : ["3.2.14"]
              }
            },

            "sources" : {
              "/home/ec2-user/sample" : "https://s3.amazonaws.com/cloudformation-examples/CloudFormationRailsSample.zip"
            },

            "files" : {

              "/home/ec2-user/sample/config/database.yml" : {
               "content" : { "Fn::Join" : ["", [
                  "development:n",
                  "  adapter: mysql2n",
                  "  encoding: utf8n",
                  "  socket: /var/lib/mysql/mysql.sockn"
                  ]]},
                "mode"  : "000644",
                "owner" : "root",
                "group" : "root"
              }
            },

            "services" : {
              "sysvinit" : {
                "mysqld" : {
                  "enabled"       : "true",
                  "ensureRunning" : "true"
                }
              }
            }
          }
        }
      },
      "Properties": {
        "ImageId" : { "Fn::FindInMap" : [ "AWSRegionArch2AMI", { "Ref" : "AWS::Region" },
                          { "Fn::FindInMap" : [ "AWSInstanceType2Arch", { "Ref" : "InstanceType" }, "Arch" ] } ] },
        "InstanceType"   : { "Ref" : "InstanceType" },
        "SecurityGroups" : [ {"Ref" : "FrontendGroup"} ],
        "UserData"       : { "Fn::Base64" : { "Fn::Join" : ["", [
          "#!/bin/bash -vn",
          "yum update -y aws-cfn-bootstrapn",
          "# Helper functionn",
          "# All is well so signal successn",
          "/opt/aws/bin/cfn-signal -e 0 -r "Rails application setup complete" '", { "Ref" : "WaitHandle" }, "'n"
        ]]}}
      }
    },

    "WaitHandle" : {
      "Type" : "AWS::CloudFormation::WaitConditionHandle"
    },

    "WaitCondition" : {
      "Type" : "AWS::CloudFormation::WaitCondition",
      "DependsOn" : "WebServer",
      "Properties" : {
        "Handle" : {"Ref" : "WaitHandle"},
        "Timeout" : "1500"
      }
    },

    "FrontendGroup" : {
      "Type" : "AWS::EC2::SecurityGroup",
      "Properties" : {
        "GroupDescription" : "Enable HTTP access via port 3000",
        "SecurityGroupIngress" : [
          {"IpProtocol" : "tcp", "FromPort" : "3000", "ToPort" : "3000", "CidrIp" : "0.0.0.0/0"}
        ]
      }
    }
  },

  "Outputs" : {
    "WebsiteURL" : {
      "Value" : { "Fn::Join" : ["", ["http://", { "Fn::GetAtt" : [ "WebServer", "PublicDnsName" ]}, ":3000" ]] },
      "Description" : "URL for newly created Rails application"
    }
  }
}

おお、構築の手順が…JSON で見える、見えるぞ…もう、秘伝のタレ、秘伝の技とは言わせない!

上の JSON を見ているとテンプレートは以下のような要素で定義されていることが判ります。

要素 定義
“AWSTemplateFormatVersion” テンプレートのバージョン
“Description” テンプレートの詳細
“Parameters” CloudFormation 実行時に渡すパラメータを指定
“Mappings” Hash みたいなものでキーに応じて値を指定(リージョンとインスタンス ID とか…)
“Resources” EC2 等のサービスを指定する(type にて AWS のサービスを指定)
“Outputs” スタック構築後に出力したい内容を定義

ほうほう。それぞれの要素の中でもさらにパラメータを定義しているけど JSON 可読性の良さで自分のようなヘタレでもこのテンプレートが何をやっているのかが見える、見えるぞ…

最後に…

ということで、マネジメントコンソールから使う CloudFormation あまりに簡単過ぎて拍子抜けしてしまいました…。次回はテンプレートを作って EC2 インスタンスを起動してみたいと思います。

ところで、そのむかーし、3 日間位で 40 台以上のサーバーを独りでセットアップしなければいけないって時があってデータセンターの寒さに凍えながら納品されたサーバーのダンボールに包まってポチポチ一台、一台、丹精込めてインストールしたのを思い出してしまいました…。あの時って結局、ちゃんとセットアップ出来たんだっけな…。

元記事はこちらです。
【米俵餅の AWS 記念日(1)】たくさんのインスタンスを起動したいから、今日は CloudFormation 記念日(1)