Video Thumbnail for Lesson
6.3: Building the App Images

Building the Application Container Images

Before we can deploy the demo application we need Docker images for each service. These images are defined by Dockerfiles and then pushed to a container registry that our Kubernetes cluster can access.

A traditional docker build command only creates an image for the architecture of the machine doing the build. Because development laptops may differ from production servers, the repository includes tasks for building multi‑arch images with Docker Buildx.

Bootstrapping Buildx

# devops-directive-kubernetes-course/06-demo-application/Taskfile.yaml
$ docker buildx create --name mybuilder --driver docker-container --driver-opt network=host --use
$ docker buildx inspect mybuilder --bootstrap
$ docker buildx use mybuilder

The project also provides a task for running a local registry if you do not have credentials for a remote registry.

$ docker run -d -p 5000:5000 registry:2.8

Postgres migrations image

The database runs the public postgres image, but we build a small companion image to execute schema migrations.

FROM migrate/migrate:v4.17.1
COPY ./migrations/* /app/migrations/

Build commands are defined in the Taskfile to produce single or multi‑arch images.

Go API with ko

The Go service uses ko to build and publish the image without a Dockerfile.

KO_DOCKER_REPO='<registry>/<repo>' ko build --bare --tags=<tag> --platform=all

The resulting image is automatically pushed and includes binaries for many architectures.

Node.js API

The Node API uses a multi‑stage Dockerfile to install dependencies with npm ci and run the application as a non‑root user.

FROM node:20.14-bullseye-slim AS base
WORKDIR /usr/src/app
COPY package*.json ./
RUN --mount=type=cache,target=/usr/src/app/.npm \
  npm set cache /usr/src/app/.npm && \
  npm ci --only=production
USER node
COPY --chown=node:node ./src/ .
EXPOSE 3000
CMD ["node", "index.js"]

Both single and multi‑arch build tasks are available using Buildx.

React client

The React frontend is built once using Node and served from an nginx container.

FROM node:20.14-bullseye-slim AS build
WORKDIR /usr/src/app
COPY package*.json ./
RUN --mount=type=cache,target=/usr/src/app/.npm \
  npm set cache /usr/src/app/.npm && \
  npm install
COPY . .
RUN npm run build
FROM nginxinc/nginx-unprivileged:1.23-alpine-perl
COPY --link nginx.conf /etc/nginx/conf.d/default.conf
COPY --link --from=build usr/src/app/dist/ /usr/share/nginx/html
EXPOSE 8080

Python load generator

The load generator uses a multi‑stage Python image and Poetry to manage dependencies.

FROM python:3.12-slim AS generate-requirements
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
RUN curl -sSL https://install.python-poetry.org | python3 -
COPY pyproject.toml poetry.lock ./
RUN /root/.local/bin/poetry export --output requirements.txt

FROM python:3.12-slim AS production
COPY --from=generate-requirements /requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "main.py"]

Each service exposes tasks to build images locally and to push multi‑architecture images to a registry. Once the images are built and published, we can reference them from our Kubernetes manifests in the next section.