pratikbin commited on
Commit
46a104e
·
1 Parent(s): e36d11c

refactor(docker): migrate to bake with multi-variant distroless images

Browse files
Files changed (4) hide show
  1. .github/workflows/docker.yml +43 -27
  2. Dockerfile +44 -12
  3. README.md +34 -0
  4. docker-bake.hcl +88 -0
.github/workflows/docker.yml CHANGED
@@ -12,12 +12,31 @@ env:
12
  permissions:
13
  contents: read
14
  packages: write
15
- attestations: write
16
- id-token: write
17
 
18
  jobs:
19
- docker:
20
  runs-on: ubuntu-latest
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  steps:
22
  - uses: actions/checkout@v6
23
 
@@ -34,34 +53,31 @@ jobs:
34
  username: ${{ github.actor }}
35
  password: ${{ secrets.GITHUB_TOKEN }}
36
 
37
- - name: Extract metadata
38
  id: meta
39
  uses: docker/metadata-action@v6
40
  with:
41
  images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
42
  tags: |
43
- type=ref,event=branch
44
- type=ref,event=pr
45
- type=semver,pattern={{version}}
46
- type=semver,pattern={{major}}.{{minor}}
47
- type=sha,prefix=sha-
48
-
49
- - name: Build and push
50
- id: push
51
- uses: docker/build-push-action@v7
52
- with:
53
- context: .
54
- platforms: linux/amd64,linux/arm64
55
- tags: ${{ steps.meta.outputs.tags }}
56
- labels: ${{ steps.meta.outputs.labels }}
57
- cache-from: type=gha
58
- cache-to: type=gha,mode=max
59
- provenance: true
60
- sbom: true
61
 
62
- - name: Attest build provenance
63
- uses: actions/attest-build-provenance@v4
 
64
  with:
65
- subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
66
- subject-digest: ${{ steps.push.outputs.digest }}
67
- push-to-registry: true
 
 
 
 
 
 
 
12
  permissions:
13
  contents: read
14
  packages: write
 
 
15
 
16
  jobs:
17
+ docker-variant-tags:
18
  runs-on: ubuntu-latest
19
+ strategy:
20
+ fail-fast: false
21
+ matrix:
22
+ include:
23
+ - variant: ""
24
+ bake_target: runtime
25
+ - variant: nonroot
26
+ bake_target: runtime-nonroot
27
+ - variant: code
28
+ bake_target: runtime-code
29
+ - variant: code-nonroot
30
+ bake_target: runtime-code-nonroot
31
+ - variant: slim
32
+ bake_target: runtime-slim
33
+ - variant: slim-nonroot
34
+ bake_target: runtime-slim-nonroot
35
+ - variant: code-slim
36
+ bake_target: runtime-code-slim
37
+ - variant: code-slim-nonroot
38
+ bake_target: runtime-code-slim-nonroot
39
+
40
  steps:
41
  - uses: actions/checkout@v6
42
 
 
53
  username: ${{ github.actor }}
54
  password: ${{ secrets.GITHUB_TOKEN }}
55
 
56
+ - name: Extract metadata (variant)
57
  id: meta
58
  uses: docker/metadata-action@v6
59
  with:
60
  images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
61
  tags: |
62
+ type=ref,event=branch,suffix=${{ matrix.variant != '' && format('-{0}', matrix.variant) || '' }}
63
+ type=ref,event=pr,suffix=${{ matrix.variant != '' && format('-{0}', matrix.variant) || '' }}
64
+ type=semver,pattern={{version}},suffix=${{ matrix.variant != '' && format('-{0}', matrix.variant) || '' }}
65
+ type=semver,pattern={{major}}.{{minor}},suffix=${{ matrix.variant != '' && format('-{0}', matrix.variant) || '' }}
66
+ type=semver,pattern={{major}},suffix=${{ matrix.variant != '' && format('-{0}', matrix.variant) || '' }}
67
+ type=sha,prefix=${{ matrix.variant != '' && format('{0}-', matrix.variant) || 'sha-' }}
68
+ type=raw,value=${{ matrix.variant }},enable=${{ matrix.variant != '' }}
69
+ type=raw,value=latest,enable=${{ matrix.variant == '' }}
 
 
 
 
 
 
 
 
 
 
70
 
