数据库/中间件部署

数据库和中间件是有状态应用的典型代表,部署这些应用需要特殊考虑数据持久性、网络标识和启动顺序等问题。Kubernetes 提供了 StatefulSet、PersistentVolume 等资源来支持有状态应用的部署。

有状态应用架构
graph LR
    API[(API Server)] --> MYSQL1
    API --> PG1
    API --> REDIS1
    API --> KAFKA1
    API --> RMQ1
    
    MYSQL1 --> PV1
    MYSQL2 --> PV2
    MYSQL3 --> PV3
    PG1 --> PGP1
    PG2 --> PGP2
    REDIS1 --> REDISPV1
    REDIS2 --> REDISPV2
    REDIS3 --> REDISPV3
    RMQ1 --> RMQPV1
    RMQ2 --> RMQPV2
    RMQ3 --> RMQPV3

    subgraph "K8s Cluster"
        subgraph "Control Plane"
            API
            ETCD[(etcd)]
        end
        
        subgraph "Databases and Middleware"
            subgraph "MySQL Cluster"
                MYSQL1("MySQL-0")
                MYSQL2("MySQL-1")
                MYSQL3("MySQL-2")
            end
            
            subgraph "PostgreSQL Cluster"
                PG1("PostgreSQL-0")
                PG2("PostgreSQL-1")
            end
            
            subgraph "Redis Cluster"
                REDIS1("Redis-0")
                REDIS2("Redis-1")
                REDIS3("Redis-2")
            end
            
            subgraph "Kafka Cluster"
                KAFKA1("Kafka-0")
                KAFKA2("Kafka-1")
                KAFKA3("Kafka-2")
            end
            
            subgraph "RabbitMQ Cluster"
                RMQ1("RabbitMQ-0")
                RMQ2("RabbitMQ-1")
                RMQ3("RabbitMQ-2")
            end
        end
        
        subgraph "Storage Layer"
            PV1("PV-MySQL-0")
            PV2("PV-MySQL-1")
            PV3("PV-MySQL-2")
            PGP1("PV-PostgreSQL-0")
            PGP2("PV-PostgreSQL-1")
            REDISPV1("PV-Redis-0")
            REDISPV2("PV-Redis-1")
            REDISPV3("PV-Redis-2")
            RMQPV1("PV-RabbitMQ-0")
            RMQPV2("PV-RabbitMQ-1")
            RMQPV3("PV-RabbitMQ-2")
        end
    end
                                
数据库/中间件部署详解

关系型数据库 (MySQL/PostgreSQL)

关系型数据库需要持久化存储和稳定的网络标识,适合使用 StatefulSet 部署。

  • 使用 PersistentVolume 保证数据持久性
  • 通过 Headless Service 提供稳定的网络标识
  • 配置主从复制或集群模式实现高可用
  • 使用 Secret 管理数据库凭证

缓存/消息中间件 (Redis/Kafka/RabbitMQ)

中间件通常需要特定的配置和集群模式,也适合使用 StatefulSet 部署。

  • Redis: 配置主从复制或集群模式
  • Kafka: 保证分区和副本的持久性
  • RabbitMQ: 设置镜像队列确保高可用
  • 所有中间件都需要持久化存储
数据库与中间件对比
应用类型数据特点部署要求典型用途
MySQL关系型数据,ACID特性主从复制,高可用事务处理,数据存储
PostgreSQL关系型数据,扩展性强主从复制,读写分离复杂查询,数据分析
Redis键值存储,内存为主主从复制,集群模式缓存,会话存储
Kafka分布式消息队列分区复制,持久化实时数据流处理
RabbitMQ消息队列,多种协议镜像队列,集群异步消息传递
MySQL 部署示例
# MySQL StatefulSet 示例
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  serviceName: mysql
  replicas: 3
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:8.0
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: password
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
  volumeClaimTemplates:
  - metadata:
      name: mysql-persistent-storage
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 20Gi
# MySQL Headless Service
apiVersion: v1
kind: Service
metadata:
  name: mysql
  labels:
    app: mysql
spec:
  ports:
  - port: 3306
    name: mysql
  clusterIP: None
  selector:
    app: mysql
