diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish-cpu.yml similarity index 81% rename from .github/workflows/docker-publish.yml rename to .github/workflows/docker-publish-cpu.yml index c3b6453..02c06cc 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish-cpu.yml @@ -1,4 +1,4 @@ -name: Publish Docker Image +name: Publish CPU Image on: push: @@ -35,21 +35,23 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Log in to the Container registry + - name: Log in to GHCR uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Build and push Docker image + - name: Build and push CPU image uses: docker/build-push-action@v6 with: context: . file: ./Dockerfile + target: runtime-cpu platforms: linux/amd64,linux/arm64 push: true tags: ghcr.io/sudolulo/if-curator-headless:latest - cache-from: type=gha - cache-to: type=gha,mode=max + cache-from: type=gha,scope=cpu + cache-to: type=gha,mode=max,scope=cpu + github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/docker-publish-gpu.yml b/.github/workflows/docker-publish-gpu.yml new file mode 100644 index 0000000..956d23b --- /dev/null +++ b/.github/workflows/docker-publish-gpu.yml @@ -0,0 +1,54 @@ +name: Publish GPU Image + +on: + push: + branches: ["main"] + paths-ignore: + - "**.md" + - "docs/**" + - ".github/workflows/release.yml" + - ".github/workflows/lint.yml" + - ".github/dependabot.yml" + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Free up disk space + run: | + sudo rm -rf /usr/share/dotnet + sudo rm -rf /opt/ghc + sudo rm -rf "/usr/local/share/boost" + sudo rm -rf "$AGENT_TOOLSDIRECTORY" + echo "Disk space freed." + + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push GPU image + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + target: runtime-gpu + platforms: linux/amd64 + push: true + tags: ghcr.io/sudolulo/if-curator-headless:gpu + cache-from: type=gha,scope=gpu + cache-to: type=gha,mode=max,scope=gpu + github-token: ${{ secrets.GITHUB_TOKEN }} + diff --git a/Dockerfile b/Dockerfile index b69501d..dacb07f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,25 +1,16 @@ -# ── Platform-conditional base image ────────────────────────────────────── -# amd64: NVIDIA CUDA 12.6 runtime (matches cu126 torch wheels) -# arm64: Plain Ubuntu (CPU-only; no CUDA on ARM64) +# ── Build stage: CPU (amd64 + arm64) ───────────────────────────────────── +FROM --platform=$BUILDPLATFORM ubuntu:22.04 AS build -FROM --platform=$BUILDPLATFORM nvidia/cuda:12.6.3-runtime-ubuntu22.04 AS base-amd64 -FROM --platform=$BUILDPLATFORM ubuntu:22.04 AS base-arm64 - -# ── Build stage ─────────────────────────────────────────────────────────── ARG TARGETARCH -FROM base-${TARGETARCH} AS build - ENV DEBIAN_FRONTEND=noninteractive -# Certificates + curl RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates \ curl \ gnupg \ && rm -rf /var/lib/apt/lists/* -# Python 3.12 + system libs RUN apt-get update && apt-get install -y --no-install-recommends \ software-properties-common \ && add-apt-repository ppa:deadsnakes/ppa -y \ @@ -29,32 +20,73 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ && rm -rf /var/lib/apt/lists/* \ && ln -sf /usr/bin/python3.12 /usr/bin/python3 -# Install uv RUN curl -LsSf https://astral.sh/uv/install.sh | sh \ && cp /root/.local/bin/uv /usr/local/bin/uv WORKDIR /app -# Copy project files for deterministic, cached builds +# Install dependencies first (cached unless pyproject.toml/uv.lock change) COPY pyproject.toml uv.lock ./ +RUN uv sync --extra object --frozen \ + && uv cache clean + +# Copy source code (invalidates cache only when code changes) COPY if_curator/ if_curator/ COPY entrypoint.sh scheduler.py ./ -# Install dependencies — uv resolves per-platform via tool.uv.sources markers -# amd64: --extra gpu --extra object → CUDA torch + onnxruntime-gpu -# arm64: --extra object → CPU torch + onnxruntime -RUN if [ "$TARGETARCH" = "amd64" ]; then \ - uv sync --extra gpu --extra object; \ - else \ - uv sync --extra object; \ - fi \ - && uv add croniter \ - && uv cache clean - RUN chmod +x /app/entrypoint.sh -# ── Runtime stage ───────────────────────────────────────────────────────── -FROM build AS runtime +# ── Runtime stage: CPU ──────────────────────────────────────────────────── +FROM build AS runtime-cpu + +RUN groupadd -g 568 apps && useradd -u 568 -g apps -m -s /bin/bash appuser \ + && mkdir -p /models/.insightface /models/huggingface \ + && chown -R appuser:apps /app /models + +USER appuser +ENV HF_HOME=/models/huggingface INSIGHTFACE_HOME=/models + +HEALTHCHECK CMD test -f /app/entrypoint.sh || exit 1 +ENTRYPOINT ["tini", "--", "/app/entrypoint.sh"] + +# ── Build stage: GPU (amd64 only) ──────────────────────────────────────── +FROM --platform=linux/amd64 nvidia/cuda:12.6.3-runtime-ubuntu22.04 AS build-gpu + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + curl \ + gnupg \ + && rm -rf /var/lib/apt/lists/* + +RUN apt-get update && apt-get install -y --no-install-recommends \ + software-properties-common \ + && add-apt-repository ppa:deadsnakes/ppa -y \ + && apt-get update && apt-get install -y --no-install-recommends \ + python3.12 python3.12-venv python3.12-dev \ + libgl1 libglib2.0-0 libxext6 g++ tini \ + && rm -rf /var/lib/apt/lists/* \ + && ln -sf /usr/bin/python3.12 /usr/bin/python3 + +RUN curl -LsSf https://astral.sh/uv/install.sh | sh \ + && cp /root/.local/bin/uv /usr/local/bin/uv + +WORKDIR /app + +# Install dependencies first (cached unless pyproject.toml/uv.lock change) +COPY pyproject.toml uv.lock ./ +RUN uv sync --extra gpu --extra object --frozen \ + && uv cache clean + +# Copy source code (invalidates cache only when code changes) +COPY if_curator/ if_curator/ +COPY entrypoint.sh scheduler.py ./ + +RUN chmod +x /app/entrypoint.sh + +# ── Runtime stage: GPU ─────────────────────────────────────────────────── +FROM build-gpu AS runtime-gpu RUN groupadd -g 568 apps && useradd -u 568 -g apps -m -s /bin/bash appuser \ && mkdir -p /models/.insightface /models/huggingface \