[新機能] AWS OpsWorksをCloudFormationでまとめて作成する

AWS

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

ども、大瀧です。 今朝かた、AWSコンポーネントを自動作成するAWS CloudFormationAWS OpsWorksをサポートしました。
「CloudFormationとOpsWorksって役割がかぶってない?」と思われる方もいるかもしれませんが、おおむね管理するレイヤーによって棲み分けができていて、両方を上手く組み合わせることが可能です。以下の図のように、OpsWorksはEC2の内部で構成するLinux/ミドルウェア/アプリケーションをChefで効率よく管理することができ、CloudFormationはRDS/DynamoDB(データベース)やS3(ストレージ)、VPC(ネットワーク)など複数のAWSサービスを管理するのに向いています *1

opsworks-cfn00-2

従来はOpsWorksを利用するために事前にVPCやセキュリティグループなどを準備する必要があり、その整合性も管理者が自分で調整する必要がありました。今回の連携によって、OpsWorksはより密接にAWSの全体構成と連携することができるようになります。工夫すれば、CloudFormationでRDSやS3を作り、それらへの接続設定をOpsWorksでアプリに自動デプロイするなんてことも仕組みとして実装できそうです。

サンプルテンプレート

AWSより、OpsWorksの各コンポーネントを作成するサンプルテンプレートが公開されています。こちらを試してみました。

https://s3.amazonaws.com/cloudformation-templates-us-east-1/OpsWorks.template

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

  "Description" : "AWS CloudFormation Sample Template: Launches OpsWorks stack, layer, instances and associated resources to run a PHP application. ** This template creates one or more Amazon EC2 instances. You will be billed for the AWS resources used if you create a stack from this template.",

  "Resources": {

    "myStack": {
      "Type": "AWS::OpsWorks::Stack",
      "Properties": {
        "Name": {
          "Ref": "AWS::StackName"
        },
        "ServiceRoleArn": {
          "Fn::GetAtt": [ "OpsWorksServiceRole", "Arn"]            
        },
        "DefaultInstanceProfileArn": {
          "Fn::GetAtt": [ "OpsWorksInstanceProfile", "Arn"]
        }
      }
    },

    "myLayer": {
      "Type": "AWS::OpsWorks::Layer",
      "DependsOn": "myApp",
      "Properties": {
        "StackId": {
          "Ref": "myStack"
        },
        "Name": "MyPHPApp",
        "Type": "php-app",
        "Shortname": "php-app",
        "EnableAutoHealing": "true",
        "AutoAssignElasticIps": "false",
        "AutoAssignPublicIps": "true"
      }
    },

    "myInstance1": {
      "Type": "AWS::OpsWorks::Instance",
      "Properties": {
        "StackId": {
          "Ref": "myStack"
        },
        "LayerIds": [
          {
            "Ref": "myLayer"
          }
        ],
        "InstanceType": "m1.small"
      }
    },

    "myInstance2": {
      "Type": "AWS::OpsWorks::Instance",
      "Properties": {
        "StackId": {
          "Ref": "myStack"
        },
        "LayerIds": [
          {
            "Ref": "myLayer"
          }
        ],
        "InstanceType": "m1.small"
      }
    },

    "myApp": {
      "Type": "AWS::OpsWorks::App",
      "Properties": {
        "StackId": {
          "Ref": "myStack"
        },
        "Name": "MyPHPApp",
        "Type": "php",
        "AppSource": {
          "Type": "git",
          "Url": "git://github.com/amazonwebservices/opsworks-demo-php-simple-app.git",
          "Revision": "version1"
        },
        "Attributes": {
          "DocumentRoot": " "
        }
      }
    },

    "OpsWorksServiceRole": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "AssumeRolePolicyDocument": {
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": {
                "Service": [
                  "opsworks.amazonaws.com"
                ]
              },
              "Action": [
                "sts:AssumeRole"
              ]
            }
          ]
        },
        "Path": "/",
        "Policies": [
          {
            "PolicyName": "opsworks-service",
            "PolicyDocument": {
              "Statement": [
                {
                  "Effect": "Allow",
                  "Action": [
                    "ec2:*",
                    "iam:PassRole",
                    "cloudwatch:GetMetricStatistics",
                    "elasticloadbalancing:*"
                  ],
                  "Resource": "*"
                }
              ]
            }
          }
        ]
      }
    },

    "OpsWorksInstanceRole": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "AssumeRolePolicyDocument": {
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": {
                "Service": [
                  "ec2.amazonaws.com"
                ]
              },
              "Action": [
                "sts:AssumeRole"
              ]
            }
          ]
        },
        "Path": "/"
      }
    },

    "OpsWorksInstanceProfile": {
      "Type": "AWS::IAM::InstanceProfile",
      "Properties": {
        "Path": "/",
        "Roles": [
          {
            "Ref": "OpsWorksInstanceRole"
          }
        ]
      }
    }
  },

  "Outputs" : {
    "StackId" : {
      "Value" : { "Ref" : "myStack" }
    },

    "AppId" : {
      "Value" : { "Ref" : "myApp" }
    }
  }
}

テンプレートの実行は、従来のCloudFormationの操作と特に変わりません。

CloudFormationスタックが作成されると、OpsWorksの各コンポーネントが作成されていきます。「スタック」というコンポーネントはCloudFormationとOpsWorks両方にあり(どちらも最上位のコンテナを指す)、今後はCFnスタックOWスタックなどと区別して呼ばないと大混乱を招きそうですw

CloudFormationのリソース一覧画面

opsworks-cfn02

もちろん、OpsWorksの画面でもちゃんと確認できます。

OpsWorksのスタック管理画面

opsworks-cfn03

...ん、最後のデプロイは手動でやるしかないのかな?また、Update Stackする場合のOpsWorks側の動きは検証する必要がありそうです。あと、地味にすごいのがリージョンを意識する必要がない点です。AMIは各レイヤーで吸収できますし、セキュリティグループもOpsWorks既定のグループが各リージョンに作成されているためです。もちろん、カスタマイズしたAMIやセキュリティグループを使う場合は、実行するリージョンに事前作成する必要があります。

CloudFormationリファレンスを見ましょう

具体的に、どんな構成ができるのかはCloudFormationのテンプレートリファレンスを参照しましょう。

opsworks-cfn01

OpsWorksのスタック、レイヤー、インスタンス、アプリケーションなどの定義をそれぞれ確認できます。

まとめ

CloudFormationテンプレートにOpsWorksを定義する様子をご紹介しました。なかなか作り込みが大変なサービスだとは思いますが、一度作ってしまえば運用が格段に楽になる仕組みだと思いますので、みなさんもぜひ評価してみてください!

また、最近公開されたKumogataなどのツールと組み合わせてもいいかもしれません。

参考URL

脚注

  1. CloudFormationにも、ユーザーデータやCfn-initなどLinux/ミドルウェアを管理する仕組みがあります。