AWS Lambda Layersを他アカウントと共有する #reinvent

re:Invent 2018で発表されたLambda Layersを利用し、共通のコンポーネントを集中的に管理し、他アカウントのLambda関数から利用する方法を紹介します。
2018.12.13

はじめに

re:Invent 2018で発表されたAWS Lambda Layersを利用すると、複数のLambda関数が利用するような共通ライブラリを外出しして管理出来ます。

さらに、外出したレイヤーは、他のAWSアカウントからも利用出来ます。

今回は、作成したLambda Layerを他のAWSアカウントと共有する方法を紹介します。

ポイント

  • Layerのバージョンごとに共有設定が必要
  • 共有アカウントの粒度は個別・組織・全アカウントなど柔軟
  • リージョンをまたいだ共有はできない

作業の流れ

以下のステップでLambda Layerを他アカウントと共有します

  1. Lambda Layerの登録
  2. 同じアカウントのLambda関数からLayerを利用
  3. Layerを他アカウントと共有
  4. 他アカウントのLambda関数からLayerを利用
  5. 共有設定を除外した場合の動作

Lambda のランタイムには Python 3.7 を利用し、Lambda Layer には requests ライブラリを利用します。

1. Lambda Layerの登録

まずはrequestsをLambda Layer向けにパッケージングします。

ライブラリのバージョン情報をlayerのdescriptionに付与します。

$ pip install -t python requests==2.21.0
$ zip -r python.zip python
$ aws lambda publish-layer-version \
  --layer-name requests \
  --description "requests 2.21.0"
  --zip-file fileb://python.zip \
  --compatible-runtimes python3.7
{
    "Content": {
        "Location": "https://awslambda-eu-cent-1-layers.s3.eu-central-1.amazonaws.com/snapshots/...",
        "CodeSha256": "e72w5zIow1ua4WJ+jYDSJEgQZjVUudlBkCV8s7lIQIk=",
        "CodeSize": 903621
    },
    "LayerArn": "arn:aws:lambda:eu-central-1:123456789012:layer:requests",
    "LayerVersionArn": "arn:aws:lambda:eu-central-1:123456789012:layer:requests:1",
    "Description": "requests 2.20.0",
    "CreatedDate": "2018-12-12T15:37:49.027+0000",
    "Version": 1,
    "CompatibleRuntimes": [
        "python3.7"
    ]
}

Lambda関数からLambda Layerを利用する際には、LayerVersionArnを利用します。 このARNを控えておいてください。

2. 同じアカウントのLambda関数からLayerを利用

Layerを利用することで requests ライブラリが利用できるようになることを確認します。

requestsライブラリを利用する関数を用意します。

test.py

import requests

def lambda_handler(event, context):
    return 'ok'

Layer呼び出しに成功すると、インポート文も成功するため、Lambdaの実行が成功します。

レイヤー利用前

Layer を利用せずにLambda関数を登録します。

$ zip test.zip test.py
$ aws lambda create-function \
    --function-name test \
    --runtime python3.7 \
    --role arn:aws:iam::123456789012:role/lambda_basic_execution \
    --handler test.lambda_handler \
    --zip-file fileb://test.zip
{
    "FunctionName": "test",
    "FunctionArn": "arn:aws:lambda:eu-central-1:123456789012:function:test",
    "Runtime": "python3.7",
    "Role": "arn:aws:iam::123456789012:role/lambda_basic_execution",
    "Handler": "test.lambda_handler",
    "CodeSize": 271,
    "Description": "",
    "Timeout": 3,
    "MemorySize": 128,
    "LastModified": "2018-12-12T15:24:39.238+0000",
    "CodeSha256": "AdDE1ghNn14icmCWkTemCVVVpRd1ZU1NXFbKnMuzVEA=",
    "Version": "$LATEST",
    "TracingConfig": {
        "Mode": "PassThrough"
    },
    "RevisionId": "8532d2d6-d607-4a15-a7d3-cd6d1d4f0297"
}

Lambda関数を実行すると、requestsライブラリをインポート出来ないため、エラーになります。

$ aws lambda invoke --function-name test output.txt
{
    "StatusCode": 200,
    "FunctionError": "Unhandled",
    "ExecutedVersion": "$LATEST"
}
$ cat output.txt
{"errorMessage": "Unable to import module 'test'"}

レイヤー利用後

次にrequestsライブラリのLayerを利用するようにLambda関数を更新します。

--layers引数でLayerを指定します。

$ aws lambda update-function-configuration \
  --function-name test \
  --layers arn:aws:lambda:eu-central-1:123456789012:layer:requests:1
{
    ...
    "Layers": [
        {
            "Arn": "arn:aws:lambda:eu-central-1:123456789012:layer:requests:1",
            "CodeSize": 903621
        }
    ]
}

Lambda 関数を再実行します。

