Skip to content

StatefulSet 深入实践指南

StatefulSet是Kubernetes中专门用于管理有状态应用的工作负载控制器。与Deployment不同,StatefulSet为每个Pod提供稳定的网络标识、持久化存储和有序的部署与扩展,使其成为部署数据库、消息队列等有状态服务的首选方案。

🎯 StatefulSet核心概念

StatefulSet vs Deployment

yaml
statefulset_vs_deployment:
  deployment_characteristics:
    pod_identity: "随机名称 + 随机ID"
    network_identity: "不稳定,Pod重启后变化"
    storage: "共享存储或临时存储"
    scaling: "并行扩缩容"
    updates: "并行滚动更新"
    
    use_cases:
      - "无状态Web应用"
      - "API服务"
      - "批处理任务"
      - "缓存层服务"
    
    example_naming:
      - "nginx-deployment-7d8c6df8c5-abc12"
      - "nginx-deployment-7d8c6df8c5-def34"
      - "nginx-deployment-7d8c6df8c5-ghi56"
  
  statefulset_characteristics:
    pod_identity: "稳定的序号标识"
    network_identity: "稳定的DNS名称"
    storage: "每个Pod独立的持久化存储"
    scaling: "有序扩缩容"
    updates: "有序滚动更新"
    
    use_cases:
      - "数据库集群"
      - "消息队列"
      - "分布式存储"
      - "有状态中间件"
    
    example_naming:
      - "postgres-0"
      - "postgres-1"
      - "postgres-2"
yaml
statefulset_mechanics:
  pod_management:
    ordered_deployment:
      description: "Pod按序号顺序创建"
      process:
        1: "创建postgres-0,等待Running"
        2: "创建postgres-1,等待Running"
        3: "创建postgres-2,等待Running"
      
      configuration: |
        apiVersion: apps/v1
        kind: StatefulSet
        metadata:
          name: postgres
        spec:
          serviceName: postgres
          replicas: 3
          podManagementPolicy: OrderedReady  # 有序创建
          selector:
            matchLabels:
              app: postgres
    
    parallel_deployment:
      description: "Pod并行创建"
      configuration: |
        spec:
          podManagementPolicy: Parallel  # 并行创建
    
    ordered_termination:
      description: "Pod按倒序删除"
      process:
        1: "删除postgres-2,等待终止"
        2: "删除postgres-1,等待终止"
        3: "删除postgres-0,等待终止"
  
  network_identity:
    stable_hostnames:
      pattern: "$(pod-name).$(service-name).$(namespace).svc.cluster.local"
      examples:
        - "postgres-0.postgres.default.svc.cluster.local"
        - "postgres-1.postgres.production.svc.cluster.local"
        - "kafka-2.kafka.streaming.svc.cluster.local"
    
    headless_service: |
      # StatefulSet必须与Headless Service配对
      apiVersion: v1
      kind: Service
      metadata:
        name: postgres
      spec:
        clusterIP: None  # Headless Service
        selector:
          app: postgres
        ports:
        - port: 5432
          targetPort: 5432
  
  persistent_storage:
    volume_claim_templates:
      description: "为每个Pod自动创建PVC"
      configuration: |
        spec:
          volumeClaimTemplates:
          - metadata:
              name: data
            spec:
              accessModes: ["ReadWriteOnce"]
              storageClassName: fast-ssd
              resources:
                requests:
                  storage: 100Gi
    
    volume_binding:
      description: "Pod与PVC的稳定绑定"
      pattern:
        - "postgres-0 → data-postgres-0"
        - "postgres-1 → data-postgres-1"
        - "postgres-2 → data-postgres-2"

🗄️ 数据库StatefulSet实践

PostgreSQL集群部署

