Help us understand the problem. What is going on with this article?

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

More than 3 years have 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
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away