如意 Django CRM 容器化实战:后端、前端、数据库、Redis 的协同启动逻辑

OK,OK,大家好,欢迎大家来到大鹏 AI 教育,我是张大鹏。

把 Django、SvelteKit、PostgreSQL、Redis 和 Celery 写进同一个 docker-compose.yml,只代表它们被放进了同一张配置表。

真正决定开发环境是否可靠的,是六个服务怎样分工、谁在等谁、初始化发生在哪个生命周期,以及我们用什么证据判断"已经可用"。

这篇文章沿着如意 Django CRM 当前的开发 Compose 拆解这些问题。

结论先说在前面:Compose 能组织启动前置条件,却不会替我们证明业务已经健康。

一、六个服务怎样形成开发拓扑

这套环境不是一条从数据库排到前端的直线,而是一张带有并行分支的依赖图。

1.1 数据、应用、异步与页面服务分别负责什么

当前 Compose 一共定义六个服务。

先不要急着看容器名称,沿着青蓝箭头观察依赖关系。

哪些服务需要等待健康门,哪个服务没有进入这条等待链?

图中六个节点可以按职责分成四组。

  • 🗄️ 数据层: db 保存 PostgreSQL 数据,redis 承担缓存与 Celery broker。
  • 🧩 应用层: backend 运行 Django API、迁移和开发初始化流程。
  • ⚙️ 异步层: celery-worker 消费任务,celery-beat 产生定时调度事件。
  • 🖥️ 页面层: frontend 运行 SvelteKit 开发服务,并独立暴露页面端口。

我在源码里核对到,backend、worker 和 beat 都同时依赖 db 与 redis,而 frontend 没有 depends_on

因此这张图最重要的不是"六个容器都在",而是下面三个结构事实。

  • 🧭 职责分开: Web 请求、异步执行和定时调度没有挤在同一个进程里。
  • 🪢 基础共享: Django 与两个 Celery 服务复用后端镜像和同一套项目代码。
  • 🛤️ 页面并行: frontend 会与后端依赖链并行启动,而不是等 backend 可用后才启动。

1.2 depends_on 等待了什么,又没有保证什么

dbredis 都配置了 healthcheck。

backend、worker 与 beat 使用 condition: service_healthy,把二者首次通过健康检查作为自己的启动前置条件。

这项约束能回答"上层容器什么时候开始创建",却不能回答"应用之后是否一直可用"。

  • 🚦 能够证明: 初次启动时,三个上层服务不会抢在 db 和 redis 的健康检查之前启动。
  • 🧯 不能证明: 依赖后来失效时,上层业务不会因此自动完成恢复或重新验收。
  • 🩺 仍需补充: backend、frontend、worker 与 beat 自身没有 Compose healthcheck,运行状态不等于应用健康。

二、数据库首次初始化与后端每次启动为何不同

最容易混淆的两个动作,是 PostgreSQL 初始化目录与 backend 入口脚本。

2.1 PostgreSQL 初始化脚本只在空数据卷执行

观察左右两条时间轴,它们的触发条件并不相同。

为什么保留数据卷重启时,左侧不会再走一遍?

左侧属于数据库首次创建生命周期。

  • 🌱 空卷首启: PostgreSQL 创建数据目录后,才会处理初始化目录中的 SQL 文件。
  • 👤 开发角色: 当前脚本创建开发角色并授予开发所需权限,用于支持本地数据访问边界。
  • 💾 已有数据: 数据卷不为空时,官方镜像不会重复执行这批初始化脚本。

我把这一点单独画出来,是因为"重启容器"与"重新初始化数据库"是完全不同的操作。

由此可以得到两个直接结论。

  • 🧪 首启要测: 新数据卷验收负责发现初始化 SQL、角色和权限问题。
  • ♻️ 重启也要测: 保留数据卷重启负责发现幂等性、持久化和旧状态兼容问题。

初始化脚本仍是开发配置。

固定开发角色和较宽的 schema 权限不能被包装成生产最小权限方案,正文也不应公开复制开发密码。

2.2 Backend entrypoint 串起迁移、管理员、翻译与静态资源

右侧时间轴属于 backend 每次启动生命周期。

