[PR] あなたが Kindle で読みたいその本、Kindle に対応したら Twitter でお知らせします。

「Hey Siri, デプロイおじさんに電話して」 Lambda と Twilio でワンコールデプロイをやってみた

Posted on

この記事は AWS Lambda 縛り Advent Calendar 2015 の 18 日目 & Twilio Advent Calendar 2015 の 19 日目の投稿です。

みなさんのチームではどのようにデプロイしていますか?

ワンクリックデプロイはもはや当たり前、ChatOps が進んでいるところだと Hubot でデプロイしているかもしれません。でも、映画「バック・トゥ・ザ・フューチャー」の世界では 2015 年は未来の日。もっとクールなデプロイ方法があっても良さそうです。

今回は Lambda と Twilio を組み合わせて、電話するだけでデプロイできる ワンコールデプロイ を実装してみました。 Siri を使うと手を動かす必要すらなく「Hey Siri, デプロイおじさんに電話して」と話すだけでデプロイできるようになります!

ワンコールデプロイの仕組み

フローは次の図のようになります。 Twilio から直接 Lambda ファンクションを実行できないので間に API Gateway を挟んでいます。

ワンコールデプロイの仕組み

  1. iPhone に「Hey Siri, デプロイおじさんに電話して」と話すとアドレス帳に登録した Twilio 番号に発信する
  2. Twilio に着信して API Gateway にリクエスト。レスポンスで発信者に返答するための XML を取得する
  3. Lambda が実行されて Jenkins の REST API にリクエスト
  4. Jenkins が GitHub の最新コードを取得して S3 にデプロイする

説明を簡略化するために、今回は GitHub にコミットされた静的ファイルを S3 の静的ウェブサイトホスティングにデプロイするという例にしています。プロダクション環境のデプロイはもっと複雑ですが、Twilio と Lambda を連携するところは変わりません。

Lambda と API Gateway の実装

Lambda ファンクションは Node.js で書きました。 Node.js から OS のコマンドを実行するのは「AWS Lambda をいろいろ暴く」を参考にさせてもらいました。

var my_number = '+8180xxxxxxxx',
    api_user  = 'manabusakai',
    api_key   = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
    api_url   = 'http://(Jenkins Server)/job/s3-sync/build';

exports.handler = function(event, context) {
  console.log(JSON.stringify(event, null, 2));

  // 自分の番号以外は fail させる
  if (my_number !== event.params.from) {
    context.fail('Unauthorized number.');
  }

  var exec = require('child_process').exec;
  var cmd = 'curl -X POST --user ' + api_user + ':' + api_key + ' ' + api_url;

  exec(cmd, function(error, stdout, stderr) {
    if (! error) {
      context.done();
    } else {
      console.log('error code: ' + error.code);
      console.log(error);
      context.fail();
    }
  });
};

Twilio からのリクエストに発信者情報が含まれているので、API Gateway の Integration Request で Lambda に値を渡しています。 Mapping template で定義した値は第一引数の event で取れるので、これを使って発信者番号が自分の番号かチェックしています。

Integration Request の Mapping template

{
  "params": {
    "from": "$input.params('From')",
    "to": "$input.params('To')"
  }
}

同じように Integration Response で TwiML (Twilio に命令するための XML) を定義しています。 Twilio はこの XML を受け取って "OK, start the deploy." と読み上げます。ほかにも発信者の声を録音したり、ダイアルを操作させるといったこともできます。

Integration Response の Mapping template

<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Say voice="alice">OK, start the deploy.</Say>
</Response>

Jenkins のビルドスクリプトは S3 に同期しているだけです。

aws s3 sync "${WORKSPACE}" "s3://(Bucket Name)" --exclude ".git/*"

わずかこれだけでワンコールデプロイが実現できます!

コスト

気になるコストですが、お試しで使う分には実質 0 円で構築できます。 AWS はこれで儲かるのか不思議なくらいです。

  • Lambda
    • 100 万回のリクエスト、400,000 GB/sec のコンピューティング時間が無料
  • API Gateway
    • 100 万回のリクエストあたり $4.25 + データ転送費用
  • Twilio
    • トライアルアカウントで実現可能

まとめ

Lambda と API Gateway を組み合わせれば簡単に Twilio と連携できました。

AWS の各サービスを連携させるのに便利な Lambda ですが AWS 内にとどまるのはもったいないです。 Lambda + API Gateway をハブにして AWS の外にある様々な API を組み合わせると、ローコスト & ノーメンテナンスでサービスを構築できます。アイデアを持っているエンジニアの方はぜひ試してみてください!

ドッグフーディングとして、まずはこのブログをワンコールデプロイできるようにしてみようと思います。