Files
wecom_it_smart_desk/docs/07-项目管理/任务说明书/任务说明书-77-后端部署卷挂载改造.md
T
Simon 44e77dcb0e chore(docs): docs/ 目录全面重新编号 + 重组
**重构前**(旧编号 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 行
2026-08-03 18:46:55 +08:00

21 KiB
Raw Blame History

任务说明书 — 后端部署卷挂载改造

版本: 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 状态为 restartingunhealthy
2 健康检查连续失败 curl -sf http://localhost:8000/health 返回非 200
3 关键模块导入失败(如 auth docker exec wecom_it_backend python -c "from app.auth import router" 报错
4 卷挂载路径不存在或权限拒绝 容器日志出现 ModuleNotFoundErrorPermissionError
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 分钟
清理 S748h 后) 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.md Step -1 部署前同步检查
  • task-intake skill.workbuddy/skills/task-intake/SKILL.md Step 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 "===== 清理完成 ====="