$ aws lambda invoke --function-name test output.txt
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}

$ cat output.txt
"ok"

Layer経由でrequestsライブラリを読み込めたため、Lambdaの実行が成功しました。

3. Layerを他アカウントと共有

本題です。

Lambda Layerを他アカウントと共有します。

共有設定は (レイヤー, バージョン) の組み合わせごとに指定が必要です。 つまり、レイヤーを更新するたびに、共有設定も再設定が必要です。

また、この組み合わせに対して、複数の共有設定を指定出来ます。

例えば、

  • Layer名 : requests
  • Layerバージョン : 1

  • AWSアカウント : 111111111111

から利用できるようにする場合、以下の様に指定します。

$ aws lambda add-layer-version-permission \
 --layer-name requests \
 --statement-id share-with-dev \
 --version-number 1 \
 --principal 111111111111 \
 --action lambda:GetLayerVersion
{
    "Statement": "{\"Sid\":\"share-with-dev\",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"arn:aws:iam::111111111111:root\"},\"Action\":\"lambda:GetLayerVersion\",\"Resource\":\"arn:aws:lambda:eu-central-1:123456789012:layer:requests:1\"}",
    "RevisionId": "01285c2c-3be3-4279-9d5b-8ea45654431a"
}

ポリシー部分を見やすいように整形します。

policy.json

{
  "Sid": "share-with-dev",
  "Effect": "Allow",
  "Principal": {
    "AWS": "arn:aws:iam::111111111111:root"
  },
  "Action": "lambda:GetLayerVersion",
  "Resource": "arn:aws:lambda:eu-central-1:123456789012:layer:requests:1"
}

共有先AWSアカウント(Principal)が、Lambda Layer(Resource) を lambda:GetLayerVersion を実行できるように許可しています。

共有先アカウントの指定例

lambda:AddLayerVersionPermission 実行時の共有先アカウントの指定例をいくつか紹介します。

共有先アカウントが一つの場合

--principal 111111111111

ある組織配下の全アカウント

--organization-id o-1234567890 \
--principal '*'

任意のアカウント

--principal '*'

4. 他アカウントのLambda関数からLayerを利用

Layerの共有先アカウントから Layer を呼び出してみます。

ステップ2と同じように Lambda 関数を登録します。

--layers 引数には、共有しているLayerのARNを指定します。

$ aws lambda create-function \
    --function-name test \
    --runtime python3.7 \
    --role arn:aws:iam::111111111111:role/ROLE \
    --handler test.lambda_handler \
    --zip-file fileb://test.zip \
    --layers arn:aws:lambda:eu-central-1:123456789012:layer:requests:1
{
    "FunctionName": "test",
    ...
    "Layers": [
        {
            "Arn": "arn:aws:lambda:eu-central-1:123456789012:layer:requests:1",
            "CodeSize": 903621
        }
    ]
}

Layer を呼び出せることを確認します。

$ aws lambda invoke --function-name test output.txt
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}
$ cat output.txt
"ok"

無事成功しています。

Lambda関数登録時、 - Layerが共有されていない - LayerとLambda関数のリージョンが違う 場合、以下の様なエラーが発生します。

$ aws lambda create-function \
  ...
An error occurred (AccessDeniedException) when calling the CreateFunction operation: User: arn:aws:sts::XXX is not authorized to perform: lambda:GetLayerVersion on resource: arn:aws:lambda:eu-central-1:共有元AWS-ACCOUNT-ID:layer:requests:1

5. 共有設定を除外した場合の動作

Lambda Layer の共有を解除すると、共有Layerを利用しているLambda関数への影響はどうなるでしょうか?

動作確認する限りでは、Lambda はLayerを利用可能でした。

共有設定を解除

共有設定を解除するには remove-layer-version-permission API を呼び出します。

$ aws lambda remove-layer-version-permission \
  --layer-name requests \
  --version-number 1 \
  --statement-id share-with-dev
$

共有解除後に Lambda 関数を実行すると、正常実行されました。

Layer 共有時にアクション lambda:GetLayerVersion を許可することから、Layer を利用する Lambda 関数を登録するタイミングで Layer も一緒にコピーされ、コピー後のLayerの共有設定の変更はLambdaの実行には影響がないものと思われます。

最後に

Lambda Layerを他のAWSアカウントと共有する方法を紹介しました。

他アカウントとレイヤーを共有することで

  • 本番・開発でAWSアカウントを分割。開発環境でレイヤーのバージョンアップを試験。動作確認の取れた新バージョンを本番アカウントでも利用
  • Lambda 関数の付随サービスを提供。レイヤーをインポートすれば利用できるように全アカウント向けに公開

といったことが可能になります。

  • 複数アカウントでサービス開発しているユーザー
  • 複数アカウントにサービス提供したいユーザー

どちらにとっても便利な機能ではないでしょうか。

それでは。

参考