先月に、AWSはデプロイメントガイドとCloudFormation templateをあわせたAWS Enterprise Accelerator: Microsoft Servers on the AWS Cloudをリリースしました。このブログ記事では、複雑なWindowsワークロードをデプロイする方法とサーバーの依存関係に起因する問題をAWS CloudFormationがどのように解決するのかについて解説しています。
このAWS Enterprise Acceleratorソリューションは、もっとも要望の多い4つのマイクロソフトサーバー製品 - SQL Server、Exchange Server、Lync Server、そしてSharePoint Server - をAWS上で高可用性を実現するためmulti-AZアーキテクチャーにデプロイします。その基礎としてのActive Directoryドメインサービスをふくんでいます。このソリューションの以下のステップによって、AWS IaaSプラットフォーム上でこれらのサーバーによって提供されるeメール、コラボレーション、コミュニケーション、そしてディレクトリ機能を利用することができます。
このソリューションのサーバー間にあるさまざまな依存関係があります。例えば以下のようのものです:
- Active Directory
- インターネットアクセス
- クラスタに追加のサーバーを追加する前に最初のサーバーインスタンスを作成する必要があるなどサーバークラスタ内の依存関係
- 共通のVPC、NATゲートウェイ、インターネットゲートウェイ、DNS、ルーティングなどを共有しているなどAWSインフラストラクチャへの依存関係
このインフラストラクチャとサーバーは3つの論理的なレイヤーで構成されます。マスターテンプレートはマイクロソフトサーバーごとにひとつのスタックからなるスタックの構築とスタック間の依存関係の管理をオーケストレーションします。それぞれのCloudFormationスタックはマイクロソフトサーバーをOSレベルで立ち上げるためにPowerShellを使用します。OSを構成する前に、CloudFormationはそれぞれのWindowsサーバーで必要となるAWSインフラストラクチャを構成します。CloudFormationとPowerShellは迅速に、繰り返し可能な形でサーバーのデプロイパターンを作成します。このソリューションは10,000ユーザーをサポートしています。インフラストラクチャとアプリケーションレベルそれぞれにおけるモジュラー化により、さらに多くのユーザーをサポートできます。
スタックの依存関係の管理
スタック間の依存関係をどのように有効にするかを説明すると、SQL ServerがActive Directoryに依存しているためSQLStackはADStackに依存しており、同様にSharePointStackはSQLStackに依存しています。マスターでは、これらの標準的な依存関係はCloudFormationでは以下のようにコード化されます:
"Resources": {
"ADStack": …AWS::CloudFormation::Stack…
"SQLStack": {
"Type": "AWS::CloudFormation::Stack",
"DependsOn": "ADStack",
"Properties": …
}
そして
"Resources": {
"ADStack": …AWS::CloudFormation::Stack…
"SQLStack": {
"Type": "AWS::CloudFormation::Stack",
"DependsOn": "ADStack",
"Properties": …
},
"SharePointStack": {
"Type": "AWS::CloudFormation::Stack",
"DependsOn": "SQLStack",
"Properties": …
}
スタック定義にある"DependsOn"ステートメントはダイアグラムの通りの順序でのスタックの実行を強制します。下のレイヤーは上のレイヤーよりも先に実行されて正常に完了します。"DependsOn"を使用しなければ、CloudFormationはスタックを並行で実行します。並列実行の例はADStackがSUCCESSを返したあとに起こります。2つの高レベルのスタック、SQLStackとExchangeStackは次のレベル(レイヤー2)で並行に実行されます。SharePointとLyncはレイヤー3で並列に実行されます。ダイアグラムの矢印はスタックの依存関係をあらわしています。
スタック間でパラメータを渡す
スタックレイヤー間でインフラストラクチャのパラメータを渡す方法について知るために、このソリューションのすべてのスタックで同じVPCCIDRを渡す例をみていきましょう。VPCCIDRは以下のようにマスターでパラメータとして定義されます:
"VPCCIDR": {
"AllowedPattern": "[a-zA-Z0-9]+\\..+",
"Default": "10.0.0.0/16",
"Description": "CIDR Block for the VPC",
"Type": "String"
}
マスターでVPCCIDRを定義してユーザーにこの値を入力してもらうと、この値はマスターと呼び出されるスタックとの間でID名と入力したパラメータを使用してADStackに渡されます。
"VPCCIDR": {
"Description": "CIDR Block for the VPC",
"Type": "String",
"Default": "10.0.0.0/16",
"AllowedPattern": "[a-zA-Z0-9]+\\..+"
}
マスターがVPCCIDRを定義した後、ADStackは"Ref:VPCCIDR"を最初のドメインコントローラーのCIDRレンジを必要とする任意のリソース(DomainController1SGのようなセキュリティグループなど)で使用することができます。スタック間っで共通のパラメータを渡すかわりに、ほかのオプションはあるスタックからの出力をつぎへの入力として渡すことです。例えば、VPCIDを2つのスタックで渡したい場合は、以下のようにして実現できます。最初のスタックでVPCIDなどの出力を作成します:
“Outputs” : {
“VPCID” : {
“Value” : “ {“Ref” : “VPC”},
“Description” : “VPC ID”
}, …
}
2番目のスタックでは、同じ名前と型でパラメータを作成します:
“Parameters” : {
“VPCID” : {
“Type” : “AWS::EC2::VPC::Id”,
}, …
}
最初のテンプレートが2番目のテンプレートを呼び出すときに、VPCIDは最初のテンプレートの出力として渡され2番目への入力パラメータになります。
スタック内部のリソース間での依存関係の管理
ここまですべての依存関係はスタック間のものでした。別のタイプの依存関係としてはスタック内のリソース間のものがあります。マイクロソフトサーバー製品のケースでは、スタック間の依存関係の例は最初のドメインコントローラー、DC1を2番目のドメインコントローラー、DC2を作成する前に作成する必要があるというものです。
DC1は、多くのクラスタサーバーのように、共通の状態(ドメインオブジェクト)をDC2に複製するため最初に作成する必要があります。このソリューションでのマイクロソフトサーバー製品の場合、すべてのサーバーはあとに続くサーバーで使用するファームまたはクラスタ構成を定義するためにひとつのサーバー(DC1またはExch1のように)が完全に作成されていることを必要とします。
こちらがスタック間の依存関係の別の例です: マイクロソフトサーバー製品はインスタンスが使用可能になる前にAmazon EC2インスタンス上で完全に構成されている必要があります。したがってインスタンスが正常に作成された後、のこりのスタックの実行(あとに続くサーバーのデプロイなど)が継続できるようになる前に、スタック内でソフトウェアの完了に対する依存関係があります。「ソフトウェアが完全にインストールされる」などのスタック間の依存関係はwait conditionsを使用することで管理します。Wait conditionsはEC2インスタンスのようなリソースにスタック内の依存関係を管理するために前に言及した"DependsOn"属性を使用することができます。たとえば、DC1が完了するまでDC2の作成を停止するために、wait conditionを使用して以下のように"DependsOn"属性を構成します。以下の例の(1)を参照してください:
"DomainController1": {
"Type": "AWS::EC2::Instance",
"DependsOn": "NATGateway1",
"Metadata": {
"AWS::CloudFormation::Init": {
"configSets": {
"config": [
"setup",
"rename",
"installADDS",
"configureSites",
"installADCS",
"finalize"
]
}, …
},
"Properties" : …
},
"DomainController2": {
"Type": "AWS::EC2::Instance",
[1] "DependsOn": "DomainController1WaitCondition",
"Metadata": …,
"Properties" : …
},
WaitCondition (2)はWaitHandle (3)と呼ばれるCloudFormationコンストラクトに依存しており、最初のドメインコントローラーの作成からSUCCESSまたはFAILUREシグナルを受け取ります:
"DomainController1WaitCondition": {
"Type": "AWS::CloudFormation::WaitCondition",
"DependsOn": "DomainController1",
"Properties": {
"Handle": {
[2] "Ref": "DomainController1WaitHandle"
},
"Timeout": "3600"
}
},
"DomainController1WaitHandle": {
[3] "Type": "AWS::CloudFormation::WaitConditionHandle"
}
SUCCESSはDC1の「ファイナライズ」ステップ中に(4)のcfn-signal.exe -exit-code 0によりシグナルされ、CloudFormationがDC2をEC2リソースとして実行することを可能にします。
"finalize": {
"commands": {
"a-signal-success": {
"command": {
"Fn::Join": [
"",
[
[4] "cfn-signal.exe -e 0 \"",
{
"Ref": "DomainController1WaitHandle"
},
"\""
]
]
}
}
}
}
}
ステップ(2)でタイムアウトになった場合、自動的にFAILUREがシグナルされてADStackとMasterスタックの実行を中止します。
このブログ記事でみてきたように、ネストされたスタックとネストされた依存関係を作成して標準パラメータまたは出力によってスタック間でパラメータを渡すことができます。スタック内では、wait conditionsとcfn-signalインフラストラクチャを使用することでほかのリソースに依存したリソースを構成することができます。AWSエンタープライズアクセラレーターソリューションはAWS上でのマイクロソフトバックオフィスソリューションのために複数のマイクロソフトサーバー製品をひとつのVPCにデプロイするためにこれらのテクニックを使用しています。
今後のブログ記事では、PowerShellを使用してダウンロードしたユーティリティによるWindowsインスタンスのブートストラップと構成をCloudFormationスタックと統合して実行する方法について解説したいと思います。
(日本語訳はSA渡邉(@gentaw0)が担当しました。原文はBuilding a Microsoft BackOffice Server Solution on AWS with AWS CloudFormation)