[CDK] ImageType=Image な Lambda をマルチステージ構成の Dockerfile を使ってデプロイしようとすると GHA でのデプロイがうまく動かないので調査
- CDK で PackageType = Image の Lambda を GitHub Actions からデプロイしたい
- デプロイは GHA (GitHub Actions) から行いたい
- デプロイするイメージの Dockerfile はマルチステージ構成
という前提条件でデプロイすると2回目以降(なのか?)のデプロイがコケてしまう。
何がどうなって再現するのかさっぱり見えないので調査する。
再現実験用に作ったコードはこちら
Dockerfile はこれ
# for Mac M1
FROM public.ecr.aws/lambda/python:3.9 AS build-arm64
COPY requirements.txt .
RUN pip3 install -r requirements.txt --target "${LAMBDA_TASK_ROOT}"
COPY . ${LAMBDA_TASK_ROOT}
# CMD [ "handler.handler" ]
# for Lambda Runtime
FROM public.ecr.aws/lambda/python:3.9 AS build-amd64
# Install the function's dependencies using file requirements.txt
# from your project folder.
COPY requirements.txt .
RUN pip3 install -r requirements.txt --target "${LAMBDA_TASK_ROOT}"
COPY . ${LAMBDA_TASK_ROOT}
# CMD [ "handler.handler" ]
Lambda リソースを定義する CDK の実装はこれ
interface CreatePythonLambdaFromAssetProps {
path: string
handler: string
}
function createPythonLambdaFromAsset(scope: Construct, id: string, props: CreatePythonLambdaFromAssetProps) {
const f = new aws_lambda.DockerImageFunction(scope, id, {
code: aws_lambda.DockerImageCode.fromImageAsset(props.path, {
cmd: [props.handler],
}),
timeout: cdk.Duration.minutes(1),
tracing: aws_lambda.Tracing.ACTIVE,
});
new cdk.CfnOutput(scope, `${id}Arn`, {
value: f.functionArn,
});
return f;
}
成功した初回デプロイはこれ
2回目のデプロイで失敗したのがこれ
docker build が失敗している。
デプロイ時のログ
[07:47:14] Checking for previously published assets
[07:47:14] Retrieved account ID *** from disk cache
[07:47:14] Assuming role 'arn:aws:iam::***:role/cdk-hnb659fds-deploy-role-***-ap-northeast-1'.
[07:47:14] Retrieved account ID *** from disk cache
[07:47:14] Assuming role 'arn:aws:iam::***:role/cdk-hnb659fds-deploy-role-***-ap-northeast-1'.
[07:47:15] Retrieved account ID *** from disk cache
[07:47:15] Assuming role 'arn:aws:iam::***:role/cdk-hnb659fds-file-publishing-role-***-ap-northeast-1'.
[07:47:15] Retrieved account ID *** from disk cache
[07:47:15] Assuming role 'arn:aws:iam::***:role/cdk-hnb659fds-image-publishing-role-***-ap-northeast-1'.
[07:47:15] CdkMultiStageImageLambdaGhaStack: check: Check s3://cdk-hnb659fds-assets-***-ap-northeast-1/af892d23d92db68bb6faa363d4813d5724bf6fceded4cb8d201f4200cc4ffcff.json
[07:47:16] CdkMultiStageImageLambdaGhaStack: found: Found s3://cdk-hnb659fds-assets-***-ap-northeast-1/af892d23d92db68bb6faa363d4813d5724bf6fceded4cb8d201f4200cc4ffcff.json
[07:47:16] CdkMultiStageImageLambdaGhaStack: check: Check ***.dkr.ecr.ap-northeast-1.amazonaws.com/cdk-hnb659fds-container-assets-***-ap-northeast-1:19223eadef6724113659c318155a5065154bc16a8b58865b8d53038e4db83f11
[07:47:17] Call failed: describeImages({"repositoryName":"cdk-hnb659fds-container-assets-***-ap-northeast-1","imageIds":[{"imageTag":"19223eadef6724113659c318155a5065154bc16a8b58865b8d53038e4db83f11"}]}) => The image with imageId {imageDigest:'null', imageTag:'19223eadef6724113659c318155a5065154bc16a8b58865b8d53038e4db83f11'} does not exist within the repository with name 'cdk-hnb659fds-container-assets-***-ap-northeast-1' in the registry with id '***' (code=ImageNotFoundException)
[07:47:17] 2 total assets, 1 still need to be published
[07:47:17] Retrieved account ID *** from disk cache
[07:47:17] Waiting for stack CDKToolkit to finish creating or updating...
CdkMultiStageImageLambdaGhaStack: start: Building 19223eadef6724113659c318155a5065154bc16a8b58865b8d53038e4db83f11:***-ap-northeast-1
[07:47:19] CdkMultiStageImageLambdaGhaStack: debug: docker login --username AWS --password-stdin https://***.dkr.ecr.ap-northeast-1.amazonaws.com
[07:47:20] CdkMultiStageImageLambdaGhaStack: debug: docker inspect cdkasset-19223eadef6724113659c318155a5065154bc16a8b58865b8d53038e4db83f11
[07:47:20] CdkMultiStageImageLambdaGhaStack: build: Building Docker image at /home/runner/work/cdk-multi-stage-image-lambda-gha/cdk-multi-stage-image-lambda-gha/cdk.out/asset.19223eadef6724113659c318155a5065154bc16a8b58865b8d53038e4db83f11
[07:47:20] CdkMultiStageImageLambdaGhaStack: debug: docker build --tag cdkasset-19223eadef6724113659c318155a5065154bc16a8b58865b8d53038e4db83f11 .
Sending build context to Docker daemon 4.096kB
Step 1/8 : FROM --platform=linux/arm64 public.ecr.aws/lambda/python:3.9 AS build-arm64
3.9: Pulling from lambda/python
371a402111f6: Pulling fs layer
8ed46a04bcaa: Pulling fs layer
bd477373174d: Pulling fs layer
455bc9247f6c: Pulling fs layer
91d1ce2b26de: Pulling fs layer
9a72daca427d: Pulling fs layer
455bc9247f6c: Waiting
91d1ce2b26de: Waiting
9a72daca427d: Waiting
8ed46a04bcaa: Download complete
bd477373174d: Verifying Checksum
bd477373174d: Download complete
455bc9247f6c: Verifying Checksum
455bc9247f6c: Download complete
9a72daca427d: Verifying Checksum
9a72daca427d: Download complete
91d1ce2b26de: Verifying Checksum
91d1ce2b26de: Download complete
371a402111f6: Verifying Checksum
371a402111f6: Download complete
371a402111f6: Pull complete
8ed46a04bcaa: Pull complete
bd477373174d: Pull complete
455bc9247f6c: Pull complete
91d1ce2b26de: Pull complete
9a72daca427d: Pull complete
Digest: sha256:696a74214bac1cf4afe6427331c6fc609c8b58a343f62e0ed9e3a483f120d1f1
Status: Downloaded newer image for public.ecr.aws/lambda/python:3.9
---> 1a7025f31878
Step 2/8 : COPY requirements.txt .
---> 3f9db90a2dd9
Step 3/8 : RUN pip3 install -r requirements.txt --target "${LAMBDA_TASK_ROOT}"
Warning: rning] The requested image's platform (linux/arm64/v8) does not match the detected host platform (linux/amd64) and no specific platform was requested
---> Running in 514ca6707cb5
exec /bin/sh: exec format error
The command '/bin/sh -c pip3 install -r requirements.txt --target "${LAMBDA_TASK_ROOT}"' returned a non-zero code: 1
CdkMultiStageImageLambdaGhaStack: fail: docker build --tag cdkasset-19223eadef6724113659c318155a5065154bc16a8b58865b8d53038e4db83f11 . exited with error code 1: The command '/bin/sh -c pip3 install -r requirements.txt --target "${LAMBDA_TASK_ROOT}"' returned a non-zero code: 1
❌ Deployment failed: Error: Failed to build asset 19223eadef6724113659c318155a5065154bc16a8b58865b8d53038e4db83f11:***-ap-northeast-1
at Deployments.buildSingleAsset (/home/runner/work/cdk-multi-stage-image-lambda-gha/cdk-multi-stage-image-lambda-gha/node_modules/aws-cdk/lib/index.js:415:11476)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async Object.buildAsset (/home/runner/work/cdk-multi-stage-image-lambda-gha/cdk-multi-stage-image-lambda-gha/node_modules/aws-cdk/lib/index.js:415:150805)
at async /home/runner/work/cdk-multi-stage-image-lambda-gha/cdk-multi-stage-image-lambda-gha/node_modules/aws-cdk/lib/index.js:415:137021
[07:47:33] Notices refreshed
Failed to build asset 19223eadef6724113659c318155a5065154bc16a8b58865b8d53038e4db83f11:***-ap-northeast-1
[07:47:33] Error: Failed to build asset 19223eadef6724113659c318155a5065154bc16a8b58865b8d53038e4db83f11:***-ap-northeast-1
at Deployments.buildSingleAsset (/home/runner/work/cdk-multi-stage-image-lambda-gha/cdk-multi-stage-image-lambda-gha/node_modules/aws-cdk/lib/index.js:415:11476)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async Object.buildAsset (/home/runner/work/cdk-multi-stage-image-lambda-gha/cdk-multi-stage-image-lambda-gha/node_modules/aws-cdk/lib/index.js:415:150805)
at async /home/runner/work/cdk-multi-stage-image-lambda-gha/cdk-multi-stage-image-lambda-gha/node_modules/aws-cdk/lib/index.js:415:137021
Error: Process completed with exit code 1.
途中のログで docker build
していることと、そこで build-arm64
のステージを見ていることがわかる。
このステージは2つ定義したステージのうちの1つ目であり、これは本来ビルドしてほしくない。
また、現状の私の認識では --target
を省いた場合の docker build
は最後に定義したステージ(とそのステージが依存する他のステージ)のみがビルドされる想定だった。
今回の Dockerfile の内容からすると build-arm64
がビルドされるのは想定外。
[07:47:20] CdkMultiStageImageLambdaGhaStack: debug: docker build --tag cdkasset-19223eadef6724113659c318155a5065154bc16a8b58865b8d53038e4db83f11 .
Sending build context to Docker daemon 4.096kBStep 1/8 : FROM --platform=linux/arm64 public.ecr.aws/lambda/python:3.9 AS build-arm64
ログを見返すと
Step 2/8 : COPY requirements.txt .
---> 3f9db90a2dd9
Step 3/8 : RUN pip3 install -r requirements.txt --target "${LAMBDA_TASK_ROOT}"
Warning: rning] The requested image's platform (linux/arm64/v8) does not match the detected host platform (linux/amd64) and no specific platform was requested
---> Running in 514ca6707cb5
exec /bin/sh: exec format error
とあり、warn は出てるが失敗の原因はまた別のところにありそうな気もしてきた・・・。
まずは pip3 install
の方をしっかり見た方がよさげかも。それはそれとして意図してないステージが build されてるのは解消したいが
現状整理する。
最初にデプロイ成功した際にできたイメージが ECR にいるので、それを inspect した
docker inspect .dkr.ecr.ap-northeast-1.amazonaws.com/cdk-hnb659fds-container-assets--ap-northeast-1:5ab4aa8d130d7c303a8c438dce746929ceed5fe4bd30246a16319dadac31706b | jq
ECR にいるデプロイ成功時のイメージを inspect
[
{
"Id": "sha256:3d209cefab3e8eb27ef412a8424d494d19d4de2503a0ee5b9b4f32b29abc8e04",
"RepoTags": [
"************.dkr.ecr.ap-northeast-1.amazonaws.com/cdk-hnb659fds-container-assets-************-ap-northeast-1:5ab4aa8d130d7c303a8c438dce746929ceed5fe4bd30246a16319dadac31706b",
"cdkasset-5ab4aa8d130d7c303a8c438dce746929ceed5fe4bd30246a16319dadac31706b:latest"
],
"RepoDigests": [
"************.dkr.ecr.ap-northeast-1.amazonaws.com/cdk-hnb659fds-container-assets-************-ap-northeast-1@sha256:8a857443b29f6e82ebc69badfe3cf64ed499296e3cd1c4b205346adcda34ef3d"
],
"Parent": "",
"Comment": "buildkit.dockerfile.v0",
"Created": "2023-07-21T14:22:44.480560465Z",
"Container": "",
"ContainerConfig": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": null,
"Cmd": null,
"Image": "",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": null
},
"DockerVersion": "",
"Author": "",
"Config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"LANG=en_US.UTF-8",
"TZ=:/etc/localtime",
"PATH=/var/lang/bin:/usr/local/bin:/usr/bin/:/bin:/opt/bin",
"LD_LIBRARY_PATH=/var/lang/lib:/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib:/opt/lib",
"LAMBDA_TASK_ROOT=/var/task",
"LAMBDA_RUNTIME_DIR=/var/runtime"
],
"Cmd": null,
"Image": "",
"Volumes": null,
"WorkingDir": "/var/task",
"Entrypoint": [
"/lambda-entrypoint.sh"
],
"OnBuild": null,
"Labels": null
},
"Architecture": "amd64",
"Os": "linux",
"Size": 708311546,
"VirtualSize": 708311546,
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/lz4uign93bkb1b6rxpi5atj8q/diff:/var/lib/docker/overlay2/igvletwu9fvev7mwiq5sbzjqm/diff:/var/lib/docker/overlay2/6892cb14de351a224c35cbe159af671f8b50b08822bb8a325471c2ffacfb4cf6/diff:/var/lib/docker/overlay2/83d4f2a740c3cfe6cc85b8163c34da760784f60040fb7823d78a6434c1329bee/diff:/var/lib/docker/overlay2/b83af63cf8994a7e5ec84c7a1e97235f8ec40073a5bdce51b14cd7216da0181e/diff:/var/lib/docker/overlay2/6a8f15ae89e1bc17c481246d5fecffbbea999273f051873f90e61bc9ccc9960d/diff:/var/lib/docker/overlay2/10ff1b32ad2f5add0dce0d7bebbaa6f7b6a87d95c6e761c8b99ab48065f079e1/diff:/var/lib/docker/overlay2/ed404b15aa329ebb67f35512e0be2e2db080f652ec5003b6855fdc7260ea50de/diff",
"MergedDir": "/var/lib/docker/overlay2/p8uw4o5v8zykk3zxszrbu4v2o/merged",
"UpperDir": "/var/lib/docker/overlay2/p8uw4o5v8zykk3zxszrbu4v2o/diff",
"WorkDir": "/var/lib/docker/overlay2/p8uw4o5v8zykk3zxszrbu4v2o/work"
},
"Name": "overlay2"
},
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:2a9a5b39a0415dc5625eb8704426badbbe9e6ec672abd2ae038edc7c1fbb0f28",
"sha256:16a264f580b93a5ebd9313794f6331dc2a68af9abf9d3abf55bb427a07681e92",
"sha256:b67819d500bceb343dfb29903093bbacb0507a2a41008c5522bf86dc89fde30c",
"sha256:15dd6c63f3a24e6a848a62cbdbef587fde902e26d3b13cf50aed08bf988fdf7b",
"sha256:6c25fad1f01114fd54cceca2ce3c6a1c284a17c88a7b91487abbf8084f7a8752",
"sha256:4c2065c968c85ff39471c53d91b86d4fcd19e992a4a4238720df99169523dfdb",
"sha256:7830dd83e76651d4be182e9ffb1e7206d80ddcb5b9b96ecde5df4d1f4ac99000",
"sha256:ec8e58bda745a8edf608221e68326fa8fe313f932f01dfff64383448f6711812",
"sha256:6e07e7e6bf43f01604bbaa1e7144e593ae0d4a50bb9124cbf6e6e6b389a8e4d0"
]
},
"Metadata": {
"LastTagTime": "2023-07-22T05:21:36.601203588Z"
}
}
]
amd64 っぽい。
CloudFormation の AWS::Lambda::Function
の方も Architecture
が未指定なのでデフォルトの x86_64
になっている。なのでここは期待値と一致している。
2回目以降(という認識であっているのかどうかも定かではないが)のデプロイで arm64 用のステージが呼ばれている。
CDK 側でターゲットを明示してみた。
結果はこれ。変わらず build-arm64
をビルドしようとしている。
[09:50:16] CdkMultiStageImageLambdaGhaStack: debug: docker build --tag cdkasset-605c8af913d031315061bc31acacbed6a30c43ba4494119bc6988a8736569872 --target build-amd64 .
Sending build context to Docker daemon 4.096kBStep 1/8 : FROM --platform=linux/arm64 public.ecr.aws/lambda/python:3.9 AS build-arm64
<中略>
Step 2/8 : COPY requirements.txt .
---> fa8279846842
Step 3/8 : RUN pip3 install -r requirements.txt --target "${LAMBDA_TASK_ROOT}"
Warning: rning] The requested image's platform (linux/arm64/v8) does not match the detected host platform (linux/amd64) and no specific platform was requested
---> Running in ed3bf830f430
exec /bin/sh: exec format error
The command '/bin/sh -c pip3 install -r requirements.txt --target "${LAMBDA_TASK_ROOT}"' returned a non-zero code: 1
CdkMultiStageImageLambdaGhaStack: fail: docker build --tag cdkasset-605c8af913d031315061bc31acacbed6a30c43ba4494119bc6988a8736569872 --target build-amd64 . exited with error code 1: The command '/bin/sh -c pip3 install -r requirements.txt --target "${LAMBDA_TASK_ROOT}"' returned a non-zero code: 1
エラーに見えるテキストを拾うと、pip install が 1 で終了している様子。
あと、その手前で
exec /bin/sh: exec format error
このエラーが出ている。
ホストと対象ファイルで実行形式に互換性がないっぽい。
pip3 ってバイナリだったか・・・?bash から呼び出すスクリプトだったはず??? と思った。
ベースイメージに bash がいないのなら、確かにこのエラーになるのも納得できる。ローカルで docker build して入ってみた。
手元は M1 Mac なので arm64 で試す
$ docker build --target build-arm64 -t test .
$ docker run --entrypoint /bin/bash -it test
bash-4.2# echo $SHELL
/bin/bash
普通に bash は動くっぽい。 sh -c
を介して実行するとこれは動作せず pip3 のヘルプが出るだけ。
bash-4.2# sh -c pip3 install -r requirements.txt
ただ、RUN コマンドの書き方自体は AWS のドキュメント にも同様な記載があるし、手元の環境で docker build が動作することからもここの指定の仕方が悪いとは考えにくい。
結局、
FROM --platform=linux/arm64 public.ecr.aws/lambda/python:3.9 AS build-arm64
これがビルドされてることが想定外でありそこを解消するのが早そうな気はする
Warning: rning] The requested image's platform (linux/arm64/v8) does not match the detected host platform (linux/amd64) and no specific platform was requested
と書いてあるので、ホスト (GitHub Actions, github hosted runner) のアーキテクチャである amd64 とマッチしないものをビルドしようとしているのが悪い気がする
単一ステージで構成される Dockerfile で amd64 を明示してみたら、こちらは無事デプロイ成功。
↑の状態で、GHA 経由だけでなく手元の M1 Mac からデプロイして動作を見てみた。
自分の記憶ではこのケースだと exec /bin/sh: exec format error
で Lambda の実行がコケる想定だったが、これもうまく行ってしまった
ということで、プラットフォームごとにステージを定義する Dockerfile はそもそも使わんでOKということになりそう
この scrap を立てる契機になった事象の原因は解明できてないが、Lambda のデプロイをする用事は満たせているので(すっきりしないが)問題は解消としておく
M1 の手元でのちょっとした動作検証なら↓こんな感じで良さそう。CDK は無関係の話にできる。
一方で、CDK 的には Lambda のアーキテクチャと同じプラットフォームで docker build
が動くようにしておきたい。cdk deploy
を実行するマシンの環境に関係なく、Lambda を動かしたいアーキテクチャで cdk deploy / docker build が動くように明示できてればOK
よって、Function
に指定する Architecture と ImageAsset
で指定する platform を供給するための StackProps を定義してあげればそれで良さそう。Lambda のアーキテクチャを変えるなんてそうそうないと思うので、 prop 指定するまでもなく個別にハードコード、でも良いかもしれない。