基于 Rocky Linux 的多节点云业务平台部署与故障演练项目。
这是一个面向云计算运维、Linux 运维和初级 SRE 岗位的个人实践项目。项目围绕“服务事件”这一简单业务对象,完整串起了应用部署、反向代理、依赖健康检查、日志定位、访问控制、备份校验、隔离恢复和可逆故障演练。
项目重点不是堆叠组件,而是证明一条运维链路可以被部署、检查、解释和恢复:
- 能说明每个节点的职责和请求、数据流向;
- 能用 systemd、健康接口、监听端口和日志判断服务状态;
- 能在保留 SELinux 和 firewalld 的情况下定位访问问题;
- 能在不覆盖在线库的前提下完成备份完整性检查和隔离恢复;
- 能安全停止并恢复单个应用节点,并用入口和日志验证结果。
公开说明使用示意名称,不暴露实验环境的真实内网地址。详细记录中的 192.0.2.0/24 属于 RFC 5737 文档专用地址段,部署时必须替换为实际实验环境的主机名或私网地址。
mgmt-host
|
v
node-01:80 Nginx 统一入口
|
+--> node-01:5000 Flask/Gunicorn 应用节点 1
|
+--> node-02:5000 Flask/Gunicorn 应用节点 2
|
+--> db-01:3306 MySQL 8
db-01:6379 Redis 主库
node-01 还运行 Redis 从库,用于复制状态观察;应用写入只指向 Redis 主库。
请求和数据流:
- 管理机访问
node-01的 Nginx。 - Nginx 将请求转发给两个 Flask/Gunicorn 应用节点。
- 应用节点访问
db-01上的 MySQL 保存服务事件。 - 应用按需访问 Redis 主库;Redis 从库只用于复制观察。
- 应用日志、Nginx access/error log、journald 和巡检脚本共同提供定位证据。
| 领域 | 技术 |
|---|---|
| 操作系统 | Rocky Linux 9 |
| Web 入口 | Nginx |
| 应用 | Python Flask |
| 应用进程 | Gunicorn |
| 系统管理 | systemd、journald |
| 数据库 | MySQL 8 |
| 缓存 | Redis 6 |
| 运维脚本 | Bash/Shell |
| 网络与安全 | firewalld、SELinux、SSH、项目内网 |
| 版本管理 | Git |
- 搭建三节点 Rocky Linux 实验平台,部署两个使用同一代码归档的 Flask/Gunicorn 应用节点。
- 使用 systemd 管理 Gunicorn,验证服务启停、监听端口、journald 日志和依赖健康状态。
- 配置 Nginx upstream,验证两个应用节点都实际收到过请求。
- 在 SELinux
Enforcing状态下定位 Nginx 访问后端的502问题,仅启用所需的httpd_can_network_connect布尔值。 - 配置 firewalld 必要链路的最小来源规则,保留 Redis 从库只读和 MySQL 应用账号最小权限边界。
- 编写三角色 Bash 只读巡检脚本,检查服务、监听端口、健康接口、依赖和限定范围日志。
- 使用 MySQL 逻辑备份工具生成备份,完成 SHA-256、gzip 完整性校验,并恢复到独立测试库
cloudops_restore_test。 - 实际停止并恢复两个应用节点,验证单个应用节点停止期间入口仍返回 HTTP 200,恢复后节点重新接收请求。
GET /health:分别返回应用、MySQL、Redis 和总体健康状态;依赖异常时返回 HTTP503。GET /api/events:查询服务事件。POST /api/events:创建服务事件。PATCH /api/events/<id>:更新服务事件字段。DELETE /api/events/<id>:删除服务事件。
应用代码使用环境变量或本地 .env 读取配置,MySQL 写操作使用参数化 SQL,错误响应不回显底层连接错误或凭据。
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
Copy-Item .env.example .env
.\.venv\Scripts\python.exe -m pytest -q当前本地测试证据为 12 passed。本地测试使用 mock 依赖,不会写入真实 MySQL 或 Redis;运行真实服务前需要在未提交的 .env 中填写实验环境配置。
按以下顺序阅读和执行:
docs/deployment-guide.md:部署、检查、回滚和边界。docs/acceptance-checklist.md:按证据验收,不把历史记录当成当前实时状态。docs/demo-runbook.md:10 至 15 分钟可逆演示路径。scripts/cloudops-check.sh:app1、app2、db三角色只读巡检。
| 验证范围 | 结果 |
|---|---|
| Flask 应用和依赖健康接口 | 两个应用节点均曾验证 HTTP 200,app/mysql/redis/overall 均为 healthy |
| 双节点入口 | Nginx access log 曾记录两个 upstream 地址均返回 HTTP 200 |
| 单节点停止演练 | 两个应用节点分别停止并恢复;停止期间入口仍由另一节点返回 HTTP 200 |
| 只读巡检 | 历史记录:app1 pass=15 fail=0 info=1、app2 pass=8 fail=0 info=0、db pass=9 fail=0 info=2 |
| MySQL 备份 | 备份文件 SHA-256 和 gzip -t 校验通过 |
| MySQL 恢复 | 在隔离库恢复表结构和索引,未覆盖在线库;当时业务表为空 |
| 自动化测试 | 12 passed |
上表的服务器数字来自历史验收记录,重新部署、重启或修改配置后需要重新执行检查。完整证据见 docs/module-7-failure-drill-record.md、docs/module-6-logs-backup-restore-record.md 和 docs/progress.md。
当前实际完成的是安全、可逆的应用节点演练:
- 演练前检查两个应用节点、入口、依赖和巡检结果。
- 只停止一个应用节点的
cloudops-gunicorn.service。 - 通过 Nginx
/health和 access log 确认入口仍返回 HTTP 200,并由另一节点响应。 - 使用 systemd 启动被停止的节点。
- 结合服务状态、监听端口、直连
/health、巡检结果和 upstream 日志确认恢复。
这证明的是“单个应用节点停止期间入口仍可用”的演练结果,不等同于完整高可用、自动故障切换或零停机发布。
已完成:
- MySQL 逻辑备份生成;
- SHA-256 和 gzip 完整性校验;
- 恢复到独立的
cloudops_restore_test测试库; - 表结构和索引恢复验证;
- 确认没有覆盖在线
cloudops数据库。
当前不能据此声称:
- 非空业务数据恢复已验证;
- 在线数据库覆盖恢复或灾难恢复已完成;
- 已验证 RTO/RPO;
- 已建立持续备份、自动轮转或完整灾备系统。
- 仓库只提交
.env.example,不提交密码、Token、Cookie、私钥或完整连接凭据。 - 公开文档统一使用
mgmt-host、node-01、node-02、db-01和文档专用地址;真实实验地址只应保存在本地未提交配置中。 - 项目内网 SSH 的现状风险、MySQL X Protocol
33060、日志持久化/轮转和主机重启自动恢复均有记录,但没有包装成已完成的生产安全能力。 - Docker、Kubernetes、Prometheus/Grafana 不属于当前交付范围。
PROJECT_PLAN.md:项目范围和模块完成标准。docs/progress.md:进度、证据和遗留边界。docs/deployment-guide.md:部署、检查、排障和回滚手册。docs/acceptance-checklist.md:按证据验收平台状态和边界。docs/demo-runbook.md:可逆演示和故障恢复流程。docs/module-2-integration-record.md:应用与数据库、缓存的集成记录。docs/module-4-integration-record.md:Nginx、upstream 和入口访问记录。docs/module-5-security-baseline-record.md:firewalld、SELinux 和 SSH 基线。docs/module-6-logs-backup-restore-record.md:日志、巡检、备份和隔离恢复记录。docs/module-7-failure-drill-record.md:故障演练、恢复证据和未验证边界。
这是个人隔离实验项目。仓库中的配置模板和演示命令只适用于实验环境,使用前应替换示意主机名、账号和本地配置,不要直接用于生产环境。