読者です 読者をやめる 読者になる 読者になる

プログラマでありたい

おっさんになっても、プログラマでありつづけたい

API Gateway+Lambda+fabricでGitHubのWebHookと連携

 API Gatewayの登場で、LambdaをWebからのエンドポイントとして利用できるようになりました。一般的なメリットとしては、モバイルアプリやJavaScriptアプリのバックエンドとして利用しやすくなったということです。それ以外にも便利なのが、WebHookとの連携です。WebHookは、何らかの処理をトリガーに、Web上にあるHTTP/Sのサービスをコールして処理を連係するといった機能です。抽象的にいうと解りにくいですが、GitHubにコミットされたタイミングで、ビルド処理を走らせる為にJenkinsやCircleCIのビルドをURL呼び出しで叩くということです。最近では、サービス間の連係によく使われるようになりつつあり、WebHookの機能が実装されているサービスが増えています。

API Gateway+LambdaとWebHook



 WebHookを利用する際に、サービス間の連係だけで完結する場合は特に問題はありません。しかし、フックした後の処理が、サーバ上のOSコマンドやシェルプログラムを叩くものの場合だと少し問題があります。WebHookの呼び元からHTTP(/S)で呼ぶ出せるサーバを用意する必要があります。少々、面倒くさいですね。またWebHookを利用する場合、サービスの実行頻度が低く常時利用する必要もないものが多いです。そのために、24時間動いてるサーバを用意するのは非効率なことが多いです。
 そこで、API Gateway+Lambdaです。Lambdaはご存知の通りイベント駆動のコンピュートエンジンです。課金体系も、プログラム処理が走っている時間だけの従量課金で、しかも課金の単位はミリ秒単位です。そして、API Gatewayと組み合わせると、極めてローコストなWebからのエンドポイントになります。WebHookとの連係にピッタリですね。

API Gateway+LambdaとWebHookのユースケース



 それでは、どんなケースが考えられるでしょうか。WebHookは通知系のサービスと相性は良いですが、通知機能自体はサービス自身にデフォルトで実装されていることが多いです。あとは、SendGridのようなメールサービスで、メール受信後にリプライを返し自動応答や空メール機能といったことが考えられます。
 それ以外に相性がよいのが、コードのリポジトリとの連係です。GitやGitHubには当然のごとくフック機能が付属しています。プッシュした後に自動でデプロイしてくれれば嬉しいですね。そういった用途には一般的にはJenkinsやCircleCIなどのCIツールを使いますが、単なる静的コンテンツ等のビルド不要な場合はもう少し手軽に出来る方法があっても良いのではないでしょうか?そこで、GitHub + APIGateway + Lambda(Python) + Fabricを試してみました。

f:id:dkfj:20151208090754p:plain

Lambdaからgitコマンド



 まず試したのが、Lambdaにgitコマンドを叩けるようにするということです。Git CloneでTrunkもしくはBranchからデプロイ対象のソースを落とすことを想定してです。まず認証の部分ですが、GitHubには設定画面でDeploy keyを作れます。ここで、簡単にリードオンリーのキーを作れるので、これを利用してLambdaのプロジェクト内にキーを配置すればよいかと考えました。次に、gitコマンドです。Lambdaが動いている環境には、gitコマンドは用意されていません。じゃぁ、ビルドして同梱しようと気軽に考えていたのですが茨の道でした。
 Lambda用のモジュールを作る時は、基本的には静的リンクでビルドして依存関係のライブラリを内包させて作ります。私の知識不足で、それが難しかったです。
 まず、下記のコマンドで作ってみました。

$ make configure
$ ./configure CFLAGS="${CFLAGS} -static-libgcc"

 モジュールは出来るもののLambdaから呼び出した次のようなエラーが出ます。理由としては、curlライブラリが無いのが問題のようです。

fatal: Unable to find remote helper for 'https'

 じゃぁ、HttpsではなくSSHで呼ぶ出そうとしたのですが、やっぱりsshコマンドはありません。sshのモジュールはもう一段敷居が高いのでギブアップです。ということで、今のところはzipダウンロードが代替手段かと考えています。ただこのケースでいうと、GitHubを使うよりS3イベントに連動させた方が、100倍楽です。

LambdaでFabric



 こちらの方は、gitコマンドを作るよりかは苦労しません。pip install -tでローカルに必要なモジュールをどんどんインストールしてLambdaに同梱していけばよいです。具体的に依存関係があってどのモジュールをインストールしたのか忘れましたので、また手順を載せます。

pip install pyasn1 -t ./
pip install rsa

 ディレクトリをみると、下記のような感じになっていました。
f:id:dkfj:20151208091217p:plain

 Fabricの呼び出しは、最小限引用すると下記のような感じです。
fabricのシェルを作成し

import commands
import json
import os
from cStringIO import StringIO
import re

print('Loading function')

def _(cmd):
    return commands.getoutput(cmd) 

def lambda_handler(event, context):
    print(_("mkdir /tmp/fabric"))

    #コンテンツの取得処理を実装

    print(_("cp ./private.key /tmp/private.key"))   
    print(_("chmod 600 /tmp/private.key"))   

    #fabricコマンドの呼び出し
    print("./fab home_dir -H hostname -i /tmp/private.key -u fabric-user")

あとは、fabファイルの方で任意の処理を書くイメージです。

感想



 今のところは、CIツールを作る方が楽です。phantom-lambda-templateプロジェクト形式みたいに始めると皆が幸せになれるかもしれませんね。あと、ビルドの知識が根本的に掛けていることに改めて気がついたので、今度誰かに弟子入りしてパワーアップしてきます。
 今回はFabricの例でしたが、WebHookはAPI Gateway+Lambdaを組み合わせることで応用性が10倍広がります。皆さん是非一緒に妄想してみましょう!!

See Also:
SWF ✕ Lambda
Lambda PythonでAWS CLIコマンドを実行する方法
今年もやるよ!AWS Lambda縛り Advent Calendar 2015 - Qiita
プログラマになりたい Advent Calendar 2015 - Qiita
プログラマになりたい Advent Calendar 2015 - Adventar