71
+ - name: Build and push variant (bake)
72
+ id: bake
73
+ uses: docker/bake-action@v7
74
  with:
75
+ files: |
76
+ ./docker-bake.hcl
77
+ cwd://${{ steps.meta.outputs.bake-file-tags }}
78
+ cwd://${{ steps.meta.outputs.bake-file-labels }}
79
+ targets: ${{ matrix.bake_target }}
80
+ push: true
81
+ set: |
82
+ *.cache-from=type=gha
83
+ *.cache-to=type=gha,mode=max
Dockerfile CHANGED
@@ -1,5 +1,8 @@
 
 
 
1
  # ---- Build stage: compile native extensions, build wheel ----
2
- FROM python:3.11-slim AS builder
3
 
4
  RUN apt-get update && \
5
  apt-get install -y --no-install-recommends \
@@ -13,34 +16,41 @@ WORKDIR /build
13
 
14
  # Layer 1: install deps only (cached unless pyproject.toml/uv.lock change)
15
  COPY pyproject.toml uv.lock README.md ./
16
- # Stub package so uv can resolve the local ".[proxy]" without full source
17
  RUN mkdir -p headroom && touch headroom/__init__.py
 
18
  RUN --mount=type=cache,target=/root/.cache/uv \
19
- uv pip install --system ".[proxy]"
20
 
21
  # Layer 2: copy real source, reinstall only headroom-ai (no deps)
22
  COPY headroom/ headroom/
23
  RUN --mount=type=cache,target=/root/.cache/uv \
24
  uv pip install --system --no-deps --reinstall-package headroom-ai .
25
 
26
- # ---- Runtime stage: minimal image with only what's needed ----
27
- FROM python:3.11-slim AS runtime
 
 
28
 
29
  RUN apt-get update && \
30
  apt-get install -y --no-install-recommends curl && \
