Files
AIclinicalresearch/docs/05-部署文档/10-Node.js后端-Docker镜像构建手册.md
HaHafeng 5523ef36ea feat(admin): Complete Phase 3.5.1-3.5.4 Prompt Management System (83%)
Summary:
- Implement Prompt management infrastructure and core services
- Build admin portal frontend with light theme
- Integrate CodeMirror 6 editor for non-technical users

Phase 3.5.1: Infrastructure Setup
- Create capability_schema for Prompt storage
- Add prompt_templates and prompt_versions tables
- Add prompt:view/edit/debug/publish permissions
- Migrate RVW prompts to database (RVW_EDITORIAL, RVW_METHODOLOGY)

Phase 3.5.2: PromptService Core
- Implement gray preview logic (DRAFT for debuggers, ACTIVE for users)
- Module-level debug control (setDebugMode)
- Handlebars template rendering
- Variable extraction and validation (extractVariables, validateVariables)
- Three-level disaster recovery (database -> cache -> hardcoded fallback)

Phase 3.5.3: Management API
- 8 RESTful endpoints (/api/admin/prompts/*)
- Permission control (PROMPT_ENGINEER can edit, SUPER_ADMIN can publish)

Phase 3.5.4: Frontend Management UI
- Build admin portal architecture (AdminLayout, OrgLayout)
- Add route system (/admin/*, /org/*)
- Implement PromptListPage (filter, search, debug switch)
- Implement PromptEditor (CodeMirror 6 simplified for clinical users)
- Implement PromptEditorPage (edit, save, publish, test, version history)

Technical Details:
- Backend: 6 files, ~2044 lines (prompt.service.ts 596 lines)
- Frontend: 9 files, ~1735 lines (PromptEditorPage.tsx 399 lines)
- CodeMirror 6: Line numbers, auto-wrap, variable highlight, search, undo/redo
- Chinese-friendly: 15px font, 1.8 line-height, system fonts

Next Step: Phase 3.5.5 - Integrate RVW module with PromptService

Tested: Backend API tests passed (8/8), Frontend pending user testing
Status: Ready for Phase 3.5.5 RVW integration
2026-01-11 21:25:16 +08:00

13 KiB
Raw Blame History

Node.js 后端 - Docker 镜像构建操作手册

文档版本: v1.0
创建时间: 2024-12-24
适用范围: AIclinicalresearch 平台 - Node.js 后端服务
目标读者: 运维工程师、后端开发工程师


📋 目录

  1. 构建概述
  2. 前置准备
  3. 构建流程
  4. 镜像信息
  5. 故障排查
  6. 最佳实践

1. 构建概述

1.1 构建策略

本次构建采用改进版方案B本地编译+Docker打包,相比传统方案有以下优势:

对比项 传统方案 改进版方案B
TypeScript编译 Docker中执行 本地预编译
依赖安装 完整依赖 仅生产依赖
网络依赖 高(易超时) 低(更稳定)
构建时间 ~10分钟 ~5分钟
平台兼容性 有风险 无风险

1.2 构建成果

镜像名称: backend-service:v1.0
镜像大小: 838MB (压缩后 ~186MB)
镜像ID: a4ffb61c15af
构建时间: ~5分钟
ACR地址: crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/backend-service:v1.0

2. 前置准备

2.1 环境检查

# 1. 检查Docker状态
docker --version
docker ps

# 2. 检查Node.js环境
node --version  # 应该显示 v22.x
npm --version

# 3. 确认当前目录
cd D:\MyCursor\AIclinicalresearch\backend

2.2 代码准备

必须完成的步骤

步骤1Prisma反向同步必须

# 设置临时RDS连接环境变量
$env:DATABASE_URL = "postgresql://airesearch:Xibahe%40fengzhibo117@pgm-2zex1m2y3r23hdn5oo.pg.rds.aliyuncs.com:5432/ai_clinical_research?connection_limit=18&pool_timeout=10"

# 执行Prisma反向同步
npx prisma db pull

# 输出应该显示:
# ✔ Introspected 32 models and wrote them into prisma\schema.prisma

步骤2生成Prisma Client

npx prisma generate

# 输出应该显示:
# ✔ Generated Prisma Client (v6.17.0)

步骤3TypeScript编译必须

# 编译TypeScript代码
npm run build

# 成功输出:
# > ai-clinical-backend@1.0.0 build
# > tsc
# (无错误信息)

# 检查dist目录是否生成
ls dist

⚠️ 重要提示如果编译失败必须先修复所有TypeScript错误


3. 构建流程

3.1 完整构建步骤

# 1. 确保在backend目录
cd D:\MyCursor\AIclinicalresearch\backend

# 2. 检查Dockerfile和.dockerignore
ls Dockerfile
ls .dockerignore

# 3. 开始构建需要5分钟
docker build -t backend-service:v1.0 .

# 4. 查看构建结果
docker images backend-service:v1.0

3.2 构建过程说明

阶段1依赖安装约4分钟

FROM node:alpine AS builder
RUN apk add --no-cache openssl
COPY package*.json ./
COPY prisma ./prisma/
RUN npm ci --production  # 仅安装生产依赖
RUN npx prisma generate  # 生成Prisma Client

阶段2复制本地编译结果约10秒

COPY dist ./dist  # 复制本地已编译的TypeScript代码

阶段3运行镜像打包约1分钟

FROM node:alpine
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/prisma ./prisma

3.3 推送到ACR

# 1. 登录ACR使用个人版实例地址
echo "fengzhibo117" | docker login --username=gofeng117@163.com --password-stdin crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com

# 输出Login Succeeded

# 2. 打标签
docker tag backend-service:v1.0 crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/backend-service:v1.0
docker tag backend-service:v1.0 crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/backend-service:latest

# 3. 推送镜像
docker push crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/backend-service:v1.0
docker push crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/backend-service:latest

# 输出:
# v1.0: digest: sha256:a4ffb61c15af... size: 856
# latest: digest: sha256:a4ffb61c15af... size: 856

4. 镜像信息

4.1 镜像地址

VPC内网地址SAE部署使用

crpi-cd5ij4pjt65mweeo-vpc.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/backend-service:v1.0

公网地址(查看和拉取):

crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/backend-service:v1.0

4.2 镜像详情

镜像摘要: sha256:a4ffb61c15af1cd1ed9de187b4464a1aab773918e5b41b4df5b8ad96514f9941
大小: 838MB (压缩后 ~186MB)
架构: linux/amd64
基础镜像: node:alpine
Node版本: 22.x
标签: v1.0, latest

4.3 镜像内容

/app/
├── node_modules/          # 生产依赖
├── dist/                  # 编译后的JS代码
├── prisma/                # Prisma Schema
│   └── schema.prisma
├── package.json
└── uploads/               # 临时文件目录

4.4 启动命令

CMD ["node", "dist/index.js"]

4.5 健康检查

HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
  CMD node -e "require('http').get('http://localhost:3001/health', (res) => { process.exit(res.statusCode === 200 ? 0 : 1); })"

5. 故障排查

5.1 常见问题

问题1TypeScript编译失败

症状:

error TS2554: Expected 2-3 arguments, but got 1.
error TS2322: Type 'XXX' is not assignable to type 'YYY'.

原因:

  • Prisma反向同步后缺少关系字段
  • 代码与Prisma生成的类型不匹配

解决方案:

# 1. 重新同步Prisma
npx prisma db pull
npx prisma generate

# 2. 重新编译
npm run build

# 3. 如果还有错误检查tsconfig.json配置

问题2Docker构建网络超时

症状:

npm error code ECONNRESET
npm error network aborted

原因:

  • npm镜像源网络不稳定
  • npm ci 下载依赖失败

解决方案: 使用改进版方案B本次采用的方案

  • 本地预编译跳过Docker中的编译步骤
  • 只安装生产依赖,减少网络传输

问题3推送到ACR失败403 Forbidden

症状:

Error response from daemon: login attempt to https://registry.cn-beijing.aliyuncs.com/v2/ failed with status: 403 Forbidden

原因:

  • 使用了错误的ACR地址
  • 应该使用个人版实例地址,不是企业版地址

解决方案:

# ✅ 正确的登录地址(个人版实例)
docker login crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com

# ❌ 错误的登录地址(企业版)
docker login registry.cn-beijing.aliyuncs.com

问题4VPC内网地址推送超时

症状:

failed to do request: Head "https://crpi-cd5ij4pjt65mweeo-vpc.cn-beijing.personal.cr.aliyuncs.com/...": net/http: TLS handshake timeout

原因:

  • VPC内网地址只能在阿里云VPC内访问
  • 本地开发环境无法访问VPC内网

解决方案:

# 本地开发环境使用公网地址推送
docker push crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/backend-service:v1.0

# SAE部署时使用VPC内网地址拉取在SAE控制台配置

5.2 验证镜像

# 1. 检查镜像是否存在
docker images backend-service:v1.0

# 2. 查看镜像详细信息
docker inspect backend-service:v1.0

# 3. 测试运行镜像(可选)
docker run -d `
  --name backend-test `
  -p 3001:3001 `
  -e DATABASE_URL="postgresql://..." `
  -e NODE_ENV=production `
  backend-service:v1.0

# 4. 查看容器日志
docker logs backend-test

# 5. 测试健康检查
curl http://localhost:3001/health

# 6. 清理测试容器
docker stop backend-test
docker rm backend-test

6. 最佳实践

6.1 构建前检查清单

  • 已完成Prisma反向同步npx prisma db pull
  • 已生成Prisma Clientnpx prisma generate
  • TypeScript编译成功npm run build
  • dist目录已生成且包含所有编译后的JS文件
  • Docker Desktop已启动并运行
  • 网络连接正常

6.2 版本管理

镜像标签规范:

v1.0       - 特定版本(推荐用于生产环境)
latest     - 最新版本(用于测试环境)
v1.0.1     - Bug修复版本
v1.1.0     - 功能更新版本

推送策略:

# 生产环境部署(推送特定版本)
docker push crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/backend-service:v1.0

# 测试环境部署同时推送latest
docker push crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/backend-service:v1.0
docker push crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/backend-service:latest

6.3 构建优化

1. 利用Docker缓存

# ✅ 先复制package.json利用依赖缓存
COPY package*.json ./
RUN npm ci --production

# ✅ 最后复制代码,代码变化不影响依赖层缓存
COPY dist ./dist

2. 使用.dockerignore

node_modules
.env
.env.*
dist          # 本地dist通过COPY显式复制
test
tests
*.md
.git

3. 多阶段构建

# 阶段1依赖安装
FROM node:alpine AS builder
...

# 阶段2运行时镜像更小
FROM node:alpine
COPY --from=builder ...

6.4 安全建议

  1. 不要在镜像中包含敏感信息

    • 不要把.env文件复制到镜像
    • 使用SAE环境变量注入配置
  2. 使用非root用户运行

    RUN addgroup -g 1001 -S nodejs && \
        adduser -S nodejs -u 1001
    USER nodejs
    
  3. 定期更新基础镜像

    FROM node:alpine  # 使用最新的Alpine版本
    

附录ADockerfile完整内容

# ==================== 阶段 1: 依赖安装阶段 ====================
FROM node:alpine AS builder

# 替换Alpine镜像源为阿里云镜像解决网络问题
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories

# 安装 Prisma 运行时依赖
RUN apk add --no-cache openssl

WORKDIR /app

# 1. 复制依赖文件
COPY package*.json ./

# 2. 复制 Prisma Schema用于生成Prisma Client
COPY prisma ./prisma/

# 3. 只安装生产依赖(大幅减少网络传输和安装时间)
RUN npm config set registry https://registry.npmmirror.com && \
    npm config set fetch-retry-mintimeout 20000 && \
    npm config set fetch-retry-maxtimeout 120000 && \
    npm config set fetch-retries 5 && \
    npm ci --production --prefer-offline --no-audit

# 4. 生成 Prisma Client生产环境需要
RUN npx prisma generate

# 5. 复制本地已编译好的 dist 文件夹跳过TypeScript编译
COPY dist ./dist

# ==================== 阶段 2: 运行阶段 ====================
FROM node:alpine

# 替换Alpine镜像源为阿里云镜像解决网络问题
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories

# 安装运行时依赖 + 时区数据
RUN apk add --no-cache \
    openssl \
    curl \
    ca-certificates \
    tzdata

# ⚠️ 统一时区Asia/Shanghai
ENV TZ=Asia/Shanghai

# 创建非 root 用户(安全最佳实践)
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001

WORKDIR /app

# 从构建阶段复制产物
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
COPY --from=builder --chown=nodejs:nodejs /app/package*.json ./
COPY --from=builder --chown=nodejs:nodejs /app/prisma ./prisma

# 创建上传目录(用于临时文件)
RUN mkdir -p /app/uploads && chown -R nodejs:nodejs /app/uploads

# 切换到非 root 用户
USER nodejs

# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
  CMD node -e "require('http').get('http://localhost:3001/health', (res) => { process.exit(res.statusCode === 200 ? 0 : 1); })"

# 暴露端口
EXPOSE 3001

# 🔥 启动命令(仅启动应用,不执行数据库迁移)
CMD ["node", "dist/index.js"]

附录B.dockerignore完整内容

# Node.js
node_modules
npm-debug.log
yarn-error.log

# 开发文件
.env
.env.*
*.local

# 构建产物改进方案B使用本地编译好的dist
# dist  # 暂时注释掉允许复制本地dist

# 测试文件
test
tests
*.test.ts
*.spec.ts
coverage

# 文档和临时文件
docs
*.md
.vscode
.idea
.DS_Store
Thumbs.db

# 上传文件(运行时生成)
uploads/*

# Git
.git
.gitignore

# 日志
*.log
logs

# 临时文件
temp
tmp
*.swp
*.swo
*~

# 数据库文件SQLite如果有
*.db
*.sqlite

# 脚本文件(仅开发使用)
scripts/*.ts
*.bat
*.ps1

附录C相关文档


文档维护者: AI Assistant
最后更新: 2024-12-24
版本: v1.0