こんにちは、CX 事業本部 Delivery 部の若槻です。
今回は、AWS CDK(TypeScript)で Python ランタイムの Lambda 関数を実装する方法を確認してみます。
使用したのは Amazon Lambda Python Library です。現在は Alpha 版となります。
このライブラリの PythonFunction
を使うことにより、NodejsFunctionと同様に、モジュールのパッケージングをよしなにやってくれます。
試してみた
AWS CDK(TypeScript)で Python ランタイムの Lambda 関数を実装してみます。
プロジェクト作成
CDK プロジェクトを初回作成します。言語は TypeScript です。(Python ではありません)
mkdir cdk_demo_project
cd cdk_demo_project
cdk init --language typescript
Amazon Lambda Python Library 導入
Amazon Lambda Python Library をインストールします。
npm i -D @aws-cdk/aws-lambda-python-alpha
Lambda 関数および CDK コード
Lambda 関数を配置するディレクトリを作成します。
mkdir src & mkdir src/lambda & mkdir src/lambda/hello
touch src/lambda/hello/index.py
次の内容の Lambda 関数を作成します。
src/lambda/hello/index.py
def handler(event, context):
return {
'statusCode': 200,
'body': 'Hello, CDK!'
}
PythonFunction
を使って Lambda 関数を CDK コードで実装します。
lib/cdk_demo_project-stack.ts
import { aws_lambda, Stack, StackProps } from 'aws-cdk-lib';
import { PythonFunction } from '@aws-cdk/aws-lambda-python-alpha';
import { Construct } from 'constructs';
export class CdkDemoProjectStack extends Stack {
constructor(scope: Construct, id: string, props: StackProps) {
super(scope, id, props);
new PythonFunction(this, 'helloFunction', {
functionName: 'helloFunction',
runtime: aws_lambda.Runtime.PYTHON_3_11,
entry: 'src/lambda/hello',
handler: 'handler',
});
}
}
デプロイ
さてここから CDK Deploy により Lambda 関数をデプロイしていくのですが、PythonFunction で Lambda 関数をビルドする上で必要な Docker 周りでかなりの試行錯誤があったので、その記録を残しておきます。
CDK Deploy をしようとすると Synth でエラーとなりました。ここで Docker の起動が必要であることに気が付きます。
$ cdk deploy
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/aws-cdk-lib/core/lib/private/asset-staging.js:2
`).map((line,idx)=>`${idx===0?firstLine:padding}${line}`)};const reason=proc.signal!=null?`signal ${proc.signal}`:`status ${proc.status}`,command=[prog,...args.map(arg=>/[^a-z0-9_-]/i.test(arg)?JSON.stringify(arg):arg)].join(" ");throw new Error([`${prog} exited with ${reason}`,...prependLines("--> STDOUT: ",proc.stdout)??[],...prependLines("--> STDERR: ",proc.stderr)??[],`--> Command: ${command}`].join(`
^
Error: docker exited with status 1
--> Command: docker build -t cdk-ab3de628ee1e2f62b9079f422a090e9043f44ecd2ffb1c3a0c647e07830330bd --platform "linux/amd64" --build-arg "IMAGE=public.ecr.aws/sam/build-python3.11" "/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/@aws-cdk/aws-lambda-python-alpha/lib"
at dockerExec (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/aws-cdk-lib/core/lib/private/asset-staging.js:2:237)
at Function.fromBuild (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/aws-cdk-lib/core/lib/bundling.js:1:4124)
at new Bundling (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/@aws-cdk/aws-lambda-python-alpha/lib/bundling.ts:100:39)
at Function.bundle (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/@aws-cdk/aws-lambda-python-alpha/lib/bundling.ts:61:44)
at new PythonFunction (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/@aws-cdk/aws-lambda-python-alpha/lib/function.ts:75:22)
at new CdkDemoProjectStack (/Users/wakatsuki.ryuta/projects/cdk_demo_project/lib/cdk_demo_project-stack.ts:9:5)
at Object.<anonymous> (/Users/wakatsuki.ryuta/projects/cdk_demo_project/bin/cdk_demo_project-stack.ts:6:1)
at Module._compile (node:internal/modules/cjs/loader:1105:14)
at Module.m._compile (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/ts-node/src/index.ts:1618:23)
at Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
Subprocess exited with error 1
Docker を起動させます。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
するとエラーが変わりました。
$ cdk deploy
[+] Building 0.3s (3/3) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 1.28kB 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> ERROR [internal] load metadata for public.ecr.aws/sam/build-python3.11:latest 0.3s
------
> [internal] load metadata for public.ecr.aws/sam/build-python3.11:latest:
------
failed to solve with frontend dockerfile.v0: failed to create LLB definition: failed to do request: Head "https://public.ecr.aws/v2/sam/build-python3.11/manifests/latest": Failed to lookup host: public.ecr.aws
/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/aws-cdk-lib/core/lib/private/asset-staging.js:2
`).map((line,idx)=>`${idx===0?firstLine:padding}${line}`)};const reason=proc.signal!=null?`signal ${proc.signal}`:`status ${proc.status}`,command=[prog,...args.map(arg=>/[^a-z0-9_-]/i.test(arg)?JSON.stringify(arg):arg)].join(" ");throw new Error([`${prog} exited with ${reason}`,...prependLines("--> STDOUT: ",proc.stdout)??[],...prependLines("--> STDERR: ",proc.stderr)??[],`--> Command: ${command}`].join(`
^
Error: docker exited with status 1
--> Command: docker build -t cdk-ab3de628ee1e2f62b9079f422a090e9043f44ecd2ffb1c3a0c647e07830330bd --platform "linux/amd64" --build-arg "IMAGE=public.ecr.aws/sam/build-python3.11" "/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/@aws-cdk/aws-lambda-python-alpha/lib"
at dockerExec (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/aws-cdk-lib/core/lib/private/asset-staging.js:2:237)
at Function.fromBuild (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/aws-cdk-lib/core/lib/bundling.js:1:4124)
at new Bundling (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/@aws-cdk/aws-lambda-python-alpha/lib/bundling.ts:100:39)
at Function.bundle (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/@aws-cdk/aws-lambda-python-alpha/lib/bundling.ts:61:44)
at new PythonFunction (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/@aws-cdk/aws-lambda-python-alpha/lib/function.ts:75:22)
at new CdkDemoProjectStack (/Users/wakatsuki.ryuta/projects/cdk_demo_project/lib/cdk_demo_project-stack.ts:9:5)
at Object.<anonymous> (/Users/wakatsuki.ryuta/projects/cdk_demo_project/bin/cdk_demo_project-stack.ts:6:1)
at Module._compile (node:internal/modules/cjs/loader:1105:14)
at Module.m._compile (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/ts-node/src/index.ts:1618:23)
at Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
Subprocess exited with error 1
ここで Docker Desktop のバージョンが古いことに気が付いたのでアップグレードします。するとさらにエラーが変わりました。
$ cdk deploy
[+] Building 2.2s (4/4) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 37B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> ERROR [internal] load metadata for public.ecr.aws/sam/build-python3.11:latest 2.1s
=> [auth] aws:: sam/build-python3.11:pull token for public.ecr.aws 0.0s
------
> [internal] load metadata for public.ecr.aws/sam/build-python3.11:latest:
------
failed to solve with frontend dockerfile.v0: failed to create LLB definition: unexpected status code [manifests latest]: 403 Forbidden
/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/aws-cdk-lib/core/lib/private/asset-staging.js:2
`).map((line,idx)=>`${idx===0?firstLine:padding}${line}`)};const reason=proc.signal!=null?`signal ${proc.signal}`:`status ${proc.status}`,command=[prog,...args.map(arg=>/[^a-z0-9_-]/i.test(arg)?JSON.stringify(arg):arg)].join(" ");throw new Error([`${prog} exited with ${reason}`,...prependLines("--> STDOUT: ",proc.stdout)??[],...prependLines("--> STDERR: ",proc.stderr)??[],`--> Command: ${command}`].join(`
^
Error: docker exited with status 1
--> Command: docker build -t cdk-ab3de628ee1e2f62b9079f422a090e9043f44ecd2ffb1c3a0c647e07830330bd --platform "linux/amd64" --build-arg "IMAGE=public.ecr.aws/sam/build-python3.11" "/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/@aws-cdk/aws-lambda-python-alpha/lib"
at dockerExec (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/aws-cdk-lib/core/lib/private/asset-staging.js:2:237)
at Function.fromBuild (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/aws-cdk-lib/core/lib/bundling.js:1:4124)
at new Bundling (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/@aws-cdk/aws-lambda-python-alpha/lib/bundling.ts:100:39)
at Function.bundle (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/@aws-cdk/aws-lambda-python-alpha/lib/bundling.ts:61:44)
at new PythonFunction (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/@aws-cdk/aws-lambda-python-alpha/lib/function.ts:75:22)
at new CdkDemoProjectStack (/Users/wakatsuki.ryuta/projects/cdk_demo_project/lib/cdk_demo_project-stack.ts:9:5)
at Object.<anonymous> (/Users/wakatsuki.ryuta/projects/cdk_demo_project/bin/cdk_demo_project-stack.ts:6:1)
at Module._compile (node:internal/modules/cjs/loader:1105:14)
at Module.m._compile (/Users/wakatsuki.ryuta/projects/cdk_demo_project/node_modules/ts-node/src/index.ts:1618:23)
at Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
Subprocess exited with error 1
このエラーは下記が参考になりました。
Docker で ECR にログインをします。
aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws
すると Synth が通り、デプロイまで行えました。
$ cdk deploy
[+] Building 135.5s (7/7) FINISHED docker:desktop-linux
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 1.28kB 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for public.ecr.aws/sam/build-python3.11:latest 2.4s
=> [auth] aws:: sam/build-python3.11:pull token for public.ecr.aws 0.0s
=> [1/2] FROM public.ecr.aws/sam/build-python3.11@sha256:ad08c7e702a7c2b5c13c60de6180ca4bc97be781a72063a618784596cbda65c6 32.9s
=> => resolve public.ecr.aws/sam/build-python3.11@sha256:ad08c7e702a7c2b5c13c60de6180ca4bc97be781a72063a618784596cbda65c6 0.0s
=> => sha256:10f153b3bd74382059f5423e00bb91447ae450a3b566ac8dd34b9072ac49dcf8 2.64kB / 2.64kB 0.0s
=> => sha256:29a91f87d93088b3ae82542459d827edd30a0bd0d1c2709f2e53982c258cb8b6 87.36kB / 87.36kB 0.2s
=> => sha256:877e2bfc6ba89a3fc51d12d92799e17d3ec8afefbf7cc868de9e35d078b8faa9 417B / 417B 0.3s
=> => sha256:ad08c7e702a7c2b5c13c60de6180ca4bc97be781a72063a618784596cbda65c6 772B / 772B 0.0s
=> => sha256:79a77e7c1be9a2c4f77ead609e8d8b7162377bb6905b2a244c7964d74d8c8762 2.51MB / 2.51MB 1.4s
=> => sha256:0e9b5ea721abee27f3e34c651182ec82103b32067ef6858f5d432d6b10c10099 7.04kB / 7.04kB 0.0s
=> => extracting sha256:29a91f87d93088b3ae82542459d827edd30a0bd0d1c2709f2e53982c258cb8b6 0.0s
=> => sha256:c091401fd8a0aa9d9e34453038b9e08154298a521f3f460a3f0bb2b946e4629d 128.93MB / 128.93MB 6.5s
=> => extracting sha256:877e2bfc6ba89a3fc51d12d92799e17d3ec8afefbf7cc868de9e35d078b8faa9 0.0s
=> => sha256:2e3380a3fb2644cc0f902d3001f7d8235102e976431959b3ecdcd7d9b947378f 15.31kB / 15.31kB 0.7s
=> => sha256:8ceec474a49056b91a037e8617c75feac54d4f014219d0ab05cfb0854f06f171 224.66MB / 224.66MB 13.6s
=> => extracting sha256:79a77e7c1be9a2c4f77ead609e8d8b7162377bb6905b2a244c7964d74d8c8762 0.0s
=> => sha256:4b67a36e95c8b60c0373e05feeba88d4875956eac55167b880a8c838cc6f0e14 55.82MB / 55.82MB 11.9s
=> => sha256:0344a919057170918f8b5889a6c39857e0776683651910a7769bcb0cc283dc73 79.85MB / 79.85MB 15.0s
=> => extracting sha256:c091401fd8a0aa9d9e34453038b9e08154298a521f3f460a3f0bb2b946e4629d 5.5s
=> => sha256:fcf86f09cae9b81c5101817003e642f3262b3787a61f1bff6f0ac052032940e7 205.46kB / 205.46kB 12.2s
=> => sha256:1ac678504b71b45f9cfa05f7a25ae9510e1c2cb432fdcac3a59d58c94c7ad10e 103.92kB / 103.92kB 12.4s
=> => extracting sha256:2e3380a3fb2644cc0f902d3001f7d8235102e976431959b3ecdcd7d9b947378f 0.0s
=> => extracting sha256:8ceec474a49056b91a037e8617c75feac54d4f014219d0ab05cfb0854f06f171 10.6s
=> => extracting sha256:4b67a36e95c8b60c0373e05feeba88d4875956eac55167b880a8c838cc6f0e14 2.3s
=> => extracting sha256:0344a919057170918f8b5889a6c39857e0776683651910a7769bcb0cc283dc73 4.7s
=> => extracting sha256:fcf86f09cae9b81c5101817003e642f3262b3787a61f1bff6f0ac052032940e7 0.0s
=> => extracting sha256:1ac678504b71b45f9cfa05f7a25ae9510e1c2cb432fdcac3a59d58c94c7ad10e 0.0s
=> [2/2] RUN python -m venv /usr/app/venv && mkdir /tmp/pip-cache && chmod -R 777 /tmp/pip-cache && pip install --upgrade pip && mkdir /tmp/poetry-cache && chmod -R 777 /tmp/po 99.7s
=> exporting to image 0.5s
=> => exporting layers 0.5s
=> => writing image sha256:392b68bf00a6d9cee956f0a5322492f8853b9f35d6f8678fbc47591504c80a38 0.0s
=> => naming to docker.io/library/cdk-ab3de628ee1e2f62b9079f422a090e9043f44ecd2ffb1c3a0c647e07830330bd 0.0s
What's Next?
View summary of image vulnerabilities and recommendations → docker scout quickview
Bundling asset CdkDemoProjectStack/helloFunction/Code/Stage...
WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested
sending incremental file list
index.py
sent 217 bytes received 35 bytes 504.00 bytes/sec
total size is 105 speedup is 0.42
✨ Synthesis time: 139.39s
CdkDemoProjectStack: start: Building 8efbea89c33394485407976945c492b1e7e99ef18a4c8c8544f234f2e5490a7d:current_account-current_region
CdkDemoProjectStack: success: Built 8efbea89c33394485407976945c492b1e7e99ef18a4c8c8544f234f2e5490a7d:current_account-current_region
CdkDemoProjectStack: start: Building 603ed967932afd7da0b3109bd50ddbcea827870adcdc7e1bf0ed9bb0e738d776:current_account-current_region
CdkDemoProjectStack: success: Built 603ed967932afd7da0b3109bd50ddbcea827870adcdc7e1bf0ed9bb0e738d776:current_account-current_region
CdkDemoProjectStack: start: Publishing 8efbea89c33394485407976945c492b1e7e99ef18a4c8c8544f234f2e5490a7d:current_account-current_region
CdkDemoProjectStack: start: Publishing 603ed967932afd7da0b3109bd50ddbcea827870adcdc7e1bf0ed9bb0e738d776:current_account-current_region
CdkDemoProjectStack: success: Published 603ed967932afd7da0b3109bd50ddbcea827870adcdc7e1bf0ed9bb0e738d776:current_account-current_region
CdkDemoProjectStack: success: Published 8efbea89c33394485407976945c492b1e7e99ef18a4c8c8544f234f2e5490a7d:current_account-current_region
CdkDemoProjectStack: deploying... [1/1]
CdkDemoProjectStack: creating CloudFormation changeset...
✅ CdkDemoProjectStack
✨ Deployment time: 32.56s
Stack ARN:
arn:aws:cloudformation:ap-northeast-1:XXXXXXXXXXXX:stack/CdkDemoProjectStack/00c2aac0-2abc-11ee-85b9-0a99c3b7c389
✨ Total time: 171.95s
デプロイした Lambda 関数を実行すると正常に動作していますね。
$ aws lambda invoke \
--function-name helloFunction \
response.json
{
"StatusCode": 200,
"ExecutedVersion": "$LATEST"
}
$ cat response.json
{"statusCode": 200, "body": "Hello, CDK!"}
Lambda 関数にパッケージを追加
PythonFunction
で実装する Lambda 関数にパッケージを追加したい場合は、entry パスに
requirements.txt
Pipfile
poetry.lock
のいずれかを含めれば、よしなにパッケージングをしてデプロイしてくれます。
今回は Poetry を使ってみます。pytz をインストールします。
cd src/lambda/hello
poetry init
poetry add pytz
すると src/lambda/hello/poetry.lock
が作成されます。また同ディレクトリに pyproject.toml
も作成されますが、これは Git で管理不要なので.gitignore
に追記しておきましょう。
Lambda 関数を pytz を使うように修正して、CDK Deploy します。
src/lambda/hello/index.py
from datetime import datetime
from pytz import timezone
def handler(event, context):
tokyo_dt = datetime.now(timezone('Asia/Tokyo'))
return {
'statusCode': 200,
'body': tokyo_dt.strftime('%Y-%m-%d %H:%M:%S')
}
デプロイした Lambda 関数を実行すると、pytz が使用された結果が返ってきています。良さそうですね。
$ aws lambda invoke \
--function-name helloFunction \
response.json
{
"StatusCode": 200,
"ExecutedVersion": "$LATEST"
}
$ cat response.json
{"statusCode": 200, "body": "2023-07-31 23:13:09"}
ちなみにパッケージングのその他の方法として同ライブラリの PythonLayerVersion
も使用可能です。こちらは Lambda 関数のコードとは別にレイヤーとしてパッケージングされます。
おわりに
AWS CDK(TypeScript)で Python ランタイムの Lambda 関数を実装してみました。
Python や Docker を使うのは久しぶりで四苦八苦しましたが、なんとかデプロイまで行えました。参考になれば幸いです。
参考
以上