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

10 KiB
Raw Blame History

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

部署文档模板

使用说明:本页是 DevHarness 模板,不描述任何真实服务。有常驻服务的项目复制本页,在自己的 Gitea Wiki 创建 Deployment-and-Operations 页面,并在本项目 wiki-docs.json 增加映射(建议镜像到 docs/10-deployment-and-operations.md)。填写时删除全部说明性文字,不得保留“待填写”后直接交付。没有常驻服务的项目不要创建部署页,在初始化工单记录原因即可。

本模板面向项目内部维护者。面向客户或外部运维岗位的部署说明属于交付文档,使用岗位文档模板。

标准栈为 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 输出版本号

未安装时:

sudo apt update
sudo apt install -y nginx supervisor python3-venv

预期结果:systemctl status nginx 与 systemctl status supervisor 均为 active (running)。

首次部署

1. 创建运行账号与目录

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. 取得代码

sudo -u <myapp> git clone <仓库地址> /srv/<myapp>/app
cd /srv/<myapp>/app && sudo -u <myapp> git rev-parse HEAD

预期结果:输出本次部署的完整提交哈希,记录到部署记录中。

3. 安装依赖

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. 落位配置文件

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. 数据库初始化或迁移

注意:迁移可能不可逆。执行前必须先备份,并确认回退方式。

sudo -u <myapp> /srv/<myapp>/venv/bin/python -m <myapp>.manage migrate

预期结果:输出全部迁移已应用,无失败项。

supervisor 配置

写入 /etc/supervisor/conf.d/<myapp>.conf:

[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,由应用读取。

加载配置并启动:

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:

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>;
    }
}

启用并生效:

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,先修正配置。

配置与凭据来源

配置项 用途 来源 是否敏感
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 不会加载新配置。

健康检查

每次部署、重启和回滚后必须全部执行:

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;日志无新增异常堆栈。

任何一项不符合时不视为部署成功,按“升级与回滚”处理。

升级与回滚

升级

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,随后健康检查全部通过。

升级前必须记录当前提交哈希;涉及数据库迁移时必须先备份。

回滚

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>

预期结果:健康检查全部通过。

注意:已执行的数据库迁移通常不能通过切回代码撤销。存在迁移时必须先确认迁移是否向后兼容;不兼容时按备份恢复流程处理,并停止自行操作、联系负责人。

备份与恢复

已知限制