LambCIを利用したサーバーレスなデプロイ環境の構築

2016.11.07

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

こんにちは、中山です。

以前から気になっていたのですが、なかなか触る機会のなかった LambCI を使ってみたのでご紹介します。これは一言で言うと「AWS Lambdaを利用したデプロイシステム」です。GitHub上のリポジトリに対するpushを契機に、各種AWSリソースが実行されます。その中でLambdaを実行し、Lambda上でデプロイを実施することが可能です。

今までこういったシステムを構築しようとするとJenkinsかCircleCIなどのSaaSを利用することが多かったと思います。とても便利なプロダクト/サービスではあるのですが、以下のような問題をよく指摘されていると思います。

  • Jenkinsの保守が大変
  • SaaSは高い

何を優先するのかは個人/組織の環境によって異なると思いますが、上記問題点を考慮するのであればLambCIは1つの選択肢になるかもしれません。

LambCIのアーキテクチャ

READMEに書かれている概要をご覧になると全体像が把握できるので引用します。

  1. Receives notification from GitHub (via SNS)
  2. Looks up config in DynamoDB
  3. Clones git repo using a bundled git binary
  4. Looks up config files in repo
  5. Runs install and build cmds on Lambda (or starts ECS task)
  6. Updates Slack and GitHub statuses along the way (optionally SNS for email, etc)
  7. Uploads build logs/statuses to S3

要するにSNSをトリガーにしてLambdaを実行し、そのLambdaの中でコードをcloneしてきて各種シェルコマンドを実行している訳です。やっている処理自体は非常にシンプルな構成になっています。特徴として、全てAWSのマネージドサービスを利用しているため今流行のサーバレスアーキテクチャになっています。「何をもってサーバーレスと呼ぶのか」はいろいろと議論があるようですが、Lambdaを中心とした典型的なFaaSアーキテクチャ(≒サーバーレスアーキテクチャ)になっているようです。

こういった仕組みになっているため、Jenkinsのように自分でサーバのお守りをする必要はありません。さらに、利用した分だけの従量課金なのでお値段的にもお安いです。というか、Lambdaは無料利用枠が充実しているので余程デプロイをしていなければこの範囲内に収まるのではないでしょうか。

設定ファイルは一般的なCIサービス(CircleCIであれば circle.yml ) によく似た記法で記述可能なので、そういったサービスをすでに利用したことがある方ならすぐに理解できると思います。また、デプロイ結果をS3に出力する機能まであります(なかなか「趣のあるUI」ではありますが)。

一件いいこと尽くめのように聞こえますが、、、

やはり「社会は甘くない」のでいろいろと制限はあります。特にLambdaの制限が正直厳しいと思っています。こちらのドキュメントから引用します。

  • No root access
  • 5 min max build time
  • Bring-your-own-binaries – Lambda has a limited selection of installed software
  • 1.5GB max memory
  • Linux only

Lambdaは小さなコードをパズルのピースのように関連させて動作させるという思想上、1つのLambda関数にはいろいろと制限が設けられています(2016年11月7日現在)。そのLambdaを中心としたLambCIにも各種制限が設けられているという訳です。また、当然ですがデプロイやテストに失敗したからといって調査目的でLambda内にSSHログインすることはできません。

ただし、アーキテクチャの説明で引用した文章中に 「(or starts ECS task)」と書かれているように、ECSを利用することもできるようです。ですが、まだ実験的な機能なのかこちらに書かれているようにドキュメントにはまとまっていません。この仕組みを利用すればよりできることが広がりそうです。今回は割愛しますが、別エントリでまとめたいと思います。

使ってみる

では、早速使ってみたいと思います。インストール方法はREADMEに丁寧にまとめられているので簡単に初められます。本エントリでは以下の作業はすでに完了しているものとして進めさせていただきます。

  1. GitHub tokenの取得
  2. Slack tokenの取得

サンプルコード

簡単に利用できるようにサンプルとなるTerraformのコードを用意しました。ご自由にお使いください。

サンプルコードの全体像は以下のとおりです。VPC上にEC2を1台構築するだけの非常にシンプルなものになっています。この構成をLambCIで構築してみます。

lambci-tf-2

トップディレクトリにある .lambci.json がLambCIの設定ファイルです(設定ファイル名はいろいろと指定できるようです)。

{
  "cmd": "./bin/install.sh && ./bin/test.sh && ./bin/deploy.sh",
  "build": "false",
  "branches": {
    "master": "true",
    "/^release/(dev|stg|prd)$/": "true",
    "/(release/)?(dev|stg|prd)/?": "true"
  }
}

