Node.js云原生配置管理实战指南
·
引言:从基础配置到云原生配置的演进
在掌握了 Node.js 环境变量基础后,我们需要面对更复杂的现实场景:微服务架构、多环境部署、动态配置更新、密钥安全管理等。传统的
.env文件在云原生时代已显不足,本文将带你进入现代配置管理的深水区。
第一部分:Docker 配置管理深度实践
1.1 多阶段构建中的环境优化
# Dockerfile
# 构建阶段
FROM node:18-alpine AS builder
WORKDIR /app
# 安装构建依赖
COPY package*.json ./
RUN npm ci
# 复制源码并构建
COPY . .
RUN npm run build
# 生产阶段
FROM node:18-alpine AS production
WORKDIR /app
# 安装仅生产依赖
COPY package*.json ./
RUN npm ci --only=production --ignore-scripts
# 从构建阶段复制编译结果
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
# 创建非特权用户
RUN addgroup -g 1001 -S nodejs && \
adduser -S nextjs -u 1001
# 健康检查配置
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# 环境变量默认值
ENV NODE_ENV=production \
PORT=3000 \
LOG_LEVEL=info
# 使用非root用户
USER nextjs
EXPOSE 3000
CMD ["node", "dist/server.js"]
1.2 Docker Compose 多环境配置
# docker-compose.base.yml
version: '3.8'
x-common-variables: &common-vars
NODE_ENV: production
LOG_LEVEL: info
METRICS_ENABLED: true
TRACING_ENABLED: true
services:
app:
build: .
environment:
<<: *common-vars
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
postgres:
image: postgres:14-alpine
environment:
POSTGRES_DB: ${DB_NAME}
POSTGRES_USER: ${DB_USER}
secrets:
- db_password
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
interval: 30s
timeout: 10s
retries: 3
redis:
image: redis:7-alpine
command: redis-server --requirepass ${REDIS_PASSWORD}
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 30s
timeout: 10s
retries: 3
volumes:
postgres_data:
redis_data:
secrets:
db_password:
file: ./secrets/db_password.txt
redis_password:
file: ./secrets/redis_password.txt
# docker-compose.prod.yml
version: '3.8'
services:
app:
extends:
file: docker-compose.base.yml
service: app
environment:
- DB_HOST=postgres
- DB_PORT=5432
- REDIS_URL=redis://redis:6379
- CACHE_TTL=3600
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 30s
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
networks:
- backend
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
- ./ssl:/etc/nginx/ssl
depends_on:
- app
networks:
- backend
networks:
backend:
driver: bridge
1.3 生产环境 Docker 优化配置
#!/bin/bash
# deploy.sh
#!/bin/bash
# deploy.sh - 生产环境部署脚本
set -e
echo "🚀 开始生产环境部署..."
# 环境检查
if [ -z "$DB_PASSWORD" ]; then
echo "❌ 错误: DB_PASSWORD 环境变量未设置"
exit 1
fi
# 生成密钥文件
echo "🔐 生成密钥文件..."
mkdir -p secrets
echo "$DB_PASSWORD" > secrets/db_password.txt
echo "$REDIS_PASSWORD" > secrets/redis_password.txt
# 构建镜像
echo "📦 构建 Docker 镜像..."
docker build -t my-app:latest .
# 安全扫描
echo "🔍 执行安全扫描..."
docker scan my-app:latest
# 部署服务
echo "🚀 启动服务..."
docker stack deploy -c docker-compose.prod.yml my-app
echo "✅ 部署完成!"
echo "📊 检查服务状态: docker service ls"
echo "🔍 查看日志: docker service logs my-app_app"
第二部分:Kubernetes 配置管理实战
2.1 高级 ConfigMap 与 Secret 管理
# k8s/config/app-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
labels:
app: nodejs-app
environment: production
data:
# 应用配置
app.name: "My Node.js App"
app.version: "1.0.0"
# 数据库配置
db.host: "postgres-primary"
db.port: "5432"
db.name: "myapp_production"
# Redis 配置
redis.host: "redis-master"
redis.port: "6379"
# 功能开关
feature.new_ui: "true"
feature.payments: "false"
# 性能配置
server.timeout: "30000"
server.max_payload: "10mb"
# 日志配置
log.level: "info"
log.format: "json"
# Nginx 配置
nginx.conf: |
server {
listen 80;
server_name myapp.com;
location / {
proxy_pass http://nodejs-app:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /health {
access_log off;
proxy_pass http://nodejs-app:3000/health;
}
}
# k8s/config/app-secrets.yaml
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
annotations:
sealedsecrets.bitnami.com/managed: "true"
type: Opaque
data:
# 数据库认证
db.password: <base64-encoded-password>
db.backup.password: <base64-encoded-backup-password>
# JWT 密钥
jwt.access.secret: <base64-encoded-access-secret>
jwt.refresh.secret: <base64-encoded-refresh-secret>
# 第三方服务
stripe.secret.key: <base64-encoded-stripe-key>
sendgrid.api.key: <base64-encoded-sendgrid-key>
aws.access.key: <base64-encoded-aws-access-key>
# 加密密钥
encryption.key: <base64-encoded-encryption-key>
2.2 高级 Deployment 配置
# k8s/deployment/app-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nodejs-app
labels:
app: nodejs-app
version: v1
spec:
replicas: 3
revisionHistoryLimit: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: nodejs-app
template:
metadata:
labels:
app: nodejs-app
version: v1
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "3000"
prometheus.io/path: "/metrics"
spec:
serviceAccountName: nodejs-app
securityContext:
runAsNonRoot: true
runAsUser: 1001
fsGroup: 1001
containers:
- name: app
image: my-registry.com/my-app:1.0.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 3000
name: http
protocol: TCP
# 环境变量配置
env:
- name: NODE_ENV
value: "production"
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
# 从 ConfigMap 读取配置
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: app-secrets
# 资源限制
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
# 健康检查
livenessProbe:
httpGet:
path: /health
port: http
scheme: HTTP
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: http
scheme: HTTP
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 1
startupProbe:
httpGet:
path: /health
port: http
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
failureThreshold: 30
# 安全上下文
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1001
capabilities:
drop:
- ALL
# 卷挂载
volumeMounts:
- name: config-volume
mountPath: /app/config
readOnly: true
- name: tmp-volume
mountPath: /tmp
# 生命周期钩子
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 30"]
# 初始化容器
initContainers:
- name: config-check
image: busybox:1.35
command: ['sh', '-c', 'until nslookup postgres-primary; do echo waiting for postgres; sleep 2; done']
# 卷配置
volumes:
- name: config-volume
configMap:
name: app-config
defaultMode: 0644
- name: tmp-volume
emptyDir: {}
# 亲和性配置
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- nodejs-app
topologyKey: kubernetes.io/hostname
# 容忍配置
tolerations:
- key: "node.kubernetes.io/disk-pressure"
operator: "Exists"
effect: "NoSchedule"
2.3 Service 与 Ingress 配置
# k8s/network/app-service.yaml
apiVersion: v1
kind: Service
metadata:
name: nodejs-app
labels:
app: nodejs-app
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
service.beta.kubernetes.io/aws-load-balancer-internal: "false"
spec:
selector:
app: nodejs-app
ports:
- name: http
port: 80
targetPort: 3000
protocol: TCP
- name: metrics
port: 9100
targetPort: 9100
protocol: TCP
type: LoadBalancer
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nodejs-app
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
spec:
tls:
- hosts:
- myapp.com
- www.myapp.com
secretName: tls-secret
rules:
- host: myapp.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nodejs-app
port:
number: 80
- host: www.myapp.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nodejs-app
port:
number: 80
第三部分:HashiCorp Vault 密钥管理实战
3.1 Vault 服务端配置
# vault/config.hcl
storage "raft" {
path = "/vault/data"
node_id = "node1"
}
listener "tcp" {
address = "0.0.0.0:8200"
tls_disable = 1
}
api_addr = "http://vault.example.com:8200"
cluster_addr = "https://vault.example.com:8201"
ui = true
# k8s/vault/vault-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: vault
namespace: vault
spec:
replicas: 3
selector:
matchLabels:
app: vault
template:
metadata:
labels:
app: vault
spec:
serviceAccountName: vault
containers:
- name: vault
image: hashicorp/vault:1.14
args:
- server
env:
- name: VAULT_ADDR
value: "http://127.0.0.1:8200"
- name: VAULT_API_ADDR
value: "http://127.0.0.1:8200"
- name: SKIP_SETCAP
value: "true"
ports:
- name: http
containerPort: 8200
protocol: TCP
- name: cluster
containerPort: 8201
protocol: TCP
volumeMounts:
- name: vault-config
mountPath: /vault/config
readOnly: true
- name: vault-data
mountPath: /vault/data
- name: vault-logs
mountPath: /vault/logs
readinessProbe:
httpGet:
path: /v1/sys/health
port: http
initialDelaySeconds: 5
periodSeconds: 5
livenessProbe:
httpGet:
path: /v1/sys/health
port: http
initialDelaySeconds: 30
periodSeconds: 30
volumes:
- name: vault-config
configMap:
name: vault-config
- name: vault-data
persistentVolumeClaim:
claimName: vault-data
- name: vault-logs
emptyDir: {}
3.2 Vault 动态数据库密钥
// config/vault-client.js
const vault = require('node-vault')({
apiVersion: 'v1',
endpoint: process.env.VAULT_ADDR || 'http://localhost:8200',
token: process.env.VAULT_TOKEN
});
class VaultConfigManager {
constructor() {
this.cache = new Map();
this.renewalInterval = null;
}
async initialize() {
try {
// 使用 Kubernetes 认证
if (process.env.KUBERNETES_PORT) {
const jwt = require('fs').readFileSync('/var/run/secrets/kubernetes.io/serviceaccount/token', 'utf8');
const role = process.env.VAULT_ROLE || 'nodejs-app';
const auth = await vault.kubernetesLogin({
role: role,
jwt: jwt
});
vault.token = auth.auth.client_token;
this.scheduleTokenRenewal(auth.auth.lease_duration);
}
console.log('✅ Vault 客户端初始化成功');
} catch (error) {
console.error('❌ Vault 初始化失败:', error);
throw error;
}
}
async getDatabaseCredentials() {
const cacheKey = 'db-credentials';
if (this.cache.has(cacheKey)) {
return this.cache.get(cacheKey);
}
try {
const credentials = await vault.read('database/creds/nodejs-app');
// 缓存凭据,在租约到期前自动更新
this.cache.set(cacheKey, credentials.data);
this.scheduleCredentialRenewal(credentials.lease_duration);
return credentials.data;
} catch (error) {
console.error('❌ 获取数据库凭据失败:', error);
throw error;
}
}
async getSecret(path) {
if (this.cache.has(path)) {
return this.cache.get(path);
}
try {
const secret = await vault.read(path);
this.cache.set(path, secret.data);
return secret.data;
} catch (error) {
console.error(`❌ 获取密钥失败 ${path}:`, error);
throw error;
}
}
scheduleTokenRenewal(leaseDuration) {
const renewalTime = Math.floor(leaseDuration * 0.67) * 1000; // 67% 时续订
this.renewalInterval = setInterval(async () => {
try {
await vault.tokenRenewSelf();
console.log('✅ Vault token 续订成功');
} catch (error) {
console.error('❌ Vault token 续订失败:', error);
}
}, renewalTime);
}
scheduleCredentialRenewal(leaseDuration) {
const renewalTime = Math.floor(leaseDuration * 0.5) * 1000; // 50% 时续订
setTimeout(async () => {
try {
await this.getDatabaseCredentials(); // 重新获取凭据
console.log('✅ 数据库凭据续订成功');
} catch (error) {
console.error('❌ 数据库凭据续订失败:', error);
}
}, renewalTime);
}
destroy() {
if (this.renewalInterval) {
clearInterval(this.renewalInterval);
}
this.cache.clear();
}
}
module.exports = new VaultConfigManager();
3.3 Node.js 应用集成 Vault
// config/database-vault.js
const { Pool } = require('pg');
const vault = require('./vault-client');
class VaultManagedDatabase {
constructor() {
this.pool = null;
this.credentials = null;
}
async initialize() {
try {
// 从 Vault 获取动态数据库凭据
this.credentials = await vault.getDatabaseCredentials();
this.pool = new Pool({
host: process.env.DB_HOST,
port: process.env.DB_PORT,
database: process.env.DB_NAME,
user: this.credentials.username,
password: this.credentials.password,
ssl: process.env.DB_SSL === 'true',
max: 20,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 10000,
});
// 测试连接
await this.pool.query('SELECT 1');
console.log('✅ 数据库连接已建立(Vault 管理凭据)');
return this.pool;
} catch (error) {
console.error('❌ 数据库初始化失败:', error);
throw error;
}
}
async query(text, params) {
if (!this.pool) {
await this.initialize();
}
try {
const result = await this.pool.query(text, params);
return result;
} catch (error) {
if (error.code === '28P01') { // 认证失败
console.log('🔄 数据库凭据已过期,重新获取...');
await this.initialize();
return await this.pool.query(text, params);
}
throw error;
}
}
async close() {
if (this.pool) {
await this.pool.end();
}
}
}
module.exports = new VaultManagedDatabase();
第四部分:AWS Secrets Manager 集成
4.1 AWS Secrets Manager 客户端
// config/aws-secrets.js
const { SecretsManagerClient, GetSecretValueCommand } = require('@aws-sdk/client-secrets-manager');
class AWSSecretsManager {
constructor() {
this.client = new SecretsManagerClient({
region: process.env.AWS_REGION || 'us-east-1',
// 在 ECS/EKS 中会自动使用 IAM 角色
// 本地开发使用 AWS credentials
});
this.cache = new Map();
}
async getSecret(secretName, useCache = true) {
if (useCache && this.cache.has(secretName)) {
return this.cache.get(secretName);
}
try {
const command = new GetSecretValueCommand({
SecretId: secretName,
});
const response = await this.client.send(command);
let secretValue;
if ('SecretString' in response) {
secretValue = JSON.parse(response.SecretString);
} else {
secretValue = JSON.parse(Buffer.from(response.SecretBinary, 'base64').toString('utf8'));
}
if (useCache) {
this.cache.set(secretName, secretValue);
}
return secretValue;
} catch (error) {
console.error(`❌ 获取 AWS Secret ${secretName} 失败:`, error);
throw error;
}
}
async getDatabaseConfig() {
const secret = await this.getSecret(process.env.DB_SECRET_NAME || 'prod/database');
return {
host: secret.host,
port: secret.port,
database: secret.dbname,
username: secret.username,
password: secret.password,
};
}
async getJWTConfig() {
const secret = await this.getSecret(process.env.JWT_SECRET_NAME || 'prod/jwt');
return {
accessSecret: secret.accessSecret,
refreshSecret: secret.refreshSecret,
expiresIn: secret.expiresIn || '7d',
};
}
async getThirdPartyConfigs() {
const [stripe, sendgrid, aws] = await Promise.all([
this.getSecret('prod/stripe'),
this.getSecret('prod/sendgrid'),
this.getSecret('prod/aws'),
]);
return {
stripe: {
publishableKey: stripe.publishableKey,
secretKey: stripe.secretKey,
},
sendgrid: {
apiKey: sendgrid.apiKey,
},
aws: {
accessKeyId: aws.accessKeyId,
secretAccessKey: aws.secretAccessKey,
region: aws.region,
},
};
}
clearCache() {
this.cache.clear();
}
}
module.exports = new AWSSecretsManager();
4.2 应用配置集成
// config/aws-integrated.js
const awsSecrets = require('./aws-secrets');
class AWSIntegratedConfig {
constructor() {
this.config = null;
this.lastFetchTime = null;
this.cacheTTL = 5 * 60 * 1000; // 5分钟缓存
}
async initialize() {
if (this.config && this.lastFetchTime &&
(Date.now() - this.lastFetchTime) < this.cacheTTL) {
return this.config;
}
try {
const [databaseConfig, jwtConfig, thirdPartyConfigs, appConfig] = await Promise.all([
awsSecrets.getDatabaseConfig(),
awsSecrets.getJWTConfig(),
awsSecrets.getThirdPartyConfigs(),
this.getAppConfig(),
]);
this.config = {
env: process.env.NODE_ENV || 'development',
port: parseInt(process.env.PORT, 10) || 3000,
database: {
...databaseConfig,
ssl: process.env.DB_SSL === 'true',
pool: {
max: parseInt(process.env.DB_POOL_MAX, 10) || 20,
min: parseInt(process.env.DB_POOL_MIN, 10) || 5,
acquire: parseInt(process.env.DB_POOL_ACQUIRE, 10) || 30000,
idle: parseInt(process.env.DB_POOL_IDLE, 10) || 10000,
},
},
jwt: jwtConfig,
services: thirdPartyConfigs,
api: {
prefix: `/api/${process.env.API_VERSION || 'v1'}`,
rateLimit: {
windowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS, 10) || 900000,
max: parseInt(process.env.RATE_LIMIT_MAX_REQUESTS, 10) || 100,
},
},
features: {
newUI: process.env.FEATURE_NEW_UI === 'true',
payment: process.env.FEATURE_PAYMENT === 'true',
analytics: process.env.ENABLE_ANALYTICS === 'true',
},
...appConfig,
};
this.lastFetchTime = Date.now();
return this.config;
} catch (error) {
console.error('❌ AWS 集成配置初始化失败:', error);
throw error;
}
}
async getAppConfig() {
try {
return await awsSecrets.getSecret('prod/app-config');
} catch (error) {
console.warn('⚠️ 无法获取应用配置,使用环境变量回退');
return {};
}
}
async refresh() {
awsSecrets.clearCache();
this.config = null;
this.lastFetchTime = null;
return await this.initialize();
}
getConfig() {
if (!this.config) {
throw new Error('配置未初始化,请先调用 initialize() 方法');
}
return this.config;
}
}
module.exports = new AWSIntegratedConfig();
第五部分:配置即代码与 GitOps
5.1 Kubernetes 配置即代码
# k8s/kustomize/base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: nodejs-app
resources:
- deployment.yaml
- service.yaml
- configmap.yaml
- secrets.yaml
- service-account.yaml
- pdb.yaml
configMapGenerator:
- name: app-config
behavior: merge
literals:
- APP_NAME=My Node.js App
- LOG_LEVEL=info
secretGenerator:
- name: app-secrets
behavior: replace
literals:
- DB_PASSWORD=change-me
- JWT_SECRET=change-me
images:
- name: my-app
newName: my-registry.com/my-app
newTag: latest
commonLabels:
app: nodejs-app
managed-by: kustomize
commonAnnotations:
version: "1.0.0"
description: "Node.js Application"
# k8s/kustomize/overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: nodejs-app-production
resources:
- ../../base
patchesStrategicMerge:
- deployment-patch.yaml
- configmap-patch.yaml
configMapGenerator:
- name: app-config
behavior: merge
literals:
- NODE_ENV=production
- LOG_LEVEL=warn
- METRICS_ENABLED=true
secretGenerator:
- name: app-secrets
behavior: replace
envs:
- .env.secrets
replicas:
- name: nodejs-app
count: 5
images:
- name: my-app
newTag: v1.2.3-production
commonLabels:
environment: production
track: stable
5.2 Helm Chart 配置管理
# helm/nodejs-app/Chart.yaml
apiVersion: v2
name: nodejs-app
description: A Node.js application Helm chart
type: application
version: 0.1.0
appVersion: "1.0.0"
dependencies:
- name: postgresql
version: "12.1.0"
repository: "https://charts.bitnami.com/bitnami"
condition: postgresql.enabled
- name: redis
version: "17.0.0"
repository: "https://charts.bitnami.com/bitnami"
condition: redis.enabled
# helm/nodejs-app/values.yaml
# Default values for nodejs-app
global:
appName: nodejs-app
environment: production
image:
repository: my-registry.com/my-app
pullPolicy: IfNotPresent
tag: "latest"
replicaCount: 3
service:
type: ClusterIP
port: 80
targetPort: 3000
annotations: {}
ingress:
enabled: true
className: "nginx"
hosts:
- host: myapp.com
paths:
- path: /
pathType: Prefix
tls: []
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
app:
config:
nodeEnv: production
logLevel: info
port: 3000
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
env:
- name: NODE_ENV
value: "production"
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
database:
enabled: true
host: "postgresql"
port: 5432
name: "myapp_production"
secretName: "database-secret"
redis:
enabled: true
host: "redis-master"
port: 6379
secretName: "redis-secret"
vault:
enabled: false
addr: "http://vault.vault.svc.cluster.local:8200"
role: "nodejs-app"
aws:
region: "us-east-1"
secretsManager:
enabled: true
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 80
targetMemoryUtilizationPercentage: 80
nodeSelector: {}
tolerations: []
affinity: {}
第六部分:监控与可观测性
6.1 配置监控与告警
# k8s/monitoring/config-monitor.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: config-validation-rules
data:
config-rules.yaml: |
groups:
- name: config.rules
rules:
- alert: ConfigMapModified
expr: changes(kube_configmap_metadata_resource_version[1h]) > 0
for: 0m
labels:
severity: warning
annotations:
summary: "ConfigMap 被修改"
description: "ConfigMap {{ $labels.namespace }}/{{ $labels.configmap }} 在过去1小时内被修改"
- alert: SecretUpdated
expr: changes(kube_secret_metadata_resource_version[1h]) > 0
for: 0m
labels:
severity: critical
annotations:
summary: "Secret 被更新"
description: "Secret {{ $labels.namespace }}/{{ $labels.secret }} 在过去1小时内被更新"
- alert: MissingRequiredConfig
expr: count by (namespace, pod) (
kube_pod_container_status_restarts_total{container="app"}
* on(pod) group_left(label_app)
kube_pod_labels{label_app="nodejs-app"}
) > 3
for: 5m
labels:
severity: critical
annotations:
summary: "容器频繁重启,可能缺少必要配置"
description: "Pod {{ $labels.pod }} 在过去5分钟内重启超过3次"
6.2 配置审计日志
// utils/config-audit.js
class ConfigAuditLogger {
constructor() {
this.auditLog = [];
}
logConfigAccess(configPath, user, action) {
const auditEntry = {
timestamp: new Date().toISOString(),
configPath,
user,
action,
environment: process.env.NODE_ENV,
pod: process.env.POD_NAME || 'unknown',
};
this.auditLog.push(auditEntry);
// 发送到审计系统
this.sendToAuditSystem(auditEntry);
// 本地日志
console.log(JSON.stringify({
level: 'AUDIT',
...auditEntry
}));
}
async sendToAuditSystem(entry) {
try {
// 发送到中央审计系统
// 例如:ELK、Splunk、CloudWatch Logs
if (process.env.AUDIT_ENDPOINT) {
await fetch(process.env.AUDIT_ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(entry),
});
}
} catch (error) {
console.error('❌ 发送审计日志失败:', error);
}
}
getAuditLog() {
return [...this.auditLog];
}
}
module.exports = new ConfigAuditLogger();
总结
配置管理是现代云原生应用的基石,掌握这些高级技巧将帮助你在复杂的分布式系统中构建可靠、安全、可维护的应用。
继续深入学习,探索配置管理的无限可能!🌟
这里是引用
版权声明:本教程仅供学习使用,转载请注明出处。
更多推荐



所有评论(0)