Docker 容器化 Node.js 应用完全指南

本文手把手将 Node.js 应用 Docker 化:编写 Dockerfile、多阶段构建、镜像瘦身、.dockerignore、docker-compose 多容器编排与开发环境热重载,附生产部署最佳实践。

最佳实践
事件循环与异步任务流转插画

Node.js 容器化的核心是一份分层的 Dockerfile:选官方 node 镜像、先拷贝 package.json 装依赖(利用构建缓存)、再拷贝源码、CMD 启动。生产镜像用多阶段构建砍掉 devDependencies,配合 .dockerignore 排除 node_modules,Compose 管理应用+数据库多容器。*

Dockerfile:从能跑到好用

FROM node:22-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev

FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=deps /app/node_modules ./node_modules
COPY . .
USER node
EXPOSE 3000
CMD ["node", "server.js"]

四个关键决策:先装依赖再拷源码(代码天天变、依赖不常变,便于复用依赖层缓存);alpine 基础镜像(检查原生依赖的 musl 兼容性;有兼容问题时可选 Debian slim);USER node(不用 root 跑进程,安全基线);npm ci 而非 install(严格按 lock 安装,构建可复现)。

配套 .dockerignore:

node_modules
npm-debug.log
.git
.env
coverage

构建与运行

docker build -t myapp:1.0 .
docker run -d -p 3000:3000 --name myapp myapp:1.0
docker logs -f myapp
docker exec -it myapp sh

端口映射 -p 宿主机:容器;环境变量用 -e KEY=VALUE 或 --env-file 注入,密钥绝不打进镜像层。

Compose:编排应用与依赖

真实应用要连数据库,Compose 一键拉起整套环境:

services:
  app:
    build: .
    ports: ['3000:3000']
    environment:
      DATABASE_URL: postgres://postgres:secret@db:5432/myapp
    depends_on: [db]
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_PASSWORD: secret
    volumes: [pgdata:/var/lib/postgresql/data]

volumes:
  pgdata:

docker compose up -d 启动,docker compose logs -f app 看日志,docker compose down 全部拆除。

开发环境:容器内热重载

开发场景把源码挂进容器,实现"改代码即生效":

services:
  app:
    build: .
    volumes:
      - .:/app
      - /app/node_modules     # 匿名卷保护容器内依赖
    command: npx tsx watch src/index.ts

生产最佳实践清单

  • 多阶段构建,最终镜像不含构建工具与 devDependencies;
  • 健康检查:HEALTHCHECK CMD wget -qO- http://localhost:3000/health || exit 1;
  • 优雅停机:Node 处理 SIGTERM(Node 直接作为 PID 1 时收不到信号,用 CMD ["node", ...] exec 形式即可正确接收);
  • 资源限制:K8s/Compose 中设 memory limit,Node 堆内存配合 --max-old-space-size;
  • 镜像扫描:CI 中跑 trivy 之类的漏洞扫描。

关联容器资源与 Node.js 错误

Node.js 在容器内退出时,先对照容器资源限制、退出状态和应用错误。观测云的容器采集可提供资源、对象及日志数据;应用 APM 仍需要在镜像中安装并初始化 SDK。给日志与链路设置一致的 service、env、version,并保留容器标识,才能在多副本发布时区分故障实例与正常实例。

接入文档:容器与 Kubernetes 采集 · 日志采集 · Node.js APM 接入。

常见问题(FAQ)

Q:alpine 镜像有什么坑?

musl libc 与 glibc 的差异会让部分原生模块(node-sass、bcrypt 老版本、某些 sharp 场景)编译失败。遇到时换 node:22-slim(Debian 系、体积适中)即可,别死磕 alpine。

Q:容器里 Node 内存超限被杀(OOMKilled)怎么排查?

容器 memory limit 与 Node 堆上限要协调:容器限 512MB 就设 --max-old-space-size=384(留出堆外内存)。Node 18+ 在容器内会自动感知 cgroup 限制,老版本需要显式设置。

Q:多阶段构建里 devDependencies 的 TypeScript 编译怎么做?

加一个 builder 阶段:装全部依赖 → npm run build 产出 dist → runner 阶段只拷 dist 和生产依赖。最终镜像里没有 TypeScript 和源码,体积与安全双赢。


参考文档

本文依据公开文档整理,示例需按项目依赖版本验证;不将文档中的性能描述视为本项目实测结果。

延伸阅读

获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台