入口脚本先等待 PostgreSQL TCP 端口可连接,再依次执行以下动作。

  • 🔌 连接等待: 最多尝试固定次数连接 PostgreSQL 主机与端口。
  • 🧱 结构迁移: 运行 python manage.py migrate 对齐数据库结构。
  • 🧑‍💼 开发管理员: 运行 create_default_admin 准备本地管理入口。
  • 🌐 翻译编译: 执行 compilemessages 生成运行时翻译文件。
  • 📦 静态收集: 执行 collectstatic 汇总 Django 静态资源。
  • 🎬 应用启动: 最后以 runserver 0.0.0.0:12151 启动开发服务器。

仓库脚本使用 CRLF,镜像构建阶段会先把行尾归一化。

因此宿主机直接执行 bash -n 的失败不能脱离 Dockerfile 语境解读;按镜像相同方式归一化后,脚本语法检查已经通过。

2.3 默认管理员与开发服务器必须停在开发边界内

这条入口链为了"一条命令进入开发"做了很多便利处理。

便利不等于生产方案。

  • 🔑 默认密码风险: 缺少 ADMIN_PASSWORD 时,开发管理员命令可能退回默认密码。
  • 🏗️ 应用服务器边界: Django runserver 适合本地开发,不承担生产应用服务器职责。
  • 🧰 前端服务器边界: SvelteKit 当前运行 Vite dev server,也不是生产构建产物的托管方式。

三、容器地址、宿主机地址与持久化怎样分层

同一个后端同时出现 backend:12151localhost:12151,不是重复配置,而是两个网络位置。

3.1 容器内服务名与浏览器地址不能混用

先比较门两侧的同一项服务。

为什么 PostgreSQL 在左侧使用 5432,在右侧却使用 12152?

两侧服务对象相同,但访问者所在的网络不同。

  • 🏯 容器内部: Compose DNS 解析 dbredisbackendfrontend 等服务名。
  • 🌉 端口映射: Compose 把容器端口发布到项目约定的宿主机端口。
  • 🧑‍💻 浏览器侧: 浏览器不在 Compose 网络中,只能访问宿主机公开的 localhost 地址。

我在 .env.docker 中看到的两套后端地址正好体现了这个边界。

服务端进程可以使用 http://backend:12151,浏览器公开配置则指向 http://localhost:12151

这带来两个排障判断。

  • 🛰️ 容器请求失败: 先检查服务名、容器端口和 Compose 网络。
  • 🪟 浏览器请求失败: 先检查公开 URL、宿主机端口和跨域配置。

3.2 四个回环端口分别服务谁

项目把本地开发端口集中在 12150 到 12153。

  • 🎨 12150: SvelteKit 前端页面。
  • 🐍 12151: Django 后端 API 与 Admin。
  • 🐘 12152: PostgreSQL 暴露给宿主机工具的端口。
  • 12153: Redis 暴露给宿主机工具的端口。

容器内部仍分别使用 PostgreSQL 5432 和 Redis 6379。

把宿主机端口抄进容器连接串,是本地多服务环境中非常常见的配置错误。

3.3 Bind mount、PostgreSQL 卷与 node_modules 卷解决不同问题

Compose 中的挂载并非都为了"保存数据"。

  • 📝 代码挂载: bind mount 让宿主机代码改动进入开发容器,支持快速迭代。
  • 🛢️ 数据库卷: PostgreSQL 命名卷跨容器重建保留业务数据。
  • 🧳 依赖卷: 独立依赖卷避免宿主机目录覆盖容器里的虚拟环境或 node_modules

三种挂载的失效表现也不同。

代码挂载错误会让修改不生效,数据库卷错误会造成数据生命周期异常,依赖卷错误则常表现为包突然缺失或平台不兼容。

四、Celery 为什么同时依赖 PostgreSQL 与 Redis

Celery 的运行链不只有 Redis。

4.1 Worker 与 Beat 共用镜像但承担不同生命周期

worker 和 beat 都复用后端代码与配置,但职责不同。

  • 📨 任务投递: Django 把异步消息发送到 Redis broker。
  • 🏭 任务执行: worker 消费消息,执行业务代码,并可能通过 ORM 访问 PostgreSQL。
  • 定时调度: beat 按计划生成任务事件,本身不替代 worker 执行任务。

所以两者适合共享镜像,却不应该塞进一个不可区分的进程。

独立容器让日志、重启和资源问题更容易定位到具体生命周期。

4.2 启动依赖不能代替异步任务运行验收

