AWS Lambda向けデプロイ・管理ツール「Lamvery」について

  • 54
    いいね
  • 0
    コメント

こちらは今年もやるよ!AWS Lambda縛り Advent Calendar 2015の16日の記事ですが、随時更新してます(最終更新: 2016-07-15)

はじめに

なお、記載されている情報は全て2015-07-15現在のものです。
開発は随時行っているため変わる可能性があります。

Lamveryとは?

いいからソース

コチラになります
https://github.com/marcy-terui/lamvery

概要

Lambda functionのデプロイやFunctionそのものを含めた周辺機能の設定・管理を支援するツールです。
Python製で自分自身主にPythonで使っていますが、Node.jsも一応対応していて、最低限の動作確認もしてます。

User-friendly deploy and management tool for AWS Lambda function というコンセプトの元、一つのFunctionを如何に楽に便利に扱うかと実践的なデプロイフローの確率に主眼を置いて開発しており、あくまでフレームワークではなくツールという位置づけです。

また、こういったツールはFunctionのdeployにフックしないと実現できないような付加機能を付けられることに価値があると思っていて、そういったものを積極的に追加しているのが特長です。

読み方

作者は「らんべりー」と読んでます。

語源

元々はAWSのドキュメントに書いているPythonコードのデプロイ方法があまりにも面倒で、これをなんとかできないかなと思って作ったので、「Lam bda + v irtual e nv + p y thon」1と適当にピックアップして繋げた感じですが、今ではもうちょと短くてかっこ良い名前が良かったなとか思わなくもないですw

何故他のような同じようなツールとの比較

lambda-uploader, Kappa等

  • 設定ファイルがJSON(YAMLの方がマシ)
  • 設定ファイルとコマンドラインオプションの切り分けが不明確
  • virtualenvのライブラリのパスがベタ書きだったり、格納効率が悪くてデプロイパッケージ(zip)が肥大化する
    • .py,.pycの区別がなく、片方あれば良いのに全てアーカイブに含まれる2

Apex

こちらをご確認ください。特にrollbackの仕様は一目見て「あ、これダメなやつだ」って分かったので注意した方が良いです。
http://qiita.com/marcy-terui/items/db8dae512af3c553fe72

上記を使わずにLamveryを作ったのはなぜか?

  • 扱いやすい設定ファイルが欲しかった
    • 設定ファイルはYAML、さらにjinja2 templateとして解釈されるためコードや変数の埋め込みができる
  • 多くのツールが実践を意識したパラメータやオプション設計がなされていない
    • 私的な基準ではありますが、設定ファイルとコマンドラインオプションを以下の様なポリシーで分けています
    • 設定ファイル=バージョン管理されるべきもの
    • コマンドオプション=一時的に使用するもの
  • 実践的なデプロイフローの確率
  • Pythonおよびvirtualenvの仕様にあったデプロイパッケージ作成
    • .pyはアーカイブに格納せず.pycのみ格納など3

などなど、手に馴染んで実践的に使えるツールが欲しかったのと、使っていく中で色々欲しくなりそうだったのでいっその事自分で作っちゃおうかなと。

ざっくり特長とできること一覧

  • YAML + Jinja2テンプレートによる設定記述
  • 格納効率に優れたパッケージ作成(Pythonのみ)
  • 安全なデプロイおよびロールバック
  • デプロイフック
  • 環境変数の受け渡し
  • KMSを用いた機密情報の受け渡し
  • KMSを用いた機密ファイルの受け渡し
  • API GatewayへのAPIデプロイおよび管理
  • CloudWatch Eventsによるスケジュール実行の設定と管理
  • CloudWatch Logsログ閲覧

使い方

インストール

  • PyPI
    アーカイブの効率が悪くなりますし、ディレクトリが汚くなるのでFunction本体を置くディレクトリ自体ではなくサブディレクトリにvirtualenv仮想環境を展開することをオススメします。
