# dealerhub · 生产部署 Readiness Gate 清单(P2-2) > 状态:**本地校验通过,未实际部署**。唯一会碰生产动作的项(镜像构建/上线 > 192.168.5.7)因无服务器凭证,停在"清单 + 本地校验",不伪造部署成功。 > 验证环境:Windows + Docker 29.7.2 + Python 3.12.10,2026-09-12 实跑。 ## 一、本地校验(已跑通,exit 0) | # | 项 | 命令 | 结果 | |---|---|---|---| | 1 | compose 插值 | `DJANGO_SECRET_KEY='x' POSTGRES_PASSWORD='x' docker compose -f backend/infra/docker/docker-compose.yml config` | ✅ exit 0,`DATABASE_URL`/`SECRET_KEY`/`POSTGRES_PASSWORD` 均已插值,无 `required` 残留告警 | | 2 | prod check | `DJANGO_SETTINGS_MODULE=config.settings.prod DJANGO_SECRET_KEY=dummy DJANGO_ALLOWED_HOSTS=example.com DATABASE_URL=postgres://u:p@localhost:5432/db python manage.py check` | ✅ `System check identified no issues (0 silenced)` | | 3 | collectstatic | 同上环境 `python manage.py collectstatic --noinput`(实跑非 dry-run,写 `backend/staticfiles/`) | ✅ `216 static files copied` | | 4 | ALLOWED_HOSTS 空值门 | 不传 `DJANGO_ALLOWED_HOSTS`(仅继承 dev `.env` 的 `*`)→ `manage.py check` | ✅ 被拒:`生产环境不允许 DJANGO_ALLOWED_HOSTS 含通配符 '*'`(本次新增,见 §三) | | 5 | ALLOWED_HOSTS 显式域名 | `DJANGO_ALLOWED_HOSTS=example.com` → `manage.py check` | ✅ 0 issues | | 6 | prod 安全基线 | 同环境打印 settings | ✅ `DEBUG=False`;`dealerhub` logger `INFO`;PG `pool min2/max10/timeout10`;HSTS `2592000+include+preload`;`SESSION/CSRF_COOKIE_SECURE=True`;`SECURE_PROXY_SSL_HEADER=https`;`X_FRAME_OPTIONS=DENY` | | 7 | DB 方言 | prod `ENGINE=django.db.backends.postgresql` | ✅ 非 SQLite(SQLite 不适合生产并发,见 NEXT_PLAN P1-1) | ## 二、上线 192.168.5.7 前的 Gate(逐项打勾才能上) - [ ] **环境变量**:目标机上设置 `DJANGO_SECRET_KEY`(≥32 随机)、`POSTGRES_PASSWORD`、 `DJANGO_ALLOWED_HOSTS=实际域名`(逗号分隔,禁 `*`)、`DATABASE_URL`(PG)。 参考:` §一#1` 的插值校验。 - [ ] **镜像构建**:`docker compose -f backend/infra/docker/docker-compose.yml build backend` (Node+Python 多阶段,镜像内 `npm ci && npm run build`,不依赖宿主 `dist/`)。 - [ ] **迁移**:容器启动脚本 `infra/scripts/run_prod_granian.sh` 自动 `python manage.py migrate --noinput`;上线后用 `manage.py showmigrations` 抽查无未应用迁移。 - [ ] **collectstatic**:同脚本自动 `collectstatic --noinput`;用 `curl /static/` 抽查 200。 - [ ] **`DJANGO_DEBUG=False`**:prod 已硬编码 `DEBUG=False`(`config/settings/prod.py`), 用 §一#6 命令复验。 - [ ] **PG 已切**:`DATABASE_URL` 指向生产 PG(本机 PG 5433 仅用于并发测试,不可当生产库)。 - [ ] **HTTPS**:`SECURE_PROXY_SSL_HEADER + HSTS + Secure Cookie` 已配(§一#6), 前置反代(1Panel/nginx)必须透传 `X-Forwarded-Proto: https` 并配证书;`curl -I https://域名` 验 HSTS 头。 - [ ] **日志轮转**:应用层只打 console(`StreamHandler`,无文件 handler,见 §四); 靠宿主 `docker logs --log-opt max-size/max-file` 或 journald 做轮转,**不要**在容器内写日志文件。 - [ ] **备份**:PG 每日 `pg_dump`(或卷快照)+ 留存 ≥7 天,恢复演练每季度一次。 现状:仓库内无备份脚本(§四),上线前由运维侧补齐,本清单不虚构。 - [ ] **冒烟**:`GET /api/v1/ping/` → 登录 → 开单,三接口 200(D2 移动端同链路已在本地验证,见 `backend/scripts/verify_salesman_h5.py`)。 ## 三、本轮修补(P2-2 附带真缺口) `prod.py` 的 ALLOWED_HOSTS 空值门可被 dev `.env`(`DJANGO_ALLOWED_HOSTS=*`, 经 `base.py:read_env` 写进 `os.environ`)绕过:不传变量时读到 `*` 而非空, 门禁静默通过,生产接受任意 Host。修法:显式拒绝通配符(`backend/config/settings/prod.py`), 验证见 §一#4/#5。无测试依赖 prod 通配符(全仓 grep `settings.prod` 零命中)。 **本轮追加(2026-09-13)**:`backend/docker-compose.yml`(1Panel 上线栈)的 `backend`/`worker` 两处 `DJANGO_ALLOWED_HOSTS: "*"` 硬编码,与 prod.py 的 通配符门禁直接冲突 —— 该 compose 一上线就会被 prod 拒绝启动。 修法:改为 `${DJANGO_ALLOWED_HOSTS:?required}`,与 `DJANGO_SECRET_KEY` / `POSTGRES_PASSWORD` 同级必填,上线时必须显式传入真实域名。 `backend/infra/docker/docker-compose.yml`(本地自带 db 栈)无此问题(未设该变量, 走 prod 门禁显式报错)。`docker compose config` 需带齐三个变量重验: `DJANGO_SECRET_KEY=x DJANGO_ALLOWED_HOSTS=example.com POSTGRES_PASSWORD=x`。 ## 四、诚实记录(未做,不虚报) 1. **镜像构建未跑**:本机 Docker daemon 未运行(`docker compose build` 报 `failed to connect to the docker API at npipe://...dockerDesktopLinuxEngine`), 且首次不带变量构建即暴露插值必填(`DJANGO_SECRET_KEY/POSTGRES_PASSWORD required`)。 `build/up` 留待 Docker 就绪的目标机。Dockerfile 多阶段构建本身未动(Node20 前端构建 + Python3.12 后端,`frontend_dist` 由镜像内构建,不依赖宿主 `dist/`)。 2. **未上线 192.168.5.7**:无服务器凭证,`up -d` 与三接口冒烟留待部署时按 §二执行。 上线前必须先按 §三追加项设置 `DJANGO_ALLOWED_HOSTS=真实域名`(compose 现为必填, 不传则 config 直接报错,不会静默带 `*` 上线)。 3. **日志轮转/备份无代码交付**:现状应用层无文件日志、无备份脚本, §二对应项为运维动作,仓库不伪造实现。 4. **P3-1 行业专版跳过**:按 NEXT_PLAN P3-1/D3 定调"按客户驱动,不提前做", 本轮未建 `Serial`/辅助属性/套装拆件,grep 仍零命中即为预期状态。