Lambda関数をOSXのローカルにてバージョン管理しつつ更新するフローまで組み立てた記録
管理コンソール上で直接作成したLambda関数を、バージョン管理の導入とユニットテスト及びローカルからの更新フローに切り替えたくなった時、大体以下のような課題が出てきます。
- リポジトリへの登録
- AWS上への反映
- テストの実施
構成を出来る限りシンプルにしつつ、手戻り等が発生しないことを心掛けながら、再現性のある手続きを備忘録として残してみました。
環境整理
Python3.6で、以下のライブラリを使います。
ライブラリ名 | 導入方法 |
---|---|
pipenv | Homebrew |
anyenv | Homebrew |
direnv | Homebrew |
tox | pypi |
lambda-uploader | pypi |
pytest | pypi |
ディレクトリ構成
Lambda関数の実装をインターフェイスとモデルで分離する場合は、srcとtests内に配置します。
. |-- Brewfile |-- .gitignore |-- .envrc |-- Pipfile |-- Pipfile.lock |-- README.md |-- function.py |-- lambda.json |-- requirements.txt |-- reset_lambda_json.py |-- src | `-- lib | |-- __init__.py | `-- lib.py |-- tests | |-- conftest.py | |-- fixtures | | `-- event.json | `-- test_lib.py |-- tox.ini |-- update_lambda_json.py `-- upload.sh
gitignore
環境に依存するファイルや一時ディレクトリを記録します。
% vim .gitignore .venv/ .tox/ .envrc *.pyc
ライブラリ導入
pipenvにはdev指定で入れることにより、requirements.txtの更新生成手順を簡潔にします。
% brew install pipenv direnv anyenv % echo 'eval "$(direnv hook zsh)"' >> ~/.zshrc % echo 'eval "$(anyenv init -)"' >> ~/.zshrc % echo 'export PIPENV_VENV_IN_PROJECT=true' >> ~/.zshrc % echo 'eval "$(pipenv --completion)"' >> ~/.zshrc % exec $SHELL -l % pipenv install --python 3.6 % pipenv install --dev python-lambda-local lambda-uploader pytest tox
環境変数設定
個別の環境に依存する変数も想定して、コミットせずにローカル管理します。
3行目に追加しているsourceによって、作業ディレクトリへ移動すると同時に仮想環境を起動しています。
% direnv edit . export AWS_PROFILE=xxxxx export XXX=xxx source .venv/bin/activate
テスト環境設定
direnvで設定した環境変数でテスト時に使いたいものがある場合はpassenvに追加指定します。
% vim tox.ini [tox] skipsdist = True [testenv] passenv = AWS_PROFILE deps = pipenv setenv = PYTHONPATH = {toxinidir}/src PIPENV_VERBOSITY = -1 commands = pipenv sync --dev pipenv run py.test tests/ --pdb
Lambda更新用手続き
lambda-uploader実行の為の設定を行います。基本的にはlambda.jsonに追記するだけです。
ただし、環境変数に関してはdirenvの内容を反映しつつもコミットを防止するため、更新時するタイミングでlambda.jsonへ反映し、更新が終わったら反映を元に戻します。
function.pyへの移植
管理コンソール上で直接書いていた関数の内容を移植します。
後は必要に応じて実装を分離してlib以下に配置します。
lambda.jsonへの設定
コンソール上に反映したくないファイルをignoreセクションに追加します。多少追加もれがあるかもしれません。
{ "name": "<function name>", "description": "function", "region": "ap-northeast-1", "runtime": "python3.6", "handler": "function.lambda_handler", "role": "arn:aws:iam::00000000000:role/lambda_basic_execution", "ignore": [ "lambda_function\.zip$", "circle\.yml$", "\.git$", "\.gitignore$", "/.*\.pyc$", "Pipfile$", "Pipfile\.lock$", "tests$", "\.envrc$", "tox\.ini$", "upload_lamdba_json\.py$", "reset_lamdba_json\.py$", "\.tox$" "\.venv$" ], "timeout": 300, "memory": 128, "variables": {} }
アップロード処理
極力環境変数を引数に渡さないで実行可能にします。MFA設定済みのアカウントの場合は毎回認証が発生します。
% vim upload.sh direnv allow . #本来想定している環境変数で上書きする rm -rf .tox/ #テスト用の一時ディレクトリが存在する場合、消滅したファイルを参照しにいくケースがあったため rm -rf .lambda_uploader_temp/ #前回のアップロード用一時ファイルを参照しようとして、エラーになるケースがあったため pipenv lock -r > requirements.txt python update_lambda_json.py #lambda.jsonに環境変数を一時的に書き加える lambda-uploader python reset_lambda_json.py #lambda.jsonに加えた一時的な更新を元に戻す
MFA設定
AWSアカウントに二段階認証(MFA)を設定済みの場合は、configにmfa_serialを忘れずに設定します。
% vim ~/.aws/config [profile XXXX] mfa_serial = arn:aws:iam::000000000000000000:mfa/XXXXXXXXXXXXXXXX ...
upload_lambda_json.py
lamdba.jsonの環境変数指定部分を一時的に書き換えるためのバッチです。
import json import os envs = ['XXXX', 'LINE_CHANNEL_ACCESS_TOKEN', 'LINE_CHANNEL_SECRET'] dir_path = os.path.dirname(__file__) json_data = None with open(os.path.join(dir_path, 'lambda.json'), 'r') as r: json_data = json.load(r) variables = dict() for env in envs: variables.update({env: os.environ[env]}) json_data['variables'] = variables with open(os.path.join(dir_path, 'lambda.json'), 'w') as w: json.dump(json_data, w, indent=4)
reset_lambda_json.py
lambda.jsonで書き換えた環境変数指定部分をリセットするためのバッチです。
import json import os dir_path = os.path.dirname(__file__) json_data = None with open(os.path.join(dir_path, 'lambda.json'), 'r') as r: json_data = json.load(r) json_data['variables'] = dict() with open(os.path.join(dir_path, 'lambda.json'), 'w') as w: json.dump(json_data, w, indent=4)
各フェイスでの操作
環境変数等の指定をdirenvに極力寄せたため、単純にコマンドを実行するだけになります。
テスト
テストで失敗するとデバッガが作動します。
tox
更新
必要に応じてワンタイムトークンを表示するデバイスも準備してください。
sh upload.sh
まとめ
開発とテストが極力繰り返しやすくなるような設計にしてみました。特に環境変数指定で悩まされることがなくなるはずです。
あくまでも一例ですが、Lambda関数等の開発で参考になれば幸いです。