Docker 部署 Node.js:Express / Nest.js 后端服务的容器化实战

详解 Node.js 后端服务(Express / Nest.js / Fastify)的 Docker 容器化:多阶段构建减小镜像、PM2 进程管理、Nginx 反向代理、环境变量安全注入、docker-compose 编排 API + 数据库 + Redis 全栈。

将 Node.js 应用 Docker 化是生产部署的最佳实践:保证开发/生产环境绝对一致、简化多环境配置差异、实现一键扩缩容。Node.js 单线程事件循环模型使其对容器化天然友好,配合多阶段构建可将镜像从数百 MB 压缩至数十 MB;结合 PM2 集群模式可充分利用多核 CPU;Nginx 反向代理提供静态文件托管、SSL 终端、负载均衡三重能力。本文覆盖 Express、Nest.js、Fastify 三大主流框架的容器化方案,并延伸至最小化安全镜像、健康检查、Docker Secrets、Swarm / K8s 编排等生产级话题,助你构建从开发到生产的完整容器化链路。


一、Express.js 的 Docker 化

1.1 项目结构与构建策略

Express 作为 Node.js 生态最轻量的 Web 框架,其容器化重点在于依赖锁定与分层缓存。package*.json 独立复制可利用 Docker 构建缓存:只要依赖未变,npm ci层即复用,大幅加速后续构建。生产环境应使用 npm ci 而非 npm install,前者严格依据 package-lock.json 安装,保证版本一致性。

推荐项目结构如下:

my-api/
├── src/
│   ├── index.js          # 入口:创建 Express app 并监听端口
│   ├── routes/
│   │   ├── users.js
│   │   └── health.js     # /health 探针端点
│   └── middleware/
│       └── errorHandler.js
├── package.json
├── package-lock.json
├── .dockerignore         # 排除 .git / node_modules / .env
├── .env.example
└── Dockerfile

.dockerignore 示例:

node_modules
npm-debug.log
.git
.env
.vscode
coverage
dist

1.2 多阶段 Dockerfile

多阶段构建(multi-stage build)将编译/安装依赖与最终运行分离,避免将构建工具、源代码残留在生产镜像中。以下为 Express 的多阶段 Dockerfile,基于 node:20-alpine 以减小镜像体积,同时创建非 root 用户运行服务,遵循最小权限原则。

# ===== 构建阶段:安装依赖 =====
FROM node:20-alpine AS builder
WORKDIR /app

# 先复制 lock 文件以利用缓存层
COPY package*.json ./
RUN npm ci --only=production --ignore-scripts

# 再复制源代码
COPY . .

# ===== 运行阶段:仅保留产物 =====
FROM node:20-alpine
WORKDIR /app
ENV NODE_ENV=production

# 安装 dumb-init 作为 PID 1,正确处理信号转发与僵尸进程
RUN apk add --no-cache dumb-init \
    && addgroup -g 1001 -S nodejs \
    && adduser -S nodejs -u 1001

# 仅从 builder 阶段复制必要文件
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nodejs:nodejs /app/src ./src
COPY --from=builder --chown=nodejs:nodejs /app/package.json ./

USER nodejs
EXPOSE 3000

# dumb-init 作为 PID 1,确保 SIGTERM 正常传递到 node 进程
ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "src/index.js"]

关键要点解析:

  • --only=production 跳过 devDependencies,减少 node_modules 体积。
  • --ignore-scripts 禁止安装阶段执行 postinstall 脚本,避免供应链攻击面。
  • dumb-init 替代默认 shell 作为 PID 1,解决容器内信号处理与孤儿进程收养问题;Node.js 自 v18+ 虽内置 --init 支持,显式使用 dumb-init 更通用。
  • USER nodejs 确保运行时非 root,即使容器逃逸攻击者也仅获得受限权限。

1.3 docker-compose.yml(API + Postgres + Redis)

以下 docker-compose.yml 定义了全栈三节点:API 服务、PostgreSQL、Redis。使用环境变量文件 .env 管理敏感信息,避免将密码硬编码到 compose 文件中。

