Node 应用上 Docker:从 Dockerfile 到 Compose 一键起服务

Node 应用上 Docker:从 Dockerfile 到 Compose 一键起服务

DockerNode.js容器化部署
📶 进阶 🧩 Docker · Node.js

Node 应用上 Docker:从 Dockerfile 到 Compose 一键起服务

把「在我机器上能跑」的 Node.js 项目扔到服务器,常常会遇到依赖报错和环境不一致。本文探讨「在我机器上能跑」的 Node 应用怎么变成服务器上一条命令就能起的容器服务。将应用打包进 Docker 容器,是抹平环境差异的最短路径,我们直接通过多阶段构建和 Compose 给出锁定环境的标准方案。

部署前必知的 3 件事

  • 资源配额底线:即使是结构最简单的 Express API,Node.js 进程通常也需要 256MB 内存基础。官方实践建议为单实例预留 512MB 内存,防范高峰期的 OOM 崩溃。
  • 环境隔离策略:容器本身对性能影响很小,实际的问题在于文件读写权限与 node_modules 层管理。容器以非 root 用户运行应用是基础的安全防线。
  • 数据持久化原则:容器是即用即抛的设计。应用若产生本地日志文件或操作 SQLite,必须通过挂载数据卷写入宿主机,避免容器重启导致数据丢失。

编写 Compose 脚本拉起服务

用多阶段构建编写 Dockerfile,剥离不需要的构建工具。以 Node 22 LTS 官方 Alpine 镜像为基础环境:

# 构建阶段
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
COPY . .

# 生产阶段
FROM node:22-alpine AS production
WORKDIR /app
RUN addgroup -g 1001 -S nodejs && adduser -S nodeuser -u 1001
COPY --from=builder --chown=nodeuser:nodejs /app /app
USER nodeuser
EXPOSE 8080
CMD ["node", "app.js"]

剥离 npm ci 阶段的源码后,生产环境的最终镜像体积会大幅缩小。配合 .dockerignore,避免将本地的大体积无关文件打包到容器上下文中:

node_modules
npm-debug.log
Dockerfile
.dockerignore
.git

写好 Dockerfile 后,用 Docker Compose v2 统管应用运行参数。建立 docker-compose.yml 配置文件:

services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      - NODE_ENV=production
      - PORT=8080
    networks:
      - app-network
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "node", "-e", "require('http').get('http://localhost:8080', (res) => { process.exit(res.statusCode === 200 ? 0 : 1) })"]
      interval: 30s
      timeout: 10s
      retries: 3
networks:
  app-network:
    driver: bridge

在同级目录执行 docker compose up -d 即可在后台启动服务。配置文件自带存活探针,应用进程卡死时 Docker 引擎会自动标记异常状态。

启动后 5 分钟搞定反代接入

直接对外暴露 8080 端口并不安全。在生产环境中,Node 服务通常只作为上游资源,前端依靠 Nginx 处理 HTTPS 卸载与静态资源转发。

在 docker-compose.yml 中补充反向代理容器节点:

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - app
    networks:
      - app-network
    restart: unless-stopped

通过配置 depends_on,Nginx 会等待 Node 服务就绪后再启动。两者在自定义虚拟网络 app-network 内,通过服务名 app 就能直接内网互通。

2核2G 机器能否带动 Node 服务

以当前主流云厂商实况衡量入门机型表现。阿里云 2核2G 配置(当前参考价 ¥199,价格查询于 2026-10-02),跑纯 Node.js API 加轻量级 Redis 缓存游刃有余。系统空闲内存通常还能剩余 1GB 左右。

配置档位Node 实例数RedisNginx 反代能否本地构建
1核1G1 个勉强可以❌ 易 OOM
2核2G2 个轻松可以✅ 无压力
2核4G4 个以上轻松可以✅ 流畅

不要在 1核1G 机器上直接跑 npm install 源码编译。这种低配实例遭遇突发的内存开销会被内核直接清理进程。 正确的做法是在开发机本地构建出完整镜像,推送到镜像仓库后,再让低配服务器执行拉取动作。

解决 3 个高频部署问题

  • 容器里 localhost 连不上宿主机数据库? 容器拥有独立的网络命名空间,内部的 localhost 指向容器内部网络。要连接宿主机上的服务,需填写宿主机的内网 IP,或在 Compose 配置中指定 host.docker.internal。
  • 镜像构建太慢,怎么利用缓存加速? 在 Dockerfile 中,把 COPY package*.json ./ 和 RUN npm ci 指令放在拷贝业务代码指令的前面。只要依赖列表不改变,引擎就自动命中依赖层缓存,大幅压缩构建耗时。
  • 为什么要用 Alpine 变体镜像? 常规的完整版 Node 镜像占用往往超 300MB,携带许多冗余系统库。Alpine 变体不到 100MB,减小镜像体积的同时,也精简了暴露漏洞的风险。

把 Node.js 服务封装进 Docker,就是将环境依赖直接锁定在配置脚本里。一次写准多阶段构建与 Compose 配置,日后无论切换什么服务器节点,一条 docker compose up 就能重现相同的服务状态。

参考链接:

  • Docker 官方文档
  • Node.js 官方指南