31
  rm -rf /var/lib/apt/lists/*
32
 
33
- RUN groupadd --gid 1000 headroom && \
34
- useradd --uid 1000 --gid headroom --create-home headroom
35
-
36
  COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
37
  COPY --from=builder /usr/local/bin/headroom /usr/local/bin/headroom
38
 
39
- RUN mkdir -p /data /home/headroom/.headroom && \
40
- chown -R headroom:headroom /data /home/headroom/.headroom
 
 
 
 
 
 
 
41
 
42
- USER headroom
43
- WORKDIR /home/headroom
44
 
45
  ENV HEADROOM_HOST=0.0.0.0 \
46
  PYTHONUNBUFFERED=1 \
@@ -50,3 +60,25 @@ EXPOSE 8787
50
 
51
  ENTRYPOINT ["headroom", "proxy"]
52
  CMD ["--host", "0.0.0.0", "--port", "8787"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ARG PYTHON_VERSION=3.11
2
+ ARG DISTROLESS_IMAGE=gcr.io/distroless/python3-debian13
3
+
4
  # ---- Build stage: compile native extensions, build wheel ----
5
+ FROM python:${PYTHON_VERSION}-slim AS builder
6
 
7
  RUN apt-get update && \
8
  apt-get install -y --no-install-recommends \
 
16
 
17
  # Layer 1: install deps only (cached unless pyproject.toml/uv.lock change)
18
  COPY pyproject.toml uv.lock README.md ./
19
+ # Stub package so uv can resolve the local extras without full source
20
  RUN mkdir -p headroom && touch headroom/__init__.py
21
+ ARG HEADROOM_EXTRAS=proxy,code
22
  RUN --mount=type=cache,target=/root/.cache/uv \
23
+ uv pip install --system ".[${HEADROOM_EXTRAS}]"
24
 
25
  # Layer 2: copy real source, reinstall only headroom-ai (no deps)
26
  COPY headroom/ headroom/
27
  RUN --mount=type=cache,target=/root/.cache/uv \
28
  uv pip install --system --no-deps --reinstall-package headroom-ai .
29
 
30
+ # ---- Runtime stage (python-slim): supports root/nonroot via build arg ----
31
+ FROM python:${PYTHON_VERSION}-slim AS runtime-slim-base
32
+
33
+ ARG RUNTIME_USER=nonroot
34
 
35
  RUN apt-get update && \
36
  apt-get install -y --no-install-recommends curl && \
37
  rm -rf /var/lib/apt/lists/*
38
 
 
 
 
39
  COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
40
  COPY --from=builder /usr/local/bin/headroom /usr/local/bin/headroom
41
 
42
+ RUN mkdir -p /home/nonroot /data && \
43
+ if [ "$RUNTIME_USER" = "nonroot" ]; then \
44
+ groupadd --gid 1000 nonroot && \
45
+ useradd --uid 1000 --gid nonroot --create-home nonroot && \
46
+ mkdir -p /home/nonroot/.headroom && \
47
+ chown -R nonroot:nonroot /data /home/nonroot; \
48
+ else \
49
+ mkdir -p /root/.headroom; \
50
+ fi
51
 
52
+ USER ${RUNTIME_USER}
53
+ WORKDIR /home/nonroot
54
 
55
  ENV HEADROOM_HOST=0.0.0.0 \
56
  PYTHONUNBUFFERED=1 \
 
60
 
61
  ENTRYPOINT ["headroom", "proxy"]
62
  CMD ["--host", "0.0.0.0", "--port", "8787"]
63
+
64
+ FROM ${DISTROLESS_IMAGE} AS runtime-slim
65
+
66
+ ARG RUNTIME_USER=nonroot
67
+
68
+ COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
69
+
70
+ USER ${RUNTIME_USER}
71
+ WORKDIR /app
72
+
73
+ ENV HEADROOM_HOST=0.0.0.0 \
74
+ PYTHONUNBUFFERED=1 \
75
+ PYTHONDONTWRITEBYTECODE=1 \
76
+ PYTHONPATH=/usr/local/lib/python3.11/site-packages
77
+
78
+ EXPOSE 8787
79
+
80
+ ENTRYPOINT ["python3", "-m", "headroom.cli", "proxy"]
81
+ CMD ["--host", "0.0.0.0", "--port", "8787"]
82
+
83
+ # Default published image remains python-slim runtime
84
+ FROM runtime-slim-base AS runtime
README.md CHANGED
@@ -390,6 +390,40 @@ pip install "headroom-ai[langchain]" # LangChain (experimental)
390
  pip install "headroom-ai[evals]" # Evaluation framework only
391
  ```
392
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
393
  Python 3.10+
394
 
395
  ---
 
390
  pip install "headroom-ai[evals]" # Evaluation framework only
391
  ```
392
 
393
+ ### Container images (GHCR tags)
394
+
395
+ - supported platforms: `linux/amd64`, `linux/arm64`
396
+ - tags `:code` - image with Code-Aware Compression (AST-based) i.e. `pip install "headroom-ai[proxy,code]"`
397
+ - tags `:slim` - image with distorless base
398
+
399
+ | Tag | | Extras | Docker Bake target |
400
+ |---------------------|------------------------------------------------------|--------------|-----------------------------|
401
+ | `<version>` | ```ghcr.io/chopratejas/headroom:<version>``` | `proxy` | `runtime` |
402
+ | `latest` | ```ghcr.io/chopratejas/headroom:latest``` | `proxy` | `runtime` |
403
+ | `nonroot` | ```ghcr.io/chopratejas/headroom:nonroot``` | `proxy` | `runtime-nonroot` |
404
+ | `code` | ```ghcr.io/chopratejas/headroom:code``` | `proxy,code` | `runtime-code` |
405
+ | `code-nonroot` | ```ghcr.io/chopratejas/headroom:code-nonroot``` | `proxy,code` | `runtime-code-nonroot` |
406
+ | `slim` | ```ghcr.io/chopratejas/headroom:slim``` | `proxy` | `runtime-slim` |
407
+ | `slim-nonroot` | ```ghcr.io/chopratejas/headroom:slim-nonroot``` | `proxy` | `runtime-slim-nonroot` |
408
+ | `code-slim` | ```ghcr.io/chopratejas/headroom:code-slim``` | `proxy,code` | `runtime-code-slim` |
409
+ | `code-slim-nonroot` | ```ghcr.io/chopratejas/headroom:code-slim-nonroot``` | `proxy,code` | `runtime-code-slim-nonroot` |
410
+
411
+ ### Docker Bake
412
+
413
+ ```bash
414
+ # List all available build targets
415
+ docker buildx bake --list targets
416
+
417
+ # Build default image locally (proxy + nonroot)
418
+ docker buildx bake runtime-default
419
+
420
+ # Build one variant and load to local Docker image store
421
+ docker buildx bake runtime-code-slim-nonroot \
422
+ --set runtime-code-slim-nonroot.platform=linux/amd64 \
423
+ --set runtime-code-slim-nonroot.tags=headroom:local \
424
+ --load
425
+ ```
426
+
427
  Python 3.10+
428
 
429
  ---
docker-bake.hcl ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ target "docker-metadata-action" {}
2
+
3
+ target "_common" {
4
+ context = "."
5
+ dockerfile = "Dockerfile"
6
+ platforms = ["linux/amd64", "linux/arm64"]
7
+ }
8
+
9
+ target "runtime-default" {
10
+ inherits = ["_common", "docker-metadata-action"]
11
+ target = "runtime"
12
+ args = {
13
+ HEADROOM_EXTRAS = "proxy"
14
+ RUNTIME_USER = "nonroot"
15
+ }
16
+ }
17
+
18
+ target "runtime" {
19
+ inherits = ["_common", "docker-metadata-action"]
20
+ target = "runtime"
21
+ args = {
22
+ HEADROOM_EXTRAS = "proxy"
23
+ RUNTIME_USER = "root"
24
+ }
25
+ }
26
+
27
+ target "runtime-nonroot" {
28
+ inherits = ["_common", "docker-metadata-action"]
29
+ target = "runtime"
30
+ args = {
31
+ HEADROOM_EXTRAS = "proxy"
32
+ RUNTIME_USER = "nonroot"
33
+ }
34
+ }
35
+
36
+ target "runtime-code" {
37
+ inherits = ["_common", "docker-metadata-action"]
38
+ target = "runtime"
39
+ args = {
40
+ HEADROOM_EXTRAS = "proxy,code"
41
+ RUNTIME_USER = "root"
42
+ }
43
+ }
44
+
45
+ target "runtime-code-nonroot" {
46
+ inherits = ["_common", "docker-metadata-action"]
47
+ target = "runtime"
48
+ args = {
49
+ HEADROOM_EXTRAS = "proxy,code"
50
+ RUNTIME_USER = "nonroot"
51
+ }
52
+ }
53
+
54
+ target "runtime-slim" {
55
+ inherits = ["_common", "docker-metadata-action"]
56
+ target = "runtime-slim"
57
+ args = {
58
+ HEADROOM_EXTRAS = "proxy"
59
+ RUNTIME_USER = "root"
60
+ }
61
+ }
62
+
63
+ target "runtime-slim-nonroot" {
64
+ inherits = ["_common", "docker-metadata-action"]
65
+ target = "runtime-slim"
66
+ args = {
67
+ HEADROOM_EXTRAS = "proxy"
68
+ RUNTIME_USER = "nonroot"
69
+ }
70
+ }
71
+
72
+ target "runtime-code-slim" {
73
+ inherits = ["_common", "docker-metadata-action"]
74
+ target = "runtime-slim"
75
+ args = {
76
+ HEADROOM_EXTRAS = "proxy,code"
77
+ RUNTIME_USER = "root"
78
+ }
79
+ }
80
+
81
+ target "runtime-code-slim-nonroot" {
82
+ inherits = ["_common", "docker-metadata-action"]
83
+ target = "runtime-slim"
84
+ args = {
85
+ HEADROOM_EXTRAS = "proxy,code"
86
+ RUNTIME_USER = "nonroot"
87
+ }
88
+ }