-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDockerfile.distroless
More file actions
79 lines (64 loc) · 3.49 KB
/
Copy pathDockerfile.distroless
File metadata and controls
79 lines (64 loc) · 3.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# Distroless Docker build for Agentomatic
# Minimal attack surface using Google's distroless base — runs as the
# built-in ``nonroot`` account (numeric UID 65532) so images honour
# Kubernetes ``runAsNonRoot`` admission policies out of the box.
# ---- Build stage ------------------------------------------------------------
# Python 3.11 on purpose: ``distroless/python3-debian12`` ships Debian 12's
# Python 3.11, and dependencies must be built for the interpreter that will
# actually import them. Building on 3.12 produced an image that could not start
# at all — the venv's ``bin/python`` symlinked to the builder's 3.12 binary,
# which does not exist in the runtime stage, so the ENTRYPOINT was a dangling
# symlink; and even resolved, cp312 wheels cannot be imported under 3.11.
FROM python:3.11-slim AS builder
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
UV_LINK_MODE=copy
# Install system dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
curl \
&& rm -rf /var/lib/apt/lists/*
# Install uv from PyPI at a pinned version. An unpinned `uv:latest` image made
# builds irreproducible, and PyPI is already required by every other layer.
ARG UV_VERSION=0.8.17
RUN pip install --no-cache-dir "uv==${UV_VERSION}"
WORKDIR /app
# README.md is required: pyproject.toml declares it as the project readme, so
# installing the project below fails without it.
COPY pyproject.toml uv.lock README.md ./
COPY src/ ./src/
# ``--target`` instead of a virtualenv: the runtime stage runs the distroless
# image's own interpreter, which cannot use a venv built around a different
# Python binary. A plain directory on ``PYTHONPATH`` works with any 3.11.
#
# Extras matter here: a bare install ships core dependencies only, leaving the
# image without sqlalchemy, langgraph, prometheus-client or pyjwt — /metrics
# served nothing, DATABASE_URL failed with "No module named 'sqlalchemy'", and
# JWT auth could not be enabled. ``db-postgres`` and ``openai`` are named
# separately because ``all`` deliberately omits provider SDKs and the
# PostgreSQL driver.
RUN --mount=type=cache,target=/root/.cache/uv \
uv pip install --target=/app/deps ".[all,db-postgres,openai]"
# ---- Runtime stage ----------------------------------------------------------
FROM gcr.io/distroless/python3-debian12:nonroot
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PYTHONPATH="/app/deps"
WORKDIR /app
COPY --from=builder --chown=65532:65532 /app/deps /app/deps
COPY --from=builder --chown=65532:65532 /app/src /app/src
COPY --from=builder --chown=65532:65532 /app/pyproject.toml /app/
# Explicit numeric UID so Kubernetes ``runAsNonRoot`` admission does not
# have to introspect the base image at admit-time.
USER 65532:65532
EXPOSE 8000
# Distroless has no shell, so run the CLI as a module through the base image's
# own interpreter; dependencies come from PYTHONPATH. Invoking the console
# script directly would go through its shebang, which points at the builder's
# interpreter and is not present here.
ENTRYPOINT ["/usr/bin/python3", "-m", "agentomatic.cli.commands"]
CMD ["run", "--agents-dir", "agents", "--host", "0.0.0.0", "--port", "8000"]
# No shell and no curl in this image — hit /health with the base interpreter
# instead (exec form, so no shell is needed to run this CMD either).
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD ["/usr/bin/python3", "-c", "import urllib.request as u; u.urlopen('http://localhost:8000/health', timeout=5)"]