SparkleFormation-虹とユニコーンを備えたCloudFormationテンプレートジェネレーター

スパークルフォーメーション



AWS(Amazon Web Services)を真剣に使用している場合、JSONテンプレートを使用してインフラストラクチャを記述する機能についておそらくご存知でしょう。 AWSでは、このサービスはCloudFormationと呼ばれます。 本質的に、このソリューションでは、AWSで利用可能なリソース(インスタンス、opsworks、ELBレイヤー、セキュリティグループなど)の望ましい状態を記述することができます。 リソースのセットはスタックと呼ばれます。 CloudFormationテンプレートを読み込んだ後、システム自体が必要なリソースをスタック上に作成します(まだ存在しない場合)、または既存のリソースを目的の状態に更新しようとします。



これは、リソースが少ない場合はうまく機能しますが、インフラストラクチャが拡大するとすぐに問題が発生します。



このような問題を回避するために、 Heavy Waterのエンジニアは、Ruby DSLおよびCLIでSparkleFormationgithub )と呼ばれるこれらのテンプレートを生成および操作するように書きました。



ドライ



現在のプロジェクトに来たとき、約1,500行のリソース記述と約0行のコメントを含むCloudFormationテンプレートがありました。 SparkleFormationを使用した後、テンプレートは300行を占有し始めましたが、その多くはコメントです。 どのようにしてこれを達成しましたか? まず、CloudFormationの仕組みを見てみましょう。典型的なリソースの説明は次のようになります。

ELBの作成
"AppsElb": { "Type": "AWS::ElasticLoadBalancing::LoadBalancer", "Properties": { "Scheme": "internal", "Subnets": [ {"Ref": "Subnet1"}, {"Ref": "Subnet2"} ], "SecurityGroups": [ {"Ref": "SG"} ], "HealthCheck": { "HealthyThreshold": "2", "Interval": "5", "Target": "TCP:80", "Timeout": "2", "UnhealthyThreshold": "2" }, "Listeners": [ { "InstancePort": "80", "LoadBalancerPort": "80", "Protocol": "TCP", "InstanceProtocol": "TCP" }, { "InstancePort": "22", "LoadBalancerPort": "2222", "Protocol": "TCP", "InstanceProtocol": "TCP" }, { "InstancePort": "8500", "LoadBalancerPort": "8500", "Protocol": "TCP", "InstanceProtocol": "TCP" } ] } }
      
      







SparkleFormationではDSL内で通常のルビーコードを使用できるため、次のように書き換えることができます。

SparkleFormationでELBを作成する
  resources(:AppsElb) do type 'AWS::ElasticLoadBalancing::LoadBalancer' properties do scheme 'internal' subnets [PARAMS[:Subnet1], PARAMS[:Subnet2]] security_groups [ref!(:SG)] # port mapping 80->80, 22 -> 2222, etc. listeners = { :'80' => 80, :'2222' => 22, :'8500' => 8500 }.map do |k, v| { 'LoadBalancerPort' => k.to_s, 'InstancePort' => v, 'Protocol' => 'TCP', 'InstanceProtocol' => 'TCP' } end listeners listeners health_check do target 'TCP:80' healthy_threshold '2' unhealthy_threshold '2' interval '5' timeout '2' end end end
      
      







ご覧のとおり、各ポートの説明はもう繰り返さないため、新しいポートを追加しても1行だけで済みます。 さらに、多くのほぼ同じリソースを作成する必要があるが、パラメーターが1〜2異なる場合、SparkleFormationはそのようなエンティティーをダイナミクスとして提供します。

ドキュメントの例
 # dynamics/node.rb SparkleFormation.dynamic(:node) do |_name, _config={}| unless(_config[:ssh_key]) parameters.set!("#{_name}_ssh_key".to_sym) do type 'String' end end dynamic!(:ec2_instance, _name).properties do key_name _config[:ssh_key] ? _config[:ssh_key] : ref!("#{_name}_ssh_key".to_sym) end end
      
      





そして、テンプレート内でこの抽象リソースを呼び出すことができます。

 SparkleFormation.new(:node_stack) do dynamic!(:node, :fubar) dynamic!(:node, :foobar, :ssh_key => 'default') end
      
      







したがって、必要なリソースを再利用し、必要に応じてすべてを1か所で変更できます。



クラウド初期化



cloud-initをロードするときにyamlファイルの形式で設定を転送し、それを使用してパッケージをインストールし、CoreOS、個々のサービスおよびその他の設定を設定する機会をしばしば利用します。 問題は、yamlがインスタンスをCloudFormationテンプレートのユーザーデータに渡す必要があることで、次のようになります。

クレイジーエスケープ
  "UserData": { "Fn::Base64": { "Fn::Join": [ "", [ "#cloud-config\n", "\n", "coreos:\n", " etcd:\n", " discovery: ", {"Ref": "AppDiscoveryURL"}, "\n", " addr: $private_ipv4:4001\n", " peer-addr: $private_ipv4:7001\n", " etcd2:\n", ...
      
      







ご覧のとおり、構文の強調表示を忘れる可能性があることは言うまでもなく、これは絶対に判読できず、見苦しく、メンテナンスが不十分です。 DSL内でrubyコードを使用できるため、yaml全体を別のファイルに移動して、単に呼び出すことができます。

 user_data Base64.encode64(IO.read('files/cloud-init.yml'))
      
      





ご覧のとおり、これはJSON内で編集するよりもはるかに優れています。 IO.readの代わりに、必要に応じて任意のパラメーターにHTTP呼び出しを使用できます。



CLI



このプロジェクトでは、テンプレートを管理するために独自のラッパーを使用しますが、この同じコマンドはsfnと呼ばれるテンプレートを管理するためのCLI(コマンドラインインターフェイス)を提供します。 これを使用すると、sfn create、sfn destroy、およびsfn updateコマンドを使用してCloudFormationスタックをダウンロード、削除、および更新できます。 ナイフとの統合も実装されています。



一般に、SparkleFormationを4か月使用した後は満足しています。インフラストラクチャを説明するために単純なJSONに戻らないことを願っています。 計画は、Heavy Waterチームが提供するsfnを含むワークフロー全体を試すことです。



All Articles