Skip to content

部署 Node.js

示例应用

javascript
// app.js
const http = require('http');
const PORT = process.env.PORT || 3000;

const server = http.createServer((req, res) => {
  if (req.url === '/health') {
    res.writeHead(200, {'Content-Type': 'application/json'});
    res.end(JSON.stringify({ status: 'ok' }));
    return;
  }
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello from Node.js in Docker!\n');
});

server.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

process.on('SIGTERM', () => {
  server.close(() => process.exit(0));
});

Dockerfile

dockerfile
FROM node:20-alpine

# 创建非 root 用户
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

WORKDIR /app

# 先复制依赖文件(利用缓存)
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force

# 复制应用代码
COPY --chown=appuser:appgroup . .

USER appuser

ENV NODE_ENV=production \
    PORT=3000

EXPOSE 3000

HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
  CMD wget -qO- http://localhost:3000/health || exit 1

CMD ["node", "app.js"]

构建和运行

bash
# 构建镜像
docker build -t mynode-app:1.0 .

# 运行容器
docker run -d \
  --name mynode-app \
  --restart unless-stopped \
  -p 3000:3000 \
  -e NODE_ENV=production \
  mynode-app:1.0

# 测试
curl http://localhost:3000
curl http://localhost:3000/health

Express 应用 Dockerfile

dockerfile
# 多阶段构建(含 TypeScript)
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci

FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

FROM node:20-alpine AS runtime
RUN addgroup -S app && adduser -S app -G app
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY package.json .
USER app
ENV NODE_ENV=production
EXPOSE 3000
CMD ["node", "dist/app.js"]

开发环境(热更新)

yaml
# docker-compose.dev.yml
services:
  app:
    build:
      context: .
      target: deps
    command: npx nodemon --watch src src/app.js
    volumes:
      - ./src:/app/src
      - ./package.json:/app/package.json
    ports:
      - "3000:3000"
    environment:
      NODE_ENV: development

Docker Compose(生产)

yaml
services:
  app:
    build:
      context: .
      target: runtime
    restart: unless-stopped
    ports:
      - "3000:3000"
    environment:
      NODE_ENV: production
      DB_HOST: mysql
    depends_on:
      mysql:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_DATABASE: myapp
    volumes:
      - mysql-data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping"]
      interval: 10s
      retries: 5

volumes:
  mysql-data:

常见问题

端口绑定

Node.js 应用在容器内需要监听 0.0.0.0 而非 127.0.0.1

javascript
server.listen(PORT, '0.0.0.0', () => {
  console.log(`Server listening on 0.0.0.0:${PORT}`);
});

环境变量配置

通过 -e 或 Compose 的 environment 传入,不要将 .env 文件打包进镜像。