将 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"]
三阶段设计说明:
- builder 阶段:安装全部依赖(含 devDependencies),执行
npm run build生成dist/。 - prod-deps 阶段:独立安装仅生产依赖,确保最终镜像不含
@types/*、[typescript](/posts/typescript/)、jest等开发包。 - 运行阶段:合并
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 容器化架构中承担多重角色:
- 反向代理:将外部 80/443 流量转发至内部 Node.js 容器端口。
- SSL 终端:集中管理 TLS 证书,Node.js 内部可保持 HTTP。
- 静态文件托管:图片、JS/CSS bundle 等直接由 Nginx 处理,不经过 Node.js。
- 负载均衡:多个 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 的响应是直接退出,导致正在处理的请求被中断。优雅关闭流程应:
- 接收到
SIGTERM后停止接受新连接。 - 等待现有请求处理完毕(设置超时,如 30 秒)。
- 关闭数据库连接、Redis 连接等资源。
- 退出进程。
// 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 端点:
| 框架 | 平均 QPS | 99% 延迟 | 内存峰值 | 镜像体积 |
|---|---|---|---|---|
| Express | 12,000 | 8 ms | 85 MB | 185 MB |
| Nest.js | 10,500 | 12 ms | 120 MB | 195 MB |
| Fastify | 28,000 | 4 ms | 65 MB | 180 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 从负载均衡中移除,配合应用内的优雅关闭逻辑,实现真正零停机。
相关阅读
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。