yaml
postgresql_cluster:
  master_slave_config:
    statefulset: |
      apiVersion: apps/v1
      kind: StatefulSet
      metadata:
        name: postgres
        namespace: production
      spec:
        serviceName: postgres
        replicas: 3
        selector:
          matchLabels:
            app: postgres
        template:
          metadata:
            labels:
              app: postgres
          spec:
            securityContext:
              runAsUser: 999
              runAsGroup: 999
              fsGroup: 999
            
            containers:
            - name: postgres
              image: postgres:14-alpine
              
              env:
              - name: POSTGRES_DB
                value: "myapp"
              - name: POSTGRES_USER
                valueFrom:
                  secretKeyRef:
                    name: postgres-secret
                    key: username
              - name: POSTGRES_PASSWORD
                valueFrom:
                  secretKeyRef:
                    name: postgres-secret
                    key: password
              - name: PGUSER
                valueFrom:
                  secretKeyRef:
                    name: postgres-secret
                    key: username
              
              # 根据Pod序号配置主从角色
              - name: POD_NAME
                valueFrom:
                  fieldRef:
                    fieldPath: metadata.name
              
              ports:
              - containerPort: 5432
                name: postgres
              
              volumeMounts:
              - name: data
                mountPath: /var/lib/postgresql/data
              - name: config
                mountPath: /etc/postgresql
              - name: init-scripts
                mountPath: /docker-entrypoint-initdb.d
              
              # 生命周期钩子
              lifecycle:
                postStart:
                  exec:
                    command:
                    - /bin/bash
                    - -c
                    - |
                      # 配置主从复制
                      if [[ "${POD_NAME##*-}" == "0" ]]; then
                        echo "Configuring as master"
                        # 主节点配置
                        echo "wal_level = replica" >> /var/lib/postgresql/data/postgresql.conf
                        echo "max_wal_senders = 3" >> /var/lib/postgresql/data/postgresql.conf
                        echo "wal_keep_segments = 64" >> /var/lib/postgresql/data/postgresql.conf
                      else
                        echo "Configuring as slave"
                        # 从节点配置
                        pg_basebackup -h postgres-0.postgres -D /var/lib/postgresql/data -U replicator -W
                        echo "standby_mode = 'on'" >> /var/lib/postgresql/data/recovery.conf
                        echo "primary_conninfo = 'host=postgres-0.postgres port=5432 user=replicator'" >> /var/lib/postgresql/data/recovery.conf
                      fi
              
              # 健康检查
              livenessProbe:
                exec:
                  command:
                  - /bin/bash
                  - -c
                  - pg_isready -U $POSTGRES_USER -d $POSTGRES_DB
                initialDelaySeconds: 30
                periodSeconds: 10
                
              readinessProbe:
                exec:
                  command:
                  - /bin/bash
                  - -c
                  - pg_isready -U $POSTGRES_USER -d $POSTGRES_DB
                initialDelaySeconds: 5
                periodSeconds: 5
              
              # 资源限制
              resources:
                requests:
                  memory: "512Mi"
                  cpu: "500m"
                limits:
                  memory: "2Gi"
                  cpu: "1"
            
            volumes:
            - name: config
              configMap:
                name: postgres-config
            - name: init-scripts
              configMap:
                name: postgres-init
        
        # 持久化存储模板
        volumeClaimTemplates:
        - metadata:
            name: data
          spec:
            accessModes: ["ReadWriteOnce"]
            storageClassName: fast-ssd
            resources:
              requests:
                storage: 100Gi
    
    supporting_resources:
      secret: |
        apiVersion: v1
        kind: Secret
        metadata:
          name: postgres-secret
          namespace: production
        type: Opaque
        stringData:
          username: "postgres"
          password: "secure-password-123"
          replication-username: "replicator"
          replication-password: "repl-password-456"
      
      configmap: |
        apiVersion: v1
        kind: ConfigMap
        metadata:
          name: postgres-config
        data:
          postgresql.conf: |
            # 连接设置
            listen_addresses = '*'
            port = 5432
            max_connections = 200
            
            # 内存设置
            shared_buffers = 256MB
            effective_cache_size = 1GB
            work_mem = 4MB
            maintenance_work_mem = 64MB
            
            # WAL设置
            wal_level = replica
            max_wal_senders = 3
            wal_keep_segments = 64
            archive_mode = on
            archive_command = 'test ! -f /archive/%f && cp %p /archive/%f'
            
            # 日志设置
            logging_collector = on
            log_directory = '/var/log/postgresql'
            log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
            log_min_duration_statement = 1000
          
          pg_hba.conf: |
            # TYPE  DATABASE        USER            ADDRESS                 METHOD
            local   all             postgres                                peer
            local   all             all                                     md5
            host    all             all             127.0.0.1/32            md5
            host    all             all             ::1/128                 md5
            host    all             all             0.0.0.0/0               md5
            host    replication     replicator      0.0.0.0/0               md5
      
      services: |
        # 主服务(读写)
        apiVersion: v1
        kind: Service
        metadata:
          name: postgres-master
        spec:
          selector:
            app: postgres
            role: master
          ports:
          - port: 5432
            targetPort: 5432
        
        ---
        # 从服务(只读)
        apiVersion: v1
        kind: Service
        metadata:
          name: postgres-slave
        spec:
          selector:
            app: postgres
            role: slave
          ports:
          - port: 5432
            targetPort: 5432
        
        ---
        # Headless服务
        apiVersion: v1
        kind: Service
        metadata:
          name: postgres
        spec:
          clusterIP: None
          selector:
            app: postgres
          ports:
          - port: 5432
            targetPort: 5432
