awsサービス cloudformationが想像以上に良い!!
以前サーバー移行の話をちらっとだけ書きました。
デプロイ方式はBlueGreenでやっています。
この時、環境構築するのにcloudformationを使用していました。
テンプレートに記述するだけで新規・既存問わずに自由に設定できます。
僕の時は「既存のVPCの中の既存のセキュリティグループにインスタンス2台(web機とapplication機)を構築する」という感じで作っていました。
でロードバランサーにALBを使いたかったので、2台立てることになりました。
ALBはpublicにしかアタッチできないとか聞いてなかったです笑
本当はこうしたかった でもこうなった
publicにweb機(nginx)、privateにapplication機(unicorn)をおくことにしました。で、この時cloudformationでここの関係性も定義しました。こういうのを用意しておいてくれるのは本当に助かりました。
良かったところ①
インスタンス構築時にこういうプロパティ宣言ができるようになっています。
"UserData" : { "Fn::Base64" : { "Fn::Join" : ["", [
"#! /bin/bash -v\n",
"yum update -y\n",
"yum install -y nginx\n"
]]}}
なのでnginxインストール済みのインスタンス作成できちゃいます。
しかもこれ結構よくてnginxの/etc/nginx/test.confファイルに
こう書いておく
location / {
proxy_pass http://private_ip_address:8080;
}
cloudformatationのnginxのインスタンスのUserData
にこう書く
"sudo sed -i \"s/private_ip_address/",
{
"Fn::GetAtt": [
"Ec2Instance",
"PrivateIp"
]
},
"/g\" /etc/nginx/test.conf \n",
]]}}
そうするとnginxインストール済みインスタンスとunicornを設定済みインスタンスの繋ぎこみ完了している状態で作成されます笑。これできた時は少し感動でした。
良かったところ②
lambdaが使えるところww
インスタンス立ち上げ終わったあとに、lambdaキックさせてターゲットグループの入れ替えというのをやりたかったんですけど、この辺もカバーしてくれて助かりました笑。
それを実現するためにカスタムリソースってのが用意されてます。
"CustomResource" : {
"Type" : "AWS::CloudFormation::CustomResource",
"Properties" : {
"ServiceToken": "lambdaのARN",
"Test1": {"Ref": "InstanceName"},
"Test2": {"Ref": "TargetGroupName"}
}
}
ServiceTokenってのにlambdaのARN入れるだけで構築し終わった後そのlambdaを叩きにいってくれます。しかもTest1といった任意の文字列もeventの一部として渡せます!便利なんですけど、ここで詰まったポイントがあります。
詰まりポイント
lambdaにある記述を書かないとcloudformationのタスクがプログレスのまま終わらない・・・そして一時間後くらいにfailedとなってしまう。
なんか cfn-response モジュールなるものが必要らしいです。
cfn-response
モジュールは、ZipFile
プロパティを使用してソースコードを作成した場合にのみ使用できます。S3 バケットに保存されたソースコードには使用できません。S3 バケットのコードでは、独自の関数を作成してレスポンスを送信する必要があります。
とあります。これURL見てもらえらば良いのですが、
"ZipFile": { "Fn::Join": ["", [
"var response = require('cfn-response');",
"exports.handler = function(event, context) {",
" var input = parseInt(event.ResourceProperties.Input);",
" var responseData = {Value: input * 5};",
" response.send(event, context, response.SUCCESS, responseData);",
"};"
上記のようにcloudformationの中に直接コードを書きましょということです。
・・・・・いや、さすがにそれはいやだよ?w
ってことでマネジメントコンソールの方に記述したいからこの方法とったのに・・・と思ったんですがgithubみるとこんな感じ。
結局、cloudformation側にレスポンス返せばいいだけだったので自分で実装してしおうと思いました。(まあコピペですな)lambda側に以下を書いてあげれば良い。
var SUCCESS = "SUCCESS";
var FAILED = "FAILED";
var cfnResponse = function(event, context, responseStatus, responseData, physicalResourceId) {
var responseBody = JSON.stringify({
Status: responseStatus,
Reason: "See the details in CloudWatch Log Stream: " + context.logStreamName,
PhysicalResourceId: physicalResourceId || context.logStreamName,
StackId: event.StackId,
RequestId: event.RequestId,
LogicalResourceId: event.LogicalResourceId,
Data: responseData
});
console.log("Response body:\n", responseBody);
var https = require("https");
var url = require("url");
var parsedUrl = url.parse(event.ResponseURL);
var options = {
hostname: parsedUrl.hostname,
port: 443,
path: parsedUrl.path,
method: "PUT",
headers: {
"content-type": "",
"content-length": responseBody.length
}
};
var request = https.request(options, function(response) {
console.log("Status code: " + response.statusCode);
console.log("Status message: " + response.statusMessage);
context.done();
});
request.on("error", function(error) {
console.log("send(..) failed executing https.request(..): " + error);
context.done();
});
request.write(responseBody);
request.end();
}
exports.handler = (event, context, callback) => {
// TODO implement
if(event['RequestType'] == 'Delete'){
cfnResponse(event, context, SUCCESS, {});
}
console.log(event);
cfnResponse(event, context, SUCCESS, {});
};
これで無事lambdaにキックさせ、かつタスクもちゃんと終わってくれます。
まとめ
さすがAWSって感じですね!
ちゃんと手を届かせてくれるあたりがユーザーとしては満足度高いです。
もっと知ればいろんなことができそうなので他のサービス含め触り倒していきたいなって思います。
あっGoogle I/O見ないと・・・・
終わり。