@@ -62,9 +62,13 @@ docker images
6262
6363---
6464
65+ ## 1. The foundation
66+
67+ ** Goal:** Get a working container environment running.
68+
6569### Create project folder
6670
67- Create a new folder for your project in a sensible location, for example :
71+ Create a new folder for your project:
6872
6973``` shell
7074mkdir -p ~ /Documents/daemon-labs/docker-aws-lambda
@@ -76,307 +80,41 @@ mkdir -p ~/Documents/daemon-labs/docker-aws-lambda
7680### Open the new folder in your code editor
7781
7882> [ !TIP]
79- > If you are using VSCode, we can now do everything from within the code editor.
83+ > If you are using VSCode, we can now do everything from within the code editor.
84+ > You can open the terminal window via Terminal -> New Terminal.
85+
86+ ### Create the code subdirectory
8087
81- ### Create ` ./nodejs ` folder
88+ We keep our application code separate from infrastructure config.
89+
90+ ``` shell
91+ mkdir nodejs
92+ ```
8293
83- ### Create ` Dockerfile `
94+ ### Create the ` Dockerfile `
8495
85- Add the following content:
96+ Create the file at ` nodejs/Dockerfile ` (inside the subdirectory).
8697
8798``` Dockerfile
8899FROM public.ecr.aws/lambda/nodejs:24
89100```
90101
91102### Create ` docker-compose.yaml `
92103
93- Add the following content to define your service:
104+ Create this file in the ** root ** of your project.
94105
95106``` yaml
96- ---
97107services :
98108 lambda :
99- build : .
100- ` ` `
101-
102- ### Initial image check
103-
104- Run the following command:
105-
106- ` ` ` shell
107- docker compose build
109+ build : ./nodejs
108110` ` `
109111
110- > [ !NOTE]
111- > If you now run ` docker images ` , you'll see a newly created image which should be around 436MB in size.
112+ ### Initialise the container
112113
113- Run the following command:
114+ Run this command to start an interactive shell.
114115
115116` ` ` shell
116117docker compose run -it --rm --entrypoint /bin/sh -v ./nodejs:/var/task lambda
117118```
118119
119- > [ !NOTE]
120- > This command opens an interactive session with the container.
121-
122- Run the following command:
123-
124- ``` shell
125- node --version
126- ```
127-
128- > [ !NOTE]
129- > The output should start with ` v22 ` followed by the latest minor and patch version.
130-
131- ### Initialise project and install dev dependencies
132-
133- Run the following command:
134-
135- ``` shell
136- npm init -y
137- ```
138-
139- > [ !NOTE]
140- > Notice how the ` nodejs ` directory is automatically created on your host machine due to the volume mount.
141-
142- Run the following command:
143-
144- ``` shell
145- npm add --save-dev @types/node@22 @types/aws-lambda @tsconfig/recommended typescript
146- ```
147-
148- > [ !NOTE]
149- > Notice this automatically creates a ` package-lock.json ` file.
150-
151- ### Exit the container
152-
153- Run the following command:
154-
155- ``` shell
156- exit
157- ```
158-
159- > [ !NOTE]
160- > We are now done with the interactive container at this stage and no longer need it.
161-
162- ### Create ` ./nodejs/tsconfig.json `
163-
164- Create ` ./nodejs/tsconfig.json ` and add the following content to configure the TypeScript compiler:
165-
166- ``` json
167- {
168- "extends" : " @tsconfig/recommended/tsconfig.json" ,
169- "compilerOptions" : {
170- "outDir" : " ./build"
171- }
172- }
173- ```
174-
175- > [ !NOTE]
176- > While you could auto-generate this file, our manual configuration using a recommended preset keeps the file minimal and clean.
177-
178- ### Create source file and scripts
179-
180- Create ` ./nodejs/src/index.ts ` with the following:
181-
182- ``` typescript
183- import { Handler } from " aws-lambda" ;
184-
185- export const handler: Handler = (event , context ) => {
186- console .log (" Hello world!" );
187- console .log ({ event , context });
188- };
189- ```
190-
191- Add the following to the ` scripts ` section in your ` package.json ` :
192-
193- ``` json
194- "build" : " tsc" ,
195- ```
196-
197- ---
198-
199- Update the ` Dockerfile `
200-
201- ``` Dockerfile
202- FROM public.ecr.aws/lambda/nodejs:24
203-
204- HEALTHCHECK --interval=1s --timeout=1s --retries=30 \
205- CMD [ "curl" , "-I" , "http://localhost:8080" ]
206-
207- COPY ./nodejs ${LAMBDA_TASK_ROOT}
208-
209- RUN npm ci && npm run build
210-
211- CMD [ "build/index.handler" ]
212- ```
213-
214- Run ` docker compose up --build `
215-
216- > [ !WARNING]
217- > This Lambda starts but nothing happens.
218- > ** Exit your container by pressing ` Ctrl+C ` ** on your keyboard.
219-
220- Add a new service to the ` docker-compose.yaml ` file
221-
222- ``` yaml
223- curl :
224- image : curlimages/curl
225- depends_on :
226- lambda :
227- condition : service_healthy
228- command :
229- - -s
230- - -d {}
231- - http://lambda:8080/2015-03-31/functions/function/invocations
232- ` ` `
233-
234- Run ` docker compose up --build`
235-
236- > [!WARNING]
237- > The Lambda and cURL containers start and execute, the cURL container responded with an exit code of 0 but is still hanging.
238- > **Exit your container by pressing `Ctrl+C`** on your keyboard.
239-
240- Run `docker compose up --build --abort-on-container-exit`
241-
242- > [!NOTE]
243- > The Lambda and cURL containers start and execute, the cURL container responded with an exit code of 0 and both containers shut down.
244-
245- Add the following environment variables to the `Dockerfile`
246-
247- ` ` ` Dockerfile
248- ENV AWS_LAMBDA_FUNCTION_MEMORY_SIZE=128
249- ENV AWS_LAMBDA_FUNCTION_TIMEOUT=3
250- ` ` `
251-
252- > [!TIP]
253- > This replicates the default settings of an AWS Lambda.
254- > Without these the Docker image defaults to a memory size of 3008MB and potentially an infinite timeout.
255-
256- Add the following environment variable to the `Dockerfile`
257-
258- ` ` ` Dockerfile
259- ENV AWS_LAMBDA_LOG_FORMAT=JSON
260- ` ` `
261-
262- > [!TIP]
263- > When we executed our Lambda you might have noticed our two logs were printed as plain text.
264- > In fact, the `console.log({ event, context });` didn't actually print out anything useful at all.
265-
266- # ## Set up an event
267-
268- Create an `events` directory
269-
270- Create a `test.json` file, add the following and save :
271-
272- ` ` ` json
273- {
274- "test": "test"
275- }
276- ` ` `
277-
278- # ## Update the command in `docker-compose.yaml`
279-
280- Update the command for the cURL container :
281-
282- ` ` ` yaml
283- command:
284- - -s
285- - -d
286- - ${LAMBDA_INPUT:-{}}
287- - http://lambda:8080/2015-03-31/functions/function/invocations
288- ` ` `
289-
290- > [!NOTE]
291- > By defining the command like this, by default it will pass in `{}` as the event still.
292-
293- Add the `events` directory as a volume to the cURL container :
294-
295- ` ` ` yaml
296- volumes:
297- - ./events:/events:ro
298- ` ` `
299-
300- Run `LAMBDA_INPUT=@/events/test.json docker compose up --build --abort-on-container-exit`
301-
302- > [!TIP]
303- > The `@` tells the cURL command that it should include the contents of a file rather than passing as a string.
304-
305- # ## Update the `Dockerfile` for optimised caching
306-
307- ` ` ` Dockerfile
308- FROM public.ecr.aws/lambda/nodejs:24
309-
310- ENV AWS_LAMBDA_FUNCTION_MEMORY_SIZE=128
311- ENV AWS_LAMBDA_FUNCTION_TIMEOUT=3
312- ENV AWS_LAMBDA_LOG_FORMAT=JSON
313-
314- HEALTHCHECK --interval=1s --timeout=1s --retries=30 \
315- CMD [ "curl", "-I", "http://localhost:8080" ]
316-
317- COPY ./nodejs/package*.json ${LAMBDA_TASK_ROOT}
318-
319- RUN npm ci
320-
321- COPY ./nodejs ${LAMBDA_TASK_ROOT}
322-
323- RUN npm run build
324-
325- CMD [ "build/index.handler" ]
326- ` ` `
327-
328- # ## Update the `Dockerfile` for multi-stage builds
329-
330- ` ` ` Dockerfile
331- FROM public.ecr.aws/lambda/nodejs:24 AS base
332-
333- ENV AWS_LAMBDA_FUNCTION_MEMORY_SIZE=128
334- ENV AWS_LAMBDA_FUNCTION_TIMEOUT=3
335- ENV AWS_LAMBDA_LOG_FORMAT=JSON
336-
337- HEALTHCHECK --interval=1s --timeout=1s --retries=30 \
338- CMD [ "curl", "-I", "http://localhost:8080" ]
339-
340- FROM base AS build
341-
342- COPY ./nodejs/package*.json ${LAMBDA_TASK_ROOT}
343-
344- RUN npm ci
345-
346- COPY ./nodejs ${LAMBDA_TASK_ROOT}
347-
348- RUN npm run build
349-
350- FROM base
351-
352- COPY --from=build ${LAMBDA_TASK_ROOT}/package*.json ${LAMBDA_TASK_ROOT}
353- COPY --from=build ${LAMBDA_TASK_ROOT}/build ${LAMBDA_TASK_ROOT}/build
354-
355- RUN npm ci --only=production
356-
357- CMD [ "build/index.handler" ]
358- ` ` `
359-
360- # ## Create a `python` directory
361-
362- # ## Create a `./python/Dockerfile`
363-
364- # ## Create the `./python/requirements.txt`
365-
366- # ## Create the handler file at `./python/app.py`
367-
368- # ## Update `docker-compose.yaml`
369-
370- # ## Run the Lambda
371-
372- - SAM
373-
374- - cleanup
375-
376120---
377-
378- # # 🎉 Congratulations
379-
380- You've just learnt to build and develop AWS Lambda functions locally with Docker.
381-
382- # ## Recap of what you built
0 commit comments