virtualenv -p <path-to-python2.7> .venv
. .venv/bin/activate
pip install lamvery
  • Apt
echo "deb https://dl.bintray.com/willyworks/deb trusty main" | sudo tee -a /etc/apt/sources.list
sudo apt-get update
sudo apt-get install lamvery
export PATH=/opt/lamvery/bin:$PATH
  • Yum
echo "
[bintraybintray-willyworks-rpm]
name=bintray-willyworks-rpm
baseurl=https://dl.bintray.com/willyworks/rpm/centos/\$releaserver/\$basearch/
gpgcheck=0
enabled=1
" | sudo tee -a /etc/yum.repos.d/bintray-willyworks-rpm.repo
sudo yum install lamvery
export PATH=/opt/lamvery/bin:$PATH

設定方法

まず、以下のコマンドで設定ファイルの雛形を作成します。

lamvery init

いくつかの設定ファイルの雛形が出来上がるので、それを編集します。
上述のとおり、jinja2 templateとして解釈されるため{{ }}で囲むと変数展開、{% %}で囲むとコードの埋め込みができます。
また、よく使われると思われる環境変数については簡潔に書けるようenvという変数に入れておきました。

以下、設定例です。

基本設定

.lamvery.yml
profile: private
region: us-east-1
versioning: true
default_alias: test
clean_build: false
configuration:
  name: lamvery-test
  runtime: python2.7
  role: {{ env['AWS_LAMBDA_ROLE'] }}
  handler: lambda_function.lambda_handler
  description: This is sample lambda function.
  timeout: 10
  memory_size: 128
  vpc_config:
    subnets:
    - subnet-cadf2993
    security_groups:
    - sg-4d095028

イベント(CloudWatch Events)設定

.lamvery.event.yml
rules:
- name: foo
  description: bar
  schedule: 'rate(5 minutes)'
  targets:
  - id: test-target-id
    input:
      this:
      - is: a
      - sample: input

機密情報(KMS)に関する設定

.lamvery.secret.yml
key_id: xxxx-yyyy-zzzz
cipher_texts: {}
secret_files: {}

除外リスト

.lamvery.exclude.yml
- ^\.lamvery\.yml$
- ^\.lamvery\.event\.yml$
- ^\.lamvery\.secret\.yml$
- ^\.lamvery\.exclude\.yml$

アクションフック

.lamvery.hook.yml
build:
  pre:
  - pip install -r requirements.txt -t ./
  post: []

API Gateway

.lamvery.api.yml
api_id: myipugal74
stage: dev
cors:
  origin: '*'
  methods:
  - GET
  - OPTION
  headers:
  - Content-Type
  - X-Amz-Date
  - Authorization
  - X-Api-Key
configuration:
  swagger: '2.0'
  info:
    title: Sample API
  schemes:
  - https
  paths:
    /:
      get:
        produces:
        - application/json
        parameters:
        - name: sample
          in: query
          required: false
          type: string
        responses:
          '200':
            description: 200 response
            schema:
              $ref: '#/definitions/Sample'
  definitions:
    Sample:
      type: object

設定項目

基本設定

  • profile
    aws configure --profile hogeで設定する hoge の部分です。
    Credential情報やRegionの設定が入るやつです。
    http://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-chap-getting-started.html
    指定しない場合(nullと書く or 項目ごと削除)はAWS SDK(boto3)の挙動に準じます(EC2上ならIAM Role、ローカルだとAWS_SECRET_KEY等の環境変数とか)
  • region
    そのまんまリージョン名
  • versioning
    バージョニングの有効(true)、無効(false)
  • default_alias
    オプションで指定しない場合のエイリアス
  • clean_build
    デプロイパッケージ作成時にソースコードをテンポラリのディレクトリに移動した上でパッケージを作成します。ビルドにフックしてライブラリなどをインストールしてデプロイしたい場合などに作業ディレクトリを汚さずに済みます(フックについては後述)
  • configuration
    基本的にLambdaの設定項目そのままです。
    • name
      Functionの名前です。APIでFunctionの特定に使われるため重複不可。
    • runtime
      Functionのランタイム。現状は python2.7 or nodejsをサポートしています。
    • role
      Lambda実行時に与えるIAM RoleのARN。
    • handler
      Functionのエントリーポイントとなる関数の名前空間。
    • description
      Functionの説明。
    • timeoute Functionの最大実行時間(秒)
    • memory_size
      Function(のコンテナ)に与えるメモリサイズ(MB)
    • vpc_config
      VPC内で動かすための設定
      subnetsにサブネットID,security_groupsにセキュリティグループのIDをそれぞれリストで指定します。

