Files
OnlineJudge/docs/superpowers/plans/2026-06-14-debian-slim-image.md
2026-06-14 09:13:05 -06:00

5.7 KiB

Debian Slim Backend Image Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Replace the Alpine backend image with Debian slim while improving dependency installation speed and preserving runtime behavior.

Architecture: Keep the existing single-stage backend image and process model. Use Debian runtime packages plus manylinux Python wheels, then adapt the shell entrypoint and nginx worker account to Debian conventions.

Tech Stack: Docker BuildKit, Python 3.12 slim-bookworm, Debian apt, POSIX shell, nginx, supervisord


Task 1: Establish Migration Checks

Files:

  • Inspect: Dockerfile

  • Inspect: deploy/entrypoint.sh

  • Inspect: deploy/nginx/nginx.conf

  • Step 1: Verify the current base image check fails

Run:

grep -q '^FROM python:3\.12-slim-bookworm$' Dockerfile

Expected: exit status 1 because the current image is Alpine.

  • Step 2: Verify the current shell portability check fails

Run:

sh -n deploy/entrypoint.sh
! grep -n '\[\[' deploy/entrypoint.sh

Expected: sh -n succeeds, but the grep assertion exits non-zero because the script contains [[.

  • Step 3: Verify the current nginx user check fails

Run:

grep -q '^user www-data;$' deploy/nginx/nginx.conf

Expected: exit status 1 because the configuration uses the Alpine nginx account.

Task 2: Migrate the Docker Build

Files:

  • Modify: Dockerfile

  • Step 1: Replace the Alpine build with Debian slim

Use this package and cache structure:

FROM python:3.12-slim-bookworm
ARG TARGETARCH
ARG TARGETVARIANT

ENV OJ_ENV=production
WORKDIR /app

COPY ./deploy/requirements.txt /app/deploy/

RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=apt-cache-$TARGETARCH$TARGETVARIANT-final \
    --mount=type=cache,target=/root/.cache/pip,id=pip-cache-$TARGETARCH$TARGETVARIANT-final \
    <<EOS
set -ex
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
apt-get update
apt-get install -y --no-install-recommends \
  ca-certificates \
  clang-format \
  curl \
  libjpeg62-turbo \
  libpq5 \
  nginx \
  openssl \
  passwd \
  supervisor \
  unzip \
  zlib1g
pip install -r /app/deploy/requirements.txt
rm -rf /var/lib/apt/lists/*
EOS

Keep the existing application copy, permissions, health check, exposed port, and entrypoint.

  • Step 2: Run Dockerfile static checks

Run:

grep -q '^FROM python:3\.12-slim-bookworm$' Dockerfile
! grep -nE 'apk|alpine|gcc|python3-dev|--no-cache-dir' Dockerfile
grep -q 'target=/var/cache/apt' Dockerfile
grep -q 'target=/root/.cache/pip' Dockerfile

Expected: all checks exit 0.

Task 3: Make Runtime Configuration Debian-Compatible

Files:

  • Modify: deploy/entrypoint.sh

  • Modify: deploy/nginx/nginx.conf

  • Step 1: Replace non-POSIX tests and Alpine account commands

Use POSIX [ ] tests. Replace runtime account creation with:

if ! getent group spj >/dev/null; then
    groupadd --system --gid 903 spj
fi
if ! id -u server >/dev/null 2>&1; then
    useradd --system --uid 900 --gid spj --no-create-home --shell /usr/sbin/nologin server
fi

Quote filesystem paths and variable expansions touched by the script.

  • Step 2: Change the nginx worker user

Set the first line of deploy/nginx/nginx.conf to:

user www-data;
  • Step 3: Run runtime configuration checks

Run:

sh -n deploy/entrypoint.sh
! grep -n '\[\[' deploy/entrypoint.sh
! grep -nE 'addgroup|adduser' deploy/entrypoint.sh
grep -q 'groupadd --system --gid 903 spj' deploy/entrypoint.sh
grep -q 'useradd --system --uid 900 --gid spj' deploy/entrypoint.sh
grep -q '^user www-data;$' deploy/nginx/nginx.conf

Expected: all checks exit 0.

Task 4: Build And Inspect The Image

Files:

  • Verify: Dockerfile

  • Verify: deploy/entrypoint.sh

  • Verify: deploy/nginx/nginx.conf

  • Step 1: Build the backend image

Run:

DOCKER_BUILDKIT=1 docker build --progress=plain -t onlinejudge-backend:slim .

Expected: exit status 0, with Python dependencies installed from wheels.

  • Step 2: Verify native dependencies and runtime tools

Run:

docker run --rm --entrypoint /bin/sh onlinejudge-backend:slim -c \
  'python -c "from PIL import Image; import lxml.etree; import psycopg" &&
   clang-format --version &&
   nginx -t -c /app/deploy/nginx/nginx.conf &&
   supervisord --version &&
   openssl version &&
   groupadd --help >/dev/null &&
   useradd --help >/dev/null'

Expected: exit status 0.

  • Step 3: Exercise distribution-specific entrypoint startup

Run the image with unreachable local service names and a timeout:

timeout 15 docker run --rm \
  -e POSTGRES_HOST=127.0.0.1 \
  -e REDIS_HOST=127.0.0.1 \
  -e JUDGE_SERVER_TOKEN=test \
  onlinejudge-backend:slim

Expected: the application may time out while retrying database migration, but output must not contain shell syntax errors, missing groupadd/useradd, missing shared libraries, or an unknown nginx user.

Task 5: Final Verification

Files:

  • Verify: all modified files

  • Step 1: Run repository checks

Run:

git diff --check
sh -n deploy/entrypoint.sh
git status --short
git diff -- Dockerfile deploy/entrypoint.sh deploy/nginx/nginx.conf

Expected: no whitespace errors or shell syntax errors; the diff contains only the approved migration and its documentation.

  • Step 2: Record build evidence

Record the image ID and size:

docker image inspect onlinejudge-backend:slim --format '{{.Id}} {{.Size}}'

Expected: one image ID and byte size.