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。