イベント(CloudWatch Events)設定

  • name
    Eventの一意なルール名
  • description
    ルールの説明
  • targets
    Eventのターゲット(複数可)
    • id
      ターゲットの一意なID
    • input
      ターゲットに渡す入力(Functionのevent引数)に入る
    • input_path
      inputとの併用不可。JSONPath形式で、inputを指定しない場合にデフォルトで渡される引数の中から任意の部分だけ扱いたい場合に使用する

機密情報に関する設定

  • key_id
    KMSの秘密鍵ID(ARNではなくIDの部分だけ)
  • cipher_texts
    特定の文字列をKMSによって暗号化された暗号文が入ります。基本的にコマンドから設定するため、この項目を手動で書くことはまず無いです。 ここに入った情報がFunctionへ渡され、機密情報を安全に受け渡すことができます(詳しくは後述)
  • secret_files
    特定のファイルの中身をKMSによって暗号化された暗号文とが入ります。基本的にコマンドから設定するため、この項目を手動で書くことはまず無いです。 ここに入った情報がFunctionへ渡され、機密情報が含まれたファイルを安全に受け渡すことができます(詳しくは後述)

除外リスト

ただひたすら正規表現で除外したいファイルのパスを列挙する

アクションフック

現状は build のみサポートします。 build はデプロイパッケージを作成する処理です。
pre に前、 post に後に実行させたいコマンドを列挙します。

API Gateway

  • api_id
    API GatewayにおけるAPIのID(rest-api-id)です。
    apiコマンドでAPIを初回作成する際に -w オプションを付けると自動で記入されます。
  • stage
    APIをデプロイするステージ名です。api コマンド実行時にオプションで指定されない場合に使用されます。
    https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/stages.html
  • cors
    APIの各リソースに自動的に付与したいCORS (Cross-Origin Resource Sharing)オプションを指定します。
    • origin
      Access-Control-Allow-Origin に指定するオプション
    • headers
      Access-Control-Allow-Headers に指定するオプション
    • methods
      Access-Control-Allow-Methods に指定するオプション
  • configuration
    Swagger形式でAPIの設定を記述します。
    Lambdaファンクションを実行するためやCORSの設定等に必要な x-amazon-apigateway-integration の内容は、設定ファイルに明示的に記載されていない場合は自動的に付与されます。Mapping Templateも全てのパラメータを渡すものを自動で設定します(この挙動はオプションで抑制可能です)

コマンド

コマンドライン引数については、よく使用するもののみは記載しています。
全てを知りたい場合はREADMEからご確認ください。

init

$ lamvery init -h
usage: lamvery init [-h] [-c CONF_FILE]

optional arguments:
  -h, --help            show this help message and exit
  -c CONF_FILE, --conf-file CONF_FILE
                        Configuration YAML file (default: .lamvery.yml)

各種設定ファイルの雛形を作成するインストール後に最初に実行するコマンドです。

generate

$ lamvery generate -h
usage: lamvery generate [-h] [-c CONF_FILE] -k KIND