利用している設定についてその意味を以下にまとめます。より詳細な設定については先程のリンクを参照してください。

設定 意味 今回の設定
cmd Lambda上で実行するシェルコマンド。 シェルスクリプトにコマンドをまとめて && でつなげて実行。注意点として、シェルスクリプトは実行権限が必要。
build デフォルトでビルド( cmd の実行)するか。 下の設定と組み合わせて特定のブランチでのみビルドを実施。
branches ビルドするブランチを指定。正規表現が利用可能。 ビルドするブランチ( true )とそうでないブランチ( false )を指定。

bin ディレクトリ以下にLambda上で実行するシェルスクリプトを配置しています。それぞれ以下のとおりです。

  • bin/install.sh
#!/usr/bin/env bash

TF_VER="0.7.9"
BIN_PATH="${HOME}/.local/bin"

[[ ! -d "$BIN_PATH" ]] && mkdir -p "$BIN_PATH"

curl "https://releases.hashicorp.com/terraform/${TF_VER}/terraform_${TF_VER}_linux_amd64.zip" \
  -o "${HOME}/terraform.zip"
unzip "${HOME}/terraform.zip" -d "$BIN_PATH"

Terraformのバイナリをインストールして展開しています。LambCIはデフォルトで /tmp/lambci/home がホームディレクトリになります。 /tmp/lambci/home/.local/bin にパスが通っているのでそこに展開したバイナリファイルを配置しています。

余談ですが、LambCIではLambdaで各種コマンドを実行する都合上、Lambdaにどういったコマンドが入っているのかなどを確認したい場合が多くなると思います。その場合、以下のエントリを参照していただくとREPL形式でコマンドを実行できるので便利かと思います。

#!/usr/bin/env bash

if [[ "$LAMBCI_BRANCH" =~ (release/)?dev/? ]]; then
  make remote-enable ENV="dev"
  make terraform ENV="dev" ARGS="get -update"
  make terraform ENV="dev" ARGS="plan"
elif [[ "$LAMBCI_BRANCH" =~ (release/)?stg/? ]]; then
  make remote-enable ENV="stg"
  make terraform ENV="stg" ARGS="get -update"
  make terraform ENV="stg" ARGS="plan"
elif [[ "$LAMBCI_BRANCH" =~ (release/)?prd/?|^master$ ]]; then
  make remote-enable ENV="prd"
  make terraform ENV="prd" ARGS="get -update"
  make terraform ENV="prd" ARGS="plan"
fi

正規表現で特定のブランチにマッチした場合、 plan でテストを実行しています。LambCIは LAMBCI_BRANCH という環境変数にpushしたブランチ名を保存しておいてくれるのでそれを利用しています。その他にもいろいろと環境変数を設定してくれるようです。 env コマンドの実行結果を貼り付けておきます。

LAMBCI=true
SHELL=/bin/bash
TERM=xterm-256color
AWS_SESSION_TOKEN=************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
SLACK_TOKEN=******************************************
MOCHA_COLORS=true
LD_LIBRARY_PATH=/tmp/lambci/home/usr/lib64:/usr/local/lib64/node-v4.3.x/lib:/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib
LAMBCI_CLONE_REPO=knakayama/lambci-tf-demo
AWS_REQUEST_ID=************************************
PATH=/tmp/lambci/home/.local/bin:/tmp/lambci/home/usr/bin:/var/task/vendor/python/bin:/var/task/node_modules/.bin:/usr/local/lib64/node-v4.3.x/bin:/usr/local/bin:/usr/bin/:/bin
_=/usr/bin/env
LAMBCI_PULL_REQUEST=
PWD=/tmp/lambci/build/knakayama/lambci-tf-demo
LAMBCI_CHECKOUT_BRANCH=test/dev
AWS_SECRET_ACCESS_KEY=****************************************
NODE_PATH=/var/runtime:/var/task:/var/runtime/node_modules
AWS_REGION=us-east-1
GIT_TEMPLATE_DIR=/tmp/lambci/home/usr/share/git-core/templates
FORCE_COLOR=true
LAMBCI_BRANCH=test/dev
AWS_ACCESS_KEY_ID=********************
HOME=/tmp/lambci/home
SHLVL=2
CI=true
LAMBCI_COMMIT=6c37bbdc4f24b36583cfbf30ae7bb7bf0ac692b1
PYTHONPATH=/var/task/vendor/python/lib/python2.7/site-packages
NPM_CONFIG_COLOR=always
GITHUB_TOKEN=****************************************
LAMBCI_BUILD_NUM=13
LAMBCI_REPO=knakayama/lambci-tf-demo
GIT_EXEC_PATH=/tmp/lambci/home/usr/libexec/git-core
  • bin/deploy.sh
