2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

AWS CDK で Lambda Layers をデプロイする(TypeScript / 複数環境対応)

Posted at

AWS CDKを使ってLambda Layersを複数環境(開発/検証/本番)にデプロイするプロジェクトを実験的に作りました。cdk等のコマンドをShell Scriptでラップして極力シンプルなコマンドでビルドからデプロイまでを実行できるように試行錯誤しています。ちなみに今回は扱っていませんが、Lambda LayersだけでなくVPCやEC2などもデプロイできます。

リポジトリ

42milez / cdk-experiment

機能

  • 複数Lambda Layersのデプロイに対応
  • 複数環境(開発/検証/本番)に対応
  • etc.

必要なもの

とりあえずデプロイしてみる

デモ的なやつです。Lambda Layersを含むリソースをdevelopment環境にデプロイしてみます。なお、AWSアカウント取得済みでNamed Profileも設定済みであることが前提になります。

1. Named Profileの指定

cmd/config.ymlcli.profileキーにNamed Profileを指定します。

2. Dockerイメージ、およびプロジェクトのビルド

docker-compose build
docker-compose run --rm npm install
./cmd/code.sh build --env development

--env オプションは省略可能です。省略された場合は development をデフォルト値として使います。また、環境名はcmd/config.ymlで変更できます。

3. CDKToolkitスタックの作成

./cmd/cdk.sh bootstrap

bootstrapプロセスについて詳しく知りたい場合は、オフィシャルリポジトリのaws-cdk/design/cdk-bootstrap.mdを参照してください。

4. レイヤーを含むスタックのデプロイ

src/stack/service2.tsが対象スタック、src/resource/service2に各リソースの定義ファイルが含まれています。src/functionはLambda関数とレイヤーのソースを含みます。ちなみにsrc/stack/service1.tsは空のスタックです。VPCなどのサンプル定義を書こうと思ってますが、まだ手を付けてません。

./cmd/cdk.sh deploy --env development --stack 'service2'

こんな感じでデプロイが進みます。

❯ ./cmd/cdk.sh deploy --env development --stack 'service2'
Creating cdk-experiment_cdk_run ... done
This deployment will make potentially sensitive changes according to your current security approval level (--require-approval broadening).
Please confirm you intend to make the following modifications:

IAM Statement Changes
┌───┬─────────────────────────────────────────────────┬────────┬──────────────────────────────────────────────────────────┬──────────────────────────────┬───────────┐
│   │ Resource                                        │ Effect │ Action                                                   │ Principal                    │ Condition │
├───┼─────────────────────────────────────────────────┼────────┼──────────────────────────────────────────────────────────┼──────────────────────────────┼───────────┤
│ + │ ${LambdaRole.Arn}                               │ Allow  │ sts:AssumeRole                                           │ Service:lambda.amazonaws.com │           │
├───┼─────────────────────────────────────────────────┼────────┼──────────────────────────────────────────────────────────┼──────────────────────────────┼───────────┤
│ + │ *                                               │ Allow  │ xray:PutTelemetryRecords                                 │ AWS:${LambdaRole}            │           │
│   │                                                 │        │ xray:PutTraceSegments                                    │                              │           │
├───┼─────────────────────────────────────────────────┼────────┼──────────────────────────────────────────────────────────┼──────────────────────────────┼───────────┤
│ + │ arn:aws:logs:${AWS::Region}:${AWS::AccountId}:* │ Allow  │ logs:CreateLogGroup                                      │ AWS:${LambdaRole}            │           │
│   │                                                 │        │ logs:CreateLogStream                                     │                              │           │
│   │                                                 │        │ logs:PutLogEvents                                        │                              │           │
└───┴─────────────────────────────────────────────────┴────────┴──────────────────────────────────────────────────────────┴──────────────────────────────┴───────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)

Do you wish to deploy these changes (y/n)? y
service2-development: deploying...
[0%] start: Publishing b6b4159901c4c58ae071d371127d68a28c9e264a3583c7b77013c805a8917605:current
[33%] success: Published b6b4159901c4c58ae071d371127d68a28c9e264a3583c7b77013c805a8917605:current
[33%] start: Publishing e392921aca9408c62afcbbebef20da8635c2c1fbc99fe88aeb87d991a66a17a3:current
[66%] success: Published e392921aca9408c62afcbbebef20da8635c2c1fbc99fe88aeb87d991a66a17a3:current
[66%] start: Publishing f9caa1f9683d6033e1b15d578f01eec056ec166e9aec850c67456e9691e48fee:current
[100%] success: Published f9caa1f9683d6033e1b15d578f01eec056ec166e9aec850c67456e9691e48fee:current
service2-development: creating CloudFormation changeset...
[██████████████████████████████████████████████████████████] (8/8)






 ✅  service2-development

