Completed: - Add 6 core database documents (docs/01-平台基础层/07-数据库/) Architecture overview, migration history, environment comparison, tech debt tracking, seed data management, PostgreSQL extensions - Restructure deployment docs: archive 20 legacy files to _archive-2025/ - Create unified daily operations manual (01-日常更新操作手册.md) - Add pending deployment change tracker (03-待部署变更清单.md) - Update database development standard to v3.0 (three iron rules) - Fix Prisma schema type drift: align @db.* annotations with actual DB IIT: UUID/Timestamptz(6), SSA: Timestamp(6)/VarChar(20/50/100) - Add migration: 20260227_align_schema_with_db_types (idempotent ALTER) - Add Cursor Rule for auto-reminding deployment change documentation - Update system status guide v6.4 with deployment and DB doc references - Add architecture consultation docs (Prisma guide, SAE deployment guide) Technical details: - Manual migration due to shadow DB limitation (TD-001 in tech debt) - Deployment docs reduced from 20+ scattered files to 3 core documents - Cursor Rule triggers on schema.prisma, package.json, Dockerfile changes Made-with: Cursor
629 lines
13 KiB
Markdown
629 lines
13 KiB
Markdown
# 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 环境检查
|
||
|
||
```powershell
|
||
# 1. 检查Docker状态
|
||
docker --version
|
||
docker ps
|
||
|
||
# 2. 检查Node.js环境
|
||
node --version # 应该显示 v22.x
|
||
npm --version
|
||
|
||
# 3. 确认当前目录
|
||
cd D:\MyCursor\AIclinicalresearch\backend
|
||
```
|
||
|
||
### 2.2 代码准备
|
||
|
||
#### ✅ 必须完成的步骤
|
||
|
||
**步骤1:Prisma反向同步(必须)**
|
||
|
||
```powershell
|
||
# 设置临时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**
|
||
|
||
```powershell
|
||
npx prisma generate
|
||
|
||
# 输出应该显示:
|
||
# ✔ Generated Prisma Client (v6.17.0)
|
||
```
|
||
|
||
**步骤3:TypeScript编译(必须)**
|
||
|
||
```powershell
|
||
# 编译TypeScript代码
|
||
npm run build
|
||
|
||
# 成功输出:
|
||
# > ai-clinical-backend@1.0.0 build
|
||
# > tsc
|
||
# (无错误信息)
|
||
|
||
# 检查dist目录是否生成
|
||
ls dist
|
||
```
|
||
|
||
⚠️ **重要提示**:如果编译失败,必须先修复所有TypeScript错误!
|
||
|
||
---
|
||
|
||
## 3. 构建流程
|
||
|
||
### 3.1 完整构建步骤
|
||
|
||
```powershell
|
||
# 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分钟)**
|
||
```dockerfile
|
||
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秒)**
|
||
```dockerfile
|
||
COPY dist ./dist # 复制本地已编译的TypeScript代码
|
||
```
|
||
|
||
**阶段3:运行镜像打包(约1分钟)**
|
||
```dockerfile
|
||
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
|
||
|
||
```powershell
|
||
# 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 镜像详情
|
||
|
||
```yaml
|
||
镜像摘要: 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 启动命令
|
||
|
||
```dockerfile
|
||
CMD ["node", "dist/index.js"]
|
||
```
|
||
|
||
### 4.5 健康检查
|
||
|
||
```dockerfile
|
||
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 常见问题
|
||
|
||
#### ❌ 问题1:TypeScript编译失败
|
||
|
||
**症状:**
|
||
```
|
||
error TS2554: Expected 2-3 arguments, but got 1.
|
||
error TS2322: Type 'XXX' is not assignable to type 'YYY'.
|
||
```
|
||
|
||
**原因:**
|
||
- Prisma反向同步后缺少关系字段
|
||
- 代码与Prisma生成的类型不匹配
|
||
|
||
**解决方案:**
|
||
```powershell
|
||
# 1. 重新同步Prisma
|
||
npx prisma db pull
|
||
npx prisma generate
|
||
|
||
# 2. 重新编译
|
||
npm run build
|
||
|
||
# 3. 如果还有错误,检查tsconfig.json配置
|
||
```
|
||
|
||
#### ❌ 问题2:Docker构建网络超时
|
||
|
||
**症状:**
|
||
```
|
||
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地址
|
||
- 应该使用个人版实例地址,不是企业版地址
|
||
|
||
**解决方案:**
|
||
```powershell
|
||
# ✅ 正确的登录地址(个人版实例)
|
||
docker login crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com
|
||
|
||
# ❌ 错误的登录地址(企业版)
|
||
docker login registry.cn-beijing.aliyuncs.com
|
||
```
|
||
|
||
#### ❌ 问题4:VPC内网地址推送超时
|
||
|
||
**症状:**
|
||
```
|
||
failed to do request: Head "https://crpi-cd5ij4pjt65mweeo-vpc.cn-beijing.personal.cr.aliyuncs.com/...": net/http: TLS handshake timeout
|
||
```
|
||
|
||
**原因:**
|
||
- VPC内网地址只能在阿里云VPC内访问
|
||
- 本地开发环境无法访问VPC内网
|
||
|
||
**解决方案:**
|
||
```powershell
|
||
# 本地开发环境使用公网地址推送
|
||
docker push crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com/ai-clinical/backend-service:v1.0
|
||
|
||
# SAE部署时使用VPC内网地址拉取(在SAE控制台配置)
|
||
```
|
||
|
||
### 5.2 验证镜像
|
||
|
||
```powershell
|
||
# 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 Client(`npx prisma generate`)
|
||
- [ ] TypeScript编译成功(`npm run build`)
|
||
- [ ] dist目录已生成且包含所有编译后的JS文件
|
||
- [ ] Docker Desktop已启动并运行
|
||
- [ ] 网络连接正常
|
||
|
||
### 6.2 版本管理
|
||
|
||
**镜像标签规范:**
|
||
```
|
||
v1.0 - 特定版本(推荐用于生产环境)
|
||
latest - 最新版本(用于测试环境)
|
||
v1.0.1 - Bug修复版本
|
||
v1.1.0 - 功能更新版本
|
||
```
|
||
|
||
**推送策略:**
|
||
```powershell
|
||
# 生产环境部署(推送特定版本)
|
||
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缓存**
|
||
```dockerfile
|
||
# ✅ 先复制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. 多阶段构建**
|
||
```dockerfile
|
||
# 阶段1:依赖安装
|
||
FROM node:alpine AS builder
|
||
...
|
||
|
||
# 阶段2:运行时镜像(更小)
|
||
FROM node:alpine
|
||
COPY --from=builder ...
|
||
```
|
||
|
||
### 6.4 安全建议
|
||
|
||
1. **不要在镜像中包含敏感信息**
|
||
- ❌ 不要把`.env`文件复制到镜像
|
||
- ✅ 使用SAE环境变量注入配置
|
||
|
||
2. **使用非root用户运行**
|
||
```dockerfile
|
||
RUN addgroup -g 1001 -S nodejs && \
|
||
adduser -S nodejs -u 1001
|
||
USER nodejs
|
||
```
|
||
|
||
3. **定期更新基础镜像**
|
||
```dockerfile
|
||
FROM node:alpine # 使用最新的Alpine版本
|
||
```
|
||
|
||
---
|
||
|
||
## 附录A:Dockerfile完整内容
|
||
|
||
```dockerfile
|
||
# ==================== 阶段 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:相关文档
|
||
|
||
- [Node.js后端-SAE容器部署指南](./05-Node.js后端-SAE容器部署指南.md)
|
||
- [部署进度总览](./00-部署进度总览.md)
|
||
- [PostgreSQL数据库部署操作手册](./08-PostgreSQL数据库部署操作手册.md)
|
||
- [Python微服务-SAE部署操作手册](./09-Python微服务-SAE部署操作手册.md)
|
||
|
||
---
|
||
|
||
**文档维护者**: AI Assistant
|
||
**最后更新**: 2024-12-24
|
||
**版本**: v1.0
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|