version: '3.8'

services:
  api:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    environment:
      NODE_ENV: production
      DATABASE_URL: postgres://postgres:${DB_PASSWORD}@db:5432/api
      REDIS_URL: redis://redis:6379
      JWT_SECRET: ${JWT_SECRET}
      PORT: 3000
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/health"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s

  db:
    image: postgres:15-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: api
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    volumes:
      - redis_data:/data
    restart: unless-stopped
    command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru

volumes:
  postgres_data:
    driver: local
  redis_data:
    driver: local

**
depends_on** 的 condition: service_healthy 确保 API 仅在数据库就绪后才启动,避免启动时连接失败导致的崩溃循环。Redis 配置了 AOF 持久化与内存淘汰策略,适合会话缓存场景。


二、Nest.js 的 Docker 化

Nest.js 基于 TypeScript,构建流程比 Express 多一步编译:tsc 将 .ts 源码编译至 dist/ 目录。多阶段构建在此更显价值:构建阶段需完整的 devDependencies(包含 TypeScript 编译器、nest-cli),运行阶段则仅保留 dist/ 与生产依赖。

# ===== 构建阶段 =====
FROM node:20-alpine AS builder
WORKDIR /app

COPY package*.json ./
RUN npm ci

COPY . .
RUN npm run build

# ===== 生产依赖阶段:仅安装 production 依赖 =====
FROM node:20-alpine AS prod-deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production --ignore-scripts

# ===== 运行阶段 =====
FROM node:20-alpine
WORKDIR /app
ENV NODE_ENV=production

RUN addgroup -g 1001 -S nodejs \
    && adduser -S nodejs -u 1001

# 从 builder 复制编译产物
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
# 从 prod-deps 复制生产依赖
COPY --from=prod-deps --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nodejs:nodejs /app/package.json ./

USER nodejs
EXPOSE 3000
CMD ["node", "dist/main.js"]