PostgreSQL 部署示例
# PostgreSQL StatefulSet 示例
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgresql
spec:
  serviceName: postgresql
  replicas: 2
  selector:
    matchLabels:
      app: postgresql
  template:
    metadata:
      labels:
        app: postgresql
    spec:
      containers:
      - name: postgresql
        image: postgres:13
        env:
        - name: POSTGRES_DB
          value: "mydb"
        - name: POSTGRES_USER
          valueFrom:
            secretKeyRef:
              name: postgresql-secret
              key: user
        - name: POSTGRES_PASSWORD
          valueFrom:
            secretKeyRef:
              name: postgresql-secret
              key: password
        ports:
        - containerPort: 5432
          name: postgresql
        volumeMounts:
        - name: postgresql-persistent-storage
          mountPath: /var/lib/postgresql/data
  volumeClaimTemplates:
  - metadata:
      name: postgresql-persistent-storage
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 30Gi
Redis 部署示例
# Redis 集群 StatefulSet 示例
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis
spec:
  serviceName: redis
  replicas: 6
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: redis:6.2-alpine
        command:
        - redis-server
        - /etc/redis/redis.conf
        ports:
        - containerPort: 6379
          name: redis
        volumeMounts:
        - name: redis-config
          mountPath: /etc/redis
        - name: redis-persistent-storage
          mountPath: /data
    volumes:
    - name: redis-config
      configMap:
        name: redis-config
  volumeClaimTemplates:
  - metadata:
      name: redis-persistent-storage
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 10Gi
Kafka 部署示例
# Kafka StatefulSet 示例
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: kafka
spec:
  serviceName: kafka
  replicas: 3
  selector:
    matchLabels:
      app: kafka
  template:
    metadata:
      labels:
        app: kafka
    spec:
      containers:
      - name: kafka
        image: confluentinc/cp-kafka:latest
        ports:
        - containerPort: 9092
          name: kafka
        env:
        - name: KAFKA_BROKER_ID
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: KAFKA_ZOOKEEPER_CONNECT
          value: "zookeeper:2181"
        - name: KAFKA_ADVERTISED_LISTENERS
          value: "PLAINTEXT://$(HOSTNAME).kafka:9092"
        volumeMounts:
        - name: kafka-persistent-storage
          mountPath: /var/lib/kafka/data
  volumeClaimTemplates:
  - metadata:
      name: kafka-persistent-storage
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 50Gi
数据库/中间件部署策略

MySQL 部署策略

MySQL 作为关系型数据库,需要考虑数据一致性、高可用性、备份恢复等关键问题。

  • 使用 StatefulSet 确保 Pod 有序部署和稳定的网络标识
  • 配置主从复制或组复制实现高可用
  • 使用 PersistentVolume 确保数据持久存储
  • 配置合适的资源限制和请求
  • 设置定期备份和监控告警

PostgreSQL 部署策略

PostgreSQL 是功能强大的开源关系型数据库,支持复杂查询和扩展功能。

  • 利用 StatefulSet 的特性部署主从架构
  • 配置流复制实现数据同步
  • 使用专用存储确保 I/O 性能
  • 配置连接池优化性能
  • 定期执行维护任务(VACUUM, ANALYZE 等)

Redis 部署策略

Redis 作为高性能缓存和数据结构存储,支持多种部署模式。

  • 主从模式:实现读写分离和高可用
  • 哨兵模式:自动故障转移
  • 集群模式:数据分片和高可用
  • 持久化策略:RDB 和 AOF 配置
  • 内存管理:配置合适的 maxmemory 和淘汰策略

Kafka & RabbitMQ 部署策略

消息中间件需要高吞吐量和可靠的消息传递。

  • Kafka: 配置副本因子确保数据可靠性
  • Kafka: 调整分区数量平衡性能和吞吐量
  • RabbitMQ: 配置镜像队列确保高可用
  • 持久化消息存储防止数据丢失
  • 监控消费者滞后(Consumer Lag)
有状态应用部署最佳实践

存储最佳实践

  • 使用 PersistentVolume 和 PersistentVolumeClaim
  • 选择合适的存储类型和访问模式
  • 配置存储类 (StorageClass) 以实现动态供应
  • 定期备份和恢复策略

网络最佳实践

  • 使用 Headless Service 保持稳定的网络标识
  • 配置正确的 Pod 亲和性和反亲和性
  • 设置合理的健康检查探针
  • 使用 NetworkPolicy 控制访问
常见部署问题与解决方案
问题原因解决方案
Pod 无法启动PVC 绑定失败检查 StorageClass 是否可用,PV 是否足够
数据丢失存储卷配置错误使用 volumeClaimTemplates 确保数据持久化
网络连接失败Headless Service 配置错误确保 Service 配置正确,Pod 间网络通畅
启动顺序问题缺少启动顺序控制使用 init containers 或自定义启动脚本
性能问题存储性能不足使用高性能存储,优化 I/O 配置
数据库/中间件运维要点

监控与告警

  • 数据库性能指标:连接数、查询延迟、QPS、TPS
  • 存储空间使用率:磁盘空间、I/O 延迟
  • 缓存命中率:Redis 命中率、内存使用
  • 消息队列:队列长度、消费者滞后、吞吐量
  • 集群健康状态:节点可用性、副本同步状态

备份与恢复

  • 定期执行逻辑备份(mysqldump, pg_dump)
  • 配置物理备份(WAL 归档、快照备份)
  • 测试恢复流程确保备份有效性
  • 配置备份保留策略
  • 考虑跨区域备份以防灾难

安全配置

应用类型安全配置要点实施方法
MySQL/PostgreSQL 认证、授权、加密传输 使用 Secret 管理凭证,启用 SSL/TLS,配置角色权限
Redis 访问控制、网络隔离 设置密码认证,使用 NetworkPolicy 限制访问
Kafka SSL/TLS、SASL 认证 配置加密通信,启用用户认证和授权
RabbitMQ 用户管理、TLS 加密 创建专用用户,配置 SSL 证书,限制权限
部署验证
# 检查 StatefulSet 状态
kubectl get statefulsets
# 查看 Pod 状态
kubectl get pods -l app=mysql
# 检查持久化卷
kubectl get pvc -l app=mysql
# 测试数据库连接
kubectl run -it --rm debug --image=mysql:8.0 --restart=Never -- mysql -h mysql-0.mysql -uroot -p