如意 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 等待了什么,又没有保证什么
db和redis都配置了 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:12151和localhost:12151,不是重复配置,而是两个网络位置。
3.1 容器内服务名与浏览器地址不能混用
先比较门两侧的同一项服务。
为什么 PostgreSQL 在左侧使用 5432,在右侧却使用 12152?
两侧服务对象相同,但访问者所在的网络不同。
- 🏯容器内部:Compose DNS 解析
db、redis、backend与frontend等服务名。 - 🌉端口映射: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 order 与 Compose networking。