blog

[lambda] [middleman] [wercker]
静的ビルドサイトに足りない機能をLambdaで補ってみる - 予約投稿編

はじめに

この記事は foodison advent calendar 2015 の23日目の記事です。

前回の記事ではWercker 使った Middleman サイトのビルド・デプロイについて紹介しました。
今回はMiddleman等の静的ビルドサイトに足りない機能をLambdaで補ってみる - 予約投稿編について紹介します。

AWS Lambdaとは

AWS Lambda

AWS Lambda は簡単に言うと EC2 インスタンス等のサーバーなしでコードを実行できるコンピューティングサービスです。詳しく知りたい方は公式サイトに日本語のドキュメントが用意されているのでそちらを読んでみてください。

AWS Lambda は、コードを AWS Lambda にアップロードすると、サービスが AWS インフラストラクチャを使用してコードの実行を代行するコンピューティングサービスです。コードをアップロードして、Lambda 関数と呼ばれる関数を作成することで、AWS Lambda がコードを実行するサーバーのプロビジョニングおよび管理を行います。

静的サイトでのLambda活用

Middleman 等で作成した静的サイトで課題となったのがこの3つ。
色々とググったところ、すべて AWS Lambda を活用することで解決できそうです。

  • Amazon S3 にアップロードされた画像のリサイズ
  • 記事の予約投稿(CIサービスを活用している前提)
  • CloudFront の Invalidation

AWS Lambda の使い方

AWS Lambda の使い方をなんとなく覚えるには公式ドキュメントにある 使用開始 2: AWS Lambda コンソールを使用して Amazon S3 イベントを処理する (Node.js) がわかりやすいく、実際に Amazon S3 の画像を使ってサムネイルを自動生成する Lambda 関数をつくることができます。

Lambda関数を作成する際にはまったところ

実際に公式ドキュメントの Lambda 関数を作る際にいくつかはまったことがあったのでメモっておきます。

Zipファイルに余計なファイルを含ませない

余計なファイルを含んでいるとUnable to import module '...': Errorが発生します。Lambda 関数に必要なのはメインのコードが書かれたファイル(index.js)とnode_modulesだけです。

$ zip -r HogeFuga.zip index.js node_modules

関数の終了を明確に定義しないとタイムアウトするまで実行する

Lambda 関数は handler メソッドの引数から受け取る Lambda function の呼び出しコンテキストを使って Lambda 側に終了したことを伝えてやらないとタイムアウトするまでリトライが発生します。リトライについては以下の記事に詳しく書かれています。

記事の予約投稿

AWS Lambda にはスケジュールイベント(Using AWS Lambda with Scheduled Events)とという機能があり、Cron のような処理を実現できます。この機能を使い一定間隔でCIサービスのデプロイAPIを叩くことで時限公開のようなことが可能になります。 Middleman-blog では指定日時を過ぎていない記事はビルド対象になりません。この条件を前提にすることでその指定時間後にビルドされた記事が公開されます。ちなみに12月23日は祝日なのでこの記事が公開されていれば時限公開が正しく動いていることになります。

wercker の API を叩く Lambda 関数を作ってみる

wercker にはAPIが用意されているのでこれを参考にリクエストを投げる Lambda 関数を作ってみます。wercker の API を叩くには wercker の API トークンとアプリケーション ID が必要です。

APIトークン

API トークンは設定画面のPersonal token から取得できます。

Personal tiken

アプリケーション ID

アプリケーション ID はアプリケーション画面の URL の最後https://app.wercker.com/#applications/********の部分です。

Lambda関数

今回は request モジュールを使わずに Node に Built in された https モジュールを使います。

var http = require('https');

// Valiables
var wercker_api_token = "<WERCKER_API_TOKEN>";
var post_data = {
  'applicationId': '<WERCKER_APP_ID>',
  'branch':        'master',
  'message':       'Deploy from Lambda Schedule Event.'
};
var post_options = {
  hostname: 'app.wercker.com',
  port:     443,
  path:     '/api/v3/builds',
  method:   'POST',
  headers: {
    'Content-Type':   'application/json',
    'Authorization':  'Bearer ' + wercker_api_token,
    'Content-Length': Buffer.byteLength(JSON.stringify(post_data))
  }
};

exports.handler = function(event, context) {
  // Set up the request
  var post_req = http.request(post_options, function(res) {
      res.setEncoding('utf8');
      res.on('data', function (chunk) {
          console.log('Response: ' + chunk);
          context.succeed();
      });
  });

  // Post the data
  post_req.write(JSON.stringify(post_data));
  post_req.end();
};

スケジュールイベント

日本時間(UTC+9)の毎朝9時にデプロイを実行するように設定したいので cron(0 00 * * * *) を設定します。これで予約投稿が可能になりますね!

JAWS Framework

JAWS

何度か Lambda 関数を作って上げてみると CLI から上げるのが面倒くさく感じてきます。そんなときに便利なサーバーレスアプリケーションフレームワーク JAWSなんてものがあるらしいです(まだ触ってない)。詳しくは紹介しませんが、そのうち触ってみたら何か書きたいと思います。

まとめ

サーバーレス楽しいですね!今までサーバーを使わなければならなかったことが Lambda を使うことでシンプルになり、必要な時に必要なだけ使えます。
小さなWebサイトこそサーバーレス化することであらゆるコストを抑えることができるのでどんどん活用していきたいですね。アドベントカレンダーの担当はこれで終わりになりますが、また試したことを記事にしていきたいと思います。それではメリークリスマス!良いお年を。

参考