Skip to content

Commit 0e51bee

Browse files
committed
Update README.md
1 parent 71c6122 commit 0e51bee

File tree

1 file changed

+20
-282
lines changed

1 file changed

+20
-282
lines changed

README.md

Lines changed: 20 additions & 282 deletions
Original file line numberDiff line numberDiff line change
@@ -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
7074
mkdir -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
8899
FROM 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-
---
97107
services:
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
116117
docker 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

Comments
 (0)