44e77dcb0e
**重构前**(旧编号 02-11): - docs/02-产品需求/ → 00 产品规划/PRD - docs/03-技术架构/ → 01-05 子目录散落 - docs/04-原型设计/ → 01-02 产品设计(HTML 原型) - docs/05-原型设计/ → screens/ - docs/06-测试素材/ → 02-E2E / 03-功能 / 04-版本测试 - docs/07-项目管理/ → 任务说明书/日报/计划 - docs/08-安全审计/ → 审计报告 - docs/09-堡垒运维/ → toolbox / deploy - docs/10-项目管理/ → 任务说明书(重复) - docs/11-历史归档/ → deploy-nas-archived **重构后**(新编号 00-07,语义化): - docs/00-产品开发流程与文档管理规范.md - docs/00-版本迭代总览.md - docs/01-产品文档/ (PRD/原型/认证/会话/AI 服务/坐席/集成) - docs/02-技术文档/ (技术方案/架构图/重构记录/前端改造/实现配置) - docs/03-测试文档/ (E2E/功能用例/版本报告/缺陷单) - docs/04-运维文档/ (部署运维/运维指南) - docs/05-运营文档/ (品牌推广/用户手册) - docs/06-安全审计/ (审计报告) - docs/07-项目管理/ (任务说明书/日报/计划/看板) **净收益**: - 目录编号与产品文档管理规范对齐(按文档阶段 01-07 编号) - 消除 02-产品需求 与 10-项目管理 的编号重叠 - 子目录按文档类型分组(如 01-产品文档/00-产品规划、01-产品文档/01-认证与登录) - 把运维/安全/项目管理从 0X 散落改为 04/06/07 合计 494 文件 + 78495 行 / - 14076 行
21 KiB
21 KiB
任务说明书 — 后端部署卷挂载改造
版本: v1.0 | 日期: 2026-07-10
📋 基本信息
| 项目 | 内容 |
|---|---|
| 任务名称 | 后端部署架构改造:镜像烘焙 → 代码卷挂载 |
| 任务ID | #107 |
| 优先级 | 🟠 P1 |
| 类型 | 部署优化 |
| 状态 | 待开始 |
| 负责人 | 宋献 |
| 创建日期 | 2026-07-10 |
| 计划完成日期 | 2026-07-11 |
📥 输入项来源
产品需求
| 来源文档 | 相关章节 | 说明 |
|---|---|---|
| 无 | — | 运维需求,非产品功能 |
技术架构
| 来源文档 | 相关章节 | 说明 |
|---|---|---|
03-技术架构/IT智能服务台-系统架构设计文档v2.md |
§5.0 部署模式演进 | 方案 C 设计说明 |
04-运维文档/部署运维/卷挂载重构方案.md |
全文 | 完整 8 章节方案(架构师高见远产出) |
04-运维文档/部署运维/00-标准故障排查手册.md |
§1.4 + CASE-20260710-02 | 部署前同步检查清单 + 镜像缺文件案例 |
事故背景
| 日期 | 事故 | 根因 |
|---|---|---|
| 2026-07-07 | 认证路由缺失 | Docker 镜像未重新构建 |
| 2026-07-10 | auth.py 缺失导致认证全断 | 镜像从 backend/app/(旧代码)构建,两份代码不同步 |
共同根因:代码通过 COPY . . 烘焙进镜像,服务器两份代码不同步导致构建出缺文件的镜像。
📤 输出成果要求
交付物清单
| # | 交付物 | 类型 | 说明 |
|---|---|---|---|
| 1 | backend/Dockerfile |
配置 | 删除 COPY . .,新增 ENV PYTHONDONTWRITEBYTECODE=1 |
| 2 | docker-compose.yml |
配置 | backend 服务 volumes 新增 ./app:/app/app |
| 3 | 服务器代码目录调整 | 运维 | app/ 成为唯一代码源,backend/app/ 保留 48h 后删除 |
| 4 | 部署验证通过 | 验证 | 健康检查 + auth 模块 + 卷挂载 + 代码一致性 |
代码要求
- Dockerfile 变更:仅删除
COPY . .(第 53 行),新增ENV PYTHONDONTWRITEBYTECODE=1,其余不变 - docker-compose.yml 变更:backend 服务 volumes 段新增一行
./app:/app/app,插入到backend-uploads行之前 - 不涉及任何 Python 业务代码变更
文档要求
- 架构设计文档已更新(§5.0 部署模式演进,v2.1)
- 项目管理主文档已更新(看板新增 #107)
- 完整方案文档已归档:
docs/04-运维文档/部署运维/卷挂载重构方案.md
🔧 验证方式
功能验证
| 验证项 | 验证方法 | 预期结果 |
|---|---|---|
| 容器运行状态 | docker compose ps backend |
Status 为 Up (healthy) |
| 健康检查 | curl -sf http://localhost:8000/health |
返回 {"status":"healthy"} 或类似 |
| Auth 模块 | docker exec wecom_it_backend python -c "from app.auth import router; print('OK')" |
输出 OK(曾因缺失导致故障) |
| 卷挂载 | docker exec wecom_it_backend ls -la /app/app/main.py |
文件存在且可读 |
| 代码一致性 | md5sum 对比宿主机与容器内 main.py |
md5 值一致 |
| 日志检查 | docker compose logs --tail=50 backend |
无 ModuleNotFoundError / ImportError |
安全验证
| 验证项 | 验证方法 | 预期结果 |
|---|---|---|
COPY . . 已移除 |
grep -c "COPY . ." backend/Dockerfile |
返回 0 |
| 卷挂载已添加 | grep "app:/app/app" docker-compose.yml |
返回匹配 |
__pycache__ 禁止 |
docker exec wecom_it_backend python -c "import sys; print(sys.dont_write_bytecode)" |
输出 True |
性能验证
| 验证项 | 验证方法 | 预期结果 |
|---|---|---|
| 容器重启速度 | 部署后 docker compose restart backend + time 计时 |
< 15 秒(此前需 4-6 分钟) |
| 健康检查就绪 | 重启后 curl /health 轮询 |
15 秒内就绪 |
🔄 回滚策略(CRITICAL)
回滚触发条件
满足以下 任一 条件即触发回滚:
| # | 触发条件 | 检测方法 |
|---|---|---|
| 1 | 容器启动失败(反复重启) | docker compose ps backend 状态为 restarting 或 unhealthy |
| 2 | 健康检查连续失败 | curl -sf http://localhost:8000/health 返回非 200 |
| 3 | 关键模块导入失败(如 auth) | docker exec wecom_it_backend python -c "from app.auth import router" 报错 |
| 4 | 卷挂载路径不存在或权限拒绝 | 容器日志出现 ModuleNotFoundError 或 PermissionError |
| 5 | 业务接口大面积 500 错误 | Nginx 日志或后端日志大量 500 状态码 |
回滚步骤(可通过 jumpserver-V2 执行)
#!/bin/bash
# =============================================================================
# 回滚脚本:卷挂载方案 → 镜像烘焙方案
# 执行方式:通过 jumpserver-V2 在 10.90.5.110 上执行
# 前提:备份文件存在(部署时已创建 .bak.{TIMESTAMP} 后缀文件)
# =============================================================================
set -e
cd /opt/wecom-it-desk
echo "===== 回滚开始: $(date) ====="
# --- 步骤 1: 恢复备份的配置文件 ---
echo ">>> [1/6] 恢复配置文件..."
COMPOSE_BAK=$(ls -t /opt/wecom-it-desk/docker-compose.yml.bak.* 2>/dev/null | head -1)
if [ -z "$COMPOSE_BAK" ]; then
echo "ERROR: 未找到 docker-compose.yml 备份文件!"
echo "可手动从 git 恢复: git checkout -- docker-compose.yml"
exit 1
fi
cp "$COMPOSE_BAK" /opt/wecom-it-desk/docker-compose.yml
echo " 已恢复 docker-compose.yml <- $COMPOSE_BAK"
DOCKERFILE_BAK=$(ls -t /opt/wecom-it-desk/backend/Dockerfile.bak.* 2>/dev/null | head -1)
if [ -z "$DOCKERFILE_BAK" ]; then
echo "ERROR: 未找到 Dockerfile 备份文件!"
echo "可手动从 git 恢复: git checkout -- backend/Dockerfile"
exit 1
fi
cp "$DOCKERFILE_BAK" /opt/wecom-it-desk/backend/Dockerfile
echo " 已恢复 Dockerfile <- $DOCKERFILE_BAK"
# --- 步骤 2: 确保 backend/app/ 存在(回滚安全网) ---
echo ">>> [2/6] 检查 backend/app/ 目录..."
if [ ! -d /opt/wecom-it-desk/backend/app/ ] || [ -z "$(ls -A /opt/wecom-it-desk/backend/app/ 2>/dev/null)" ]; then
echo " backend/app/ 不存在或为空,从 app/ 同步代码..."
mkdir -p /opt/wecom-it-desk/backend/app/
cp -r /opt/wecom-it-desk/app/* /opt/wecom-it-desk/backend/app/
cp -r /opt/wecom-it-desk/app/.* /opt/wecom-it-desk/backend/app/ 2>/dev/null || true
echo " 已同步代码到 backend/app/"
else
echo " backend/app/ 已存在,跳过同步"
fi
# --- 步骤 3: 重新构建镜像(使用原始 Dockerfile,含 COPY . .) ---
echo ">>> [3/6] 重新构建后端镜像..."
docker compose build --no-cache backend
# --- 步骤 4: 重启容器 ---
echo ">>> [4/6] 重启后端容器..."
docker compose up -d backend
# --- 步骤 5: 等待服务就绪 ---
echo ">>> [5/6] 等待服务启动..."
echo " 等待 45 秒(healthcheck start_period)..."
sleep 45
# --- 步骤 6: 验证 ---
echo ">>> [6/6] 验证回滚结果..."
echo "--- 容器状态 ---"
docker compose ps backend
echo "--- 健康检查 ---"
if curl -sf http://localhost:8000/health > /dev/null 2>&1; then
echo " PASS: /health 返回正常"
else
echo " WARN: /health 未就绪,再等待 15 秒..."
sleep 15
if curl -sf http://localhost:8000/health > /dev/null 2>&1; then
echo " PASS: /health 返回正常(延迟就绪)"
else
echo " FAIL: /health 仍然失败"
echo " 查看日志: docker compose logs --tail=50 backend"
fi
fi
echo "--- Auth 模块验证 ---"
if docker exec wecom_it_backend python -c "from app.auth import router; print('auth OK')" 2>/dev/null; then
echo " PASS: auth 模块可导入"
else
echo " FAIL: auth 模块导入失败"
echo " 查看日志: docker compose logs --tail=50 backend"
fi
echo ""
echo "===== 回滚完成: $(date) ====="
echo ""
echo "如回滚后仍有问题,请检查:"
echo " 1. backend/app/ 代码是否完整: ls -la /opt/wecom-it-desk/backend/app/"
echo " 2. 镜像构建是否成功: docker images | grep wecom-it-desk-backend"
echo " 3. 容器日志: docker compose logs -f backend"
回滚后验证
| 验证项 | 命令 | 预期结果 |
|---|---|---|
| 容器运行状态 | docker compose ps backend |
Status 为 Up (healthy) |
| 健康检查 | curl -sf http://localhost:8000/health |
返回正常 |
| Auth 模块 | docker exec wecom_it_backend python -c "from app.auth import router; print('OK')" |
输出 OK |
| Nginx 代理 | curl -sf http://localhost:80/itdesk/health |
返回正常 |
| 日志无异常 | docker compose logs --tail=50 backend |
无 ModuleNotFoundError / ImportError |
回滚时间预估
| 步骤 | 耗时 |
|---|---|
| 恢复配置文件 | 5 秒 |
| 检查/同步 backend/app/ | 5-30 秒 |
| 重建镜像 | 2-4 分钟 |
| 重启容器 | 10 秒 |
| 等待就绪 | 45 秒 |
| 验证 | 15 秒 |
| 总计 | 3.5-5.5 分钟 |
✅ 完成标准
验收条件
- Dockerfile 已删除
COPY . .,已新增PYTHONDONTWRITEBYTECODE=1 - docker-compose.yml 已新增
./app:/app/app卷挂载 - 镜像已重建并重启成功
- 健康检查通过(
/health返回正常) - Auth 模块可导入(
from app.auth import router成功) - 卷挂载验证通过(容器内
/app/app/main.py可访问) - 代码一致性验证通过(宿主机与容器 md5 一致)
- 日志无
ModuleNotFoundError/ImportError - 部署 48 小时后
backend/app/已清理 - 文档已更新(架构设计文档 v2.1、项目管理主文档 v2.1)
产出确认
- 服务器 Dockerfile 已更新
- 服务器 docker-compose.yml 已更新
- 镜像重建成功
- 容器运行正常
- 回滚脚本已验证可用
- 备份文件已创建(
.bak.{TIMESTAMP}) .rollback-info文件已记录
📊 工作分解
子任务
| 子任务 | 负责人 | 预估工时 | 状态 | 依赖 |
|---|---|---|---|---|
| S1: 前置验证与备份 | 宋献 | 2 分钟 | 待开始 | 无 |
| S2: 代码同步与验证 | 宋献 | 1 分钟 | 待开始 | S1 |
| S3: 修改 Dockerfile | 宋献 | 1 分钟 | 待开始 | S2 |
| S4: 修改 docker-compose.yml | 宋献 | 1 分钟 | 待开始 | S3 |
| S5: 重建镜像并重启 | 宋献 | 3 分钟 | 待开始 | S4 |
| S6: 部署后验证 | 宋献 | 2 分钟 | 待开始 | S5 |
| S7: 清理 backend/app/(48h 后) | 宋献 | 1 分钟 | 待开始 | S6 + 48h |
预估总时间
| 阶段 | 步骤 | 耗时 |
|---|---|---|
| 准备 | S1 + S2 | 3 分钟 |
| 变更 | S3 + S4 | 2 分钟 |
| 部署 | S5 | 3 分钟 |
| 验证 | S6 | 2 分钟 |
| 总计 | S1-S6 | 约 10 分钟 |
| 清理 | S7(48h 后) | 1 分钟 |
| 回滚(如需) | 回滚脚本 | 3.5-5.5 分钟 |
📞 依赖与阻塞
前置依赖
| 依赖任务 | 依赖说明 | 状态 |
|---|---|---|
| 无 | 独立运维任务 | — |
阻塞因素
| 阻塞项 | 影响范围 | 解决方案 |
|---|---|---|
| 无 | — | — |
⚠️ 风险评估
| # | 风险 | 概率 | 等级 | 对策 |
|---|---|---|---|---|
| R1 | __pycache__ 污染宿主机代码目录 |
中 | 中 | Dockerfile 设置 PYTHONDONTWRITEBYTECODE=1 |
| R2 | 宿主机代码被意外修改导致运行中服务异常 | 低 | 中 | 生产不启用 --reload;限制目录写权限 |
| R3 | backend/app/ 被提前删除导致回滚失败 |
低 | 高 | 48 小时内不删除;回滚脚本含自动同步逻辑 |
| R4 | requirements.txt 与代码不同步 | 低 | 中 | 部署前 diff 对比;新增依赖时先重建镜像 |
| R5 | 卷挂载路径与现有挂载冲突 | 极低 | 低 | /app/app 与 /app/uploads、/app/logs 无交集 |
📈 变更记录
| 日期 | 变更内容 | 变更人 | 说明 |
|---|---|---|---|
| 2026-07-10 | 创建任务 | 宋献 | 初始版本,基于架构师高见远的卷挂载重构方案 |
📎 附件
- 完整方案文档:
docs/04-运维文档/部署运维/卷挂载重构方案.md(含完整命令块、时序图、风险评估) - 架构设计文档:
docs/03-技术架构/IT智能服务台-系统架构设计文档v2.md§5.0 - 故障排查手册:
docs/04-运维文档/部署运维/00-标准故障排查手册.md§1.4 + CASE-20260710-02 - deploy-troubleshoot skill:
~/.workbuddy/skills/deploy-troubleshoot/SKILL.mdStep -1 部署前同步检查 - task-intake skill:
.workbuddy/skills/task-intake/SKILL.mdStep 3.1 部署运维前置检查
📝 完整部署命令块
以下命令可通过 jumpserver-V2 在服务器 10.90.5.110 上按步骤执行。
S1: 前置验证与备份
#!/bin/bash
set -e
cd /opt/wecom-it-desk
# 1. 验证当前服务正常
echo ">>> 验证当前服务状态..."
curl -sf http://localhost:8000/health > /dev/null && echo " PASS: 当前服务正常" || { echo " FAIL: 当前服务异常,请先修复再部署"; exit 1; }
# 2. 备份配置文件
BACKUP_TS=$(date +%Y%m%d%H%M%S)
cp /opt/wecom-it-desk/docker-compose.yml /opt/wecom-it-desk/docker-compose.yml.bak.${BACKUP_TS}
cp /opt/wecom-it-desk/backend/Dockerfile /opt/wecom-it-desk/backend/Dockerfile.bak.${BACKUP_TS}
echo " PASS: 备份完成 (timestamp: ${BACKUP_TS})"
# 3. 记录回滚信息
cat > /opt/wecom-it-desk/.rollback-info << EOF
ROLLBACK_TIMESTAMP=${BACKUP_TS}
COMPOSE_BAK=/opt/wecom-it-desk/docker-compose.yml.bak.${BACKUP_TS}
DOCKERFILE_BAK=/opt/wecom-it-desk/backend/Dockerfile.bak.${BACKUP_TS}
DEPLOY_DATE=$(date)
EOF
echo " PASS: 回滚信息已记录到 .rollback-info"
echo ""
echo "===== S1 完成 ====="
S2: 代码同步与验证
#!/bin/bash
set -e
cd /opt/wecom-it-desk
# 1. 验证 app/ 目录存在且包含关键文件
echo ">>> 验证代码目录..."
for f in app/__init__.py app/main.py app/config.py app/database.py; do
if [ -f "$f" ]; then
echo " PASS: $f 存在"
else
echo " FAIL: $f 不存在!请先解压部署包: tar -xf /tmp/deploy-backend.tar -C ./"
exit 1
fi
done
# 2. 验证 auth 模块(关键!曾因缺失导致故障)
if [ -f "app/api/auth.py" ]; then
echo " PASS: app/api/auth.py 存在"
else
echo " FAIL: app/api/auth.py 不存在!认证功能将不可用"
exit 1
fi
# 3. 统计代码文件数
FILE_COUNT=$(find app/ -name "*.py" | wc -l)
echo " INFO: app/ 目录共 ${FILE_COUNT} 个 Python 文件"
echo ""
echo "===== S2 完成 ====="
S3: 修改 Dockerfile
#!/bin/bash
set -e
DOCKERFILE=/opt/wecom-it-desk/backend/Dockerfile
echo ">>> 修改 Dockerfile..."
# 直接写入完整文件(最可靠)
cat > "$DOCKERFILE" << 'DOCKERFILE_EOF'
# =============================================================================
# 企微IT智能服务台 — 后端 Docker 镜像构建文件
# =============================================================================
# 说明:基于 Python 3.12 构建后端镜像
# 变更:2025-07-10 方案C — 代码改为 volume 挂载,镜像不再 COPY 业务代码
# 用法:docker build -t wecom-it-desk-backend .
# =============================================================================
# --------------------------------------------------------------------------
# 第一阶段:构建阶段
# --------------------------------------------------------------------------
FROM python:3.12-slim AS builder
# 设置工作目录
WORKDIR /app
# 安装系统依赖(psycopg2 编译需要 + qrcode 图片处理需要 + healthcheck 需要 curl)
RUN apt-get update && \
apt-get install -y --no-install-recommends gcc libpq-dev libjpeg-dev zlib1g-dev curl && \
rm -rf /var/lib/apt/lists/*
# 复制依赖声明文件并安装(利用 Docker 层缓存,依赖不变则不重新安装)
COPY requirements.txt .
RUN pip install --no-cache-dir \
--timeout 180 \
--retries 5 \
-i https://mirrors.aliyun.com/pypi/simple/ \
--trusted-host mirrors.aliyun.com \
-r requirements.txt
# --------------------------------------------------------------------------
# 第二阶段:运行阶段(更小的镜像体积)
# --------------------------------------------------------------------------
FROM python:3.12-slim
# 设置标签信息
LABEL maintainer="IT服务台开发团队"
LABEL description="企微IT智能服务台后端服务"
LABEL changelog="2025-07-10: 移除 COPY . .,代码改为 volume 挂载"
# 安装运行时依赖(psycopg2 运行时需要 libpq + healthcheck 需要 curl)
RUN apt-get update && \
apt-get install -y --no-install-recommends libpq5 curl && \
rm -rf /var/lib/apt/lists/*
# 设置工作目录
WORKDIR /app
# 禁止 Python 写入 __pycache__(防止污染宿主机代码目录)
ENV PYTHONDONTWRITEBYTECODE=1
# 从构建阶段复制已安装的 Python 包
COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin
# 业务代码通过 docker-compose volumes 挂载(./app:/app/app),不再 COPY 进镜像
# 暴露端口
EXPOSE 8000
# 启动命令(Docker Compose 中会覆盖)
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
DOCKERFILE_EOF
# 验证
if grep -q "COPY . ." "$DOCKERFILE"; then
echo " FAIL: Dockerfile 仍包含 COPY . ."
exit 1
fi
if grep -q "PYTHONDONTWRITEBYTECODE" "$DOCKERFILE"; then
echo " PASS: PYTHONDONTWRITEBYTECODE 已设置"
else
echo " FAIL: PYTHONDONTWRITEBYTECODE 未找到"
exit 1
fi
echo " PASS: Dockerfile 已更新"
echo ""
echo "===== S3 完成 ====="
S4: 修改 docker-compose.yml
#!/bin/bash
set -e
cd /opt/wecom-it-desk
COMPOSE_FILE=/opt/wecom-it-desk/docker-compose.yml
echo ">>> 修改 docker-compose.yml..."
# 在 backend-uploads 行之前插入代码卷挂载行
sed -i '/backend-uploads:\/app\/uploads/i\ - ./app:/app/app # 代码卷挂载(方案C)' "$COMPOSE_FILE"
# 验证
if grep -q "./app:/app/app" "$COMPOSE_FILE"; then
echo " PASS: 代码卷挂载已添加"
else
echo " FAIL: 代码卷挂载未找到,请手动编辑 docker-compose.yml"
echo " 在 backend 服务的 volumes: 下添加:"
echo " - ./app:/app/app"
exit 1
fi
echo ""
echo "===== S4 完成 ====="
S5: 重建镜像并重启
#!/bin/bash
set -e
cd /opt/wecom-it-desk
echo ">>> 重建后端镜像..."
docker compose build backend
echo " PASS: 镜像构建完成"
echo ""
echo ">>> 重启后端容器..."
docker compose up -d backend
echo " PASS: 容器已启动"
echo ""
echo ">>> 等待服务就绪 (45秒)..."
sleep 45
echo ""
echo "===== S5 完成 ====="
S6: 部署后验证
#!/bin/bash
cd /opt/wecom-it-desk
echo "===== 部署后验证 ====="
echo ""
# 1. 容器状态
echo "--- 1. 容器状态 ---"
docker compose ps backend
echo ""
# 2. 健康检查
echo "--- 2. 健康检查 ---"
if curl -sf http://localhost:8000/health; then
echo ""
echo " PASS: /health 正常"
else
echo " FAIL: /health 异常"
fi
echo ""
# 3. Auth 模块验证
echo "--- 3. Auth 模块验证 ---"
if docker exec wecom_it_backend python -c "from app.auth import router; print(' auth module: OK')" 2>/dev/null; then
echo " PASS: auth 模块可导入"
else
echo " FAIL: auth 模块导入失败"
echo " 查看日志: docker compose logs --tail=50 backend"
fi
echo ""
# 4. 卷挂载验证
echo "--- 4. 卷挂载验证 ---"
if docker exec wecom_it_backend ls -la /app/app/main.py > /dev/null 2>&1; then
echo " PASS: /app/app/main.py 可访问(卷挂载正常)"
else
echo " FAIL: /app/app/main.py 不可访问(卷挂载异常)"
fi
echo ""
# 5. 代码来源验证
echo "--- 5. 代码来源验证 ---"
HOST_HASH=$(md5sum /opt/wecom-it-desk/app/main.py | awk '{print $1}')
CONTAINER_HASH=$(docker exec wecom_it_backend md5sum /app/app/main.py 2>/dev/null | awk '{print $1}')
if [ "$HOST_HASH" = "$CONTAINER_HASH" ]; then
echo " PASS: 宿主机与容器代码一致 (md5: ${HOST_HASH})"
else
echo " WARN: 宿主机与容器代码不一致"
echo " 宿主机: $HOST_HASH"
echo " 容器: $CONTAINER_HASH"
fi
echo ""
# 6. 日志检查
echo "--- 6. 最近 20 行日志 ---"
docker compose logs --tail=20 backend
echo ""
echo "===== 验证完成 ====="
echo ""
echo "如全部 PASS,部署成功。"
echo "如出现 FAIL,请执行回滚脚本(见上方回滚策略章节)。"
S7: 清理(48 小时后执行)
#!/bin/bash
set -e
cd /opt/wecom-it-desk
echo ">>> 清理旧代码目录..."
echo " 注意:仅在部署成功 48 小时后执行此步骤!"
echo ""
# 确认服务稳定
curl -sf http://localhost:8000/health > /dev/null && echo " 服务正常" || { echo " 服务异常,取消清理"; exit 1; }
# 删除 backend/app/(不再需要)
if [ -d /opt/wecom-it-desk/backend/app/ ]; then
echo " 删除 backend/app/..."
rm -rf /opt/wecom-it-desk/backend/app/
echo " PASS: backend/app/ 已删除"
else
echo " INFO: backend/app/ 已不存在,跳过"
fi
echo ""
echo "===== 清理完成 ====="