[初学者向け]PythonとTypeScriptを使ってLambdaで始めるaws-cdk

cdk(TypeScript)とPythonを使って、Layerを使ったLambda関数を実装開始するまでの過程をまとめてみました。
2020.04.27

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

cdkを使い始めて、AWSのリソース設定が柔軟に行える魅力を実感し始めました。ですが、複数の言語を利用する場合、動作に問題のないディレクトリ構成が必要となります。

今回はTypeScriptとPythonにて、試行錯誤した結果を備忘録含めて書き出してみました。

ファイル・ディレクトリ構成

作業用ディレクトリルートのappにはビルド用のスクリプト等を置いています。

- app
  ├ Pipfile
  ├ deploy.sh
  ├ cdk/ # cdk用ファイル一式
  └ lambda_function/ # python実装

初期設定

mkdir app/
cd app/
export PIPENV_VENV_IN_PROJECT=1
pipenv --python 3.8
mkdir lambda_function/
mkdir cdk/
cd cdk/
cdk init app --language typescript
cdk bootstrap

pipenvを作業ディレクトリルートで行っていますが、これはPythonでのLambda関数実装ディレクトリとLayer用モジュールのインストールディレクトリの分離を優先したためです。

分離させてLambda関数実装ディレクトリのサイズを抑えることで、管理コンソール上からの確認が行いやすくなります。

Layer用ディレクトリはデプロイ時にのみ作成しているため、ここでは触れません。

サービス関係の実装

詳細なLambda実装を除いて、cdkで収めてしまいました。

npm install @aws-cdk/aws-lambda @aws-cdk/aws-iam

cdk/lib/cdk-stack.ts

import cdk = require( '@aws-cdk/core' );
import lambda = require('@aws-cdk/aws-lambda');
import iam = require('@aws-cdk/aws-iam');
export class CdkStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const executionLambdaRole = new iam.Role(this, 'secureLambdaRole', {
      roleName: 'lambdaSecureExecutionRole',
      assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
      managedPolicies: [
        iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'),
      ]
    });

    const pythonModulesLayer = new lambda.LayerVersion(this, 'moduleLayer', {
      code: lambda.AssetCode.fromAsset('../.lambda_layer'),
      compatibleRuntimes: [lambda.Runtime.PYTHON_3_8],
    });

    const exampleFunction = new lambda.Function(this, 'exampleFunction', {
      functionName: 'example-function',
      runtime: lambda.Runtime.PYTHON_3_8,
      code: lambda.AssetCode.fromAsset('../lambda_function'),
      layers: [pythonModulesLayer],
      handler: 'function.lambda_hundler',
      timeout: cdk.Duration.seconds(900),
      role: executionLambdaRole,
      environment: {
        TZ: "Asia/Tokyo",
      }
    });
  }
}

忘れずに設定しておきたいのはLambdaのtimeout指定です。初期設定では3秒となっているため、込み入った動作の場合は完了しないと思われます。

各プロパティ指定について公式ドキュメントと見比べてみてください。

aws-lambda module · AWS CDK

利用モジュールのインストール

pipenv経由でインストールするだけです。Layer用にはデプロイ時に別途操作を行います。

lambda関数の実装

空の実装にも等しいですが、今回は実装前の準備を重点的に行った結果となります。

lambda_function/function.py

def lambda_hundler(event, context):
    return {
        'status': 'ok'
    }

デプロイ

既にインストールしているモジュールをLayer用ディレクトリに追加します。その後、cdkによるデプロイを行います。

deploy.sh

#!/bin/sh
WORKDIR=$(cd $(dirname $0) && pwd)
rm -rf ${WORKDIR}/.lambda_layer/
mkdir -p ${WORKDIR}/.lambda_layer/python
cd ${WORKDIR}/.lambda_layer
pipenv lock -r > requirements.txt
pip install -r requirements.txt  -t python/
cd ${WORKDIR}/cdk
npm run build
cdk deploy
% sh deploy.sh

WARNING: pip is being invoked by an old script wrapper. This will fail in a future version of pip.
Please see https://github.com/pypa/pip/issues/5599 for advice on fixing the underlying issue.
To avoid this problem you can invoke Python with '-m pip' instead of running pip directly.
DEPRECATION: Python 2.7 reached the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 is no longer maintained. A future version of pip will drop support for Python 2.7. More details about Python 2 support in pip, can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support

> cdk@0.1.0 build /path/to/app/cdk
> tsc

(node:00000) ExperimentalWarning: The fs.promises API is experimental
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 │
├───┼─────────────────────────┼────────┼────────────────┼──────────────────────────────┼───────────┤
│ + │ ${secureLambdaRole.Arn} │ Allow  │ sts:AssumeRole │ Service:lambda.amazonaws.com │           │
└───┴─────────────────────────┴────────┴────────────────┴──────────────────────────────┴───────────┘
IAM Policy Changes
┌───┬─────────────────────┬────────────────────────────────────────────────────────────────────────────────┐
│   │ Resource            │ Managed Policy ARN                                                             │
├───┼─────────────────────┼────────────────────────────────────────────────────────────────────────────────┤
│ + │ ${secureLambdaRole} │ arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole │
└───┴─────────────────────┴────────────────────────────────────────────────────────────────────────────────┘
(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
CdkStack: deploying...
[0%] start: Publishing XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:current
[50%] success: Published XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:current
[50%] start: Publishing YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY:current
[100%] success: Published YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY:current
CdkStack: creating CloudFormation changeset...
 0/5 | 22:11:46 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata        | CDKMetadata
 0/5 | 22:11:47 | CREATE_IN_PROGRESS   | AWS::Lambda::LayerVersion | moduleLayer (moduleLayer00000000)
 0/5 | 22:11:47 | CREATE_IN_PROGRESS   | AWS::IAM::Role            | secureLambdaRole (secureLambdaRole00000000)
 0/5 | 22:11:48 | CREATE_IN_PROGRESS   | AWS::IAM::Role            | secureLambdaRole (secureLambdaRole00000000) Resource creation Initiated
 0/5 | 22:11:49 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata        | CDKMetadata Resource creation Initiated
 1/5 | 22:11:49 | CREATE_COMPLETE      | AWS::CDK::Metadata        | CDKMetadata
 1/5 | 22:11:53 | CREATE_IN_PROGRESS   | AWS::Lambda::LayerVersion | moduleLayer (moduleLayer00000000) Resource creation Initiated
 2/5 | 22:11:53 | CREATE_COMPLETE      | AWS::Lambda::LayerVersion | moduleLayer (moduleLayer00000000)
 3/5 | 22:12:07 | CREATE_COMPLETE      | AWS::IAM::Role            | secureLambdaRole (secureLambdaRole00000000)

 ✅  CdkStack

.lambda_layer以下にpythonのパスを追加しているのは、レイヤーに収めるために以下のいずれかのフォルダが必要なためです。

  • python
  • python/lib/python3.8/site-packages

AWS Lambda レイヤー - AWS Lambda

あとがき

今回cdkを使ってみての大きなメリットは、Layerの追加作業に関して本当に手間が掛からなくなった点でした。

ディレクトリ構成がやや歪に感じていますが、何かしらの設定での対処方法がないか、今後継続して調べてみます。