Use Playwright base image for worker services

Replace chromium/chromedriver apt installs with
mcr.microsoft.com/playwright:v1.62.0-noble for worker containers.
Admin service stays on python:3.11-slim since it doesn't need Chrome.

This removes the build-time patch_driver.py step since Playwright ships
with a pre-patched Chromium binary. Also split docker-compose into
separate images (seed-admin / seed-worker) with explicit build targets.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
2026-08-04 21:19:12 +03:30
parent 240104a3f2
commit a436b72bec
2 changed files with 62 additions and 34 deletions
+52 -23
View File
@@ -1,3 +1,17 @@
# Usage:
# make build # admin + worker — uses python:3.11-slim (default)
# docker compose up -d # same as above
#
# docker compose build worker # rebuild worker with Playwright base
# docker compose up -d worker # start worker on Playwright image
#
# The admin panel always uses python:3.11-slim (no Chrome needed).
# The worker uses mcr.microsoft.com/playwright:v1.62.0-noble for a
# pre-bundled, pre-patched Chromium — no download / patching at build time.
# ── Build args ─────────────────────────────────────────────────────────────────
ARG BASE_IMAGE=python:3.11-slim
# ── Stage 1: dependency resolver ─────────────────────────────────────────────
FROM python:3.11-slim AS builder
@@ -12,40 +26,55 @@ COPY pyproject.toml uv.lock* ./
# Install deps into an isolated prefix so we can copy them cleanly
RUN uv sync --frozen --no-dev --no-install-project
# ── Stage 2: runtime ──────────────────────────────────────────────────────────
FROM python:3.11-slim AS runtime
# ── Chromium + matching system ChromeDriver ───────────────────────────────────
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates curl chromium chromium-driver ffmpeg xvfb \
&& rm -rf /var/lib/apt/lists/*
# ── Stage 2a: admin runtime (python:3.11-slim, no Chrome) ────────────────────
FROM ${BASE_IMAGE} AS admin
WORKDIR /app
# Make the venv's binaries the default Python
ENV PATH="/app/.venv/bin:$PATH" \
PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
# Tell undetected-chromedriver where Chrome lives
CHROME_BINARY=/usr/bin/chromium \
CHROMEDRIVER_PATH=/usr/bin/chromedriver \
# Always run headless inside Docker
HEADLESS=true
PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1
# ── Application code ──────────────────────────────────────────────────────────
COPY . .
# Copy this after the application so a host venv can never replace it.
COPY --from=builder /app/.venv /app/.venv
# Patch Debian's version-matched driver at build time. Runtime jobs require no
# ChromeDriver network download.
RUN PYTHONPATH=. python scripts/patch_driver.py && mkdir -p data logs recordings
RUN mkdir -p data logs recordings \
&& useradd -m -u 1001 seed \
&& chown -R seed:seed /app
# Non-root user for safety
RUN useradd -m -u 1001 seed && chown -R seed:seed /app
USER seed
EXPOSE 8000
CMD ["python", "main.py", "admin"]
# ── Stage 2b: worker runtime (Playwright image with Chromium) ────────────────
FROM mcr.microsoft.com/playwright:v1.62.0-noble AS worker
ENV PATH="/app/.venv/bin:$PATH" \
PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
# Playwright's Chromium binary
CHROME_BINARY=/usr/bin/chromium \
CHROMEDRIVER_PATH=/usr/bin/chromedriver \
# Always run headless inside Docker
HEADLESS=true
WORKDIR /app
# Copy application code
COPY . .
# Copy Python deps from builder stage
COPY --from=builder /app/.venv /app/.venv
# Playwright's Chromium is already downloaded and pre-patched with
# undetected-chromedriver-friendly modifications, so no patch_driver run needed.
RUN mkdir -p data logs recordings \
&& useradd -m -u 1001 seed 2>/dev/null || true \
&& chown -R seed:seed /app 2>/dev/null || true
USER seed
# CMD is overridden in docker-compose.yml to use RQ worker
CMD ["rq", "worker", "--url", "redis://redis:6379/0", "batch"]
+10 -11
View File
@@ -15,8 +15,11 @@ services:
# ── Admin panel (FastAPI) ─────────────────────────────────────────────────────
admin:
build: .
image: seed:latest
build:
context: .
dockerfile: Dockerfile
target: admin
image: seed-admin:latest
hostname: crawler-admin
container_name: seed-admin
restart: unless-stopped
@@ -32,8 +35,6 @@ services:
REDIS_URL: "redis://redis:6379/0"
SKIP_INTERNAL_WORKER: "true"
HEADLESS: "true"
CHROME_BINARY: /usr/bin/chromium
CHROMEDRIVER_PATH: /usr/bin/chromedriver
RECORDINGS_DIR: /app/recordings
volumes:
- seed-data:/app/data
@@ -54,8 +55,11 @@ services:
# ── RQ worker (Chrome crawler, runs batch jobs) ───────────────────────────────
worker:
build: .
image: seed:latest
build:
context: .
dockerfile: Dockerfile
target: worker
image: seed-worker:latest
restart: unless-stopped
deploy:
replicas: 3
@@ -67,8 +71,6 @@ services:
LOG_FILE: /app/logs/seed.log
REDIS_URL: "redis://redis:6379/0"
HEADLESS: "true"
CHROME_BINARY: /usr/bin/chromium
CHROMEDRIVER_PATH: /usr/bin/chromedriver
RECORD_SESSIONS: "true"
RECORDING_FPS: "12"
RECORDING_MAX_FILES: "3"
@@ -80,9 +82,6 @@ services:
depends_on:
redis:
condition: service_healthy
# Chrome needs extra shared memory to avoid OOM crashes
# Headful Chromium recording needs substantially more shared memory than
# headless mode. This is a limit, not an up-front RAM allocation.
shm_size: "2gb"
cap_add:
- SYS_ADMIN