MX CMS 开发与部署指南
本文档记录了从项目初始化到当前稳定版本的完整开发脉络与技术决策过程,并提供了详细的部署与维护指南。
1. 软件工程全生命周期视图 (Software Engineering Lifecycle)
从软件工程的角度来看,本项目的开发遵循了典型的迭代式开发流程,但由于基础设施(Docker/DB)的复杂性,在“集成与部署”阶段经历了多次螺旋式上升的优化。
1.1 开发全流程图 (Development Flowchart)
graph TD
%% Phase 1: Initialization
Start((项目启动)) --> Req[需求分析<br/>CMS内容管理 + RBAC权限 + 静态发布]
Req --> Arch[架构选型<br/>FastAPI + React + MariaDB<br/>Docker单容器全栈部署]
%% Phase 2: Implementation (Parallel)
subgraph Implementation [核心开发阶段]
direction TB
Dev_Back[后端开发<br/>Auth/Schema/ORM]
Dev_Front[前端开发<br/>PageBuilder/组件库]
Dev_Core[核心引擎<br/>SSG静态生成/Jinja2渲染]
Dev_Back <--> Dev_Front
Dev_Back --> Dev_Core
end
Arch --> Implementation
%% Phase 3: Infrastructure (The Challenge)
Implementation --> Docker[容器化封装<br/>Dockerfile + Supervisord]
%% Phase 4: Integration & Stabilization (The "Fix" Loop)
subgraph Stabilization [集成与稳态收敛]
Docker --> Deploy{部署验证}
Deploy -- "Fail: 连不上库" --> Fix_Net[网络层修正<br/>Bind 0.0.0.0 / Env Vars]
Fix_Net --> Deploy
Deploy -- "Fail: 无初始数据" --> Fix_Data[数据层修正<br/>entrypoint.sh导入init.sql]
Fix_Data --> Deploy
Deploy -- "Fail: 进程退出/无日志" --> Fix_Proc[进程层修正<br/>Supervisord配置 / Stdout重定向]
Fix_Proc --> Deploy
Deploy -- "Fail: 图片丢失" --> Fix_Vol[持久化修正<br/>Volume挂载 / 资源预填充]
Fix_Vol --> Deploy
end
%% Phase 5: Delivery
Deploy -- "Pass: 验证通过" --> Auto[工程化完善<br/>deploy.py自动化脚本]
Auto --> Release((项目交付))
1.2 关键里程碑节点分析 (Key Milestones Analysis)
在软件工程实践中,本项目经历了以下几个关键决策点(Critical Decision Points),这些节点直接决定了项目的最终形态:
-
架构决策:单容器全栈 (All-in-One Container)
- 权衡: 为了降低交付复杂度(避免 Docker Compose 或 K8s 的门槛),选择将 Nginx, Uvicorn, MariaDB 塞入一个容器。
- 代价: 带来了进程管理(Supervisord)和初始化顺序控制(entrypoint.sh)的额外复杂度。这是后续大部分调试工作的根源。
-
数据策略:以代码为中心 vs 以数据为中心
- 转折: 最初尝试用 Python 代码生成数据(Seed),但发现无法满足复杂演示页面的需求。
- 修正: 转向“数据快照”模式,直接导出 SQL 并在启动时注入。这标志着从“开发模式”向“工程化交付模式”的转变。
-
可观测性:从黑盒到白盒
- 痛点: 容器启动即挂,或者服务不可用但无报错。
- 突破: 彻底打通日志流(Stdout/Stderr),使 Docker logs 成为唯一且真实的调试窗口。这是运维稳定性的基石。
-
自动化:脚本即文档 (Infrastructure as Code)
- 演进: 从手动敲 Docker 命令,到封装为
deploy.py。 - 价值: 将构建参数、挂载逻辑、重置策略固化为代码,降低了人为操作错误的风险,实现了可重复的部署。
- 演进: 从手动敲 Docker 命令,到封装为
2. 基础设施演进 (Infrastructure Evolution)
阶段 0: 项目骨架搭建 (Foundation)
目标: 建立前后端分离的基础架构。
- 后端: 搭建 FastAPI 框架,集成 SQLAlchemy (ORM) 和 Pydantic (Schema)。
- 定义核心模型:
User,Role,Menu,Page(CMS Core). - 实现 JWT 认证机制 (
auth.py).
- 定义核心模型:
- 前端: 初始化 React + Vite 项目。
- 集成 Bootstrap 5 UI 库。
- 实现基于 Token 的登录与路由守卫。
- 数据库: 选定 MariaDB,本地开发使用 SQLite/MySQL 进行原型验证。
阶段 1: 容器化与初次部署尝试 (Dockerization)
目标: 将所有服务打包到一个 Docker 容器中,简化交付。
- 策略: 采用
python:3.12-slim作为基础镜像,在容器内同时安装 MariaDB Server, Nginx 和 Python 环境。 - 问题:
- 数据库连接失败:后端代码硬编码了数据库凭据,未读取环境变量。
- 服务无法启动:容器启动后立即退出,因为没有前台进程保持运行。
阶段 2: 数据库初始化攻坚 (Database Initialization)
上下文: 部署后发现数据库表结构虽然创建了(通过 SQLAlchemy create_all),但 users、menus 等基础数据缺失,导致无法登录。
- 尝试 1: 仅依赖 Python 代码
seed.py。但生产环境需要更复杂的初始数据(如演示页面)。 - 尝试 2 (成功方案):
- 导出本地开发环境的完整数据为
deploy/init.sql。 - 核心挑战: 如何在 Docker 容器启动时自动导入 SQL?
- 解决方案: 编写
entrypoint.sh脚本,实现“启动临时 mysqld -> 导入 SQL -> 关闭临时 mysqld -> 启动正式服务”的逻辑。 - Fix: 修复了等待 MariaDB 关闭时的死锁问题(改用
mysqladmin shutdown)。
- 导出本地开发环境的完整数据为
阶段 3: 进程管理与服务编排 (Process Management)
上下文: 容器内需要同时运行 Nginx (Web Server) 和 Uvicorn (App Server)。
- 问题: 手动在脚本中后台运行 (
&) 导致进程管理混乱,且无法捕获日志。Nginx 经常静默失败。 - 解决方案: 引入 Supervisord。
- 创建
deploy/supervisord.conf。 - 将 Nginx 和 Uvicorn 均交由 Supervisord 托管。
- 关键配置: 设置
nodaemon=true让 Supervisord 前台运行;设置 Nginxdaemon off;。
- 创建
阶段 4: 网络连通性与日志调试 (Networking & Debugging)
上下文: 部署后外部无法访问,或者访问报错 502/Connection Refused。
- 挑战:
- 端口绑定: MariaDB 默认绑定
127.0.0.1,修改为0.0.0.0以防万一(虽单容器内通信其实localhost够用,但排查时需要)。 - Nginx 502: 后端 Uvicorn 启动失败。
- 日志不可见: 默认日志写入容器内文件
/var/log/...,docker logs看不到。
- 端口绑定: MariaDB 默认绑定
- 解决方案:
- 将 Nginx 和 Supervisord 的日志全部重定向到
/dev/stdout和/dev/stderr。 - 修复
nginx.conf语法错误(多余括号)。 - 修正 Uvicorn 启动参数,移除开发环境的
--reload。
- 将 Nginx 和 Supervisord 的日志全部重定向到
阶段 5: 静态资源持久化 (Static Assets)
上下文: 重新部署后,用户上传的图片(CMS 内容)丢失;前端页面图片 404。
- 问题:
- 容器销毁导致非挂载数据丢失。
- 简单的挂载卷会覆盖镜像内原有的静态资源(如 CSS/JS 库)。
- 解决方案:
- 挂载策略: 在
deploy.py中挂载宿主机/opt/mx-cms/static到容器/app/static。 - SELinux 修正: 挂载时添加
:Z选项。 - 资源预填充: 修改部署脚本,在启动容器前,检测宿主机目录。如果为空,自动从本地项目复制
static资源过去,确保初始资源存在。
- 挂载策略: 在
阶段 6: 自动化部署脚本完善 (Deployment Script)
上下文: 每次手动执行 Docker 命令太繁琐,且容易出错。
- 成果: 编写并完善
deploy.py。- 功能:
--build: 自动调用npm run build编译前端。--reset-db: 危险操作开关,控制是否重置数据库。- 自动处理 SSH 连接、文件上传 (SFTP)、Docker 镜像构建与容器重启。
- 功能:
3. 业务架构演进 (Business Architecture Evolution)
除了基础设施的完善,MX CMS 在业务功能上也经历了四个关键阶段,从基础管理到成熟的发布引擎:
阶段 1: 权限与管理基座 (RBAC & Admin Foundation)
核心需求: 系统需要多角色支持,不同角色看到不同菜单。
- 实现:
- 数据模型:
User<->Role<->Menu的多对多关系。 - 后端: 实现
auth.py进行 Token 签发与校验;main.py中的read_menus接口根据当前用户角色动态返回菜单树。 - 前端: 实现动态路由渲染,未授权页面自动重定向。
- 数据模型:
阶段 2: CMS 内容引擎 (Content Engine)
核心需求: 允许非技术人员通过可视化方式搭建页面。
- 实现:
- 页面构建器: 前端实现拖拽式 Page Builder,将页面结构序列化为 JSON 存储在
Page表的content字段。 - 组件库: 定义
Component模型(含 HTML/CSS/JS/Schema),支持动态注册新组件(如轮播图、文章列表)。 - 资源管理: 实现
Resource模块,支持图片上传与管理,供组件引用。
- 页面构建器: 前端实现拖拽式 Page Builder,将页面结构序列化为 JSON 存储在
阶段 3: 静态站点生成与发布 (SSG & Publishing)
核心需求: 将搭建好的 JSON 页面转化为独立的 HTML 静态网站,并支持远程部署。
- 技术实现 (
publish_page):- 解析: 读取
Page的 JSON 数据。 - 渲染: 使用 Jinja2 模板引擎,结合组件定义的代码片段 (
code) 和属性 (props) 生成 HTML 片段。 - 注入: 自动处理
DataSource(API 数据预取)和Resource(本地图片路径替换)。 - 打包: 将 HTML、CSS (自动聚合组件样式)、JS 库打包为 Zip 文件。
- 部署: (可选) 通过 Paramiko 自动将 Zip 包上传至远程 Nginx 目录解压。
- 解析: 读取
阶段 4: 运维与可观测性 (Operational Maturity)
核心需求: 监控系统使用情况,及时触达系统通知。
- 实现:
- 日志审计:
LoginLog: 记录登录 IP、时间、状态(成功/失败)。优化点:增加按 ID 倒序排列,确保最新记录置顶。OperationLog: 记录关键操作(如删除用户、发布页面)。
- 系统消息: 实现
SystemMessage模块,支持向特定角色发送站内信,并追踪已读状态 (UserMessageStatus)。
- 日志审计:
4. 当前架构说明 (Current Architecture)
容器拓扑
单容器全栈架构 (All-in-One):
- OS: Debian Bookworm (via python:3.12-slim)
- Process Manager: Supervisord (PID 1)
- ├── MariaDB (Port 3306): 数据存储
- ├── Backend (Uvicorn, Port 8088): API 服务
- └── Frontend (Nginx, Port 8087): 静态文件托管 + API 反向代理
目录结构映射
| 本地开发路径 | 容器路径 | 说明 |
|---|---|---|
frontend/dist | /usr/share/nginx/html | 前端编译产物 |
backend/ | /app/backend | 后端源码 |
static/ | /app/static | [挂载卷] 上传文件与公共资源 |
deploy/supervisord.conf | /etc/supervisor/conf.d/supervisord.conf | 进程配置 |
5. 部署操作指南 (Operation Guide)
前置条件
- 本地: Python 3.x, Node.js (构建前端用)
- 服务器: Docker, Python 3 (用于运行部署脚本的远程命令)
常用指令
在项目根目录下执行 deploy.py:
-
日常快速部署 (代码更新):
python deploy.py仅更新后端代码和前端静态文件,重启容器。不重置数据库。
-
完整发布 (前端构建 + 部署):
python deploy.py --build先执行 npm build,再部署。
-
初始化/重置环境 (清空数据):
python deploy.py --build --reset-db警告:这将删除服务器数据库数据,并重新导入
init.sql。
6. 故障排查手册 (Troubleshooting)
Q1: 部署后页面白屏或 404?
- 检查 Nginx:
docker logs mx-cms-container查看是否有 Nginx 报错。 - 检查文件: 进入容器查看
/usr/share/nginx/html是否有index.html。 - 浏览器控制台: 查看 Network 面板,JS/CSS 资源是否加载失败(MIME 类型错误或路径错误)。
Q2: 数据库数据未更新或为空?
- 场景: 修改了
init.sql但部署后没生效。 - 原因: 只要数据卷 (
/var/lib/mysql) 已有数据,entrypoint.sh就会跳过初始化。 - 解决: 使用
python deploy.py --reset-db强制重置。
Q3: 登录日志排序不对?
- 现象: 新登录的记录排在后面。
- 修复: 代码已更新为
order_by(created_at.desc(), id.desc())。如仍有问题,检查服务器与容器时区是否一致。
Q4: 端口冲突?
- 现象: 部署脚本报错 "Bind for 0.0.0.0:8087 failed: port is already allocated"。
- 解决: 修改
deploy.py中的DOCKER_PORT配置,或停止占用该端口的其他服务。
Q5: 无法连接后端 API (500/502)?
- 检查 Uvicorn:
docker logs mx-cms-container确认 Python 是否有 Traceback 报错。 - 常见原因: 依赖缺失(检查
requirements.txt)、环境变量配置错误。