#!/usr/bin/env bash

if [[ "$LAMBCI_BRANCH" =~ ^release/(dev|stg|prd)$ ]]; then
  _env="$(echo "$LAMBCI_BRANCH" | grep -oE '(dev|stg|prd)$')"
  make terraform ENV="$_env" ARGS="get -update"
  make terraform ENV="$_env" ARGS="apply"
  make terraform ENV="$_env" ARGS="remote push"
fi

こちらも同様特定ブランチにpushされた場合に apply しています。

1. リポジトリのfork

手っ取り早く動作確認するために先程のリポジトリをforkしておいてください。また、Terraformのリモートステートという機能を利用しているので、stateファイルを保存しておくバケットを作成しておいてください。

$ aws s3 mb s3://<_S3_BUCKET_>

バケット名が Makefile に書かれているのでそれも修正する必要があります。

--- Makefile    2016-11-06 15:20:34.000000000 +0900
+++ Makefile.orig       2016-11-06 10:32:06.000000000 +0900
@@ -1,4 +1,4 @@
-BUCKET_NAME = <_S3_BUCKET_>
+BUCKET_NAME = lambci-tf-demo
 REGION = ap-northeast-1
 CD = [ -d env/${ENV} ] && cd env/${ENV}
 ENV = $1

2. LambCI CloudFormationスタックの作成

AWSのマネジメントコンソールにログインした状態でこちらのリンクをクリックしてください。スタックの作成画面が開くので、以下のようにパラメータを埋めてください(スタックのリージョンは現時点で北部バージニア固定のようです)。

lambci-2

スタックの中のLambda-backed custom resourceにより、パラメータで指定したリポジトリに対してSNSとのIntegrationが自動で実施されます。スタック作成後以下のようにIntegrationが設定されていることを確認してください。

lambci-5

これでLambCIの実行環境は作成完了です。簡単ですね。

3. LambdaのIAM Roleを変更

こちらに付いてはTerraformを利用しているために必要な作業です。TerraformでさまざまなAWSリソースを作成するためにそれなりの権限、というか実質Administrator権限をLambdaのIAM Roleに付与する必要があります。必要な権限のみ付与すべきという原則からすると、正直微妙ですね。。。こちらに付いてはよりよい方法が見つかったら追記しておきます。

4. テストの実行

forkしたリポジトリからコードをcloneした後、まずはテストが実行されるのか確認してみたいと思います。テスト用ブランチ(例えば test/dev )にcheckout後、空コミットでpushしてみます。

$ git checkout -b test/dev
$ git commit --allow-empty -m 'test'
$ git push origin test/dev

するとSlackのNotificationが以下のように飛んで来ます。

lambci-3

リンクをクリックするとS3上に保存されたデプロイ結果を確認できます。

lambda-4

5. デプロイ

テストが上手くいったようなので実際にデプロイしてみます。リリースブランチ(例えば release/dev ) にcheckoutして空コミットしてみます。

$ git checkout -b release/dev
$ git commit --allow-empty -m 'release test'
$ git push origin release/dev

先程と同じようにSlackの通知が届くのでS3上でデプロイ結果を確認して、以下のように表示されればOKです。

lambci-6

使ってみた感想

  • アーキテクチャがシンプルなのはとても良い
    • やろうと思えば簡単にカスタマイズできる
    • サーバの管理しなくてよいのも嬉しい
    • 安い
  • Lambdaの制限が痛い
    • 実行時間5分&ソフトウェアのインストール制限がキツイ
    • まだproduction readyとは言い難い
    • 個人ユースで使う分にはいいと思う
  • GitHub Enterpriseで使えるようにして欲しい
  • Lambda on VPCに対応するといろいろおもしろいことできるかも

まとめ

いかがだったでしょうか。

まだまだ物足りない機能もあるようですが、サーバーレスでデプロイ環境を作れるというのは魅力的だと思います。使い始めたばかりなのでもう少し使ってから再度エントリにまとめていきたいと思います。今後も追いかけていきたいプロダクトの1つになりそうです。

本エントリがみなさんの参考になれば幸いです。