# 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