Reflex 生产部署实战:基于 Caddy 单端口静态导出与多阶段构建(production-one-port)
2026/9/10 7:40:21 网站建设 项目流程

Reflex 生产部署实战:基于 Caddy 单端口静态导出与多阶段构建(production-one-port)

【免费下载链接】reflex🕸️ Web apps in pure Python 🐍项目地址: https://gitcode.com/GitHub_Trending/re/reflex

本指南围绕仓库 docker-example/production-one-port 示例展开,讲解如何将 Reflex 应用以生产模式打包进单个容器,通过一个 HTTP 端口对外提供服务:Caddy 负责静态托管前端导出产物并把后端 API 请求反代给 Reflex 后端,容器内同时运行本地 Redis 保存用户状态。读完本文,你将掌握该 Dockerfile 的每一层构建意图、Caddyfile 的路由规则,以及如何在 Render、Heroku 等单端口应用平台上正确构建、运行和持久化该容器。

单端口部署的整体架构

production-one-port 是一个「单容器、单端口」的生产部署方案。整个容器对外只暴露一个端口:

  • 8080(对应$PORT)——由 Caddy 服务器监听,它一方面把构建期导出的前端静态文件托管出去,另一方面将后端相关请求反代给容器内的 Reflex 后端进程。

此外,容器内部还会启动一个本地 Redis 服务器,用于存储每个用户的会话状态(通过环境变量REFLEX_REDIS_URL=redis://localhost指向本地实例)。前端采用静态导出(static export)而非运行 Vite 开发服务器,因此整体资源占用更低,特别适合内存受限的部署环境。

与 simple-one-port 的区别

从概念上讲,production-one-port 与 simple-one-port 示例非常相似(都使用 Caddy + 单端口 + 本地 Redis),两者的差异体现在镜像构建方式上:

特性simple-one-portproduction-one-port
Python 依赖层缓存有(单独 COPY requirements.txt 先安装)
Reflex/Node 依赖层缓存有(先reflex init再预装 bun 依赖)
构建方式单阶段多阶段(builder + slim 最终镜像)
最终镜像体积较大更小(仅保留运行所需文件)

正因为这两点优化,production-one-port 更适合内存受限、对镜像体积敏感的生产环境。

构建与运行

构建镜像

在包含requirements.txtrxconfig.py和应用源码的项目根目录执行:

docker build -t reflex-production-one-port .

构建时可以通过--build-arg覆盖 Dockerfile 中定义的构建参数:

docker build \ --build-arg PORT=10000 \ --build-arg API_URL=https://myapp.example.com \ -t reflex-production-one-port .
  • PORT:默认8080。如果目标平台有特殊端口要求(例如 Render 期望应用监听10000端口),在此指定。
  • API_URL:默认留空。仅用于本地直连场景;当使用 TLS 时,前端 API 地址被假定与前端地址相同,无需单独设置。

运行容器

docker run -p 8080:8080 reflex-production-one-port

注意:该容器没有任何持久化,停止后所有数据都会丢失。如果需要保留数据库与上传文件,请使用 bind mount 或 named volume 挂载对应的databaseuploaded_files目录,例如:

docker run -p 8080:8080 \ -v reflex-data:/app/database \ -v reflex-uploads:/app/uploaded_files \ reflex-production-one-port

使用场景

  • 该容器应当部署在已有的负载均衡器或反向代理之后,由它们负责 TLS 终止;
  • 非常适合 Render、Heroku 这类只支持暴露单一端口、且不希望容器内常驻 node 进程的简单应用平台。

Dockerfile 逐层解析:多阶段构建与层缓存

Dockerfile 是整个方案的核心,分为 builder 与最终运行镜像两个阶段。

阶段一:builder(python:3.13)

  1. 准备虚拟环境与目录:创建/app/.web目录,并在/app/.venv中建立 Python 虚拟环境,将其加入PATH

  2. 安装 Python 依赖并利用层缓存:先单独COPY requirements.txt .并执行pip install -r requirements.txt。由于 Docker 的层缓存机制,只要requirements.txt未变化,这一层就会被复用,避免每次构建都重新安装依赖。仓库根目录 README.md 也提醒:务必保证requirements.txt始终包含reflex包并保持最新。

  3. 初始化 Reflex 并预装前端依赖COPY rxconfig.py ./后执行reflex init生成项目骨架;随后把本地已有的*.web/bun.lockb*.web/package.json复制进容器,并用 Reflex 自带的 bun 执行bun install --frozen-lockfile预装锁定的前端依赖(同样受益于层缓存)。

  4. 导出静态前端:复制完整项目上下文后,执行:

    reflex export --loglevel debug --frontend-only --no-zip

    该命令只导出前端(--frontend-only)、不打包成 zip(--no-zip),产物位于.web/build/client,随后被移动到/srv并删除.web目录,以缩小最终镜像。在源码层面,reflex export由 reflex/reflex.py 实现,它会根据REFLEX_API_URL(此处通过REFLEX_API_URL=${API_URL:-http://localhost:$PORT}计算)在前端产物中写入正确的 API 地址。

阶段二:最终镜像(python:3.13-slim)

  1. 通过 apt 安装caddyredis-server两个运行时组件,并清理 apt 缓存;
  2. 设置运行环境变量:PORTREFLEX_API_URL(默认http://localhost:$PORT)、REFLEX_REDIS_URL=redis://localhostPYTHONUNBUFFERED=1
  3. 仅从 builder 阶段拷贝/app/srv两个目录,其余构建期文件一律丢弃;
  4. STOPSIGNAL SIGKILL:在 Reflex 后端尚未正确传递 SIGTERM 之前,用 SIGKILL 保证容器能及时停止;
  5. EXPOSE $PORT声明对外端口。

容器启动命令(CMD)

[ -d alembic ] && reflex db migrate; \ caddy start && \ redis-server --daemonize yes && \ exec reflex run --env prod --backend-only

启动序列依次完成:

  • 若存在alembic目录,先执行reflex db migrate应用数据库迁移(对应源码 reflex/reflex.py 中的migrate命令);
  • 后台启动 Caddy(caddy start);
  • 以守护进程方式启动 Redis(redis-server --daemonize yes);
  • 最后以exec方式运行reflex run --env prod --backend-only,即只启动生产模式后端,不再运行前端服务器——前端由 Caddy 静态托管。

Caddyfile:前端静态托管与后端反向代理

Caddyfile 定义单端口上的全部路由逻辑:

:{$PORT} encode gzip @backend_routes path /_event/* /ping /_upload /_upload/* handle @backend_routes { reverse_proxy localhost:8000 } root * /srv route { try_files {path} {path}/ /404.html file_server }

逐行说明:

  • :{$PORT}:监听$PORT环境变量指定的端口;
  • encode gzip:对响应启用 gzip 压缩;
  • @backend_routes path /_event/* /ping /_upload /_upload/*:定义后端路由匹配器——WebSocket 事件通道/_event/*、健康检查/ping、文件上传/_upload相关路径;
  • handle @backend_routes { reverse_proxy localhost:8000 }:将上述请求反代到容器内的 Reflex 后端(默认端口 8000);
  • root * /srv:静态文件根目录为构建阶段导出的前端产物;
  • route { try_files {path} {path}/ /404.html; file_server }:优先按路径查找静态文件,找不到时回退到/404.html(保证 SPA 路由可用),最后由file_server提供静态文件服务。

Redis 与后端配置要点

容器通过环境变量REFLEX_REDIS_URL=redis://localhost指定后端状态存储。仓库源码 reflex/utils/prerequisites.py 中的parse_redis_url会对该值做校验:必须以redis://rediss://unix://开头,否则抛出ValueError。这也意味着在本示例的基础上,你可以轻松将 Redis 指向远程实例(如rediss://加密连接或外部托管服务),而不改变容器内其他结构。

关于REFLEX_API_URL:当容器被部署在负载均衡/边缘网关之后并启用 TLS 时,前端访问后端的地址与前端自身地址一致,因此无需单独配置API_URL;只有当需要本地直连、前后端地址不同时才需要显式传入该构建参数。

生产部署注意事项小结

  1. 持久化是必须自行解决的问题:容器本身无状态,务必用 bind mount 或 named volume 保存database(SQLite 数据库)与uploaded_files(用户上传文件);
  2. TLS 由边缘负责:Caddy 在容器内以 HTTP 监听$PORT,HTTPS 证书与终止应交给前置负载均衡器或平台边缘;
  3. 端口按平台调整:Render 期望PORT=10000,Heroku 等其他平台可能不同,构建时通过--build-arg PORT=...传入即可,Caddyfile 与EXPOSE都会随之生效;
  4. 前端是静态导出:容器内不运行 node/Vite 进程,内存占用更小,这正是该方案优于两端口方案(参考 simple-two-port)的核心原因,也是它被推荐用于内存受限环境的价值所在。

【免费下载链接】reflex🕸️ Web apps in pure Python 🐍项目地址: https://gitcode.com/GitHub_Trending/re/reflex

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询