yaml
mongodb_replica_set:
  statefulset_config: |
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: mongodb
      namespace: production
    spec:
      serviceName: mongodb
      replicas: 3
      selector:
        matchLabels:
          app: mongodb
      template:
        metadata:
          labels:
            app: mongodb
        spec:
          containers:
          - name: mongodb
            image: mongo:5.0
            
            env:
            - name: MONGO_INITDB_ROOT_USERNAME
              valueFrom:
                secretKeyRef:
                  name: mongodb-secret
                  key: username
            - name: MONGO_INITDB_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mongodb-secret
                  key: password
            
            command:
            - mongod
            - --replSet
            - rs0
            - --bind_ip_all
            - --auth
            - --keyFile
            - /etc/mongodb/keyfile
            
            ports:
            - containerPort: 27017
              name: mongodb
            
            volumeMounts:
            - name: data
              mountPath: /data/db
            - name: keyfile
              mountPath: /etc/mongodb
              readOnly: true
            
            # 健康检查
            livenessProbe:
              exec:
                command:
                - mongo
                - --eval
                - "db.adminCommand('ping')"
              initialDelaySeconds: 30
              periodSeconds: 10
            
            readinessProbe:
              exec:
                command:
                - mongo
                - --eval
                - "db.adminCommand('ping')"
              initialDelaySeconds: 5
              periodSeconds: 5
            
            resources:
              requests:
                memory: "1Gi"
                cpu: "500m"
              limits:
                memory: "4Gi"
                cpu: "2"
          
          # 初始化容器:配置副本集
          initContainers:
          - name: mongodb-init
            image: mongo:5.0
            env:
            - name: MONGO_INITDB_ROOT_USERNAME
              valueFrom:
                secretKeyRef:
                  name: mongodb-secret
                  key: username
            - name: MONGO_INITDB_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mongodb-secret
                  key: password
            command:
            - /bin/bash
            - -c
            - |
              # 等待MongoDB启动
              until mongo --eval "print('MongoDB is ready')"; do
                echo "Waiting for MongoDB to be ready..."
                sleep 2
              done
              
              # 初始化副本集(仅在第一个Pod上执行)
              if [[ "${HOSTNAME}" == "mongodb-0" ]]; then
                mongo --eval "
                  rs.initiate({
                    _id: 'rs0',
                    members: [
                      { _id: 0, host: 'mongodb-0.mongodb.production.svc.cluster.local:27017' },
                      { _id: 1, host: 'mongodb-1.mongodb.production.svc.cluster.local:27017' },
                      { _id: 2, host: 'mongodb-2.mongodb.production.svc.cluster.local:27017' }
                    ]
                  })
                "
              fi
          
          volumes:
          - name: keyfile
            secret:
              secretName: mongodb-keyfile
              defaultMode: 0400
      
      volumeClaimTemplates:
      - metadata:
          name: data
        spec:
          accessModes: ["ReadWriteOnce"]
          storageClassName: fast-ssd
          resources:
            requests:
              storage: 200Gi
  
  replica_set_management:
    initialization_job: |
      # MongoDB副本集初始化Job
      apiVersion: batch/v1
      kind: Job
      metadata:
        name: mongodb-init-replica-set
      spec:
        template:
          spec:
            containers:
            - name: mongodb-init
              image: mongo:5.0
              env:
              - name: MONGODB_USERNAME
                valueFrom:
                  secretKeyRef:
                    name: mongodb-secret
                    key: username
              - name: MONGODB_PASSWORD
                valueFrom:
                  secretKeyRef:
                    name: mongodb-secret
                    key: password
              command:
              - /bin/bash
              - -c
              - |
                # 连接到第一个MongoDB实例
                mongo mongodb-0.mongodb.production.svc.cluster.local:27017 \
                  -u $MONGODB_USERNAME -p $MONGODB_PASSWORD --authenticationDatabase admin \
                  --eval "
                    try {
                      rs.initiate({
                        _id: 'rs0',
                        members: [
                          { _id: 0, host: 'mongodb-0.mongodb.production.svc.cluster.local:27017', priority: 3 },
                          { _id: 1, host: 'mongodb-1.mongodb.production.svc.cluster.local:27017', priority: 2 },
                          { _id: 2, host: 'mongodb-2.mongodb.production.svc.cluster.local:27017', priority: 1 }
                        ]
                      });
                      print('Replica set initiated successfully');
                    } catch (e) {
                      print('Replica set already exists or error:', e);
                    }
                  "
            restartPolicy: OnFailure
    
    monitoring_script: |
      #!/bin/bash
      # MongoDB副本集监控脚本
      
      check_replica_set_status() {
        for i in {0..2}; do
          echo "Checking mongodb-$i status..."
          
          # 检查副本集状态
          mongo mongodb-$i.mongodb.production.svc.cluster.local:27017 \
            -u $MONGODB_USERNAME -p $MONGODB_PASSWORD \
            --authenticationDatabase admin \
            --quiet --eval "
              rs.status().members.forEach(function(member) {
                print(member.name + ': ' + member.stateStr);
              });
            "
        done
      }
      
      check_replication_lag() {
        echo "Checking replication lag..."
        mongo mongodb-0.mongodb.production.svc.cluster.local:27017 \
          -u $MONGODB_USERNAME -p $MONGODB_PASSWORD \
          --authenticationDatabase admin \
          --quiet --eval "
            rs.printSlaveReplicationInfo();
          "
      }
      
      # 主函数
      main() {
        echo "MongoDB Replica Set Health Check"
        echo "================================"
        check_replica_set_status
        echo ""
        check_replication_lag
      }
      
      main

