Skip to content

Commit 46a8c3e

Browse files
Refactor Dockerfile: factor out common layers and add dev entrypoint (#53)
* Refactor Dockerfile: factor out common layers and add dev entrypoint - Extract deps-common and dev-common stages shared by CUDA and ROCm - Move caddy/uv COPYs last in common stages (external, don't invalidate our layers) - Add deploy/entrypoint-dev.sh with recursive chown on /opt/pawn and /workspace - Install Rust in dev-common and run uv sync in dev stages so the engine is workspace-registered (fixes uv run triggering a rebuild on first use) - Entrypoint path simplified: COPY . . already includes it, CMD references deploy/entrypoint.sh directly instead of copying it to a separate location * Auto export HF_TOKEN in dev containers. * Refactor Dockerfile: use --no-install-recommends for build-essential installation in dev-common layer
1 parent 061b508 commit 46a8c3e

File tree

2 files changed

+96
-123
lines changed

2 files changed

+96
-123
lines changed

Dockerfile

Lines changed: 85 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -63,28 +63,36 @@ RUN maturin build --release
6363

6464

6565
# ═══════════════════════════════════════════════════════════════════════
66-
# CUDA stages (--extra cu128)
66+
# Shared deps base — everything before the GPU-specific uv sync
6767
# ═══════════════════════════════════════════════════════════════════════
6868

69-
# ── Deps (CUDA) ──────────────────────────────────────────────────────
70-
FROM python:3.12-slim AS deps
69+
FROM python:3.12-slim AS deps-common
7170

7271
RUN apt-get update && apt-get install -y --no-install-recommends \
7372
openssh-server tini \
7473
&& rm -rf /var/lib/apt/lists/* \
7574
&& mkdir -p /run/sshd
7675

77-
COPY --from=caddy /usr/local/bin/caddy /usr/local/bin/caddy
78-
7976
ENV PYTHONUNBUFFERED=1 \
8077
UV_LINK_MODE=copy \
8178
UV_CACHE_DIR=/tmp/uv-cache
8279

83-
COPY --from=ghcr.io/astral-sh/uv:0.10 /uv /uvx /bin/
84-
8580
WORKDIR /opt/pawn
8681
COPY pyproject.toml uv.lock ./
8782
COPY --from=builder /build/engine/target/wheels/*.whl /tmp/
83+
84+
# External binaries last — they don't depend on our layers, so placing
85+
# them here avoids invalidating the layers above on a caddy/uv release.
86+
COPY --from=caddy /usr/local/bin/caddy /usr/local/bin/caddy
87+
COPY --from=ghcr.io/astral-sh/uv:0.10 /uv /uvx /bin/
88+
89+
90+
# ═══════════════════════════════════════════════════════════════════════
91+
# CUDA stages (--extra cu128)
92+
# ═══════════════════════════════════════════════════════════════════════
93+
94+
# ── Deps (CUDA) ──────────────────────────────────────────────────────
95+
FROM deps-common AS deps
8896
RUN uv venv && \
8997
uv sync --extra cu128 --no-dev --frozen --no-install-workspace && \
9098
uv pip install /tmp/*.whl && rm -rf /tmp/*.whl ${UV_CACHE_DIR}
@@ -101,24 +109,53 @@ ENV PAWN_GIT_HASH=${GIT_HASH} \
101109
PYTHONPATH=/opt/pawn \
102110
PATH="/opt/pawn/.venv/bin:${PATH}"
103111

112+
RUN chmod +x deploy/entrypoint.sh
104113
EXPOSE 8888
105-
COPY deploy/entrypoint.sh /opt/pawn/entrypoint.sh
106-
RUN chmod +x /opt/pawn/entrypoint.sh
107114
ENTRYPOINT ["tini", "--"]
108-
CMD ["/opt/pawn/entrypoint.sh"]
115+
CMD ["/opt/pawn/deploy/entrypoint.sh"]
109116

110-
# ── Dev (CUDA) ───────────────────────────────────────────────────────
111-
# Built independently (not FROM runtime) so all /opt/pawn files are
112-
# owned by pawn from the start, avoiding a slow chown -R layer.
113-
FROM python:3.12-slim AS dev
114117

115-
RUN apt-get update && apt-get install -y --no-install-recommends \
118+
# ═══════════════════════════════════════════════════════════════════════
119+
# ROCm stages (--extra rocm)
120+
# Same python:3.12-slim base — the ROCm torch wheel (~2.8 GB) bundles
121+
# HIP, rocBLAS, MIOpen, etc. inside the wheel itself.
122+
# ═══════════════════════════════════════════════════════════════════════
123+
124+
# ── Deps (ROCm) ──────────────────────────────────────────────────────
125+
FROM deps-common AS deps-rocm
126+
RUN uv venv && \
127+
uv sync --extra rocm --no-dev --frozen --no-install-workspace && \
128+
uv pip install /tmp/*.whl && rm -rf /tmp/*.whl ${UV_CACHE_DIR}
129+
130+
# ── Runtime (ROCm) ───────────────────────────────────────────────────
131+
FROM deps-rocm AS runtime-rocm
132+
133+
COPY . .
134+
135+
ARG GIT_HASH=""
136+
ARG GIT_TAG=""
137+
ENV PAWN_GIT_HASH=${GIT_HASH} \
138+
PAWN_GIT_TAG=${GIT_TAG} \
139+
PYTHONPATH=/opt/pawn \
140+
PATH="/opt/pawn/.venv/bin:${PATH}"
141+
142+
RUN chmod +x deploy/entrypoint.sh
143+
EXPOSE 8888
144+
ENTRYPOINT ["tini", "--"]
145+
CMD ["/opt/pawn/deploy/entrypoint.sh"]
146+
147+
148+
# ═══════════════════════════════════════════════════════════════════════
149+
# Shared dev base — dev tools, non-root user, Claude Code, tmux
150+
# ═══════════════════════════════════════════════════════════════════════
151+
152+
FROM python:3.12-slim AS dev-common
153+
154+
RUN apt-get update && apt-get install -y --no-install-recommends build-essential \
116155
openssh-server tini tmux ripgrep jq curl git \
117156
&& rm -rf /var/lib/apt/lists/* \
118157
&& mkdir -p /run/sshd
119158

120-
COPY --from=caddy /usr/local/bin/caddy /usr/local/bin/caddy
121-
122159
ENV PYTHONUNBUFFERED=1 \
123160
UV_LINK_MODE=copy \
124161
UV_CACHE_DIR=/tmp/uv-cache
@@ -134,31 +171,18 @@ set -g renumber-windows on
134171
set -g set-clipboard on
135172
TMUX
136173

137-
COPY --from=ghcr.io/astral-sh/uv:0.10 /uv /uvx /bin/
138-
139-
# Create non-root user, then copy installed deps with correct ownership
174+
# Create non-root user
140175
RUN useradd -m -s /bin/bash pawn && \
141176
mkdir -p /opt/pawn && chown pawn:pawn /opt/pawn
142-
COPY --from=deps --chown=pawn:pawn /opt/pawn /opt/pawn
143177

144-
# Source code + entrypoint
178+
# Install Claude Code and Rust toolchain (for building the chess engine)
145179
USER pawn
146-
WORKDIR /opt/pawn
147-
COPY --chown=pawn:pawn . .
148-
149-
# Install Claude Code
150180
RUN curl -fsSL https://claude.ai/install.sh | bash
181+
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
151182

152-
ARG GIT_HASH=""
153-
ARG GIT_TAG=""
154-
ENV PAWN_GIT_HASH=${GIT_HASH} \
155-
PAWN_GIT_TAG=${GIT_TAG} \
156-
PYTHONPATH=/opt/pawn \
157-
PATH="/home/pawn/.local/bin:/opt/pawn/.venv/bin:${PATH}"
158-
159-
# Convenience script: drop into pawn user with claude in a tmux session.
183+
# Convenience script: drop into pawn user with claude in a tmux session
160184
USER root
161-
COPY <<'CLAUDE_DEV' /usr/local/bin/claude-dev
185+
COPY --chmod=755 <<'CLAUDE_DEV' /usr/local/bin/claude-dev
162186
#!/usr/bin/env bash
163187
set -euo pipefail
164188
SESSION="claude"
@@ -171,127 +195,65 @@ exec su - pawn -c "
171195
exec tmux attach -t $SESSION
172196
"
173197
CLAUDE_DEV
174-
RUN chmod +x /usr/local/bin/claude-dev
175198

176-
EXPOSE 8888
177-
COPY deploy/entrypoint.sh /opt/pawn/entrypoint.sh
178-
RUN chmod +x /opt/pawn/entrypoint.sh
179-
ENTRYPOINT ["tini", "--"]
180-
CMD ["/opt/pawn/entrypoint.sh"]
199+
# External binaries last (same rationale as deps-common)
200+
COPY --from=caddy /usr/local/bin/caddy /usr/local/bin/caddy
201+
COPY --from=ghcr.io/astral-sh/uv:0.10 /uv /uvx /bin/
181202

182203

183204
# ═══════════════════════════════════════════════════════════════════════
184-
# ROCm stages (--extra rocm)
185-
# Same python:3.12-slim base — the ROCm torch wheel (~2.8 GB) bundles
186-
# HIP, rocBLAS, MIOpen, etc. inside the wheel itself.
205+
# Dev images — GPU deps + source code on top of dev-common
206+
# Built independently from runtime/deps so every file in /opt/pawn
207+
# enters via COPY --chown=pawn:pawn, avoiding a slow chown -R layer
208+
# that would duplicate the multi-GB venv.
187209
# ═══════════════════════════════════════════════════════════════════════
188210

189-
# ── Deps (ROCm) ──────────────────────────────────────────────────────
190-
FROM python:3.12-slim AS deps-rocm
191-
192-
RUN apt-get update && apt-get install -y --no-install-recommends \
193-
openssh-server tini \
194-
&& rm -rf /var/lib/apt/lists/* \
195-
&& mkdir -p /run/sshd
196-
197-
COPY --from=caddy /usr/local/bin/caddy /usr/local/bin/caddy
198-
199-
ENV PYTHONUNBUFFERED=1 \
200-
UV_LINK_MODE=copy \
201-
UV_CACHE_DIR=/tmp/uv-cache
202-
203-
COPY --from=ghcr.io/astral-sh/uv:0.10 /uv /uvx /bin/
211+
# ── Dev (CUDA) ───────────────────────────────────────────────────────
212+
FROM dev-common AS dev
213+
COPY --from=deps --chown=pawn:pawn /opt/pawn /opt/pawn
204214

215+
USER pawn
205216
WORKDIR /opt/pawn
206-
COPY pyproject.toml uv.lock ./
207-
COPY --from=builder /build/engine/target/wheels/*.whl /tmp/
208-
RUN uv venv && \
209-
uv sync --extra rocm --no-dev --frozen --no-install-workspace && \
210-
uv pip install /tmp/*.whl && rm -rf /tmp/*.whl ${UV_CACHE_DIR}
211-
212-
# ── Runtime (ROCm) ───────────────────────────────────────────────────
213-
FROM deps-rocm AS runtime-rocm
217+
COPY --chown=pawn:pawn . .
214218

215-
COPY . .
219+
# Build the engine so uv run doesn't trigger a rebuild on first use
220+
RUN PATH="/home/pawn/.cargo/bin:${PATH}" \
221+
uv sync --extra cu128 --frozen
216222

217223
ARG GIT_HASH=""
218224
ARG GIT_TAG=""
219225
ENV PAWN_GIT_HASH=${GIT_HASH} \
220226
PAWN_GIT_TAG=${GIT_TAG} \
221227
PYTHONPATH=/opt/pawn \
222-
PATH="/opt/pawn/.venv/bin:${PATH}"
228+
PATH="/home/pawn/.cargo/bin:/home/pawn/.local/bin:/opt/pawn/.venv/bin:${PATH}"
223229

230+
USER root
231+
RUN chmod +x /opt/pawn/deploy/entrypoint-dev.sh /opt/pawn/deploy/entrypoint.sh
224232
EXPOSE 8888
225-
COPY deploy/entrypoint.sh /opt/pawn/entrypoint.sh
226-
RUN chmod +x /opt/pawn/entrypoint.sh
227233
ENTRYPOINT ["tini", "--"]
228-
CMD ["/opt/pawn/entrypoint.sh"]
234+
CMD ["/opt/pawn/deploy/entrypoint-dev.sh"]
229235

230236
# ── Dev (ROCm) ───────────────────────────────────────────────────────
231-
FROM python:3.12-slim AS dev-rocm
232-
233-
RUN apt-get update && apt-get install -y --no-install-recommends \
234-
openssh-server tini tmux ripgrep jq curl git \
235-
&& rm -rf /var/lib/apt/lists/* \
236-
&& mkdir -p /run/sshd
237-
238-
COPY --from=caddy /usr/local/bin/caddy /usr/local/bin/caddy
239-
240-
ENV PYTHONUNBUFFERED=1 \
241-
UV_LINK_MODE=copy \
242-
UV_CACHE_DIR=/tmp/uv-cache
243-
244-
# Developer-friendly tmux defaults
245-
RUN cat <<'TMUX' > /etc/tmux.conf
246-
set -g mouse on
247-
set -g history-limit 50000
248-
set -g default-terminal "tmux-256color"
249-
set -g base-index 1
250-
setw -g pane-base-index 1
251-
set -g renumber-windows on
252-
set -g set-clipboard on
253-
TMUX
254-
255-
COPY --from=ghcr.io/astral-sh/uv:0.10 /uv /uvx /bin/
256-
257-
# Create non-root user, then copy installed deps with correct ownership
258-
RUN useradd -m -s /bin/bash pawn && \
259-
mkdir -p /opt/pawn && chown pawn:pawn /opt/pawn
237+
FROM dev-common AS dev-rocm
260238
COPY --from=deps-rocm --chown=pawn:pawn /opt/pawn /opt/pawn
261239

262-
# Source code + entrypoint
263240
USER pawn
264241
WORKDIR /opt/pawn
265242
COPY --chown=pawn:pawn . .
266243

267-
# Install Claude Code
268-
RUN curl -fsSL https://claude.ai/install.sh | bash
244+
# Build the engine so uv run doesn't trigger a rebuild on first use
245+
RUN PATH="/home/pawn/.cargo/bin:${PATH}" \
246+
uv sync --extra rocm --frozen
269247

270248
ARG GIT_HASH=""
271249
ARG GIT_TAG=""
272250
ENV PAWN_GIT_HASH=${GIT_HASH} \
273251
PAWN_GIT_TAG=${GIT_TAG} \
274252
PYTHONPATH=/opt/pawn \
275-
PATH="/home/pawn/.local/bin:/opt/pawn/.venv/bin:${PATH}"
253+
PATH="/home/pawn/.cargo/bin:/home/pawn/.local/bin:/opt/pawn/.venv/bin:${PATH}"
276254

277255
USER root
278-
COPY <<'CLAUDE_DEV' /usr/local/bin/claude-dev
279-
#!/usr/bin/env bash
280-
set -euo pipefail
281-
SESSION="claude"
282-
exec su - pawn -c "
283-
if tmux has-session -t $SESSION 2>/dev/null; then
284-
exec tmux attach -t $SESSION
285-
fi
286-
tmux new-session -d -s $SESSION -c /opt/pawn
287-
tmux send-keys -t $SESSION 'cd /opt/pawn && claude --dangerously-skip-permissions' Enter
288-
exec tmux attach -t $SESSION
289-
"
290-
CLAUDE_DEV
291-
RUN chmod +x /usr/local/bin/claude-dev
292-
256+
RUN chmod +x /opt/pawn/deploy/entrypoint-dev.sh /opt/pawn/deploy/entrypoint.sh
293257
EXPOSE 8888
294-
COPY deploy/entrypoint.sh /opt/pawn/entrypoint.sh
295-
RUN chmod +x /opt/pawn/entrypoint.sh
296258
ENTRYPOINT ["tini", "--"]
297-
CMD ["/opt/pawn/entrypoint.sh"]
259+
CMD ["/opt/pawn/deploy/entrypoint-dev.sh"]

deploy/entrypoint-dev.sh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# Fix ownership on mount points — volumes arrive as root
5+
chown -R pawn:pawn /opt/pawn 2>/dev/null || true
6+
chown -R pawn:pawn /workspace 2>/dev/null || true
7+
8+
# Export the HF_TOKEN environment variable for the pawn user (set via RunPod console)
9+
echo "export HF_TOKEN=${HF_TOKEN}" >> /home/pawn/.bashrc
10+
11+
exec /opt/pawn/deploy/entrypoint.sh

0 commit comments

Comments
 (0)