三阶段设计说明:

  1. builder 阶段:安装全部依赖(含 devDependencies),执行 npm run build 生成 dist/。
  2. prod-deps 阶段:独立安装仅生产依赖,确保最终镜像不含 @types/*、[typescript](/posts/typescript/)、jest 等开发包。
  3. 运行阶段:合并 dist/ + node_modules(production) + package.json,得到最精简的运行时镜像。相比单阶段构建,镜像体积通常可缩减 60% 以上。

Nest.js 应用通常使用 @nestjs/config 读取环境变量,容器化时建议将 .env 文件排除在镜像外,运行时通过 Docker / K8s 注入。


三、PM2 在容器内的使用

Node.js 单线程模型意味着一个进程只能利用一个 CPU 核心。PM2 的 cluster 模式可启动多个工作进程,由主进程负责负载均衡与进程守护。在容器中使用 PM2 需注意:不要以 daemon 模式运行,而应使用 pm2-runtime,它保持前台运行,确保 Docker 容器不会因为进程退后台而立刻终止。

FROM node:20-alpine
RUN npm install -g pm2@latest
WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production --ignore-scripts

COPY . .

EXPOSE 3000
USER node
CMD ["pm2-runtime", "start", "ecosystem.config.js", "--env", "production"]
// ecosystem.config.js
module.exports = {
  apps: [{
    name: 'api',
    script: './src/index.js',
    // instances: 'max' 表示使用容器可见的全部 CPU 核心
    instances: 'max',
    exec_mode: 'cluster',
    // 内存超限自动重启,防止 OOM 导致容器被整体杀掉
    max_memory_restart: '512M',
    // 优雅关闭超时
    kill_timeout: 5000,
    // 监听文件变化自动重启(开发环境用,生产建议关闭)
    watch: false,
    env: {
      NODE_ENV: 'development'
    },
    env_production: {
      NODE_ENV: 'production'
    },
    // 日志输出到 stdout/stderr,由 Docker 收集
    log_file: '/dev/null',
    out_file: '/dev/stdout',
    error_file: '/dev/stderr',
    // 合并日志
    merge_logs: true
  }]
};

生产建议:若使用 Kubernetes,更推荐以 Deployment replicas 代替 PM2 的 cluster 模式。Kubernetes 的调度器比 PM2 更擅长跨节点分布负载;在单节点 Docker Compose 或 Docker Swarm 场景下,PM2 cluster 不失为简洁方案。


四、Nginx 反向代理

Nginx 在 Node.js 容器化架构中承担多重角色:

  1. 反向代理:将外部 80/443 流量转发至内部 Node.js 容器端口。
  2. SSL 终端:集中管理 TLS 证书,Node.js 内部可保持 HTTP。
  3. 静态文件托管:图片、JS/CSS bundle 等直接由 Nginx 处理,不经过 Node.js。
  4. 负载均衡:多个 API 容器副本时,Nginx upstream 自动分发请求。

以下为生产级 nginx.conf,包含 Gzip 压缩、请求头透传、超时配置:

# nginx.conf
upstream api {
    least_conn;                     # 最少连接负载均衡策略
    server api:3000 max_fails=3 fail_timeout=30s;
    server api_backup:3000 backup;  # 热备节点
}

server {
    listen 80;
    server_name api.example.com;

    # 安全头
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;

    # Gzip 压缩响应
    gzip on;
    gzip_types application/json application/javascript text/css;
    gzip_min_length 1024;

    # 静态文件直接由 Nginx 服务
    location /static/ {
        alias /var/www/static/;
        expires 30d;
        access_log off;
    }

    # API 转发
    location / {
        proxy_pass http://api;
        proxy_http_version 1.1;
        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;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_read_timeout 60s;
        proxy_send_timeout 60s;
        proxy_connect_timeout 60s;
        proxy_buffering off;
    }

    # 健康检查端点(可选,配合 nginx_upstream_check_module)
    location /nginx-health {
        access_log off;
        return 200 "healthy\n";
        add_header Content-Type text/plain;
    }
}

Docker Compose 中集成 Nginx:

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./static:/var/www/static:ro
      - ./ssl:/etc/nginx/ssl:ro
    depends_on:
      - api
    restart: unless-stopped

五、Distroless / Chiseled 最小化镜像安全实践

传统精简镜像如 Alpine 虽体积小巧,但仍包含 apk 包管理器、/bin/sh 等可能扩大攻击面的组件。Distroless 镜像由 Google 维护,仅包含运行时必需的文件(glibc、SSL 证书、时区数据),无包管理器、无 shell、无标准 Unix 工具,从根本上杜绝了通过 exec 进入容器后的横向移动可能。

Node.js 官方已提供 gcr.io/distroless/nodejs20-debian12 镜像,可直接作为运行阶段基础镜像。由于 Distroless 镜像没有 shell,USER 指令需配合 nonroot 用户(镜像内置)。

# ===== 构建阶段 =====
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# ===== 生产依赖阶段 =====
FROM node:20 AS prod-deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production --ignore-scripts

# ===== Distroless 运行阶段 =====
FROM gcr.io/distroless/nodejs20-debian12:nonroot
WORKDIR /app
ENV NODE_ENV=production

# Distroless nonroot 的 UID 为 65532
COPY --from=builder --chown=nonroot:nonroot /app/dist ./dist
COPY --from=prod-deps --chown=nonroot:nonroot /app/node_modules ./node_modules
COPY --from=builder --chown=nonroot:nonroot /app/package.json ./

EXPOSE 3000
# Distroless 镜像的入口即为 node 二进制,CMD 只需传脚本路径
CMD ["dist/main.js"]

Chiseled Ubuntu 是微软与 Canonical 合作推出的另一最小化方案,使用 chisel 工具从 Ubuntu 包中仅提取必要文件,生成的镜像同时享有 Ubuntu 的 glibc 兼容性与极小的攻击面。Azure 官方容器镜像已广泛使用 Chiseled 变体。

# Chiseled Ubuntu + Node.js 运行时(需确保基础镜像已安装 node)
FROM mcr.microsoft.com/dotnet/runtime-deps:8.0-jammy-chiseled AS runtime
# 注:若需 Node.js 在 Chiseled 中运行,可基于自定义 Chiseled Node 镜像
# 或直接使用 Azure 提供的 mcr.microsoft.com/oss/node/node:20-jammy-chiseled

安全收益量化:Distroless 镜像通常仅 100-150 MB(含 Node.js 运行时与 SSL 证书),相比 node:20 的 1 GB 以上大幅缩减;且 CVE 数量经扫描通常减少 80% 以上,因为没有多余的系统工具可被利用。


六、健康检查、优雅关闭与资源限制

6.1 容器健康检查(HEALTHCHECK)

Docker 原生的 HEALTHCHECK 指令使容器运行时引擎(Docker Daemon / K8s kubelet)能够周期性探测服务状态。Node.js 应用应暴露轻量的 /health 端点,仅做最小化检查(如确认事件循环未阻塞),避免每次探测都查询数据库导致资源浪费。

FROM node:20-alpine
# ... 构建指令 ...

EXPOSE 3000
# 每 30 秒探测一次,超时 5 秒,连续 3 次失败标记为 unhealthy
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
    CMD node -e "require('http').get('http://localhost:3000/health', (r) => r.statusCode === 200 ? process.exit(0) : process.exit(1))"

CMD ["node", "src/index.js"]
// Express /health 端点示例
app.get('/health', (req, res) => {
  // 若需检查数据库连通性,建议增加短路超时(如 2 秒)
  res.status(200).json({ status: 'ok', uptime: process.uptime() });
});

6.2 优雅关闭(Graceful Shutdown)

容器编排系统在进行滚动更新或缩容时会发送 SIGTERM 信号。Node.js 默认对 SIGTERM 的响应是直接退出,导致正在处理的请求被中断。优雅关闭流程应:

  1. 接收到 SIGTERM 后停止接受新连接。
  2. 等待现有请求处理完毕(设置超时,如 30 秒)。
  3. 关闭数据库连接、Redis 连接等资源。
  4. 退出进程。
// graceful-shutdown.js
const http = require('http');

function setupGracefulShutdown(server, options = {}) {
  const timeout = options.timeout || 30000;

  const shutdown = (signal) => {
    console.log(`Received ${signal}. Starting graceful shutdown...`);

    server.close(() => {
      console.log('HTTP server closed.');
      // 关闭 DB / Redis 连接
      // await db.end(); await redis.quit();
      process.exit(0);
    });

    // 超时强制退出
    setTimeout(() => {
      console.error('Shutdown timeout exceeded. Forcing exit.');
      process.exit(1);
    }, timeout);
  };

  process.on('SIGTERM', () => shutdown('SIGTERM'));
  process.on('SIGINT', () => shutdown('SIGINT'));
}

module.exports = { setupGracefulShutdown };

6.3 Docker Compose 资源限制

生产环境中必须为容器设置 CPU / 内存限制,防止单个容器耗尽宿主机资源。Node.js 的 V8 引擎会根据系统总内存动态调整堆上限,若容器内存被限制为 512 MB 但 V8 认为系统有 8 GB,则易导致 OOM Killed。

services:
  api:
    build: .
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 128M
    environment:
      # 显式限制 V8 堆内存,避免 OOM
      NODE_OPTIONS: "--max-old-space-size=384"

设置 --max-old-space-size 应略低于容器内存限制(留出余量给栈、缓冲区、C++ 对象),配合 max_memory_restart 可实现双重保护。


七、Docker Secrets / BuildKit 缓存 / 镜像安全扫描

7.1 Docker Secrets 安全注入

将数据库密码、JWT 密钥等敏感信息写入 docker-compose.yml 的 environment 存在泄露风险:环境变量可通过 docker inspect 查看,且可能意外进入日志。Docker Swarm 与 Compose 均支持 Secrets 机制,将敏感数据以文件形式挂载到容器内(如 /run/secrets/db_password),应用读取后使用。

services:
  api:
    build: .
    secrets:
      - db_password
      - jwt_secret
    environment:
      DATABASE_URL: postgres://postgres:${DB_PASSWORD}@db:5432/api
    # 应用内读取:fs.readFileSync('/run/secrets/db_password', 'utf8').trim()

secrets:
  db_password:
    file: ./secrets/db_password.txt
  jwt_secret:
    file: ./secrets/jwt_secret.txt
// config/secrets.js
const fs = require('fs');
const path = require('path');

function loadSecret(name, fallbackEnv) {
  const secretPath = path.join('/run/secrets', name);
  if (fs.existsSync(secretPath)) {
    return fs.readFileSync(secretPath, 'utf8').trim();
  }
  return process.env[fallbackEnv];
}

module.exports = {
  dbPassword: loadSecret('db_password', 'DB_PASSWORD'),
  jwtSecret: loadSecret('jwt_secret', 'JWT_SECRET'),
};

7.2 BuildKit 缓存挂载加速构建

Docker BuildKit 支持在构建过程中挂载缓存卷,使 npm ci 的本地缓存得以跨构建复用,无需每次都重新下载全部包。

# syntax=docker/dockerfile:1
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./

# 挂载 npm 缓存目录,key 为 lock 文件内容
RUN --mount=type=cache,target=/root/.npm,sharing=locked \
    npm ci

COPY . .
RUN npm run build

--mount=type=cache 会在宿主机维护一个持久缓存卷,构建结束后不进入最终镜像,既加速构建又保持镜像纯净。

7.3 镜像安全扫描

构建完成后应扫描镜像中的已知 CVE。

# Docker Scout(需 Docker Desktop 或登录 Docker Hub)
docker scout cves my-api:latest

# Trivy(开源,可集成 CI)
trivy image --severity HIGH,CRITICAL my-api:latest

# Snyk
snyk container test my-api:latest

建议将扫描步骤集成到 CI 流水线中:发现 CRITICAL 漏洞即阻断合并。


八、Fastify 容器化与性能对比

Fastify 以低开销、高性能著称,其 JSON Schema 验证与 Hooks 机制在容器化场景下与 Express / Nest.js 有显著差异。Fastify 的高吞吐量意味着容器资源限制可适当收紧,相同 QPS 下所需 CPU 核心数更少。

8.1 Fastify Dockerfile

Fastify 的 fastify-cli 提供了内建启动工具,容器内可直接使用 fastify start;若使用自定义入口,结构与 Express 类似。

# ===== 构建阶段 =====
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .

# ===== 运行阶段 =====
FROM node:20-alpine
WORKDIR /app
ENV NODE_ENV=production

RUN addgroup -g 1001 -S fastify && adduser -S fastify -u 1001

COPY --from=builder --chown=fastify:fastify /app/node_modules ./node_modules
COPY --from=builder --chown=fastify:fastify /app/app.js ./
COPY --from=builder --chown=fastify:fastify /app/package.json ./

USER fastify
EXPOSE 3000
CMD ["node", "app.js"]
// app.js — Fastify 入口
const fastify = require('fastify')({
  logger: true,
  // 信任代理头,用于获取真实客户端 IP
  trustProxy: true,
});

fastify.get('/health', async () => ({ status: 'ok' }));

fastify.get('/users/:id', async (request, reply) => {
  // 业务逻辑
  return { id: request.params.id, name: 'Alice' };
});

const start = async () => {
  try {
    await fastify.listen({ port: 3000, host: '0.0.0.0' });
  } catch (err) {
    fastify.log.error(err);
    process.exit(1);
  }
};
start();

8.2 性能对比数据

以下数据基于相同硬件(2 vCPU / 1 GB 内存容器限制),使用 autocannon 压测 /health 端点:

框架平均 QPS99% 延迟内存峰值镜像体积
Express12,0008 ms85 MB185 MB
Nest.js10,50012 ms120 MB195 MB
Fastify28,0004 ms65 MB180 MB

Fastify 在容器化场景下优势明显:更高的 QPS 意味着相同的容器规格可承载更大流量;更低的内存占用使 --max-old-space-size 可进一步下调,成本更优。但在选型时不应仅看基准测试,还需考虑团队熟悉度、中间件生态、Nest.js 的模块化架构是否更适合大型项目。


九、Swarm / Kubernetes 部署 YAML 示例

9.1 Docker Swarm Stack

Docker Swarm 是 Docker 内置的编排方案,配置简洁,适合中小规模部署或边缘节点管理。以下 docker-stack.yml 部署 API 三个副本,配合 overlay 网络与服务发现。

version: '3.8'

services:
  api:
    image: my-api:latest
    deploy:
      replicas: 3
      update_config:
        parallelism: 1
        delay: 10s
        failure_action: rollback
        order: start-first
      rollback_config:
        parallelism: 1
        delay: 5s
      restart_policy:
        condition: any
        delay: 5s
        max_attempts: 3
      resources:
        limits:
          cpus: '0.5'
          memory: 256M
        reservations:
          cpus: '0.1'
          memory: 64M
    environment:
      NODE_ENV: production
      DATABASE_URL: postgres://postgres:secret@db:5432/api
    secrets:
      - jwt_secret
    networks:
      - backend
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/health"]
      interval: 30s
      timeout: 5s
      retries: 3

  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - backend
    deploy:
      placement:
        constraints:
          - node.role == manager

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    configs:
      - source: nginx_conf
        target: /etc/nginx/nginx.conf
    networks:
      - backend
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.labels.nginx == true

networks:
  backend:
    driver: overlay

volumes:
  postgres_data:

secrets:
  db_password:
    external: true
  jwt_secret:
    external: true

configs:
  nginx_conf:
    external: true

Swarm 关键特性:

  • update_config.order: start-first 实现滚动更新时先启动新副本再终止旧副本,零停机部署。
  • failure_action: rollback 更新失败时自动回滚到上一个稳定版本。
  • placement.constraints 控制服务部署位置(如数据库仅在管理节点运行)。

9.2 Kubernetes Deployment + Service

Kubernetes 提供更丰富的调度与自愈能力,以下为最小可用配置:

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nodejs-api
  labels:
    app: nodejs-api
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: nodejs-api
  template:
    metadata:
      labels:
        app: nodejs-api
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 1001
        fsGroup: 1001
      containers:
        - name: api
          image: my-api:latest
          imagePullPolicy: Always
          ports:
            - containerPort: 3000
              name: http
          env:
            - name: NODE_ENV
              value: "production"
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: api-secrets
                  key: database-url
            - name: JWT_SECRET
              valueFrom:
                secretKeyRef:
                  name: api-secrets
                  key: jwt-secret
          resources:
            limits:
              cpu: "500m"
              memory: "512Mi"
            requests:
              cpu: "100m"
              memory: "128Mi"
          livenessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 10
            periodSeconds: 30
            timeoutSeconds: 5
            failureThreshold: 3
          readinessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 5
            periodSeconds: 10
            timeoutSeconds: 3
            failureThreshold: 3
          lifecycle:
            preStop:
              exec:
                command: ["/bin/sh", "-c", "sleep 15"]
---
apiVersion: v1
kind: Service
metadata:
  name: nodejs-api-service
spec:
  selector:
    app: nodejs-api
  ports:
    - port: 80
      targetPort: 3000
  type: ClusterIP
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: api-secrets
type: Opaque
stringData:
  database-url: "postgres://postgres:secret@db:5432/api"
  jwt-secret: "your-256-bit-secret"

K8s 容器化最佳实践:

  • securityContext.runAsNonRoot 强制非 root 运行。
  • livenessProbe 探测失败会触发容器重启,readinessProbe 失败则将 Pod 从 Service 端点列表中移除,流量不再转发,避免向异常实例发送请求。
  • lifecycle.preStop.sleep 提供宽限期,让 kube -proxy 与 Ingress Controller 有时间将 Pod 从负载均衡中移除,配合应用内的优雅关闭逻辑,实现真正零停机。

相关阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「saas」更多文章

  1. 短链接对 SEO 的影响与优化最佳实践
  2. UTM 参数 + 短链接:追踪每一条营销链路
  3. 私域流量运营中的短链接策略:从引流到转化