db 和 redis 健康,只是异步链的前置条件。

即使 celery inspect ping 返回成功,也只能证明 worker 的控制通道可响应。

  • 📡 进程证据: ping 可以确认 worker 在线并能处理控制命令。
  • 🧾 业务证据: 真实任务还要证明入队、执行、数据库读写与结果状态完整闭环。
  • 🧼 安全要求: 验收任务应无邮件、无客户数据、无外部副作用,并能安全重复执行。

五、怎样证明这套 Compose 真的可用

验证结论必须与证据层级相匹配。

5.1 静态配置、容器状态与应用健康要分层检查

从阶梯底部往上看,每一层只回答一个更强的问题。

为什么 docker compose config --quiet 通过,仍不能说环境已经可用?

七层检查对应七种证据强度。

  • 📐 配置解析: 证明 YAML、字段、引用和环境变量能被 Compose 理解。
  • 🛠️ 镜像构建: 证明依赖安装、文件复制和构建步骤能够完成。
  • 🫀 容器健康: 证明进程与已配置的 healthcheck 达到预期状态。
  • 🧬 Django 检查: 证明系统检查与迁移状态满足当前代码要求。
  • 🌍 HTTP 访问: 证明前后端端口可达,并返回预期响应。
  • 📬 异步检查: 证明 worker 可响应,并用安全任务补齐业务闭环。
  • 🔁 重启复验: 证明日志可追溯、数据可保留、已有卷重启仍然成立。

我本次实际执行了 docker compose config --quiet,结果通过。

这只允许我确认静态拓扑可以解析,不能替代尚未执行的整套隔离运行验收。

最终应坚持两个结论边界。

  • 🧲 先定位层级: 哪一层失败,就从那一层向下检查,不要直接归因业务代码。
  • 🪜 不越级宣传: 低层通过不能替高层背书,容器运行中尤其不等于业务可用。

5.2 新数据卷启动与已有数据卷重启需要分别验证

发布前应使用独立 Compose 项目名运行,不污染日常开发环境。

建议的验收顺序是:构建、首次启动、容器状态、Django 检查、迁移检查、前后端 HTTP、worker ping、日志检查,然后保留数据卷重启一次。

首次启动覆盖初始化脚本,保留数据卷重启覆盖持久化与幂等性。

只有两条路径都通过,才足以说明这套开发环境具备可重复启动的证据。

5.3 当前方案适用于本地开发,不等于生产部署

当前 Compose 的价值,是让开发者用统一拓扑复现多服务环境。

生产化仍需要补齐一组不同的问题。

  • 🏢 正式服务: 使用生产级应用服务器与静态资源交付方式。
  • 🗝️ 秘密治理: 移除默认密码和仓库内开发凭据,接入可靠的秘密管理。
  • 🧷 最小权限: 收紧数据库角色、schema 权限与网络暴露面。
  • 📈 运行治理: 增加上层服务健康探针、指标、日志、备份与恢复演练。

Compose 不是问题,模糊证据边界才是问题。

当我们把启动依赖、初始化生命周期、网络位置和验证层级分别说清楚,这套六服务开发环境才真正从"能启动"走向"可理解、可排查、可重复"。

Docker 对启动顺序与网络边界的说明可以继续阅读 Compose startup orderCompose networking

相关推荐
默_笙1 小时前
🍉 我把《天龙八部》喂给了 AI,问它"段誉会什么武功?"答案让我惊呆了
前端·javascript
东风破_2 小时前
React 自定义 Hook 实战:封装鼠标坐标与副作用清理
前端
bonechips2 小时前
useRef + Web Worker:React 中的多线程计算
前端·javascript
Sterting2 小时前
第 10 节:异步请求与 Axios —— 前端与后端
前端
Coder_Ke2 小时前
源码没错,API 却“失踪”了:微信开发者工具的一次作用域背刺
前端
海兰2 小时前
【数据库】tdsql(MySQL )的事务隔离级别
android·数据库·mysql
渣波2 小时前
从“迷路”到“瞬移”:彻底搞懂 React Router 的 `useLocation` 与 `useNavigate`
前端·javascript
申君健24864182772022 小时前
WebGPU 开发入门:从设备初始化到渲染一帧
前端
橘子星2 小时前
React Context + 自定义 Hooks:告别 Props 地狱,一行代码跨层级传数据
前端·javascript