optional arguments:
  -h, --help            show this help message and exit
  -c CONF_FILE, --conf-file CONF_FILE
                        Configuration YAML file (default: .lamvery.yml)
  -k KIND, --kind KIND  The kind of the file # accepts "function"

各種プログラムの雛形を生成するコマンドです。
-k で生成するファイルの種別を指定しますが現状は function でFunctionの雛形のみ生成可能です。

build

$ lamvery build -h
usage: lamvery build [-h] [-c CONF_FILE] [-s] [-l] [-e ENV]

optional arguments:
  -h, --help            show this help message and exit
  -c CONF_FILE, --conf-file CONF_FILE
                        Configuration YAML file (default: .lamvery.yml)
  -s, --single-file     Only use the main lambda function file
  -l, --no-libs         Archiving without all libraries
  -e ENV, --env ENV     Environment variables that pass to the function

ソースコードとライブラリのアーカイブ(ZIP)ファイルを作成します。
確認用や変則的なフローを組む場合向け。
ファイル名はFunction名.zipとなります。設定ファイルがない場合などは実行したディレクトリ名.zipです。

deploy

$ lamvery deploy -h
usage: lamvery deploy [-h] [-a ALIAS] [-c CONF_FILE] [-d] [-s] [-l] [-p]
                      [-e ENV]

optional arguments:
  -h, --help            show this help message and exit
  -a ALIAS, --alias ALIAS
                        Alias for a version of the function
  -c CONF_FILE, --conf-file CONF_FILE
                        Configuration YAML file (default: .lamvery.yml)
  -d, --dry-run         Dry run
  -s, --single-file     Only use the main lambda function file
  -l, --no-libs         Archiving without all libraries
  -p, --publish         Publish the version as an atomic operation
  -e ENV, --env ENV     Environment variables that pass to the function

以下の全てをまとめて行います。

  • ソースコードとライブラリのアーカイブを作成
  • アーカイブの中に機密情報の暗号文を埋め込む
  • アーカイブの中にオプションで指定された環境変数を埋め込む
  • Lambdaの実行設定を更新
  • アーカイブをアップロード
  • 前のバージョンに別のエイリアスを付ける(指定されている場合)
  • 新しいバージョンにエイリアスを付ける(指定されている場合)

また、日頃お世話になっているcodenize.toolsで重宝しているDry run(更新せずに差分だけ表示する)オプションを付けました。-dまたは--dry-runとなっています。

-e または --env で環境変数を指定できます。

rollback

$ lamvery rollback -h
usage: lamvery rollback [-h] [-a ALIAS] [-c CONF_FILE] [-v VERSION]

optional arguments:
  -h, --help            show this help message and exit
  -a ALIAS, --alias ALIAS
                        Alias for a version of the function
  -c CONF_FILE, --conf-file CONF_FILE
                        Configuration YAML file (default: .lamvery.yml)
  -v VERSION, --version VERSION
                        Version of the function

deployの巻き戻しを行います。Dry runにも対応しています。
Rollbackの仕様についてはこちらに記載しています。

configure

$ lamvery configure -h
usage: lamvery configure [-h] [-c CONF_FILE] [-d]

optional arguments:
  -h, --help            show this help message and exit
  -c CONF_FILE, --conf-file CONF_FILE
                        Configuration YAML file (default: .lamvery.yml)
  -d, --dry-run         Dry run

ソースコードの更新は行わず、Functionの設定のみ更新します。もちろんDry run対応。
ただし、バージョニングしている場合は新しいバージョンを発行するまで設定は反映されませんので注意してください。
デプロイ時にも設定を同期するので、ほぼバージョニングしない場合にしか使えないコマンドですw

set-alias

$ lamvery set-alias -h
usage: lamvery set-alias [-h] [-a ALIAS] [-c CONF_FILE] [-d] [-v VERSION]
                         [-t TARGET]

