1
Deployment-Template
ila edited this page 2026-09-02 11:56:52 +08:00
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.

部署文档模板

使用说明:本页是 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>

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

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

备份与恢复

已知限制