部署文档定期更新
22 KiB
Docker 容器化部署 Node.js 全指南
分类: Docker部署教程 | 标签: nodejs,docker,部署教程 | 发布时间: 2025-10-03 07:43:00
本文介绍如何在轩辕镜像查看Node.js镜像详情,提供四种镜像拉取方式(轩辕登录验证、拉取后改名、免登录、官方直连),三种Docker部署方案(快速运行、Node Web应用、docker-compose),以及结果验证方法和访问不到服务、安装依赖等常见问题的解决办法。
Node.js 简介
Node.js 是基于 Chrome V8 引擎的 JavaScript 运行时环境,打破浏览器限制,支持服务器端开发。其非阻塞、事件驱动的 I/O 模型,使其在高性能、高并发网络应用(API 服务、实时通信、微服务等)场景中表现突出。依托 npm(全球最大开源库生态之一,超 200 万个可复用包),Node.js 广泛应用于前端工程化(Webpack、Vite)与后端服务开发。
为什么用 Docker 部署 Node.js?
Docker 轻量级容器化技术为 Node.js 应用部署带来核心优势,尤其适配生产环境需求:
- 环境一致性:封装 Node.js 版本、依赖包、系统库等全部运行要素,彻底解决"开发环境可运行、生产环境报错"的经典问题,保障部署一致性。
- 隔离性:容器间资源与依赖隔离,支持同一服务器运行多版本 Node.js 应用,避免冲突与资源抢占,符合生产环境多服务部署规范。
- 快速部署与扩展:镜像可快速分发启动,配合 Docker Compose/Kubernetes 实现秒级启停与弹性扩展,适配业务流量波动。
- 版本可控:镜像标签化管理,支持历史版本回滚,降低迭代风险与故障排查成本。
- 资源高效:共享宿主机内核,相比虚拟机启动更快、资源占用更低,提升服务器利用率。
🧰 准备工作
若未安装 Docker,推荐使用轩辕镜像适配的一键安装脚本(支持多 Linux 发行版,自动配置镜像加速):
bash <(wget -qO- https://xuanyuan.cloud/docker.sh)
备用地址:
bash <(wget -qO- https://get.xuanyuan.me/docker.sh)
1、查看 Node.js 镜像详情
轩辕镜像 Node.js 官方页面:https://xuanyuan.cloud/r/library/node 提供多版本镜像(LTS 稳定版/开发版),生产环境优先选择 LTS 版本+Alpine 镜像(体积小、安全性更高)。
2、下载 Node.js 镜像(生产级规范)
核心原则
⚠️ 生产环境强制要求:指定具体 LTS 版本标签(如 20.11.1-alpine),禁止使用 latest(版本不可控,主版本升级可能导致依赖兼容故障)。
✅ 测试环境可选:临时测试可使用 latest,但上线前必须切换为固定版本。
2.1 生产级镜像拉取(推荐 Alpine 版本)
# 拉取 Node.js 20.x LTS 稳定版(Alpine 镜像,体积≈50MB,生产首选)
docker pull docker.xuanyuan.run/library/node:20.11.1-alpine
# 重命名为标准标签,简化后续使用
docker tag docker.xuanyuan.run/library/node:20.11.1-alpine node:20.11.1-alpine
# 删除临时镜像,释放存储空间
docker rmi docker.xuanyuan.run/library/node:20.11.1-alpine
2.2 免登录拉取(轩辕镜像专属)
docker pull xxx.xuanyuan.run/library/node:20.11.1-alpine \
&& docker tag xxx.xuanyuan.run/library/node:20.11.1-alpine node:20.11.1-alpine \
&& docker rmi xxx.xuanyuan.run/library/node:20.11.1-alpine
2.3 官方镜像拉取(网络允许时)
docker pull node:20.11.1-alpine
2.4 镜像验证
docker images
输出示例(生产环境合格状态):
REPOSITORY TAG IMAGE ID CREATED SIZE
node 20.11.1-alpine 7a99f5991949 2 weeks ago 51.8MB
3、部署 Node.js 应用(按生产优先级排序)
3.1 Dockerfile + Docker Compose(生产级推荐)
✅ 符合"镜像不可变"原则,支持依赖预安装、权限控制、多阶段构建,是企业级部署标准方案。
第一步:项目结构
node-app/
├── app/
│ ├── server.js # 应用代码
│ └── package.json # 依赖配置
├── Dockerfile # 构建配置
└── docker-compose.yml # 部署配置
第二步:编写生产级 Dockerfile
# 阶段1:构建依赖(多阶段构建,减小最终镜像体积)
FROM node:20.11.1-alpine AS builder
WORKDIR /app
# 复制依赖配置文件(优先复制 package.json 以利用 Docker 缓存)
COPY app/package*.json ./
# 生产环境安装依赖(npm ci 严格遵循 package-lock.json,确保依赖版本一致)
RUN npm ci --only=production
# 复制应用代码
COPY app/ ./
# 阶段2:运行环境(精简镜像,仅包含运行必需文件)
FROM node:20.11.1-alpine
# 安全配置:使用非 root 用户(node 为官方镜像内置用户,UID=1000)
USER node
# 设置工作目录(确保 node 用户有读写权限)
WORKDIR /home/node/app
# 从构建阶段复制依赖和应用代码(保持权限一致性)
COPY --from=builder --chown=node:node /app/node_modules ./node_modules
COPY --from=builder --chown=node:node /app ./
# 环境变量:明确指定生产环境(Node.js 框架会自动优化性能,如关闭调试模式)
ENV NODE_ENV=production
ENV TZ=Asia/Shanghai
# 性能优化:设置 V8 内存限制(根据容器资源配置调整,避免 OOM)
# ⚠️ 关键提示:--max-old-space-size 仅限制 V8 Heap,实际容器内存应预留 30%~50% 给非堆内存(native addon/buffer/code space 等),否则仍可能 OOM
ENV NODE_OPTIONS=--max-old-space-size=512
# 暴露应用端口(仅声明,不实际映射,增强镜像可移植性)
EXPOSE 3000
# 启动命令:使用 node 直接运行(K8s 环境推荐),或 pm2-runtime(需安装 pm2)
CMD ["node", "server.js"]
第三步:编写生产级 docker-compose.yml
version: '3.8' # 固定 Compose 版本,确保兼容性
services:
node-service:
build: . # 基于当前目录的 Dockerfile 构建镜像
image: xuanyuan-node-app:v1.0.0 # 自定义镜像名称+版本,支持版本管理
container_name: node-service
user: node # 与 Dockerfile 保持一致,非 root 运行
working_dir: /home/node/app
# 端口配置:生产环境仅监听本地地址,通过反向代理对外暴露(避免直连公网)
ports:
- "127.0.0.1:3000:3000" # 格式:宿主机IP:宿主机端口:容器端口,仅允许本地/内网访问
# 或仅暴露端口不映射(完全依赖反向代理,更安全)
# expose:
# - "3000"
environment:
- NODE_ENV=production
- TZ=Asia/Shanghai
- DB_HOST=mysql-service # 示例:其他服务依赖(如数据库),通过环境变量注入
- DB_PORT=3306
volumes:
# 仅挂载日志目录(应用代码通过镜像内置,避免运行时修改)
- ./logs:/home/node/app/logs
restart: unless-stopped # 故障自动重启(优于 always,避免无效重启)
# 健康检查:定期检测服务可用性,异常时自动重启
healthcheck:
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000', (res) => process.exit(res.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))"]
interval: 30s # 检查间隔
timeout: 5s # 超时时间
retries: 3 # 重试次数(失败 3 次判定为不健康)
start_period: 60s # 启动宽限期(避免应用未就绪时误判)
# 资源限制:普通 Docker Compose(非 Swarm)生效配置
# ⚠️ 关键提示:deploy.resources 仅在 Docker Swarm/Kubernetes 中生效,普通 docker compose 需使用以下配置
mem_limit: 1g # 限制容器最大内存(与 NODE_OPTIONS 配合,预留足够非堆内存)
cpus: 1.0 # 限制容器最大 CPU 核心数
# 只读文件系统:增强安全性,仅允许必要目录可写
read_only: true
# 临时目录挂载:Node 运行需写 /tmp(如临时文件、native module 缓存等)
tmpfs:
- /tmp
# 日志策略:限制日志大小,避免磁盘占满
logging:
driver: "json-file"
options:
max-size: "10m" # 单个日志文件最大 10MB
max-file: "3" # 最多保留 3 个日志文件
第四步:准备应用代码
app/server.js(生产级基础 Web 服务):
const http = require('http');
const PORT = process.env.PORT || 3000;
const NODE_ENV = process.env.NODE_ENV || 'development';
// 生产环境日志配置:输出到 stdout(Docker 推荐方式,便于日志收集)
const logger = (msg) => {
console.log(`[${new Date().toISOString()}] [${NODE_ENV}] ${msg}`);
};
const server = http.createServer((req, res) => {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello from Xuanyuan Production Node.js Server!\n');
logger(`Request received: ${req.method} ${req.url}`);
});
server.listen(PORT, '0.0.0.0', () => {
logger(`Server running at http://0.0.0.0:${PORT}/`);
logger(`Node.js version: ${process.version}`);
logger(`NODE_ENV: ${NODE_ENV}`);
});
// 异常处理策略:捕获未处理异常,确保容器正常重启(适合无状态服务)
// ⚠️ 关键提示:该策略适用于"无状态服务+容器重启自愈"模型,
// 若为有状态服务(如长连接、未持久化数据),需补充异常时的数据保存逻辑,避免数据丢失
process.on('uncaughtException', (err) => {
logger(`Uncaught Exception: ${err.message}\n${err.stack}`);
process.exit(1); // 退出进程,触发 Docker 自动重启
});
process.on('unhandledRejection', (reason, promise) => {
logger(`Unhandled Rejection: ${reason}\nPromise: ${promise}`);
process.exit(1);
});
app/package.json:
{
"name": "xuanyuan-node-app",
"version": "1.0.0",
"description": "Production-grade Node.js app with Docker",
"main": "server.js",
"dependencies": {}, // 实际项目添加依赖(如 express、mysql2 等)
"engines": {
"node": ">=20.0.0 <21.0.0" // 限制 Node.js 版本范围,增强兼容性
}
}
第五步:启动服务
# 构建镜像并启动容器(后台运行)
docker compose up -d
# 查看构建日志
docker compose logs -f --tail 100
3.2 Docker Compose 标准化部署(测试环境 / 单机生产 / PoC)
⚠️ 适合快速迭代的小型项目、单机生产或 PoC 场景,生产环境建议优先使用 3.1 方案。
核心配置(docker-compose.yml)
version: '3.8'
services:
node-service:
image: node:20.11.1-alpine # 直接使用官方镜像,指定固定版本
container_name: node-service
user: node
working_dir: /home/node/app
# 端口配置:生产环境仅监听本地地址
ports:
- "127.0.0.1:8080:3000" # 宿主机使用非标准端口,提升安全性
environment:
- NODE_ENV=production
- TZ=Asia/Shanghai
- NODE_OPTIONS=--max-old-space-size=512 # 预留 30%+ 非堆内存,容器内存限制 1g
volumes:
# 挂载应用代码和日志目录(开发环境便于修改,生产环境需谨慎)
- ./app:/home/node/app
- ./logs:/home/node/app/logs
command: ["node", "server.js"]
restart: unless-stopped
healthcheck:
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000', (res) => process.exit(res.statusCode === 200 ? 0 : 1)).on('error', () => process.exit(1))"]
interval: 30s
timeout: 5s
retries: 3
# 资源限制:普通 Docker Compose 生效配置
mem_limit: 1g
cpus: 1.0
# 只读文件系统+临时目录
read_only: true
tmpfs:
- /tmp
# 日志策略
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
启动命令
# 准备应用代码(同 3.1 中的 app 目录)
mkdir -p app logs && cd app
# 编写 server.js 和 package.json 后执行
docker compose up -d
3.3 快速运行(测试/临时场景)
🚫 生产环境禁止使用!仅适用于本地测试、临时验证场景。
# 1. 准备测试代码
mkdir -p /tmp/node-test && cd /tmp/node-test
echo "const http = require('http'); http.createServer((req, res) => { res.writeHead(200); res.end('Test Success'); }).listen(3000); console.log('Test server running on 3000');" > app.js
# 2. 运行容器(非 root 用户,临时挂载)
docker run -it --rm \
-u node \
-p 127.0.0.1:3000:3000 \
-v /tmp/node-test:/home/node/app \
-w /home/node/app \
-e NODE_ENV=development \
-e NODE_OPTIONS=--max-old-space-size=256 \
--read-only \
--tmpfs /tmp \
node:20.11.1-alpine \
node app.js
4、生产级验证与运维
4.1 服务可用性验证
# 1. 端口连通性测试
curl http://服务器内网IP:3000
# 输出:Hello from Xuanyuan Production Node.js Server!
# 2. 容器状态检查(健康状态为 healthy)
docker compose ps
# 输出示例:
# Name Command State Ports
# --------------------------------------------------------------------------------
# node-service node server.js Up (healthy) 127.0.0.1:3000->3000/tcp
# 3. 日志查看
docker compose logs -f --tail 50
# 4. 健康检查详情
docker inspect --format '{{json .State.Health}}' node-service | jq
# 5. 资源限制验证(确认容器内存/CPU 限制生效)
docker stats node-service --no-stream
# 输出示例(确认 MEM LIMIT 为 1.0GiB,CPU USAGE 不超过 100%)
4.2 生产环境安全加固建议
-
端口暴露限制:
- 禁止直接将 Node 服务暴露到公网,通过 Nginx/Traefik 反向代理暴露(支持 HTTPS、负载均衡、限流)。
- 示例 Nginx 配置(仅允许内网访问 Node 服务):
server { listen 443 ssl; server_name api.xuanyuan.cloud; ssl_certificate /etc/nginx/certs/cert.pem; ssl_certificate_key /etc/nginx/certs/key.pem; # 限流配置:防止恶意请求压垮服务 limit_req_zone $binary_remote_addr zone=node_limit:10m rate=10r/s; location / { limit_req zone=node_limit burst=20 nodelay; proxy_pass http://127.0.0.1:3000; # 仅内网访问容器服务 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }
-
依赖安全:
- 🔒 关键建议:生产构建阶段不通过
npm audit阻断镜像构建(避免漏洞库更新导致构建不稳定),建议在 CI 阶段执行npm audit --production --audit-level=high或使用 Snyk/Trivy 等工具进行依赖扫描,发现高危漏洞时触发告警而非构建失败。 - 定期更新依赖:结合业务迭代,定期执行
npm update升级安全补丁版本,避免依赖过期导致漏洞暴露。
- 🔒 关键建议:生产构建阶段不通过
-
容器逃逸防护:
- 禁止容器挂载宿主机敏感目录(如
/etc、/var/run、/proc/sys)。 - 启用 Docker 镜像签名验证,确保镜像未被篡改。
- 禁止容器挂载宿主机敏感目录(如
4.3 性能调优
-
V8 内存配置:
- 根据容器内存限制调整
--max-old-space-size,建议设置为容器内存的 50%-70%(例如容器内存 1G 时,设置为 512M-768M),预留足够空间给非堆内存。 - 监控 V8 内存使用:通过
process.memoryUsage()输出内存状态,结合 Prometheus+Grafana 建立监控告警。
- 根据容器内存限制调整
-
进程管理:
- 单容器多进程:使用
pm2-runtime管理(需在 Dockerfile 中安装 pm2),充分利用多核 CPU:RUN npm install -g pm2 CMD ["pm2-runtime", "ecosystem.config.js"]ecosystem.config.js:module.exports = { apps: [{ name: "app", script: "server.js", instances: "max", # 自动根据 CPU 核心数启动进程 exec_mode: "cluster", # 集群模式(利用多 CPU 核心) env_production: { NODE_ENV: "production", NODE_OPTIONS: "--max-old-space-size=512" } }] };
- 单容器多进程:使用
-
镜像体积优化:
- 始终使用 Alpine 基础镜像(体积仅为 Debian 镜像的 1/5)。
- 清理构建残留:
RUN npm ci --only=production && npm cache clean --force && rm -rf /tmp/*。
5、生产级 Node 基线规范
5.1 环境配置基线
- 强制设置
NODE_ENV=production(Node.js 框架自动关闭调试模式、启用性能优化)。 - 禁止在代码中硬编码敏感信息(数据库密码、API 密钥等),通过环境变量注入。
- 时区统一配置为
Asia/Shanghai,避免日志、时间处理出现偏差。
5.2 日志与监控基线
- 日志输出到 stdout/stderr(Docker 标准日志收集方式),禁止在容器内写日志文件(除挂载的日志目录)。
- 日志需包含时间戳、环境标识、请求 ID(便于链路追踪)。
- 接入监控系统,监控指标包括:容器 CPU/内存使用率、Node.js 进程存活状态、V8 内存使用、接口响应时间。
5.3 安全基线
- 容器运行用户必须为非 root(优先使用官方镜像内置的 node 用户)。
- 启用容器只读文件系统,仅挂载必要的可写目录(日志、临时目录)。
- 依赖包必须锁定版本(提交
package-lock.json到代码仓库),使用npm ci安装。
6、常见反模式对照表
| 反模式 | 潜在风险 | 正确做法 |
|---|---|---|
使用 node:latest 镜像 |
版本漂移,依赖兼容故障 | 指定具体 LTS 版本(如 node:20.11.1-alpine) |
| 容器以 root 用户运行 | 漏洞利用导致容器逃逸,权限过大 | 使用 user: node 非 root 用户 |
docker exec 运行 npm install |
镜像不可复现,部署一致性差 | 依赖通过 Dockerfile 构建时安装 |
| 未配置容器资源限制 | 服务占用过多资源,拖垮宿主机 | 普通 Compose 用 mem_limit/cpus,Swarm/K8s 用 deploy.resources |
| 直接暴露 Node 服务到公网 | 缺乏 HTTPS、限流防护,安全风险高 | 通过 Nginx/Traefik 反向代理暴露 |
Dockerfile 中包含 npm audit |
构建不稳定,非代码变更导致构建失败 | CI 阶段独立执行依赖扫描,仅告警不阻断构建 |
--max-old-space-size 等于容器内存 |
非堆内存不足,导致 OOM 崩溃 | 预留 30%~50% 容器内存给非堆内存 |
| 容器内写业务文件(非挂载目录) | 数据丢失,镜像不可变原则被破坏 | 仅通过挂载目录写入必要文件(日志、临时文件) |
7、常见问题(生产级排查)
7.1 服务启动失败(日志显示权限不足)
- 原因:容器内工作目录或挂载目录权限为 root,node 用户无读写权限。
- 解决:
# 宿主机调整挂载目录权限(node 用户 UID/GID 为 1000) chown -R 1000:1000 ./logs ./app
7.2 健康检查失败(容器状态 unhealthy)
- 排查步骤:
- 查看应用日志:
docker compose logs node-service - 进入容器手动测试:
docker exec -it node-service curl http://localhost:3000 - 检查应用端口是否正确(确认
EXPOSE与代码监听端口一致)。 - 延长启动宽限期(
start_period):若应用启动较慢,适当增大start_period(如 120s)。
- 查看应用日志:
7.3 容器 OOM 崩溃(日志显示 Killed)
- 原因:V8 堆内存+非堆内存超过容器内存限制。
- 解决:
- 增大容器内存限制(如
mem_limit: 1.5g)。 - 降低
--max-old-space-size,预留更多非堆内存。 - 排查内存泄漏:通过
node --inspect生成内存快照,分析泄漏点。
- 增大容器内存限制(如
7.4 依赖安装不一致(开发与生产环境差异)
- 原因:未提交
package-lock.json或使用npm install安装。 - 解决:
- 提交
package-lock.json到代码仓库。 - 生产环境强制使用
npm ci --only=production安装依赖。
- 提交
8、部署方案选型指南
| 部署方式 | 适用场景 | 生产级支持 | 核心优势 | 注意事项 |
|---|---|---|---|---|
| Dockerfile + Docker Compose | 中小型生产环境、CI/CD集成、单机生产 | ✅ 推荐 | 版本可控、配置可复用、易于维护 | 使用 mem_limit/cpus 限制资源,避免直连公网 |
| Docker Compose + 官方镜像 | 测试环境、PoC、快速迭代小型项目 | ⚠️ 慎用 | 配置简单、启动快速 | 锁定镜像版本,补充健康检查与资源限制 |
| 直接 docker run | 临时测试、本地验证 | 🚫 禁止 | 操作便捷 | 无健康检查、资源限制,风险较高 |
| Kubernetes + 自定义镜像 | 中大型生产环境、高可用需求 | ✅ 推荐 | 弹性扩展、故障自愈、集群管理 | 配置 Pod 资源限制、健康检查、HPA |
结尾
本文档经过生产级场景校验,严格遵循「不可变镜像、最小权限、资源治理、健康检查」四大核心原则,明确区分测试/生产环境差异,修正了易导致生产事故的认知偏差(如 Compose 资源限制、V8 内存配置)。
在中小企业、私有云、自建 Docker/Compose 环境中可直接落地,配合 Nginx 反向代理、监控告警系统,可实现生产级高可用部署。
如需进一步扩展,可参考:
- CI/CD 集成:通过 Jenkins/GitLab CI 自动构建镜像、推送镜像仓库、部署到目标环境。
- 高可用架构:多容器实例+负载均衡+数据持久化(数据库、缓存独立部署,避免容器内存储状态)。
- 灾备方案:镜像多仓库备份、跨区域部署、定期数据备份与恢复测试。
