关于 infra-ci-cd-docker
面向 Node.js/TypeScript 项目的 Docker 容器化技能,涵盖开发环境与生产镜像构建。重点介绍多阶段构建、缓存优化及容器安全实践。
主要功能
- 01Node.js/TypeScript 多阶段构建
- 02Docker Compose 开发环境
- 03BuildKit 缓存与层优化
- 04非 root 用户及密钥安全
- 05健康检查与优雅关闭
适用场景
- 01制作精简的生产容器镜像
- 02配置本地多容器开发环境
- 03优化依赖安装与 CI/CD 构建
- 04加固容器运行安全
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ agents-inc/ skills --skill infra-ci-cd-docker安装后在 Claude Code 中输入/infra-ci-cd-docker确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
metadata.yamlreference.mdexamples/compose.mdcore.mdproduction.md详细介绍
Docker 容器化模式
**快速指南:**使用 Docker 和 BuildKit 对 Node.js/TypeScript 应用进行容器化。使用多阶段构建生成最小化的生产镜像(从 1GB 缩减至不足 100MB)。使用 Docker Compose v2 搭建开发环境。使用 BuildKit 缓存挂载,将依赖安装速度提升 10 倍。通过非 root 用户、健康检查和机密挂载保障生产环境安全。追求体积小可选 Alpine,追求兼容性可选 Debian slim。
<critical_requirements>
关键:使用此技能前
所有代码都必须遵循项目在 CLAUDE.md 中的约定(kebab-case、命名导出、导入排序、
import type、命名常量)
(生产镜像必须使用多阶段构建——绝不能在生产镜像中包含开发依赖、TypeScript 编译器或源文件)
(容器必须以非 root 用户运行——绝不能让生产容器以 root(默认用户)身份运行)
(必须使用 CMD ["node", "server.js"](exec 形式)——绝不能在 CMD 中使用 npm start 或 shell 形式)
(构建时必须使用 BuildKit 机密挂载来处理敏感数据——绝不能将 ARG 或 ENV 用于机密信息)
(必须先复制 package.json/锁文件,再复制源代码——绝不能在 npm ci 之前执行 COPY . .(这会破坏层缓存))
</critical_requirements>
示例
- Dockerfile 模式 - 多阶段构建、Bun、monorepo、层缓存、.dockerignore、信号处理
- Docker Compose - 开发环境、网络、卷、健康检查
- 生产环境与 CI/CD - 安全加固、机密信息、CI/CD 管道、漏洞扫描
- 快速参考 - Dockerfile 指令、CLI 命令、基础镜像对比
**自动检测:**Dockerfile、docker-compose、compose.yaml、Docker、容器、多阶段构建、BuildKit、.dockerignore、Docker Compose、docker build、docker run、HEALTHCHECK、Docker Scout、docker init、容器化、容器镜像、Docker 网络、Docker 卷
适用场景:
- 为 Node.js/TypeScript 应用创建 Dockerfile
- 设置多阶段构建以缩小生产镜像体积
- 配置 Docker Compose,用于本地开发环境
- 优化 Docker 层缓存和 BuildKit 缓存挂载
- 实现容器安全(非 root、机密信息、只读文件系统)
- 为容器编排设置健康检查
- 构建用于构建并推送 Docker 镜像的 CI/CD 管道
- 团队需要在不同操作系统平台间保持开发环境一致
- 微服务架构需要隔离服务及其依赖项
不适用场景:
- 不使用容器的无服务器部署(AWS Lambda、Vercel Functions)
- 没有服务器运行时的静态网站托管(Netlify、Vercel、Cloudflare Pages)
- 通过 npm 分发的简单脚本或 CLI 工具
- 无需容器化的本地开发
- 增加的复杂性超过隔离带来的收益
- Kubernetes 专用编排模式(请使用 Kubernetes 技能)
涵盖的关键模式:
- 用于 Node.js/TypeScript 的多阶段 Dockerfile(构建器模式)
- Docker Compose v2 开发环境
- BuildKit 缓存挂载和层优化
- 容器安全(非 root、机密信息、能力、只读)
- 生产容器的健康检查
- 用于优化构建上下文的
.dockerignore - 卷挂载(命名卷、绑定挂载、tmpfs)
- Docker 网络(bridge、host、overlay)
- CI/CD 集成(GitHub Actions build-push)
- 信号处理(使用 tini 实现平滑关闭)
## 理念
容器可提供可复现、相互隔离的环境,消除“在我的机器上能运行”的问题。Docker 是将 Node.js/TypeScript 应用打包为可移植轻量级镜像的标准工具。
核心原则:
- 精简生产镜像 - 仅包含应用运行所需内容(编译后的 JS、生产依赖、运行时)
- 优化层缓存 - 组织 Dockerfile,使未更改的层可以重复使用,从而加快重建速度
- 默认安全 - 使用非 root 用户、镜像中不包含密钥、尽量缩小攻击面
- 开发环境与生产环境保持一致 - Docker Compose 在本地模拟生产环境的拓扑结构
核心模式
模式 1:用于 Node.js/TypeScript 的多阶段 Dockerfile
多阶段构建会在构建阶段编译 TypeScript,然后仅将编译后的 JS 和生产依赖复制到精简的运行时镜像中。这样可将镜像大小从 1GB 以上缩减至 100MB 以下。
生产环境的 Dockerfile 使用三个阶段:
- deps - 仅安装生产依赖
- builder - 安装所有依赖并编译 TypeScript
- runner - 将编译后的输出和生产依赖复制到精简镜像中
# Stage 1: Production dependencies
FROM node:22-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN --mount=type=cache,target=/root/.npm \
npm ci --omit=dev --no-audit --no-fund
# Stage 2: Build TypeScript
FROM node:22-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN --mount=type=cache,target=/root/.npm \
npm ci --no-audit --no-fund
COPY tsconfig.json ./
COPY src/ ./src/
RUN npm run build
# Stage 3: Production runtime
FROM node:22-alpine AS runner
WORKDIR /app
RUN apk add --no-cache tini
ENV NODE_ENV=production
RUN addgroup --system --gid 1001 appgroup && \
adduser --system --uid 1001 --ingroup appgroup appuser
COPY --from=deps --chown=appuser:appgroup /app/node_modules ./node_modules
COPY --from=builder --chown=appuser:appgroup /app/dist ./dist
COPY --chown=appuser:appgroup package.json ./
USER appuser
EXPOSE 3000
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["node", "dist/server.js"]
优点: 三阶段构建可分离各项职责,BuildKit 缓存挂载可加快 npm ci,使用非 root 用户和 tini 处理信号,最终镜像仅包含生产所需文件
完整的 Dockerfile(包括 Bun 和单仓库变体)请参阅 examples/core.md。
模式 2:使用 Docker Compose 进行开发
Docker Compose v2 用于定义多容器开发环境。使用 compose.yaml(而非 docker-compose.yml),并使用 docker compose 命令(中间不加连字符)。
# compose.yaml
services:
app:
build:
context: .
target: builder
volumes:
- .:/app:cached
- /app/node_modules
depends_on:
db:
condition: service_healthy
db:
image: postgres:17-alpine
healthcheck:
test: ["CMD-SHELL", "pg_isready -U dev"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
优点: 依赖项使用 condition: service_healthy 进行健康检查,绑定挂载使用 :cached 提升 macOS 性能,匿名卷可保护 node_modules,命名卷可持久保存数据库数据
包含 Redis、网络和卷模式的完整开发环境请参阅 examples/compose.md。
模式 3:BuildKit 缓存挂载
BuildKit(自 Docker Engine 23+ 起默认启用)提供缓存挂载,可在多次构建之间保留包管理器缓存,将安装时间缩短 10 倍甚至更多。
# npm
RUN --mount=type=cache,target=/root/.npm \
npm ci --no-audit --no-fund
# pnpm
RUN --mount=type=cache,target=/root/.local/share/pnpm/store \
pnpm install --frozen-lockfile
层顺序规则: 固定基础镜像版本 > 复制依赖清单 > 安装依赖 > 复制源代码 > 构建/编译。这样可最大限度地命中缓存。
npm、Bun、pnpm 和 apt 的缓存挂载模式请参阅 examples/core.md。
模式 4:容器安全
非 root 用户
RUN addgroup --system --gid 1001 appgroup && \
adduser --system --uid 1001 --ingroup appgroup appuser
COPY --chown=appuser:appgroup --from=builder /app/dist ./dist
USER appuser
密钥挂载(构建时)
# Secret is ephemeral - never persisted in image layers
RUN --mount=type=secret,id=npm_token,env=NPM_TOKEN \
npm ci --no-audit --no-fund
# Build: docker build --secret id=npm_token,env=NPM_TOKEN .
运行时加固
services:
app:
read_only: true
tmpfs: [/tmp]
security_opt: [no-new-privileges:true]
cap_drop: [ALL]
完整的生产环境 Compose 配置、密钥管理和 CI/CD 流水线请参阅 examples/production.md。
模式 5:健康检查
健康检查使编排工具能够检测无响应的容器并自动重启它们。
HEALTHCHECK --interval=30s --timeout=10s --retries=3 --start-period=30s \
CMD ["node", "-e", "fetch('http://localhost:3000/health').then(r => { if (!r.ok) process.exit(1) }).catch(() => process.exit(1))"]
优点: 使用 Node.js 内置的 fetch(Alpine 中无需安装 curl),启动宽限期可留出应用启动时间,检查专用健康端点,而不仅仅是根路径
健康检查端点的实现和 Compose 健康检查模式请参阅 examples/production.md。
模式 6:.dockerignore
node_modules
dist
.git
.env
.env.*
!.env.example
Dockerfile*
compose.yaml
__tests__
*.test.ts
coverage
.github
优点: 排除 node_modules(以便确定性地重新安装)、.env 文件(防止密钥泄露)和 .git(避免缓存失效),同时保持较小的构建上下文
全面的 .dockerignore 文件请参阅 examples/core.md。
模式 7:信号处理与优雅关闭
Node.js 作为 PID 1 运行时,无法正确处理 SIGTERM/SIGINT。使用 tini 作为 init 系统。
RUN apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["node", "dist/server.js"]
优点: Tini 会将信号转发给 node 进程、回收僵尸进程,并确保在执行 docker stop 时优雅关闭
完整的 TypeScript 优雅关闭实现请参阅 examples/core.md。
性能优化
镜像体积缩减
| 技术 | 影响 |
|---|---|
| 多阶段构建 | 从 1GB 缩减至不足 100MB(减少 90% 以上) |
| Alpine 基础镜像 | 135MB,而完整镜像为 1GB,精简版为 200MB |
npm ci --omit=dev | 从生产环境中移除开发依赖 |
.dockerignore | 缩小构建上下文,加快传输速度 |
构建速度优化
| 技术 | 影响 |
|---|---|
| BuildKit 缓存挂载 | 依赖安装速度提升 10 倍 |
| 图层排序(先安装依赖,再复制源代码) | 仅源代码发生变化时可命中缓存 |
.dockerignore 排除 node_modules | 避免向守护进程发送数 GB 的数据 |
--no-audit --no-fund 标志 | 跳过不必要的 npm 检查 |
| 并行多阶段构建 | BuildKit 并发构建互相独立的阶段 |
CI/CD 构建缓存
# GitHub Actions - Cache Docker layers
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v7
with:
cache-from: type=gha
cache-to: type=gha,mode=max
优势: GitHub Actions 缓存(type=gha)会在 CI 运行期间持久保存图层,mode=max 会缓存所有图层(而非仅缓存最终图层)
完整的 CI/CD 流水线示例请参阅 examples/production.md。
<decision_framework>
决策框架
基础镜像选择
Which base image?
|
+-- Need smallest image? --> Alpine (node:22-alpine, ~135MB)
| +-- musl libc compatibility issues? --> Use Debian slim instead
|
+-- Need maximum compatibility? --> Debian slim (node:22-slim, ~200MB)
| +-- Native extensions work out of the box
|
+-- Need debugging tools? --> Full image (node:22, ~1GB)
| +-- Development only, never for production
|
+-- Maximum security? --> Distroless (gcr.io/distroless/nodejs22-debian12)
+-- No shell, no package manager, smallest attack surface
Compose 与 Dockerfile 目标的选择
How to manage dev vs prod?
|
+-- Multi-stage Dockerfile with targets
| +-- `docker compose build --target builder` for dev
| +-- Final stage for production
|
+-- Separate compose files
+-- compose.yaml (base)
+-- compose.override.yaml (dev - auto-loaded)
+-- compose.prod.yaml (production)
卷策略
What data needs to persist?
|
+-- Source code (dev hot reload) --> Bind mount with :cached
+-- Database files --> Named volume
+-- Temporary/cache data --> tmpfs
+-- Secrets at runtime --> Docker secrets or tmpfs
+-- node_modules in dev --> Anonymous volume (protect from host)
</decision_framework>
集成指南
Docker 生态系统工具:
- Docker Scout:使用
docker scout cves在 CLI 和 CI 中扫描漏洞 - BuildKit:自 Docker Engine 23+ 起为默认构建引擎,可启用缓存挂载、机密挂载和多平台构建
CI/CD 集成:
Docker 镜像可集成到任何 CI/CD 流水线中。构建和推送模式及漏洞扫描工作流示例请参阅 examples/production.md。
容器编排:
生产镜像适用于任何容器编排器。无论使用何种平台,都必须配置健康检查(HEALTHCHECK 指令)并使用非 root 用户。
替代方案:
- 基于 VM 的开发环境(Docker Compose 提供更轻量的隔离)
- 手动配置服务器(Dockerfile 将环境设置过程编入代码)
- 容器中的 Node 版本管理器(Dockerfile 通过基础镜像标签固定确切的 Node 版本)
<red_flags>
重大风险
高优先级问题:
- 以 root 身份运行生产容器(默认行为——务必添加 USER 指令)
- 在 ARG/ENV/COPY 中存放机密信息(会保留在镜像层中——请使用
--mount=type=secret) - 使用单阶段构建,将开发依赖和源代码一并打包到生产环境
- 使用
npm start作为 CMD(npm 会吞掉 SIGTERM——请使用CMD ["node", "dist/server.js"]) - 没有
.dockerignore文件(node_modules 会发送给守护进程,机密信息会泄漏到镜像中) - 基础镜像使用
:latest标签(构建结果不确定)
中优先级问题:
- 在
npm ci之前执行COPY . .(任何文件变更都会使依赖缓存失效) - 未定义健康检查(编排器无法检测无响应的容器)
docker-compose.yml中包含version:键(已弃用——请改用不含 version 的compose.yaml)depends_on未设置condition: service_healthy(依赖项就绪前容器便会启动)- 在 Dockerfile 中使用
npm install而不是npm ci(构建结果不确定,速度更慢)
常见错误:
- 开发环境中忘记为 node_modules 添加匿名卷(
/app/node_modules),导致主机覆盖容器中的模块 - 在 macOS 上未对绑定挂载使用
:cached(会显著影响性能) - 在最终阶段而非构建阶段安装构建工具(gcc、make、python)
- 在生产环境的 Compose 中将数据库端口暴露给主机(仅开发环境需要)
易错点与边缘情况:
- Alpine 使用 musl libc——某些带有原生 C 绑定的 npm 包(bcrypt、sharp、canvas)可能会失败;请使用
npm rebuild或改用 Debian slim - Node.js 的
fetch()从 Node 18+ 起可用(在 Alpine 上的健康检查中无需使用 curl) - BuildKit 缓存挂载需要
# syntax=docker/dockerfile:1,或使用默认启用 BuildKit 的 Docker Engine 23+ docker compose down -v会移除具名卷(导致数据丢失)——请使用不带-v的docker compose down来保留数据- macOS/Windows 上的 Docker Desktop 文件系统性能与 Linux 不同——绑定挂载速度更慢
COPY --chown比COPY+RUN chown更高效(一层而不是两层)- 在 Alpine 上必须显式安装
tini(apk add --no-cache tini)——node:alpine镜像默认不包含它 node_modules/.cache目录在开发过程中可能变得很大——请将其添加到.dockerignore
</red_flags>
<critical_reminders>
重要提醒
所有代码都必须遵循项目在 CLAUDE.md 中的约定(kebab-case、命名导出、导入顺序、
import type、命名常量)
(生产镜像必须使用多阶段构建——绝不能在生产环境中打包开发依赖、TypeScript 编译器或源文件)
(容器必须以非 root 用户身份运行——绝不能以 root 身份运行生产容器(默认行为))
(必须使用 CMD ["node", "server.js"](exec 格式)——绝不能使用 npm start 或 shell 格式的 CMD)
(构建时必须使用 BuildKit 机密挂载来处理敏感数据——绝不能将 ARG 或 ENV 用于机密信息)
(必须先复制 package.json/lockfile,再复制源代码——绝不能在 npm ci 之前执行 COPY . .(这会破坏层缓存))
不遵循这些规则会导致镜像臃肿(1GB+)、安全漏洞(root 访问权限、机密信息泄漏)、优雅关闭失效(部署时丢失请求)以及 CI 构建缓慢(没有层缓存)。
</critical_reminders>