-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDockerfile.python
More file actions
93 lines (75 loc) · 3.83 KB
/
Copy pathDockerfile.python
File metadata and controls
93 lines (75 loc) · 3.83 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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# =============================================================================
# Stage 1: Builder
# Install build tools and resolve dependencies here so the runtime image
# never contains compilers or build headers [1][16][18][26]
# =============================================================================
FROM nvidia/cuda:12.4.1-devel-ubuntu22.04 AS builder
# PYTHONDONTWRITEBYTECODE prevents .pyc files from being written,
# PYTHONUNBUFFERED ensures logs are sent directly to stdout/stderr
# without buffering — critical for Docker log capture [2][3]
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
DEBIAN_FRONTEND=noninteractive \
UV_PYTHON_INSTALL_DIR=/app/.venv-python
WORKDIR /app
# Install build-essential and tools needed by uv to manage standalone Python
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
ca-certificates \
curl \
&& rm -rf /var/lib/apt/lists/*
# Copy uv binary from the official image [12]
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
# Copy dependency files first to maximize Docker layer cache [1][26][27]
# If pyproject.toml and uv.lock are unchanged, this layer is reused
# on every subsequent build even when source code changes [26][27]
COPY pyproject.toml uv.lock ./
# Install all dependencies into a virtual environment inside the builder [12][16]
# --frozen ensures the lockfile is respected exactly [12]
# --no-install-project skips installing the d2ql package itself at this stage
RUN uv python install 3.12 && \
uv sync --frozen --no-install-project --link-mode=copy
# =============================================================================
# Stage 2: Runtime
# Start from a clean slim image — no compilers, no build tools [1][2][18][19]
# =============================================================================
FROM nvidia/cuda:12.4.1-runtime-ubuntu22.04
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
DEBIAN_FRONTEND=noninteractive
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# create a dedicated non-root user before copying any files [1][2][3][8]
# Running as root inside a container gives an attacker full container
# privileges if the application is compromised [3][8][25]
RUN groupadd --system d2ql && \
useradd --system --gid d2ql --no-create-home d2ql && \
mkdir -p /app/outputs && \
chown -R d2ql:d2ql /app/outputs && \
mkdir -p /tmp/uv-cache && \
chown -R d2ql:d2ql /tmp/uv-cache
# Copy the uv binary into the runtime image so `uv run` works [12]
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
# Copy the standalone Python binaries and resolved virtual environment from builder stage [12][16][17]
# Only the installed packages are copied — no compilers, no pip cache [1][16][26]
COPY --chown=d2ql:d2ql --from=builder /app/.venv-python /app/.venv-python
COPY --chown=d2ql:d2ql --from=builder /app/.venv /app/.venv
# Make the virtual environment the active Python for all subsequent commands [16][17]
ENV PATH="/app/.venv/bin:$PATH" \
UV_CACHE_DIR="/tmp/uv-cache"
# Copy application source last — this is the most frequently changing layer [1][26][27]
# Keeping it at the bottom means dependency layers above are cached on code changes [26][27]
COPY --chown=d2ql:d2ql python-agent/d2ql ./d2ql
COPY --chown=d2ql:d2ql python-agent/main.py ./
# drop privileges before the process starts [1][2][3][8][21]
# All previous RUN commands executed as root to install packages and set
# ownership; now we permanently switch to the non-root user [21]
USER d2ql
# Expose TensorBoard port
EXPOSE 6006
# provide a default CMD so the image is runnable standalone [1][4]
# docker-compose.yml overrides this with the --config argument,
# but `docker run python-agent` will work without extra flags [4]
CMD ["python", "main.py"]