カスタムランタイムを使ってLambdaでAWSCLIを動かす

記事タイトルとURLをコピーする

こんにちは、技術一課の加藤です。

先々週ごろにLambda上でAWSCLIを動かしてS3 Syncする – サーバーワークスエンジニアブログというブログを書きました。
この記事ではAWS CLIをローカルインストールしてPythonのコード上から呼び出すという実装をしています

ですがそもそもAWS CLIはCLIコマンドで呼び出すもの。Pythonからサブプロセスを切ってコマンドを叩くのはやや冗長な気がします。

ならbashから直接呼ぶ方が中間層がなくてスマートなのでは…?

ということで、カスタムランタイムを使ってbashから直接AWS CLIを呼び出してみることにしました。

前提

今回はカスタムランタイムでAWS CLIを動かすのが主題なので、処理はAQWS CLIのバージョン出力にとどめます。S3 Syncなどしたい場合は、 aws —version の箇所を任意のコードに変更してください。

またカスタムランタイム自体の説明については、公式をご覧ください。
カスタム AWS Lambda ランタイム - AWS Lambda
簡単に言えば、あらゆる言語をLambdaで動かせるよ、というものになります。

手順

AWS CLIをLayerにおき、ハンドラー関数から呼ぶ構成にします。

  1. bootstrapファイルを作成
  2. bootstrapファイルをLayerとして登録
  3. ハンドラー関数を書いたfunction.shファイルを作成
  4. Lambdaを作成

bootstrapファイルを作成

bootstrapファイルはカスタムランタイムのエントリポイントとなり、必ず用意する必要があるファイルになります。このファイルでAWS CLIのインストールとメイン関数の呼び出しを行います。

#!/bin/sh

# エラー停止の設定
set -euo pipefail
# tmpフォルダ内にawscliを配置するためHOMEとPATHを設定
export HOME="/tmp"
export PATH="$HOME/.local/bin:$PATH"

# pipをインストール
cd /tmp
curl -sSL https://bootstrap.pypa.io/get-pip.py -o get-pip.py
python get-pip.py --user
# pipを用いてawscliをインストール
pip install awscli --user

# ハンドラー関数を読み込み
source $LAMBDA_TASK_ROOT/"$(echo $_HANDLER | cut -d. -f1).sh"

# ループ処理
while true
do
  HEADERS="$(mktemp)"
  # イベントを取得
  EVENT_DATA=$(curl -sS -LD "$HEADERS" -X GET "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next")
    # リクエストIDを取得
  REQUEST_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)

  # ハンドラー関数を実行
  RESPONSE=$($(echo "$_HANDLER" | cut -d. -f2) "$EVENT_DATA")

  # レスポンスを返す
  curl -X POST "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response" -d "$RESPONSE"
done

bootstrapファイルをLayerとして登録

作成したbootstrapファイルをLayerとして登録します。以下はbashのコマンドになります。

$ chmod 755 bootstrap
$ zip awscli.zip bootstrap
$ aws lambda publish-layer-version --layer-name awscli --zip-file fileb://awscli.zip

ハンドラー関数を書いたfunction.shファイルを作成

次に実際にAWS CLIを使って処理を行うハンドラー関数を作成します。
先述の通り今回はバージョン出力のみを行いますが、実際にはS3 Syncなど行いたい処理をここに書いていくことになります。

function handler () {
    # aws --versionの結果をRESPONSEに格納
  RESPONSE=$(aws --version 2>&1)
  echo $RESPONSE
}

Lambdaを作成

ハンドラー関数を作ったので、Lambdaをデプロイしてみます。
Lambdaの実行用ロールを用意しておいてください。

$ chmod 755 function.zip
$ zip function.zip function.zip
$ aws lambda create-function --function-name lambda-cli \
  --handler function.handler \
  --runtime provided \
  --role <RoleArn> \
  --zip-file fileb://function.zip \
  --layers arn:aws:lambda:ap-northeast-1:<AWSAccountID>:layer:awscli:1

では実行してみます。

$ aws lambda invoke --function-name lambda-cli response.txt
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}
$ cat response.txt
aws-cli/1.16.144 Python/2.7.12 Linux/4.14.88-90.76.amzn2.x86_64 botocore/1.12.134

無事、aws cliのバージョンを取得することができました。
他のメソッドも試して遊んでみてください。

AWS コマンドラインインターフェイス

おわりに

前回に引き続き、Lambda上でAWS CLIを呼ぶというややアクロバティックな実装をご紹介しました。

利用場面はかなり限られるとは思いますし、本番稼働するシステムに組み込むのはやや微妙な気がします。
とはいえやってできないものではないことがわかりましたので、もし必要になった場合は参考にしてみてください。