feat(deploy): 让 main 上的后端可直接部署并被浏览器访问 - #115
Conversation
让 main 上的后端可以直接部署起来并被浏览器访问。三处内容都是在一台真实云主机上
部署时踩出来的,不是照搬模板:
1. backend/Dockerfile —— builder 与 runtime **必须同路径**。uv 装出来的 venv 里,
可执行脚本 shebang 与 workspace 包 .pth 都是绝对路径;builder 在 /build、
runtime 在 /app 时,容器起来会报 "exec /app/.venv/bin/uvicorn: no such file or
directory" —— 报的不是脚本本身而是它 shebang 指向的解释器,同时 workspace 包
import 不到。两边都用 /app 即可,不需要任何 relocate 技巧。
另加国内镜像源与 UV_HTTP_TIMEOUT=180:实测宿主机访问 pypi.org 需 8s,
构建容器内默认超时会在下载 uvloop 时直接失败。
2. docker-compose.yml —— 网络显式设 mtu 1450。云主机链路 MTU 常小于 1500
(实测某部署机 eno1 为 1480),而 compose 自建网络**不继承** daemon.json 的
mtu 设置,默认仍是 1500,大包被丢,表现为 TLS 握手超时(对象存储上传挂死、
pip 下载卡死)而不是明确报错。这条是分两次才定位到的:先改 daemon 以为好了,
重建后容器里仍是 1500。
POSTGRES_PASSWORD 用 ${VAR:?} 强制显式提供,不给默认值。
3. CORS 中间件 —— 不挂的话浏览器会把前端**所有**请求拦在预检那一步
(OPTIONS 返回 405、响应无 access-control-* 头),且后端日志里连请求都看不到,
很容易被误判成前端问题。来源用 WINDUP_CORS_ORIGINS 覆盖,默认覆盖本地 dev
并放行 Vercel 预览域名。
本地验证:ruff 通过;pytest 1 passed;TestClient 实测 OPTIONS 预检返回 200 且
allow-origin 正确。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
| RUN uv sync --frozen --no-dev --no-install-workspace | ||
|
|
||
| COPY packages/ packages/ | ||
| RUN uv sync --frozen --no-dev |
There was a problem hiding this comment.
The image installs only the locked production dependencies here, but the upload path imports qiniu lazily in windup_app.server.media.service, and neither backend/uv.lock nor any backend pyproject.toml declares that package. The container can start, but /media/upload will fail at request time with ModuleNotFoundError: qiniu. Please add the SDK to the appropriate package and update the lockfile so the deployable image contains the runtime dependency.
| app.add_middleware( | ||
| CORSMiddleware, | ||
| allow_origins=_cors_origins(), | ||
| allow_origin_regex=r"https://.*\.vercel\.app", |
There was a problem hiding this comment.
This regex still allows every https://*.vercel.app origin even when WINDUP_CORS_ORIGINS is set to a specific production frontend, and allow_credentials=True is enabled below. Any unrelated Vercel app can therefore make credentialed browser requests to this backend if a user has backend credentials. Restrict this to known project/preview domains, or make the regex configurable together with the explicit origin list.
变更内容
三个文件,让
main上的后端可以一条命令起起来并被前端连上:backend/Dockerfiledocker-compose.ymlbootstrap/app.py挂 CORS 中间件不依赖任何未合入的分支,直接基于当前
main。为什么单独提这个 PR
main上目前没有Dockerfile也没有docker-compose.yml,后端无法部署;而 CORS 缺失会让浏览器把前端所有请求拦在预检那一步。这三样是「服务能起来、前端能连上」的最小集合,与后端业务功能正交,因此单独提,不必等更大的后端 PR。三条不是照搬模板的实现细节
都是在一台真实云主机上部署时踩出来的。
1. builder 与 runtime 必须同路径。
uv 装出来的 venv 里,可执行脚本的 shebang 与 workspace 包的
.pth都是绝对路径。builder 在/build、runtime 在/app时,容器起来会反复重启并报:这条最不直观 —— 那个文件其实存在,报的是它 shebang 里指向的
/build/.venv/bin/python;同时 workspace 包也 import 不到。两边都用/app即可,不需要任何 relocate 技巧。2. compose 网络要显式设 MTU。
云主机链路 MTU 常小于 1500(实测某部署机
eno1为 1480)。而 compose 自建网络不继承daemon.json里的mtu设置,默认仍是 1500 → 大包被丢,表现为 TLS 握手超时(对象存储上传挂死、pip 下载卡死)而不是明确报错。这条是分两次才定位到的:先改了 daemon 以为好了,重建后容器里仍是 1500。所以必须写在 compose 的
networks.driver_opts里。3. 国内网络要换源并拉长超时。
实测宿主机访问
pypi.org需 8s,构建容器内默认超时会在下载大包(uvloop)时operation timed out直接失败。另外
POSTGRES_PASSWORD用${VAR:?}强制显式提供,不给默认值 —— 避免默认弱口令跟着编排一起进生产。验证
本地(基于本 PR 的分支):
uv run ruff check .uv run pytest -qaccess-control-allow-origin正确在真实服务器上,用本 PR 的代码树完整验过一遍(验证用的容器与镜像已清除):
待对齐
WINDUP_CORS_ORIGINS追加,不需要改代码。