マネージメントコンソールを確認すると1つのLambda関数と2つのレイヤーが作成されているのがわかります(レイヤーは2つとも関数にアタッチされています)。

lambda.png

5. 他の環境へのデプロイする

staging環境へのデプロイ。staging用のレイヤーを用意する必要があるのでビルドから始めます。

./cmd/code.sh build --env staging
./cmd/cdk.sh deploy --env staging --stack 'service2'

production環境へのデプロイ。こちらもビルドからやります。

./cmd/code.sh build --env production
./cmd/cdk.sh deploy --env production --stack 'service2'

これで全ての環境へリソースがデプロイされました。

解説を少し

Lambda Layersを環境別にデプロイする仕組みがちょっと複雑です。まず、ソースコードを準備します。今回の例だとsrc/function/layerがそれにあたります。開発時はここへ収めたレイヤーへの参照を解決するため、以下のとおりpackage.jsonにレイヤーのパスを含めています。これをnpm installするとnode_modulesにシンボリックリンクが作成されます。

"base-layer-1-cdk-experiment-development": "file:./src/function/layer/base1",
"base-layer-2-cdk-experiment-development": "file:./src/function/layer/base2"

また、Lambda Layersをデプロイする際にはそれ自身のディレクトリ構成が以下のとおりでないといけません。

layer.out
└── development
    ├── base1
    │   └── nodejs
    │       ├── node_modules
    │       ├── package-lock.json
    │       └── package.json
    └── base2
        └── nodejs
            ├── node_modules
            ├── package-lock.json
            └── package.json

base1base2がレイヤーです。nodejsディレクトリの下にnode_modulesディレクトリを含む上記構造でないとLambda関数からレイヤーを参照できません。なので./cmd/code.sh buildの実行時にこの構造になるようにゴニョゴニョします。該当するコードは./cmd/code.shこの部分です。

: 'PREPARE LAMBDA LAYER' &&
{
  readonly LAMBDA_LAYER_SRC_DIR="${PROJECT_ROOT}/src/function/layer"
  readonly LAMBDA_LAYER_DEST_DIR="layer.out/${ENV}"

  printf 'Lambda Layer Source: %s\n' "${LAMBDA_LAYER_SRC_DIR}"
  printf 'Lambda Layer Target: %s\n' "${LAMBDA_LAYER_DEST_DIR}"

  commands=()

  while read -r dir; do
  {
    install_dir="${LAMBDA_LAYER_DEST_DIR}/${dir}/nodejs"
    mkdir -p "${install_dir}"
    cp "${LAMBDA_LAYER_SRC_DIR}/${dir}/package.json" "${install_dir}"

    # add 'dependencies' member if not exists
    if [ "$(jq '.dependencies?' "${install_dir}/package.json")" = 'null' ]; then
    {
      cat < "${install_dir}/package.json"     \
        | jq ". |= .+ {\"dependencies\": {}}" \
        > "${install_dir}/package-tmp.json"   \
      && mv "${install_dir}/package-tmp.json" "${install_dir}/package.json"
    }
    fi

    layer_name=$(jq -r '.name' "${install_dir}/package.json")

    # add self as a dependency
    cat < "${install_dir}/package.json"                                                                \
      | jq ".dependencies |= .+ {\"${layer_name}-${ENV}\": \"../../../../src/function/layer/${dir}\"}" \
      > "${install_dir}/package-tmp.json"                                                              \
    && mv "${install_dir}/package-tmp.json" "${install_dir}/package.json"

    printf '%s\n' "$(cat "${install_dir}/package.json")"

    # install 'dependencies' without 'devDependencies'
    commands+=("npm --prefix ${install_dir} install --production")
  }
  done < <(find "${LAMBDA_LAYER_SRC_DIR}" -mindepth 1 -maxdepth 1 -type d -exec basename {} \;)

  cmd=$(IFS=',' tmp="${commands[*]}" ; echo "${tmp//,/ && }")

  printf 'Commands To Be Executed: %s\n' "${cmd}"

  $BASH_CMD "${cmd}"

  printf '%s\n' "$(find ${LAMBDA_LAYER_DEST_DIR} -maxdepth 3)"
}

ざっくりとした流れは次のとおりです(すべてのレイヤーに対して同様の手続きを繰り返します)。

  1. ターゲットディレクトリにnodejsディレクトリを作成して、さらにsrc/function/layerからpackage.jsonをコピー
  2. コピーしたpackage.jsondependenciesにレイヤー自身を追加(レイヤーをパッケージとしてインストールするため)
  3. npm installでレイヤーをインストール

おわりに

AWSよりもShell Scriptのスキルレベルが上がった気がする :thinking:

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?