Edited at

apex で作成した Lambda Function を docker-lambda 上で動作させるシェルスクリプト

More than 1 year has passed since last update.


目的

apex で作成した Lambda function (python 2.7) をapex deploy

前に docker-lambda コンテナ上で動作確認するためのスクリプトを作る。


必要なもの(前提)



  • apex


    • AWS Lambda のコードを apex で管理しているものとする




  • docker



  • jq


  • AWS CLI の実行環境設定



    • AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY

    • 環境変数にセット済み




動機

職場で運用中のAWSサービスの状態通知を行うため、AWS Lambda を活用しています。

Lambda function の作成/デプロイには apex を使用していますが、以下のように考えました。



  1. apex deploy 前に, apex で作成した Lambda function の 動作テストを行いたい

  2. AWS Lambda の動作環境を複製した docker-lambda イメージがある


  3. docker-lambda コンテナ上で動作させ、通知先サービス(Slack等) に通知テストができそう

  4. どの function 向けにも使えるよう, docker-lambda コンテナを実行するスクリプトを書けば良いのでは?


作成

docker-lambda の Project Page にある Example を参考に, まず以下のように作成。


docker-lambda.sh(修正前)

#!/bin/bash

SAMPLE_MESSAGE=`cat sample/test.json` # json形式のテストメッセージ

docker run --rm \
-e AWS_ACCESS_KEY_ID \
-e AWS_SECRET_ACCESS_KEY \
-e SLACK_OPS_CHANNEL="#channel-name" \
-e SLACK_HOOK_URL="https://hooks.slack.com/services/xxx/xxx/xxx" \
-v $(pwd):/var/task lambci/lambda:python2.7 main.handle $SAMPLE_MESSAGE


-e オプションで環境変数を docker-lambda コンテナへ渡していますが、 apex では

AWS Lambda へ渡す環境変数を function.json で既に持っています。


function.json

{

"description": "Notify the state of the instance to Slack",
"timeout": 10,
"memory": 128,
"role": "arn:aws:iam::xxxxxxxxxxxx:role/LambdaEC2Readonly",
"environment": {
"SLACK_OPS_CHANNEL": "#channel-name",
"SLACK_HOOK_URL": "https://hooks.slack.com/services/xxx/xxx/xxxxxxxx"
}
}

これを読み込むため、今回は jq を使って シェルスクリプトでdocker に渡す ことにしました。


jq の処理

参考にしたのはこちらの記事です。

まずは実行結果から。


実行結果

% jq -r '.environment | to_entries | map("\(.key)=\(.value)") | join("\n")' function.json

SLACK_OPS_CHANNEL=#channel-name
SLACK_HOOK_URL=https://hooks.slack.com/services/xxx/xxx/xxxxxxxx


オプション解説

次に引数指定したオプションを、順を追って見ていきます。



  • .environment ... json の environment キーの値を読み込み


部分実行その1

% jq -r '.environment' function.json

{
"SLACK_OPS_CHANNEL": "#channel-name",
"SLACK_HOOK_URL": "https://hooks.slack.com/services/xxx/xxx/xxxxxxxx"
}



  • to_entries ... key, value の各値を持った ハッシュ配列に変換


部分実行その2

% jq -r '.environment | to_entries' function.json

[
{
"key": "SLACK_OPS_CHANNEL",
"value": "#channel-name"
},
{
"key": "SLACK_HOOK_URL",
"value": "https://hooks.slack.com/services/xxx/xxx/xxxxxxxx"
}
]



  • map("\(.key)=\(.value)") ... ハッシュ配列を .key=.value 形式の配列に変換


部分実行その3

% jq -r '.environment | to_entries | map("\(.key)=\(.value)")' function.json

[
"SLACK_OPS_CHANNEL=#channel-name",
"SLACK_HOOK_URL=https://hooks.slack.com/services/xxx/xxx/xxxxxxxx"
]



  • join("\n") ... 配列を改行コード区切りの文字列に変換


部分実行その4(実行結果に同じ)

% jq -r '.environment | to_entries | map("\(.key)=\(.value)") | join("\n")' function.json

SLACK_OPS_CHANNEL=#channel-name
SLACK_HOOK_URL=https://hooks.slack.com/services/xxx/xxx/xxxxxxxx

変換後の文字列は、スクリプトでの変数宣言と同じ形にしました。

これをファイルにリダイレクトすると、 後述の方法で docker から読み込むことができます。


docker --env-file オプション

先ほどの jq 実行結果をさらに、 .tmp.list にリダイレクトしておきます。

% jq -r '~(中略)~' function.json > .tmp.list


.tmp.list

SLACK_OPS_CHANNEL=#channel-name

SLACK_HOOK_URL=https://hooks.slack.com/services/xxx/xxx/xxxxxxxx

docker--env-file オプションを使うことで、 .tmp.list 内の文字列を、 dockerの環境変数として 読み込むことができます。


オプション指定例

% MESSAGE=`cat sample/test.json` && docker run --rm \

-e AWS_ACCESS_KEY_ID \
-e AWS_SECRET_ACCESS_KEY \
--env-file .tmp.list \
-v $
(pwd):/var/task lambci/lambda:python2.7 main.handle $MESSAGE

※便宜上、メッセージデータとなるjson を環境変数に入れる処理を行っています

詳しくは以下のドキュメントをご覧ください。


シェルスクリプト修正後

これらを踏まえて、最終的に以下のような形になりました。


docker-lambda.sh(修正後)


#!/bin/bash

set -e

cd $(dirname "$0")

create_env_file() {
local env_json="function.json"

jq -r '.environment | to_entries | map("\(.key)=\(.value)") | join("\n")' $env_json > .tmp.list
}

remove_env_file() {
rm .tmp.list
}

create_env_file

docker run --rm \
-e AWS_ACCESS_KEY_ID \
-e AWS_SECRET_ACCESS_KEY \
--env-file .tmp.list \
-v $(pwd):/var/task lambci/lambda:python2.7 main.handle "$@"

remove_env_file



実行例

目的の apex ソースディレクトリ内に docker-lambda.sh を置いて、以下のように実行してください。

% MESSAGE=`cat ./sample_messages/test.json` && ./docker-lambda.sh $MESSAGE