Docker 与 TypeScript:高级应用容器化

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

Docker 与 TypeScript:高级应用容器化

\n\n

使用 Docker 对 TypeScript 应用进行容器化,不仅仅是简单地将代码打包成镜像。在高级层面,这是一门关于优化、安全和自动化的艺术。在本文中,我们将探讨如何为 TypeScript 创建高效的 Docker 镜像、管理环境以及将容器化集成到 CI/CD 流水线中。您将学习如何将镜像体积缩小 5-10 倍、加速构建并改善依赖隔离。

\n\n

1. TypeScript 的多阶段构建

\n\n

TypeScript 的 Docker 镜像存在一个经典问题:最终镜像中包含了所有开发工具,如 TypeScript 编译器、开发依赖和源文件。这会导致镜像体积巨大(通常 > 1 GB)。解决方案是多阶段构建。

\n\n

思路很简单:第一阶段(构建)将 TypeScript 编译为 JavaScript 并安装所有依赖。第二阶段(生产)仅复制编译好的 JavaScript 和生产依赖。

\n\n
# 阶段 1:构建\nFROM node:20-alpine AS builder\nWORKDIR /app\nCOPY package*.json ./\nRUN npm ci --only=development\nCOPY . .\nRUN npm run build\n\n# 阶段 2:生产\nFROM node:20-alpine AS production\nWORKDIR /app\nCOPY --from=builder /app/dist ./dist\nCOPY --from=builder /app/package*.json ./\nRUN npm ci --only=production\nEXPOSE 3000\nCMD ["node", "dist/index.js"]
\n\n

这种方法可将镜像体积从约 800 MB 缩减至约 150 MB。请注意 --only=development 和 --only=production —— 这是分离依赖的关键标志。

\n\n

2. 优化 Docker 层缓存

\n\n

Dockerfile 中的每条指令都会创建一个层。如果层未更改,Docker 会对其进行缓存。指令的正确顺序对于加快重复构建速度至关重要。

\n\n

糟糕的示例(破坏缓存):

\n
FROM node:20-alpine\nWORKDIR /app\nCOPY . .\nRUN npm ci\nRUN npm run build\nCMD ["node", "dist/index.js"]
\n\n

在这里,源代码的任何更改(即使是 README.md)都会导致 npm ci 和构建的缓存失效。

\n\n

良好的示例(保留缓存):

\n
FROM node:20-alpine AS builder\nWORKDIR /app\nCOPY package*.json tsconfig.json ./\nRUN npm ci --only=development\nCOPY src ./src\nRUN npm run build
\n\n

为什么这样有效?依赖项很少更改,而源代码经常更改。通过单独复制 package*.json,我们确保 npm ci 层仅在这些文件更改时才重新构建。

\n\n

3. 处理环境变量和密钥

\n\n

对于 Node.js 上的 TypeScript 应用,使用环境变量是标准做法。但在 Docker 中需要注意一些细节。

\n\n

典型错误: 通过 Dockerfile 中的 ENV 传递敏感数据(API 密钥、密码)。这会将密钥保留在镜像层的历史记录中。

\n\n

正确方法:

\n
    \n
  • 在启动容器时使用 --env-file
  • \n
  • 对于生产环境,使用 Docker Secrets(在 Swarm 中)或 Kubernetes Secrets
  • \n
  • 对于本地开发,使用 .env 文件配合 docker-compose
  • \n
\n\n

docker-compose.yml 示例:

\n
version: '3.8'\nservices:\n  app:\n    build: .\n    ports:\n      - "3000:3000"\n    env_file:\n      - .env.production\n    volumes:\n      - ./logs:/app/logs
\n\n

在 TypeScript 代码中,使用 dotenv 库加载变量:

\n
import dotenv from 'dotenv';\nimport { resolve } from 'path';\n\ndotenv.config({ path: resolve(__dirname, '../.env') });\n\nconst config = {\n  port: parseInt(process.env.PORT || '3000', 10),\n  dbUrl: process.env.DATABASE_URL,\n  apiKey: process.env.API_KEY\n};\n\nexport default config;
\n\n

重要提示:切勿将真实的 .env 文件提交到版本控制中。

博客

书籍推荐