Files
dev_harness/docs/templates/deployment.md
T
ilaandClaude Opus 5 65c20611fa docs: 新增部署文档模板与位置约定 (#24)
新增 Deployment-Template 模板页及镜像,固化 nginx 反代 + supervisor
托管的 Python 服务部署做法;明确部署文档在有无常驻服务两种情况下的
落点,并在新项目初始化加入部署页判定。模板文件纳入 REQUIRED_FILES,
部署实例页不进入 CORE_DOCUMENT_REQUIREMENTS 强制检查。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 09:53:33 +08:00

283 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- gitea-wiki-mirror:start -->
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
wiki_page: Deployment-Template
wiki_url: https://git.ilapage.cn/OPC/dev_harness/wiki/Deployment-Template.-
wiki_revision: 948321c2f3e4fd7a2923f2764fa8c81c18099a56
synchronized_at: 2026-08-19T01:52:18Z
<!-- gitea-wiki-mirror:end -->
# 部署文档模板
> 使用说明:本页是 DevHarness 模板,不描述任何真实服务。有常驻服务的项目复制本页,在自己的 Gitea Wiki 创建 `Deployment-and-Operations` 页面,并在本项目 `wiki-docs.json` 增加映射(建议镜像到 `docs/10-deployment-and-operations.md`)。填写时删除全部说明性文字,不得保留“待填写”后直接交付。没有常驻服务的项目不要创建部署页,在初始化工单记录原因即可。
>
> 本模板面向项目内部维护者。面向客户或外部运维岗位的部署说明属于交付文档,使用[岗位文档模板](Audience-Document-Template.-)。
>
> 标准栈为 nginx 反向代理 + supervisor 进程托管,示例按 Python 服务(gunicorn / uvicorn)编写。实际技术栈不同时替换命令,但保留章节结构和“每条命令写明预期结果”的要求。
## 本页用途
让维护者能够在一台干净的服务器上完成首次部署、日常运维、健康检查和回滚。所有命令默认以具备 sudo 权限的账号在服务器上执行,示例以 Linux 为主。
## 安全边界
- 本页只写配置项的**名称和来源**,不写任何真实密码、令牌、私钥、证书内容、生产数据库地址或个人数据。
- 需要凭据的步骤写明“从哪里取”,例如运维密码库条目名或环境变量名。
- 涉及删除数据、数据库迁移和不可逆操作的步骤必须给出醒目警告、影响范围和回退条件。
## 服务概览
| 项目 | 内容 |
|---|---|
| 服务名(supervisor program) | `<myapp>` |
| 代码部署目录 | `/srv/<myapp>` |
| 运行账号 | `<myapp>` |
| 运行时 | Python `<3.x>` |
| 应用服务器 | gunicorn / uvicorn |
| 本地监听地址 | `127.0.0.1:<8000>` |
| 进程数 | `<n>` |
| 对外域名与路径 | `https://<example.com>/` |
| 依赖的外部服务 | 数据库 / 缓存 / 对象存储 / 无 |
| 日志目录 | `/var/log/<myapp>/` |
服务只监听 `127.0.0.1`,不直接对外暴露端口;所有外部访问经 nginx 转发。
## 环境要求
| 组件 | 版本要求 | 检查命令 | 预期结果 |
|---|---|---|---|
| 操作系统 | `<Ubuntu 22.04>` | `cat /etc/os-release` | 输出与要求一致 |
| Python | `<3.11+>` | `python3 --version` | 输出版本号且不低于要求 |
| nginx | `<1.18+>` | `nginx -v` | 输出版本号 |
| supervisor | `<4.2+>` | `supervisord --version` | 输出版本号 |
未安装时:
```bash
sudo apt update
sudo apt install -y nginx supervisor python3-venv
```
**预期结果**:`systemctl status nginx` 与 `systemctl status supervisor` 均为 `active (running)`。
## 首次部署
### 1. 创建运行账号与目录
```bash
sudo useradd --system --home /srv/<myapp> --shell /usr/sbin/nologin <myapp>
sudo mkdir -p /srv/<myapp> /var/log/<myapp>
sudo chown -R <myapp>:<myapp> /srv/<myapp> /var/log/<myapp>
```
**预期结果**:`id <myapp>` 输出该账号;两个目录存在且属主为 `<myapp>`。
服务账号使用 `nologin`,不允许直接登录。
### 2. 取得代码
```bash
sudo -u <myapp> git clone <仓库地址> /srv/<myapp>/app
cd /srv/<myapp>/app && sudo -u <myapp> git rev-parse HEAD
```
**预期结果**:输出本次部署的完整提交哈希,记录到部署记录中。
### 3. 安装依赖
```bash
sudo -u <myapp> python3 -m venv /srv/<myapp>/venv
sudo -u <myapp> /srv/<myapp>/venv/bin/pip install -r /srv/<myapp>/app/requirements.txt
```
**预期结果**:pip 以 `Successfully installed ...` 结束,无 ERROR。
### 4. 落位配置文件
```bash
sudo install -o <myapp> -g <myapp> -m 600 /dev/null /srv/<myapp>/app.env
sudo -u <myapp> vi /srv/<myapp>/app.env
```
**预期结果**:`ls -l /srv/<myapp>/app.env` 显示权限 `-rw-------` 且属主为 `<myapp>`。
配置项清单见下方“配置与凭据来源”。配置文件不进入 Git。
### 5. 数据库初始化或迁移
<!-- 无数据库时删除本节。 -->
> **注意**:迁移可能不可逆。执行前必须先备份,并确认回退方式。
```bash
sudo -u <myapp> /srv/<myapp>/venv/bin/python -m <myapp>.manage migrate
```
**预期结果**:输出全部迁移已应用,无失败项。
## supervisor 配置
写入 `/etc/supervisor/conf.d/<myapp>.conf`:
```ini
[program:<myapp>]
command=/srv/<myapp>/venv/bin/gunicorn <myapp>.wsgi:application --workers <n> --bind 127.0.0.1:<8000> --timeout 60
directory=/srv/<myapp>/app
user=<myapp>
environment=PATH="/srv/<myapp>/venv/bin",APP_ENV_FILE="/srv/<myapp>/app.env"
autostart=true
autorestart=true
startsecs=5
stopasgroup=true
killasgroup=true
stopwaitsecs=30
stdout_logfile=/var/log/<myapp>/stdout.log
stderr_logfile=/var/log/<myapp>/stderr.log
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=5
```
> 异步框架使用 uvicorn 时把 `command` 换成:
> `/srv/<myapp>/venv/bin/uvicorn <myapp>.asgi:app --host 127.0.0.1 --port <8000> --workers <n>`
不要在 `environment` 里写明文密码或令牌;敏感值放在 `app.env`,由应用读取。
加载配置并启动:
```bash
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start <myapp>
sudo supervisorctl status <myapp>
```
**预期结果**:`reread` 输出 `<myapp>: available`;`status` 显示 `RUNNING` 且 uptime 持续增长。出现 `BACKOFF` 或 `FATAL` 时查看 `stderr.log`。
## nginx 配置
写入 `/etc/nginx/sites-available/<myapp>.conf` 并软链到 `sites-enabled`:
```nginx
server {
listen 80;
server_name <example.com>;
access_log /var/log/nginx/<myapp>.access.log;
error_log /var/log/nginx/<myapp>.error.log;
client_max_body_size <20m>;
location /static/ {
alias /srv/<myapp>/app/static/;
expires 7d;
}
location / {
proxy_pass http://127.0.0.1:<8000>;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 5s;
proxy_read_timeout <60s>;
}
}
```
<!-- WebSocket 需求存在时,在对应 location 增加 Upgrade 与 Connection 头;无此需求时删除本注释。 -->
启用并生效:
```bash
sudo ln -sf /etc/nginx/sites-available/<myapp>.conf /etc/nginx/sites-enabled/<myapp>.conf
sudo nginx -t
sudo systemctl reload nginx
```
**预期结果**:`nginx -t` 输出 `syntax is ok` 与 `test is successful`;reload 无输出且 `systemctl status nginx` 仍为 `active (running)`。
`nginx -t` 未通过时不要 reload,先修正配置。
<!-- 需要 HTTPS 时在此记录证书来源、签发方式和续期检查命令;证书内容和私钥不得写入本页。 -->
## 配置与凭据来源
| 配置项 | 用途 | 来源 | 是否敏感 |
|---|---|---|---|
| `APP_ENV_FILE` | 指向配置文件路径 | supervisor 配置 | 否 |
| `<DATABASE_URL>` | 数据库连接 | `/srv/<myapp>/app.env`,值取自运维密码库条目 `<条目名>` | 是 |
| `<SECRET_KEY>` | 会话与签名 | 同上 | 是 |
| `<LOG_LEVEL>` | 日志级别 | `/srv/<myapp>/app.env` | 否 |
敏感值只记录取用位置,不在本页、工单、日志和提交中出现真实内容。
## 日常运维
| 操作 | 命令 | 预期结果 |
|---|---|---|
| 查看状态 | `sudo supervisorctl status <myapp>` | `RUNNING`,uptime 持续增长 |
| 重启服务 | `sudo supervisorctl restart <myapp>` | 输出 `stopped` 后 `started` |
| 停止服务 | `sudo supervisorctl stop <myapp>` | 输出 `stopped` |
| 实时日志 | `sudo supervisorctl tail -f <myapp> stderr` | 持续输出应用日志 |
| 应用日志 | `sudo tail -n 200 /var/log/<myapp>/stderr.log` | 输出最近日志 |
| 接入层日志 | `sudo tail -n 200 /var/log/nginx/<myapp>.error.log` | 输出 nginx 错误 |
| 重载 nginx | `sudo nginx -t && sudo systemctl reload nginx` | 测试通过后无中断生效 |
修改 supervisor 配置后必须 `reread` + `update`,只 `restart` 不会加载新配置。
## 健康检查
每次部署、重启和回滚后必须全部执行:
```bash
sudo supervisorctl status <myapp>
curl -sS -o /dev/null -w "%{http_code}\n" http://127.0.0.1:<8000><健康检查路径>
curl -sS -o /dev/null -w "%{http_code}\n" https://<example.com><健康检查路径>
sudo tail -n 50 /var/log/<myapp>/stderr.log
```
**预期结果**:状态为 `RUNNING`;两个 `curl` 均返回 `200`;日志无新增异常堆栈。
任何一项不符合时不视为部署成功,按“升级与回滚”处理。
## 升级与回滚
### 升级
```bash
cd /srv/<myapp>/app
sudo -u <myapp> git rev-parse HEAD # 记录当前提交,回滚需要
sudo -u <myapp> git fetch --all
sudo -u <myapp> git checkout <目标提交或标签>
sudo -u <myapp> /srv/<myapp>/venv/bin/pip install -r requirements.txt
sudo -u <myapp> /srv/<myapp>/venv/bin/python -m <myapp>.manage migrate # 无数据库时删除
sudo supervisorctl restart <myapp>
```
**预期结果**:restart 后 `status` 为 `RUNNING`,随后健康检查全部通过。
升级前必须记录当前提交哈希;涉及数据库迁移时必须先备份。
### 回滚
```bash
cd /srv/<myapp>/app
sudo -u <myapp> git checkout <升级前记录的提交>
sudo -u <myapp> /srv/<myapp>/venv/bin/pip install -r requirements.txt
sudo supervisorctl restart <myapp>
```
**预期结果**:健康检查全部通过。
> **注意**:已执行的数据库迁移通常不能通过切回代码撤销。存在迁移时必须先确认迁移是否向后兼容;不兼容时按备份恢复流程处理,并停止自行操作、联系负责人。
### 备份与恢复
<!-- 记录备份对象、频率、保存位置、保留期和恢复步骤;无持久化数据时说明原因。 -->
## 已知限制
<!-- 记录本环境无法验证的部分,例如未做过真实回滚演练、未验证高并发表现、灰度或多机部署尚未支持。不得留空,无限制时写“无”。 -->
- <!-- 填写 -->