optional arguments:
  -h, --help            show this help message and exit
  -a ALIAS, --alias ALIAS
                        Alias for a version of the function
  -c CONF_FILE, --conf-file CONF_FILE
                        Configuration YAML file (default: .lamvery.yml)
  -d, --dry-run         Dry run
  -v VERSION, --version VERSION
                        Version of the function
  -t TARGET, --target TARGET
                        The alias of the version that is targeted for setting
                        alias

エイリアスを設定します。こちらもDry run対応。
-a or --aliasでエイリアス名、-v or --versionでエイリアスを付けるバージョンを指定します。

encrypt

$ lamvery encrypt -h
usage: lamvery encrypt [-h] [-c CONF_FILE] [-n SECRET_NAME] [-s] text

positional arguments:
  text                  The text value to encrypt

optional arguments:
  -h, --help            show this help message and exit
  -c CONF_FILE, --conf-file CONF_FILE
                        Configuration YAML file (default: .lamvery.yml)
  -n SECRET_NAME, --secret-name SECRET_NAME
                        The name of the secret value
  -s, --store           Store encripted value to the configuration file
                        (default: .lamvery.secret.yml)

KMSを使って与えられた値を暗号化します。
-n or --nameで取り出す際に使用する名前を指定し、-s or --storeを付けるとその名前で設定ファイルに暗号文が登録されます。

encrypt-file

$ lamvery encrypt-file -h
usage: lamvery encrypt-file [-h] [-c CONF_FILE] -p PATH [-s] file

positional arguments:
  file                  The file path to encrypt

optional arguments:
  -h, --help            show this help message and exit
  -c CONF_FILE, --conf-file CONF_FILE
                        Configuration YAML file (default: .lamvery.yml)
  -p PATH, --path PATH  The path to put the decrypted file in the function
  -s, --store           Store encripted value to the configuration file
                        (default: .lamvery.secret.yml)

KMSを使って指定ファイルを暗号化します。
-n or --nameでFunctionで扱う際に使用するファイル名前を指定し、-s or --storeを付けるとその名前で設定ファイルに暗号文とファイル名が登録されます。

decrypt

$ lamvery decrypt -h
usage: lamvery decrypt [-h] [-c CONF_FILE] [-n SECRET_NAME]

optional arguments:
  -h, --help            show this help message and exit
  -c CONF_FILE, --conf-file CONF_FILE
                        Configuration YAML file (default: .lamvery.yml)
  -n SECRET_NAME, --secret-name SECRET_NAME
                        The name of the secret value

設定ファイル内の暗号文を復号します。
-n or --nameで暗号化時に決めた名前を指定します。
decrypt-file は元ファイル見ればええやんって話なので実装してませんw

events

CloudWatch Eventsの設定を行います。もちろんDry run対応です。
差分更新となっており、Event設定がない場合は作成、あれば更新します。
Functionに紐づくEventの中で記述されていないものは削除されますが、別のFunctionに紐づくものはTargetから外すのみで削除は行いません。

lamvery events [-k]

invoke

$ lamvery invoke -h
usage: lamvery invoke [-h] [-a ALIAS] [-c CONF_FILE] [-v VERSION] json

positional arguments:
  json                  The JSON string or file that pass to the function

optional arguments:
  -h, --help            show this help message and exit
  -a ALIAS, --alias ALIAS
                        Alias for a version of the function
  -c CONF_FILE, --conf-file CONF_FILE
                        Configuration YAML file (default: .lamvery.yml)
  -v VERSION, --version VERSION
                        Version of the function

Functionを起動します。
起動した結果のLogは標準出力に吐かれます。
引数で入力を指定します。JSONフォーマットで渡せばそのまま渡され、JSONの書かれたファイルのパスを指定するとその中身を渡します
バージョン指定(-v)やエイリアス指定(-a)もできます。

logs

$ lamvery logs -h
usage: lamvery logs [-h] [-c CONF_FILE] [-f] [-F FILTER] [-i INTERVAL]
                    [-s START]