⚡ StatefulSet高级特性

滚动更新策略

yaml
update_strategies:
  rolling_update:
    configuration: |
      apiVersion: apps/v1
      kind: StatefulSet
      metadata:
        name: redis
      spec:
        updateStrategy:
          type: RollingUpdate
          rollingUpdate:
            # 分批更新配置
            partition: 0  # 从最高序号开始更新
        
        # 更新策略详解
        # partition: 2 表示只更新序号 >= 2 的Pod
        # partition: 0 表示更新所有Pod
    
    controlled_rollout: |
      # 分阶段更新脚本
      #!/bin/bash
      
      STATEFULSET="redis"
      REPLICAS=5
      
      # 阶段1:更新最后一个Pod
      kubectl patch statefulset $STATEFULSET -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":4}}}}'
      kubectl wait --for=condition=ready pod/${STATEFULSET}-4 --timeout=300s
      
      # 验证Pod健康状态
      if kubectl get pod ${STATEFULSET}-4 -o jsonpath='{.status.phase}' | grep -q "Running"; then
        echo "Pod ${STATEFULSET}-4 updated successfully"
      else
        echo "Update failed, rolling back..."
        kubectl rollout undo statefulset/$STATEFULSET
        exit 1
      fi
      
      # 阶段2:更新倒数第二个Pod
      kubectl patch statefulset $STATEFULSET -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":3}}}}'
      kubectl wait --for=condition=ready pod/${STATEFULSET}-3 --timeout=300s
      
      # 继续更新其余Pod...
      for i in $(seq 2 -1 0); do
        kubectl patch statefulset $STATEFULSET -p "{\"spec\":{\"updateStrategy\":{\"rollingUpdate\":{\"partition\":$i}}}}"
        kubectl wait --for=condition=ready pod/${STATEFULSET}-$i --timeout=300s
        
        # 验证应用健康状态
        ./verify-redis-cluster-health.sh
        if [ $? -ne 0 ]; then
          echo "Health check failed at pod $i, stopping update"
          exit 1
        fi
      done
      
      echo "StatefulSet update completed successfully"
  
  on_delete_strategy:
    configuration: |
      spec:
        updateStrategy:
          type: OnDelete
    
    manual_update_process: |
      # 手动更新流程
      update_statefulset_manually() {
        local statefulset=$1
        local replicas=$2
        
        echo "Starting manual update of $statefulset"
        
        # 更新StatefulSet模板
        kubectl apply -f $statefulset-updated.yaml
        
        # 手动删除Pod进行更新
        for i in $(seq $((replicas-1)) -1 0); do
          echo "Updating ${statefulset}-${i}..."
          
          # 删除Pod
          kubectl delete pod ${statefulset}-${i}
          
          # 等待Pod重新创建并就绪
          kubectl wait --for=condition=ready pod/${statefulset}-${i} --timeout=300s
          
          # 验证Pod健康状态
          if ! verify_pod_health ${statefulset}-${i}; then
            echo "Pod ${statefulset}-${i} failed health check"
            return 1
          fi
          
          echo "Pod ${statefulset}-${i} updated successfully"
          sleep 10  # 等待间隔
        done
        
        echo "Manual update completed"
      }
