引言:从基础配置到云原生配置的演进

在掌握了 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();

总结

配置管理是现代云原生应用的基石,掌握这些高级技巧将帮助你在复杂的分布式系统中构建可靠、安全、可维护的应用。

继续深入学习,探索配置管理的无限可能!🌟


这里是引用

版权声明:本教程仅供学习使用,转载请注明出处。

更多推荐