Python
AWS
virtualenv
lambda

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

More than 1 year has passed since last update.

こちらは今年もやるよ!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. ベンチとってないのでアレですが、コンパイルが短縮されるので起動時間がごくわずかですが向上するかも?