Reputation: 1864
Demo of the application use case is here for your convenience: https://github.com/tal-rofe/demo-lambda
I set up a monorepo environment, where I simply have apps
folder with 2 applications, frontend
and pixel-api
.
I want to run these both locally with hot reload. Meaning- whenever I modify each project's source code, the artifacts refreshes. For example I run the frontend application with NextJS so it is easily supported by running next dev
inside the docker container and using Bind Mount to the source code on host machine.
Problem is I try to do the same with Lambda function. AWS support the sam local start-api
(https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-local-start-api.html) command which is helpful, but I rather not install the sam
CLI to a developer machine.
So my intention is to run the SAM CLI inside Docker.
I found this comment describing how to do so: https://github.com/aws/aws-sam-cli/issues/55#issuecomment-1717711860 And also this: https://github.com/aws/aws-sam-cli/issues/55#issuecomment-1717819156
I copied the code and tried to adjust it to my project but didn't go well.
Root/docker-compose.dev.yaml:
version: '3.8'
services:
pixel-api:
build:
context: .
dockerfile: ./docker/Dockerfile.pixel-api-dev
command: sam local start-api -t "${PWD}/apps/pixel-api/template.yaml" -v "${PWD}/.aws-sam/build" --host=0.0.0.0 --container-host=host.docker.internal --container-host-interface=127.0.0.1
ports:
- '3000:3000'
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ${PWD}:${PWD}:ro
- /dashboard/apps/pixel-api/node_modules
- /dashboard/node_modules
environment:
SAM_CLI_TELEMETRY: 0
SAM_CLI_CONTAINER_CONNECTION_TIMEOUT: 30
sam_local_environment: 'true'
frontend:
container_name: frontend
build:
context: .
dockerfile: ./docker/Dockerfile.frontend-dev
env_file:
- ./apps/frontend/envs/.env.development
ports:
- 8080:8080
restart: always
networks:
- dashboard_network
volumes:
- type: bind
source: ./apps/frontend/src
target: /dashboard/apps/frontend/src
- /dashboard/apps/frontend/node_modules
networks:
dashboard_network:
driver: bridge
The "frontend" service works as intended and succeeds.
Root/apps/pixel-api/template.yaml:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: SAM template for running locally API Gateway & Lambda function for Pixel API function
Globals:
Api:
Cors:
AllowMethods: "'POST,OPTIONS'"
AllowHeaders: "'content-type','x-api-key','x-amz-security-token','authorization','x-amz-user-agent','x-amz-date'"
AllowOrigin: "'*'"
AllowCredentials: "'*'"
Resources:
AwsApiGateway:
Type: AWS::Serverless::HttpApi
Properties:
Name: AWS API Gateway - Pixel API
StageName: development
MyLambdaFunction:
Type: 'AWS::Serverless::Function'
Properties:
Runtime: nodejs20.x
Handler: index.handler
CodeUri: build
Timeout: 30
Description: 'Pixel API Lambda function'
Environment:
Variables:
sam_local_environment: 'true'
Events:
Api:
Type: HttpApi
Properties:
ApiId: !Ref AwsApiGateway
Path: /
Method: POST
I also have "Root/apps/pixel-api/build/index.js` file as for the Lambda function NodeJS function.
I also have the Dockerfile for the pixel-api service:
Root/docker/Dockerfile.pixel-api-dev:
FROM public.ecr.aws/docker/library/docker
ENV PIP_BREAK_SYSTEM_PACKAGES 1
RUN apk update && \
apk add gcc py-pip python3-dev libffi-dev musl-dev && \
pip install --upgrade pip setuptools wheel && \
pip install --upgrade aws-sam-cli
WORKDIR /var/opt
ENTRYPOINT []
CMD ["/bin/bash"]
When I run the services using docker-compose up -d
, both services succeeds. But when I run curl -X POST http://localhost:3000
- invoking the Lambda function fails:
Mounting MyLambdaFunction at http://0.0.0.0:3000/ [POST]
You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. If you used sam build before running local commands, you will need to re-run sam build for the changes to be picked up. You only need to restart SAM CLI if you update your AWS SAM template
2024-03-20 07:24:30 WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:3000
* Running on http://172.20.0.2:3000
2024-03-20 07:24:30 Press CTRL+C to quit
Invoking index.handler (nodejs20.x)
Local image was not found.
Removing rapid images for repo public.ecr.aws/sam/emulation-nodejs20.x
Building image..........................................................................................................................................................................................................................................................................................................................................................
Using local image: public.ecr.aws/lambda/nodejs:20-rapid-x86_64.
Mounting /MY_MACHINE_PATH/dashboard/.aws-sam/build/build as /var/task:ro,delegated, inside runtime container
START RequestId: 3e76c0ee-26bb-4250-983c-f60252289641 Version: $LATEST
2024-03-20T07:25:35.076Z undefined ERROR Uncaught Exception {"errorType":"Runtime.ImportModuleError","errorMessage":"Error: Cannot find module 'index'\nRequire stack:\n- /var/runtime/index.mjs","stack":["Runtime.ImportModuleError: Error: Cannot find module 'index'","Require stack:","- /var/runtime/index.mjs"," at _loadUserApp (file:///var/runtime/index.mjs:1087:17)"," at async UserFunction.js.module.exports.load (file:///var/runtime/index.mjs:1119:21)"," at async start (file:///var/runtime/index.mjs:1282:23)"," at async file:///var/runtime/index.mjs:1288:1"]}
20 Mar 2024 07:25:35,212 [ERROR] (rapid) Init failed error=Runtime exited with error: exit status 129 InvokeID=
20 Mar 2024 07:25:35,216 [ERROR] (rapid) Invoke failed error=Runtime exited with error: exit status 129 InvokeID=33a8293f-9315-4d2a-808f-265f5620f20f
20 Mar 2024 07:25:35,218 [ERROR] (rapid) Invoke DONE failed: Sandbox.Failure
2024-03-20 07:25:36 192.168.65.1 - - [20/Mar/2024 07:25:36] "POST / HTTP/1.1" 500 -
My questions:
sam build
at all, I don't even have sam
CLI on my host machine. If I need to do so, how can I do it inside the Docker?nodejs20.x
version for my function, as you can see from the logs I attached:Local image was not found.
Removing rapid images for repo public.ecr.aws/sam/emulation-nodejs20.x
Building image..........................................................................................................................................................................................................................................................................................................................................................
Using local image: public.ecr.aws/lambda/nodejs:20-rapid-x86_64.
So, OK, no image, but it runs after first request to lambda fired. Is there a way to do it ahead of time, on Dockerfile.pixel-api-dev? So first request won't need to wait the image to download...
And of-course the main question - why did it fail to find my Lambda function?
Also, how can I enable hot reload for this lambda function so every time I modify the Root/apps/pixel-api/build/index.js
file, the lambda functions refreshes on localhost:3000
? Note that comment from the logs I attached:
You do not need to restart/reload SAM CLI while working on your functions,
changes will be reflected instantly/automatically.
If you used sam build before running local commands, you will need to re-run sam build for the changes to be picked up
Again, I don't want to use sam
CLI on my host machine, if possible.
Upvotes: 5
Views: 618
Reputation: 1864
This is not a direct solution but rather migration solution to other framework. As I don't mind the underlying framework used to test my Lambda function code, this solution is good for me.
I migrated the development framework to use the serverless framework instead. Then I just created this serverless.yaml
file:
org: kynesis
app: pixel-api
console: true
service: pixel-http-api
frameworkVersion: '3'
configValidationMode: error
provider:
name: aws
httpApi:
cors:
allowedOrigins:
- '*'
allowedHeaders:
- Content-Type
allowedMethods:
- POST
allowCredentials: false
custom:
serverless-offline:
# * https://forum.serverless.com/t/possible-to-run-serverless-in-a-docker-container/5764/4
host: 0.0.0.0
plugins:
- serverless-offline
functions:
pixel-api:
name: pixel-api
runtime: nodejs20.x
handler: ./build/index.handler
events:
- httpApi:
path: /
method: POST
modified my docker compose file to:
version: '3.8'
services:
pixel-api:
container_name: pixel-api
build:
context: .
dockerfile: ./docker/Dockerfile.pixel-api-dev
ports:
- 3000:3000
restart: always
networks:
- dashboard_network
volumes:
- type: bind
source: ./apps/pixel-api/src
target: /dashboard/apps/pixel-api/src
- /dashboard/apps/pixel-api/node_modules
- /dashboard/node_modules
frontend:
container_name: frontend
build:
context: .
dockerfile: ./docker/Dockerfile.frontend-dev
env_file:
- ./apps/frontend/envs/.env.development
ports:
- 8080:8080
restart: always
networks:
- dashboard_network
volumes:
- type: bind
source: ./apps/frontend/src
target: /dashboard/apps/frontend/src
- /dashboard/apps/frontend/node_modules
- /dashboard/node_modules
networks:
dashboard_network:
driver: bridge
modified my Dockerfile to
FROM node:20.11.1
RUN npm i -g [email protected]
WORKDIR /dashboard
COPY ./package.json ./pnpm-workspace.yaml ./.npmrc ./
COPY ./apps/pixel-api/package.json ./apps/pixel-api/
RUN pnpm i -w
RUN pnpm --filter pixel-api i
COPY ./tsconfig.base.json ./nx.json ./
COPY ./apps/pixel-api/ ./apps/pixel-api/
CMD ["pnpm", "exec", "nx", "start:dev", "blabla"]
Then I also configured nodemon.json
file to rebuild my Lambda code and restart the serverless framework server:
{
"$schema": "https://json.schemastore.org/nodemon.json",
"watch": ["./src", "./serverless.yaml"],
"ext": "ts,json",
"exec": "node ./esbuild.js && sls offline --httpPort 3000"
}
Upvotes: 0