yaml
scaling_management:
  horizontal_scaling:
    scale_up: |
      # 扩容操作
      kubectl scale statefulset postgres --replicas=5
      
      # 等待新Pod就绪
      kubectl wait --for=condition=ready pod/postgres-3 --timeout=300s
      kubectl wait --for=condition=ready pod/postgres-4 --timeout=300s
      
      # 对于数据库,需要手动将新节点加入集群
      add_postgres_replica() {
        local new_replica=$1
        
        # 在主节点上创建复制槽
        kubectl exec postgres-0 -- psql -U postgres -c "
          SELECT pg_create_physical_replication_slot('slot_${new_replica}');
        "
        
        # 配置新从节点
        kubectl exec $new_replica -- bash -c "
          pg_basebackup -h postgres-0 -D /var/lib/postgresql/data -U replicator -W -S slot_${new_replica}
          echo \"standby_mode = 'on'\" >> /var/lib/postgresql/data/recovery.conf
          echo \"primary_conninfo = 'host=postgres-0 port=5432 user=replicator'\" >> /var/lib/postgresql/data/recovery.conf
          echo \"primary_slot_name = 'slot_${new_replica}'\" >> /var/lib/postgresql/data/recovery.conf
        "
      }
    
    scale_down: |
      # 缩容操作(需要特别小心)
      scale_down_statefulset() {
        local statefulset=$1
        local current_replicas=$2
        local target_replicas=$3
        
        echo "Scaling down $statefulset from $current_replicas to $target_replicas"
        
        # 对于数据库,需要先处理数据迁移和复制关系
        for i in $(seq $((current_replicas-1)) -1 $target_replicas); do
          echo "Preparing to remove ${statefulset}-${i}..."
          
          # 如果是从节点,从主节点移除复制槽
          if [ "$i" -gt 0 ]; then
            kubectl exec ${statefulset}-0 -- psql -U postgres -c "
              SELECT pg_drop_replication_slot('slot_${statefulset}_${i}');
            "
          fi
          
          # 备份重要数据(如果需要)
          backup_pod_data ${statefulset}-${i}
          
          echo "Removing ${statefulset}-${i}"
        done
        
        # 执行缩容
        kubectl scale statefulset $statefulset --replicas=$target_replicas
        
        # 清理孤立的PVC(可选,根据策略决定)
        cleanup_orphaned_pvcs $statefulset $target_replicas $current_replicas
      }
  
  storage_expansion:
    volume_expansion: |
      # 存储扩容(需要StorageClass支持)
      expand_storage() {
        local statefulset=$1
        local new_size=$2
        
        echo "Expanding storage for $statefulset to $new_size"
        
        # 获取当前副本数
        replicas=$(kubectl get statefulset $statefulset -o jsonpath='{.spec.replicas}')
        
        # 逐个扩展PVC
        for i in $(seq 0 $((replicas-1))); do
          pvc_name="data-${statefulset}-${i}"
          
          echo "Expanding PVC $pvc_name to $new_size"
          kubectl patch pvc $pvc_name -p "{\"spec\":{\"resources\":{\"requests\":{\"storage\":\"$new_size\"}}}}"
          
          # 等待扩展完成
          kubectl wait --for=condition=FileSystemResizePending=false pvc/$pvc_name --timeout=300s
          
          # 重启Pod以识别新的存储大小
          kubectl delete pod ${statefulset}-${i}
          kubectl wait --for=condition=ready pod/${statefulset}-${i} --timeout=300s
          
          echo "Storage expansion completed for ${statefulset}-${i}"
        done
      }
    
    automated_expansion: |
      # 自动化存储监控和扩展
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: storage-monitor
      data:
        monitor.sh: |
          #!/bin/bash
          
          # 监控存储使用率
          monitor_storage_usage() {
            local threshold=80  # 使用率阈值
            
            # 获取所有StatefulSet的PVC
            kubectl get pvc -l app.kubernetes.io/managed-by=statefulset -o json | \
            jq -r '.items[] | "\(.metadata.name) \(.metadata.namespace)"' | \
            while read pvc_name namespace; do
              # 获取对应的Pod
              pod_name=$(echo $pvc_name | sed 's/data-//')
              
              # 检查存储使用率
              usage=$(kubectl exec $pod_name -n $namespace -- df -h /data | awk 'NR==2 {print $5}' | sed 's/%//')
              
              if [ "$usage" -gt "$threshold" ]; then
                echo "Storage usage for $pvc_name is ${usage}%, triggering expansion"
                expand_pvc_storage $pvc_name $namespace
              fi
            done
          }
          
          expand_pvc_storage() {
            local pvc_name=$1
            local namespace=$2
            local current_size=$(kubectl get pvc $pvc_name -n $namespace -o jsonpath='{.spec.resources.requests.storage}')
            
            # 扩展到当前大小的1.5倍
            local new_size=$(echo $current_size | sed 's/Gi//' | awk '{print int($1*1.5)"Gi"}')
            
            kubectl patch pvc $pvc_name -n $namespace -p "{\"spec\":{\"resources\":{\"requests\":{\"storage\":\"$new_size\"}}}}"
            
            # 发送告警通知
            send_alert "PVC $pvc_name expanded from $current_size to $new_size"
          }
          
          # 主循环
          while true; do
            monitor_storage_usage
            sleep 300  # 每5分钟检查一次
          done

