[Question] Nodejs20-debian12 with aws-lambda-ric
Hey, we are currently running into issues while migrating onto an distroless image for our aws lambdas. I was following the docs from aws on how to use a non-AWS base image (https://docs.aws.amazon.com/lambda/latest/dg/nodejs-image.html#nodejs-image-clients).
Since the distroless image has removed package managers and everything else. NPM and NPX are not availabe. But I couldn't find any documentation what I could give as an entrypoint in the dockerfile for the aws-lambda-ric as an alternative. The documentation mentioned above expects the following entrypoint:
# Set runtime interface client as default command for the container runtime
ENTRYPOINT ["/usr/local/bin/npx", "aws-lambda-ric"]
# Pass the name of the function handler as an argument to the runtime
CMD ["index.handler"]
Do any of you have experience in what seems to be an edge case?
I'm a person with very little node knowledge, but could you just run the index.js in aws-lambda-ric directly?
The ENTRYPOINT provided by distroless is node, so don't modify that
and your CMD is ["<wherever aws-lambda-ric is>/index.js", "index.handler"]
I've played around with it, and it might not be possible with distroless because it expects /bin/sh to be able to fork a process
This is the Dockerfile I've concocted
FROM gcr.io/distroless/nodejs22-debian12:latest
COPY --from=public.ecr.aws/lambda/nodejs:22 /usr/local/bin/aws-lambda-rie /usr/local/bin/aws-lambda-rie
COPY --from=public.ecr.aws/lambda/nodejs:22 /var/runtime /var/runtime
ENV LAMBDA_TASK_ROOT=/var/task
WORKDIR /var/task
COPY index.js ${LAMBDA_TASK_ROOT}
ENTRYPOINT [ "/usr/local/bin/aws-lambda-rie" ]
CMD [ "/nodejs/bin/node", "/var/runtime/index.mjs" "index.handler" ]
Where index.js is
exports.handler = async (event) => {
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Distroless Lambda!'),
};
return response;
};
And running it as
docker run --rm -it -p 8080:8080 lambda
And I get the following logs
15 Jan 2025 23:12:54,738 [INFO] (rapid) exec '/bin/sh' (cwd=/var/task, handler=[ "/nodejs/bin/node", "/var/runtime/index.mjs" "index.handler" ])
START RequestId: ff3314bb-03a1-4640-ad58-e4d1a858f824 Version: $LATEST
15 Jan 2025 23:12:57,851 [INFO] (rapid) INIT START(type: on-demand, phase: init)
15 Jan 2025 23:12:57,851 [INFO] (rapid) The extension's directory "/opt/extensions" does not exist, assuming no extensions to be loaded.
15 Jan 2025 23:12:57,851 [INFO] (rapid) Starting runtime without AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN , Expected?: false
15 Jan 2025 23:12:57,851 [WARNING] (rapid) First fatal error stored in appctx: Runtime.InvalidEntrypoint
15 Jan 2025 23:12:57,851 [INFO] (rapid) INIT RTDONE(status: error)
15 Jan 2025 23:12:57,851 [INFO] (rapid) INIT REPORT(durationMs: 0.527000)
15 Jan 2025 23:12:57,851 [ERROR] (rapid) Init failed error=fork/exec /bin/sh: no such file or directory InvokeID=
15 Jan 2025 23:12:57,851 [WARNING] (rapid) Shutdown initiated: spindown
15 Jan 2025 23:12:57,851 [INFO] (rapid) Waiting for runtime domain processes termination
15 Jan 2025 23:12:57,851 [INFO] (rapid) INIT START(type: on-demand, phase: invoke)
15 Jan 2025 23:12:57,851 [INFO] (rapid) The extension's directory "/opt/extensions" does not exist, assuming no extensions to be loaded.
15 Jan 2025 23:12:57,851 [INFO] (rapid) Starting runtime without AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN , Expected?: false
15 Jan 2025 23:12:57,852 [WARNING] (rapid) First fatal error stored in appctx: Runtime.InvalidEntrypoint
15 Jan 2025 23:12:57,852 [INFO] (rapid) INIT RTDONE(status: error)
15 Jan 2025 23:12:57,852 [INFO] (rapid) INIT REPORT(durationMs: 0.909000)
15 Jan 2025 23:12:57,852 [INFO] (rapid) INVOKE START(requestId: edcda402-6476-44be-99cc-57d4d12d17f9)
15 Jan 2025 23:12:57,852 [ERROR] (rapid) Invoke failed error=fork/exec /bin/sh: no such file or directory InvokeID=edcda402-6476-44be-99cc-57d4d12d17f9
15 Jan 2025 23:12:57,852 [ERROR] (rapid) Invoke DONE failed: Runtime.InvalidEntrypoint
15 Jan 2025 23:12:57,852 [WARNING] (rapid) Reset initiated: ReleaseFail
15 Jan 2025 23:12:57,852 [INFO] (rapid) Waiting for runtime domain processes termination
If I somehow manage to sneak in /bin/sh, the following error comes
15 Jan 2025 23:09:28,724 [INFO] (rapid) exec '/bin/sh' (cwd=/var/task, handler=[ "/nodejs/bin/node", "/var/runtime/index.mjs" "index.handler" ])
START RequestId: 6bc08f86-e292-4168-b43d-41a00dd0bc3c Version: $LATEST
15 Jan 2025 23:09:33,008 [INFO] (rapid) INIT START(type: on-demand, phase: init)
15 Jan 2025 23:09:33,008 [INFO] (rapid) The extension's directory "/opt/extensions" does not exist, assuming no extensions to be loaded.
15 Jan 2025 23:09:33,008 [INFO] (rapid) Starting runtime without AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN , Expected?: false
sh: /var/runtime/index.mjs: unknown operand
15 Jan 2025 23:09:33,009 [WARNING] (rapid) First fatal error stored in appctx: Runtime.ExitError
15 Jan 2025 23:09:33,009 [WARNING] (rapid) Process runtime-1 exited: exit status 2
15 Jan 2025 23:09:33,009 [INFO] (rapid) INIT RTDONE(status: error)
15 Jan 2025 23:09:33,009 [INFO] (rapid) INIT REPORT(durationMs: 1.441000)
15 Jan 2025 23:09:33,009 [ERROR] (rapid) Init failed error=Runtime exited with error: exit status 2 InvokeID=
15 Jan 2025 23:09:33,009 [WARNING] (rapid) Shutdown initiated: spindown
15 Jan 2025 23:09:33,009 [INFO] (rapid) Waiting for runtime domain processes termination
15 Jan 2025 23:09:33,009 [INFO] (rapid) INIT START(type: on-demand, phase: invoke)
15 Jan 2025 23:09:33,009 [INFO] (rapid) The extension's directory "/opt/extensions" does not exist, assuming no extensions to be loaded.
15 Jan 2025 23:09:33,009 [INFO] (rapid) INIT REPORT(durationMs: 0.267000)
15 Jan 2025 23:09:33,009 [INFO] (rapid) INVOKE START(requestId: d7a3f34f-d788-4801-a36b-2561b6028c22)
15 Jan 2025 23:09:33,009 [ERROR] (rapid) Invoke failed error=Runtime exited with error: exit status 2 InvokeID=d7a3f34f-d788-4801-a36b-2561b6028c22
15 Jan 2025 23:09:33,009 [ERROR] (rapid) Invoke DONE failed: Sandbox.Failure
15 Jan 2025 23:09:33,009 [WARNING] (rapid) Reset initiated: ReleaseFail
15 Jan 2025 23:09:33,009 [INFO] (rapid) Waiting for runtime domain processes termination
If I use the :debug variant and run this to enter the shell
docker run --rm -it -p 8080:8080 --entrypoint /busybox/sh lambda
And then run this inside the container
/usr/local/bin/aws-lambda-rie /nodejs/bin/node /var/runtime/index.mjs index.handler
I can actually curl it and get a response (takes a few tries, not sure if that's how it works, not familiar with AWS lambda).
❯ curl "http://localhost:8080/2015-03-31/functions/function/invocations" -d '{}'
{"statusCode":200,"body":"\"Hello from Distroless Lambda!\""}
With some extra digging, I've come up with this
FROM gcr.io/distroless/nodejs22-debian12:debug
COPY --from=public.ecr.aws/lambda/nodejs:22 /usr/local/bin/aws-lambda-rie /usr/local/bin/aws-lambda-rie
COPY --from=public.ecr.aws/lambda/nodejs:22 /var/runtime /var/runtime
ENV LAMBDA_TASK_ROOT=/var/task
WORKDIR /var/task
COPY index.js ${LAMBDA_TASK_ROOT}
COPY start.sh ${LAMBDA_TASK_ROOT}
ENTRYPOINT [ "/busybox/sh" ]
CMD [ "/var/task/start.sh" ]
start.sh file
/busybox/sh -c "/usr/local/bin/aws-lambda-rie /nodejs/bin/node /var/runtime/index.mjs index.handler"
I can now start the container
docker run --rm -it -p 8080:8080 lambda
And curl it
❯ curl "http://localhost:8080/2015-03-31/functions/function/invocations" -d '{}'
{"statusCode":200,"body":"\"Hello from Distroless Lambda!\""}