From 383dc385e5c38a13f5c9345387f1c4122b80cb79 Mon Sep 17 00:00:00 2001 From: Zanie Blue Date: Wed, 4 Sep 2024 11:41:40 -0500 Subject: [PATCH] Add multistage Docker example (#14) --- .github/workflows/ci.yaml | 27 ++++++++++++++++++++++++++- Dockerfile.multistage | 29 +++++++++++++++++++++++++++++ README.md | 9 +++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 Dockerfile.multistage diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 480672e..24b6a64 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -30,12 +30,27 @@ jobs: cache-to: type=gha,mode=min,scope=uv-docker-example outputs: type=docker,dest=/tmp/uv-docker-example.tar + - name: Build and export (multistage) + uses: docker/build-push-action@v6 + with: + file: Dockerfile.multistage + tags: uv-docker-example-multistage:latest + cache-from: type=gha,scope=uv-docker-example-multistage + cache-to: type=gha,mode=min,scope=uv-docker-example-multistage + outputs: type=docker,dest=/tmp/uv-docker-example-multistage.tar + - name: Upload artifact uses: actions/upload-artifact@v4 with: name: uv-docker-example path: /tmp/uv-docker-example.tar + - name: Upload artifact (multistage) + uses: actions/upload-artifact@v4 + with: + name: uv-docker-example-multistage + path: /tmp/uv-docker-example-multistage.tar + test: name: "test image" runs-on: ubuntu-latest @@ -49,9 +64,16 @@ jobs: name: uv-docker-example path: /tmp - - name: Load image + - name: Download artifact (multistage) + uses: actions/download-artifact@v4 + with: + name: uv-docker-example-multistage + path: /tmp + + - name: Load images run: | docker load --input /tmp/uv-docker-example.tar + docker load --input /tmp/uv-docker-example-multistage.tar docker image ls -a - name: Test command line @@ -61,3 +83,6 @@ jobs: run: | docker compose up --watch -d docker compose down + + - name: Test command line (multistage) + run: docker run uv-docker-example-multistage:latest hello diff --git a/Dockerfile.multistage b/Dockerfile.multistage new file mode 100644 index 0000000..e6d31b3 --- /dev/null +++ b/Dockerfile.multistage @@ -0,0 +1,29 @@ +# An example using multi-stage image builds to create a final image without uv. + +# First, build the application in the `/app` directory. +# See `Dockerfile` for details. +FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim AS builder +WORKDIR /app +RUN --mount=type=cache,target=/root/.cache/uv \ + --mount=type=bind,source=uv.lock,target=uv.lock \ + --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ + uv sync --frozen --no-install-project +ADD . /app +RUN --mount=type=cache,target=/root/.cache/uv \ + uv sync --frozen + + +# Then, use a final image without uv +FROM python:3.12-slim-bookworm +# It is important to use the image that matches the builder, as the path to the +# Python executable must be the same, e.g., using `python:3.11-slim-bookworm` +# will fail. + +# Copy the application from the builder +COPY --from=builder --chown=app:app /app /app + +# Place executables in the environment at the front of the path +ENV PATH="/app/.venv/bin:$PATH" + +# Run the FastAPI application by default +CMD ["fastapi", "dev", "--host", "0.0.0.0", "/app/src/uv_docker_example"] diff --git a/README.md b/README.md index fe8619b..3e5acec 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,9 @@ The [`Dockerfile`](./Dockerfile) defines the image and includes: - Placing environment executables on the `PATH` - Running the web application +The [`Dockerfile.multistage`](./Dockerfile.multistage) example extends the `Dockerfile` example to +use multistage builds to reduce the final size of the image. + ### Dockerignore file The [`.dockerignore`](./.dockerignore) file includes an entry for the `.venv` directory to ensure the @@ -99,3 +102,9 @@ To build the image without running anything: ```console $ docker build . ``` + +To build the multistage image: + +```console +$ docker build . --file Dockerfile.multistage +```