# ---- AirShelf core backend: Django + DRF + gunicorn / celery ---- FROM docker.m.daocloud.io/python:3.12-slim ENV PYTHONUNBUFFERED=1 \ PYTHONDONTWRITEBYTECODE=1 \ PIP_NO_CACHE_DIR=1 \ PIP_INDEX_URL=https://mirrors.aliyun.com/pypi/simple/ WORKDIR /app # 视频导出(stage5 拼接)靠 ffmpeg 二进制;字幕用 Pillow 渲染 PNG 需中文字体(fonts-noto-cjk), # 否则线上导出报 "No such file or directory: 'ffmpeg'" / 中文字幕变方块。slim 镜像默认两者都无。 # 换阿里云 apt 源避免国内构建超时(兼容 bookworm 的 deb822 与旧 sources.list 两种格式)。 RUN (sed -i 's|deb.debian.org|mirrors.aliyun.com|g; s|security.debian.org|mirrors.aliyun.com|g' /etc/apt/sources.list.d/debian.sources 2>/dev/null \ || sed -i 's|deb.debian.org|mirrors.aliyun.com|g; s|security.debian.org|mirrors.aliyun.com|g' /etc/apt/sources.list 2>/dev/null || true) \ && apt-get update \ && apt-get install -y --no-install-recommends ffmpeg fonts-noto-cjk \ && rm -rf /var/lib/apt/lists/* # PyMySQL is pure-python (install_as_MySQLdb), boto3/gunicorn need no C deps, # so the slim image is enough — no build-essential required. COPY requirements.txt . RUN pip install --upgrade pip && pip install -r requirements.txt COPY . . # Collected admin/static lands here; served by WhiteNoise (see settings/production.py) RUN mkdir -p /app/staticfiles COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh RUN chmod +x /usr/local/bin/docker-entrypoint.sh EXPOSE 8000 ENTRYPOINT ["docker-entrypoint.sh"] # timeout 300:图片生成是慢请求(等 ARK+传 TOS ~30s),300s 给慢 ARK 留充足余量。 # --threads 4:关键!纯 sync worker 下,几张图就能把 3 个 worker 占满,健康探针 /api/health/ # 拿不到 worker → 超时 → pod 被判不就绪 → 流量被掐 → 整站 502(表现:报错但后端在干活、刷新能看到图)。 # 图片生成是 I/O 密集(等 ARK/TOS 时线程让出 GIL),3 worker × 4 线程 = 12 并发,慢请求不再饿死探针。 CMD ["gunicorn", "airshelf.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "3", "--threads", "4", "--timeout", "300"]