🔧 StatefulSet运维实践

备份与恢复

yaml
backup_strategies:
  scheduled_backup:
    cronjob_config: |
      apiVersion: batch/v1
      kind: CronJob
      metadata:
        name: postgres-backup
        namespace: production
      spec:
        schedule: "0 2 * * *"  # 每天凌晨2点
        concurrencyPolicy: Forbid
        successfulJobsHistoryLimit: 3
        failedJobsHistoryLimit: 3
        
        jobTemplate:
          spec:
            template:
              spec:
                containers:
                - name: postgres-backup
                  image: postgres:14-alpine
                  env:
                  - name: POSTGRES_HOST
                    value: "postgres-0.postgres"
                  - name: POSTGRES_USER
                    valueFrom:
                      secretKeyRef:
                        name: postgres-secret
                        key: username
                  - name: POSTGRES_PASSWORD
                    valueFrom:
                      secretKeyRef:
                        name: postgres-secret
                        key: password
                  - name: BACKUP_RETENTION_DAYS
                    value: "30"
                  
                  command:
                  - /bin/bash
                  - -c
                  - |
                    # 创建备份目录
                    BACKUP_DIR="/backup/postgres/$(date +%Y-%m-%d)"
                    mkdir -p $BACKUP_DIR
                    
                    # 创建数据库备份
                    pg_dump -h $POSTGRES_HOST -U $POSTGRES_USER -d myapp \
                      --no-password --verbose --format=custom \
                      --file=$BACKUP_DIR/myapp_$(date +%H%M%S).backup
                    
                    # 备份全局对象(用户、角色等)
                    pg_dumpall -h $POSTGRES_HOST -U $POSTGRES_USER \
                      --no-password --globals-only \
                      --file=$BACKUP_DIR/globals_$(date +%H%M%S).sql
                    
                    # 上传到对象存储
                    aws s3 sync $BACKUP_DIR s3://database-backups/postgres/$(date +%Y-%m-%d)/
                    
                    # 清理本地备份文件
                    rm -rf $BACKUP_DIR
                    
                    # 清理过期的S3备份
                    aws s3 ls s3://database-backups/postgres/ | \
                    awk '{print $2}' | \
                    while read date_dir; do
                      if [[ $(date -d "$date_dir" +%s) -lt $(date -d "$BACKUP_RETENTION_DAYS days ago" +%s) ]]; then
                        aws s3 rm s3://database-backups/postgres/$date_dir --recursive
                      fi
                    done
                  
                  volumeMounts:
                  - name: backup-volume
                    mountPath: /backup
                  - name: aws-credentials
                    mountPath: /root/.aws
                    readOnly: true
                
                volumes:
                - name: backup-volume
                  emptyDir: {}
                - name: aws-credentials
                  secret:
                    secretName: aws-credentials
                
                restartPolicy: OnFailure
  
  continuous_backup:
    wal_archiving: |
      # PostgreSQL WAL归档配置
      postgresql_config:
        archive_mode: "on"
        archive_command: "aws s3 cp %p s3://database-wal-archive/postgres/%f"
        archive_timeout: "300s"
        
      # WAL恢复脚本
      recovery_script: |
        #!/bin/bash
        
        restore_from_backup() {
          local backup_date=$1
          local target_time=$2
          
          echo "Starting point-in-time recovery to $target_time"
          
          # 停止数据库
          kubectl scale statefulset postgres --replicas=0
          
          # 清理数据目录
          kubectl exec postgres-0 -- rm -rf /var/lib/postgresql/data/*
          
          # 恢复基础备份
          aws s3 cp s3://database-backups/postgres/$backup_date/myapp.backup /tmp/
          kubectl cp /tmp/myapp.backup postgres-0:/tmp/
          
          # 恢复数据
          kubectl exec postgres-0 -- pg_restore -U postgres -d myapp /tmp/myapp.backup
          
          # 配置WAL恢复
          kubectl exec postgres-0 -- bash -c "
            echo \"restore_command = 'aws s3 cp s3://database-wal-archive/postgres/%f %p'\" >> /var/lib/postgresql/data/recovery.conf
            echo \"recovery_target_time = '$target_time'\" >> /var/lib/postgresql/data/recovery.conf
            echo \"recovery_target_action = 'promote'\" >> /var/lib/postgresql/data/recovery.conf
          "
          
          # 启动数据库
          kubectl scale statefulset postgres --replicas=1
          
          echo "Point-in-time recovery completed"
        }
yaml
monitoring_alerting:
  prometheus_monitoring:
    postgres_exporter: |
      # PostgreSQL Exporter部署
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: postgres-exporter
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: postgres-exporter
        template:
          spec:
            containers:
            - name: postgres-exporter
              image: prometheuscommunity/postgres-exporter:v0.11.1
              env:
              - name: DATA_SOURCE_NAME
                valueFrom:
                  secretKeyRef:
                    name: postgres-monitoring-secret
                    key: data-source-name
              ports:
              - containerPort: 9187
                name: metrics
              
              # 自定义监控查询
              volumeMounts:
              - name: queries
                mountPath: /etc/postgres_exporter
            
            volumes:
            - name: queries
              configMap:
                name: postgres-exporter-queries
    
    monitoring_queries: |
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: postgres-exporter-queries
      data:
        queries.yaml: |
          # 复制延迟监控
          pg_replication_lag:
            query: "SELECT client_addr, pg_wal_lsn_diff(pg_current_wal_lsn(), flush_lsn) as lag_bytes FROM pg_stat_replication"
            master: true
            metrics:
              - client_addr:
                  usage: "LABEL"
                  description: "Client address"
              - lag_bytes:
                  usage: "GAUGE"
                  description: "Replication lag in bytes"
          
          # 数据库大小监控
          pg_database_size:
            query: "SELECT datname, pg_database_size(datname) as size_bytes FROM pg_database WHERE datistemplate = false"
            master: true
            metrics:
              - datname:
                  usage: "LABEL"
                  description: "Database name"
              - size_bytes:
                  usage: "GAUGE"
                  description: "Database size in bytes"
          
          # 表大小监控
          pg_table_size:
            query: "SELECT schemaname, tablename, pg_total_relation_size(schemaname||'.'||tablename) as size_bytes FROM pg_tables WHERE schemaname NOT IN ('information_schema', 'pg_catalog')"
            master: true
            metrics:
              - schemaname:
                  usage: "LABEL"
                  description: "Schema name"
              - tablename:
                  usage: "LABEL"
                  description: "Table name"
              - size_bytes:
                  usage: "GAUGE"
                  description: "Table size in bytes"
  
  alert_rules:
    prometheus_rules: |
      groups:
      - name: statefulset-alerts
        rules:
        - alert: StatefulSetReplicasMismatch
          expr: kube_statefulset_status_replicas != kube_statefulset_spec_replicas
          for: 5m
          labels:
            severity: critical
          annotations:
            summary: "StatefulSet replicas mismatch"
            description: "StatefulSet {{ $labels.statefulset }} has {{ $value }} replicas but should have {{ $labels.spec_replicas }}"
        
        - alert: StatefulSetUpdateBlocked
          expr: kube_statefulset_status_current_revision != kube_statefulset_status_update_revision
          for: 10m
          labels:
            severity: warning
          annotations:
            summary: "StatefulSet update is blocked"
            description: "StatefulSet {{ $labels.statefulset }} update has been blocked for more than 10 minutes"
        
        - alert: PostgreSQLDown
          expr: up{job="postgres-exporter"} == 0
          for: 1m
          labels:
            severity: critical
          annotations:
            summary: "PostgreSQL is down"
            description: "PostgreSQL instance {{ $labels.instance }} has been down for more than 1 minute"
        
        - alert: PostgreSQLReplicationLag
          expr: pg_replication_lag_bytes > 100*1024*1024  # 100MB
          for: 2m
          labels:
            severity: critical
          annotations:
            summary: "High PostgreSQL replication lag"
            description: "Replication lag is {{ $value | humanizeBytes }} for client {{ $labels.client_addr }}"
        
        - alert: PostgreSQLTooManyConnections
          expr: pg_stat_database_numbackends / pg_settings_max_connections * 100 > 80
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: "PostgreSQL too many connections"
            description: "PostgreSQL connection usage is {{ $value }}%"

📋 StatefulSet面试重点

基础概念类

  1. StatefulSet与Deployment的核心区别?

    • Pod身份稳定性
    • 网络标识管理
    • 存储绑定机制
    • 部署和扩展顺序
  2. StatefulSet的三个保证?

    • 稳定的网络标识
    • 稳定的持久化存储
    • 有序的部署和扩展
  3. Headless Service的作用?

    • DNS解析机制
    • Pod直接访问
    • 服务发现模式
    • 负载均衡策略

部署配置类

  1. 如何设计数据库StatefulSet?

    • 主从复制配置
    • 健康检查设计
    • 初始化容器使用
    • 资源限制策略
  2. VolumeClaimTemplate的工作原理?

    • 动态PVC创建
    • 存储绑定关系
    • 回收策略配置
    • 存储类选择
  3. StatefulSet滚动更新策略?

    • RollingUpdate配置
    • Partition参数作用
    • OnDelete更新模式
    • 更新回滚机制

运维实践类

  1. 如何安全地扩缩容StatefulSet?

    • 数据迁移策略
    • 集群重新配置
    • PVC处理方式
    • 服务可用性保证
  2. StatefulSet备份和恢复策略?

    • 快照备份机制
    • WAL归档策略
    • 点对点时间恢复
    • 跨集群灾难恢复
  3. 如何监控StatefulSet健康状态?

    • 关键监控指标
    • 告警规则设计
    • 性能基线建立
    • 故障预警机制

高级特性类

  1. StatefulSet的Pod管理策略?

    • OrderedReady vs Parallel
    • 启动依赖处理
    • 失败恢复机制
    • 数据一致性保证
  2. 如何处理StatefulSet的网络分区?

    • 脑裂检测机制
    • 自动故障转移
    • 数据同步恢复
    • 服务降级策略
  3. StatefulSet与Operator的关系?

    • Operator增强功能
    • 自动化运维能力
    • 状态管理复杂度
    • 选择决策因素

🔗 相关内容


StatefulSet是Kubernetes中部署有状态应用的核心工具,掌握其工作原理、配置方法和运维实践对于管理云原生数据平台至关重要。通过合理设计StatefulSet配置和完善的监控备份机制,可以构建稳定可靠的数据服务平台。

正在精进