mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-10-26 13:16:24 +00:00
* fix(deploy): exec the gateway server so SIGTERM reaches uvicorn The production compose command, both CMDs in backend/Dockerfile and the Helm chart start the Gateway as `sh -c "cd backend && ... uv run ..."`. dash forks uv and stays PID 1 without a SIGTERM handler, so the kernel drops the stop signal: uvicorn never shuts down, the lifespan teardown (memory flush, run drain, service stops) never runs, and every stop waits out the grace period and ends in SIGKILL (exit 137). exec the server in every production launch path, as docker/dev-entrypoint.sh already does, and give the compose gateway `init: true` so orphaned grandchildren are still reaped once sh is no longer PID 1. A regression test pins exec in each launch path. Fixes #6835 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(changelog): link the gateway SIGTERM entry to #6836 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(gateway): decouple dependency sync from exec launch form * docs: compact startup guidance to pass inherited size limits * docs: trim inherited startup guidance after main merge --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
130 lines
5.8 KiB
Docker
130 lines
5.8 KiB
Docker
# Backend Dockerfile — multi-stage build
|
|
# Stage 1 (builder): compiles native Python extensions with build-essential
|
|
# Stage 2 (dev): retains toolchain for dev containers (uv sync at startup)
|
|
# Stage 3 (runtime): clean image without compiler toolchain for production
|
|
|
|
# UV source image (override for restricted networks that cannot reach ghcr.io)
|
|
ARG UV_IMAGE=ghcr.io/astral-sh/uv:0.11.1
|
|
FROM ${UV_IMAGE} AS uv-source
|
|
|
|
# ── Stage 1: Builder ──────────────────────────────────────────────────────────
|
|
FROM python:3.12-slim-bookworm AS builder
|
|
|
|
ARG NODE_MAJOR=22
|
|
ARG APT_MIRROR
|
|
ARG UV_INDEX_URL
|
|
ARG NPM_REGISTRY
|
|
ARG LARK_CLI_NPM_VERSION=1.0.65
|
|
# Optional extras to install (e.g. "postgres" for PostgreSQL support)
|
|
# Usage: docker build --build-arg UV_EXTRAS=postgres,discord ...
|
|
ARG UV_EXTRAS
|
|
|
|
# Optionally override apt mirror for restricted networks (e.g. APT_MIRROR=mirrors.aliyun.com)
|
|
RUN if [ -n "${APT_MIRROR}" ]; then \
|
|
sed -i "s|deb.debian.org|${APT_MIRROR}|g" /etc/apt/sources.list.d/debian.sources 2>/dev/null || true; \
|
|
sed -i "s|deb.debian.org|${APT_MIRROR}|g" /etc/apt/sources.list 2>/dev/null || true; \
|
|
fi
|
|
|
|
# Install build tools + Node.js (build-essential needed for native Python extensions)
|
|
RUN apt-get update && apt-get install -y \
|
|
curl \
|
|
git \
|
|
build-essential \
|
|
gnupg \
|
|
ca-certificates \
|
|
&& mkdir -p /etc/apt/keyrings \
|
|
&& curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \
|
|
&& echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_${NODE_MAJOR}.x nodistro main" > /etc/apt/sources.list.d/nodesource.list \
|
|
&& apt-get update \
|
|
&& apt-get install -y nodejs \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Install the official Lark/Feishu CLI used by the managed Lark integration.
|
|
RUN if [ -n "${NPM_REGISTRY}" ]; then npm config set registry "${NPM_REGISTRY}"; fi \
|
|
&& npm install -g @larksuite/cli@${LARK_CLI_NPM_VERSION} \
|
|
&& lark-cli --version
|
|
|
|
# Install uv (source image overridable via UV_IMAGE build arg)
|
|
COPY --from=uv-source /uv /uvx /usr/local/bin/
|
|
|
|
# Set working directory
|
|
WORKDIR /app
|
|
|
|
# Copy backend source code
|
|
COPY backend ./backend
|
|
|
|
# Install dependencies with cache mount
|
|
# `redis` is always installed because Docker Compose defaults the stream bridge
|
|
# to Redis (DEER_FLOW_STREAM_BRIDGE_REDIS_URL); it is an optional extra elsewhere
|
|
# so slim non-Docker installs do not pull it.
|
|
# When UV_EXTRAS is set (comma- or whitespace-separated), installs optional dependencies.
|
|
RUN --mount=type=cache,target=/root/.cache/uv \
|
|
sh -c 'cd backend && \
|
|
set -f; \
|
|
extras_flags=""; \
|
|
for extra in $(printf "%s" "$UV_EXTRAS" | tr "," " "); do \
|
|
case "$extra" in \
|
|
[!A-Za-z]* | *[!A-Za-z0-9_-]*) \
|
|
echo "UV_EXTRAS entry $extra is invalid (must match [A-Za-z][A-Za-z0-9_-]*)" >&2; \
|
|
exit 1; \
|
|
;; \
|
|
esac; \
|
|
extras_flags="$extras_flags --extra $extra"; \
|
|
done; \
|
|
UV_INDEX_URL=${UV_INDEX_URL:-https://pypi.org/simple} uv sync --locked --extra redis $extras_flags'
|
|
|
|
# UTF-8 locale prevents UnicodeEncodeError on Chinese/emoji content in minimal
|
|
# containers where locale configuration may be missing and the default encoding is not UTF-8.
|
|
ENV LANG=C.UTF-8
|
|
ENV LC_ALL=C.UTF-8
|
|
ENV PYTHONIOENCODING=utf-8
|
|
|
|
# ── Stage 2: Dev ──────────────────────────────────────────────────────────────
|
|
# Retains compiler toolchain from builder so startup-time `uv sync` can build
|
|
# source distributions in development containers.
|
|
FROM builder AS dev
|
|
|
|
# Install Docker CLI (for DooD: allows starting sandbox containers via host Docker socket)
|
|
COPY --from=docker:cli /usr/local/bin/docker /usr/local/bin/docker
|
|
|
|
EXPOSE 8001
|
|
|
|
# exec: uv must replace sh as PID 1 or `docker stop`'s SIGTERM never reaches uvicorn (#6835).
|
|
CMD ["sh", "-c", "cd backend && PYTHONPATH=. exec uv run --no-sync uvicorn app.gateway.app:app --host 0.0.0.0 --port 8001"]
|
|
|
|
# ── Stage 3: Runtime ──────────────────────────────────────────────────────────
|
|
# Clean image without build-essential — reduces size (~200 MB) and attack surface.
|
|
FROM python:3.12-slim-bookworm
|
|
|
|
ENV LANG=C.UTF-8
|
|
ENV LC_ALL=C.UTF-8
|
|
ENV PYTHONIOENCODING=utf-8
|
|
|
|
# Copy Node.js runtime from builder (provides npx for MCP servers)
|
|
COPY --from=builder /usr/bin/node /usr/bin/node
|
|
COPY --from=builder /usr/lib/node_modules /usr/lib/node_modules
|
|
RUN ln -s ../lib/node_modules/npm/bin/npm-cli.js /usr/bin/npm \
|
|
&& ln -s ../lib/node_modules/npm/bin/npx-cli.js /usr/bin/npx \
|
|
&& LARK_CLI_BIN="$(node -p 'const bin=require("/usr/lib/node_modules/@larksuite/cli/package.json").bin; typeof bin === "string" ? bin : (bin["lark-cli"] || Object.values(bin)[0])')" \
|
|
&& ln -s "../lib/node_modules/@larksuite/cli/${LARK_CLI_BIN#./}" /usr/bin/lark-cli \
|
|
&& lark-cli --version
|
|
|
|
# Install Docker CLI (for DooD: allows starting sandbox containers via host Docker socket)
|
|
COPY --from=docker:cli /usr/local/bin/docker /usr/local/bin/docker
|
|
|
|
# Install uv (source image overridable via UV_IMAGE build arg)
|
|
COPY --from=uv-source /uv /uvx /usr/local/bin/
|
|
|
|
# Set working directory
|
|
WORKDIR /app
|
|
|
|
# Copy backend with pre-built virtualenv from builder
|
|
COPY --from=builder /app/backend ./backend
|
|
|
|
# Expose Gateway API port.
|
|
EXPOSE 8001
|
|
|
|
# Default command (can be overridden in docker-compose)
|
|
# exec: uv must replace sh as PID 1 or `docker stop`'s SIGTERM never reaches uvicorn (#6835).
|
|
CMD ["sh", "-c", "cd backend && PYTHONPATH=. exec uv run --no-sync uvicorn app.gateway.app:app --host 0.0.0.0 --port 8001"]
|