optional arguments:
  -h, --help            show this help message and exit
  -c CONF_FILE, --conf-file CONF_FILE
                        Configuration YAML file (default: .lamvery.yml)
  -f, --follow          Watch the log events and updates the display (like
                        `tail -f`)
  -F FILTER, --filter FILTER
                        Filtering pattern for the log messages
  -i INTERVAL, --interval INTERVAL
                        Intervals(seconds) to watch the log events
  -s START, --start START
                        Time to start the log events watching

CloudWatch Logsに吐かれるfunctionのログを閲覧します。

-ftail -f 的に流し続けたり、-s で開始日時を指定して閲覧できます。

api

$ lamvery api -h
usage: lamvery api [-h] [-c CONF_FILE] [-d] [-n] [-r] [-s STAGE] [-w]

optional arguments:
  -h, --help            show this help message and exit
  -c CONF_FILE, --conf-file CONF_FILE
                        Configuration YAML file (default: .lamvery.yml)
  -d, --dry-run         Dry run
  -n, --no-integrate    Without automatic integration
  -r, --remove          Remove your API
  -s STAGE, --stage STAGE
                        The name of the stage in API Gateway
  -w, --write-id        Write the id of your API to the configuration file
                        (default: .lamvery.api.yml)

API GatewayにAPIをデプロイします。-r を付けると削除します。

-nx-amazon-apigateway-integration 等を自動設定する挙動を抑制し、設定ファイルに書かれたそのままでデプロイできます。

機密情報を使用するLambda functionの作成の流れ(参考)

1. KMSで秘密鍵を作ります。

https://docs.aws.amazon.com/kms/latest/developerguide/create-keys.html

2. Function実行時のIAM Roleに以下の権限を追加します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "kms:Decrypt"
            ],
            "Resource": [
                "arn:aws:kms:us-east-1:<your-account-number>:key/<your-key-id>"
            ]
        }
    ]
}

3. 設定ファイルやFunctionの雛形を生成します

lamvery init
lamvery generate -k function

4. 以下のように設定ファイルに1で作った鍵のIDを設定します

.lamvery.yml
 profile: default
  region: us-east-1
  configuration:
    name: sample_lambda_function
    runtime: python2.7 # or nodejs
    role: arn:aws:iam::000000000000:role/lambda_basic_execution
    handler: lambda_function.lambda_handler
    description: This is sample lambda function.
    timeout: 10
    memory_size: 128
.lamvery.secret.yml
key_id: a5ed61a9-fa57-4ebf-9b3f-457b95de05ce # <-ここ!!!
cipher_texts: {}

5. 機密情報を暗号化して保存します

lamvery encrypt -s -n foo "This is a secret"

6. ソースを書きます

lambda_function.py
import lamvery

def lambda_handler(event, context):
    print(lamvery.secret.get('foo'))
lambda_function.js
var lamvery = require('./lamvery.js');

exports.lambda_handler = function(event, context) {
    lamvery.secret.get('foo', function(err, data) {
        console.log(data);
    });
}

7. Deploy!!

lamvery deploy

8. Functionを起動

lambery invoke {}

以下のように復号された結果が得られます。

START RequestId: 13829c9c-9f13-11e5-921b-6f048cff3c2d Version: $LATEST
This is a secret
END RequestId: 13829c9c-9f13-11e5-921b-6f048cff3c2d

最後に

かなり機能は充実してきましたが、まだまだ色々追加する予定です!
良かったら使ってみてフィードバックをいただけると嬉しいです :-)

フィードバックはコチラから↓
https://github.com/marcy-terui/lamvery


  1. Pythonのライブラリ等が隔離された仮想環境を提供するライブラリ
    https://pypi.python.org/pypi/virtualenv 

  2. .pyは素のソース、.pycはコンパイル済みのソース 

  3. ベンチとってないのでアレですが、コンパイルが短縮されるので起動時間がごくわずかですが向上するかも?