2. Kubernetes 核心概念回顾:Pod、Deployment、StatefulSet、Service、ConfigMap、Secret 在数据库场景下的作用

在将数据库迁移至 Kubernetes 之前,必须深刻理解几个核心原语在数据库工作负载下的特殊行为。数据库是有状态、高敏感、对网络和存储有严格要求的应用,与无状态微服务有着本质区别。本节将逐一剖析这些资源对象,并聚焦于它们在数据库场景中的关键作用与配置陷阱。

2.1 Pod:数据库的最小运行单元

Pod 是 Kubernetes 中可调度的最小单元,通常包含一个或多个容器。在数据库场景下,强烈建议采用单容器模式,即一个 Pod 内只运行一个数据库主进程(如 mysqld、postgres、mongod),辅助进程(如备份 Agent、监控 Exporter)应作为 Sidecar 容器注入。

特性 无状态应用(Web) 有状态数据库(MySQL/PostgreSQL)
容器模式 多容器 Sidecar 常见 主进程单容器,Sidecar 仅用于日志/备份
存储 临时卷(emptyDir) 持久卷(PVC),需 ReadWriteOnce
生命周期 可随意重启、漂移 重启需优雅关闭,避免脑裂
资源预留 requests 可低于 limits requests 应等于 limits,避免 CPU 节流
健康检查 HTTP 探针 TCP 探针 + 自定义 SQL 探针
数据库 Pod 配置要点:
  • 必须设置 terminationGracePeriodSeconds 为 60-120 秒,确保数据库完成事务回滚和 WAL 刷新。
  • 使用 preStop 钩子执行 pg_ctl stop -m fast 或 mysqladmin shutdown,而非直接发送 SIGTERM。
  • 避免使用 hostNetwork,除非对网络延迟有极致要求(如 Redis)。

2.2 Deployment vs StatefulSet:有状态应用的抉择

Deployment 适用于无状态应用,所有 Pod 副本完全对等,可以随机替换。而数据库集群(主从、分片)要求稳定的网络标识和有序的启停顺序,因此 StatefulSet 是数据库在 Kubernetes 上的唯一正确选择。

Deployment vs StatefulSet 数据库适用性对比 Deployment(不推荐) Pod 名称随机:web-xxxxx 重启后 IP 和名称均变化 滚动更新:同时替换所有 Pod 可能导致主从切换混乱 PVC 模板不固定 数据卷与 Pod 生命周期解耦困难 ⚠ 仅适合只读缓存或临时分析库 StatefulSet(推荐) Pod 名称固定:mysql-0, mysql-1 重启后名称和 DNS 不变 有序启停:从 N-1 到 0 依次操作 保证主节点最后关闭 每个 Pod 绑定独立 PVC 数据卷随 Pod 持久保留 ✅ 生产数据库唯一选择
StatefulSet 在数据库场景下的关键配置:
  • serviceName:必须指向一个 Headless Service,用于稳定的 DNS 解析(如 mysql-0.mysql-svc.namespace.svc.cluster.local)。
  • volumeClaimTemplates:自动为每个副本创建独立的 PVC,存储类应选择 WaitForFirstConsumer 以绑定本地节点。
  • podManagementPolicy:数据库主从场景建议使用 OrderedReady(默认),确保从节点先就绪,主节点最后升级。

2.3 Service:数据库流量的稳定入口

数据库客户端需要稳定的网络端点,而 Pod 的 IP 会随重启变化。Service 提供了抽象层。在数据库场景下,两种 Service 类型至关重要:

Service 类型 ClusterIP Headless Service
DNS 解析 返回 VIP,负载均衡到所有 Pod 返回所有 Pod IP 列表(A/AAAA 记录)
适用场景 读写分离代理、应用层连接池 StatefulSet 节点间通信、主从复制
数据库用例 应用通过 ClusterIP 访问数据库主服务 从节点通过 mysql-0.mysql-svc 找到主节点
注意事项 避免使用 externalTrafficPolicy: Local 导致连接分布不均 客户端需自行处理节点选择逻辑
数据库 Service 配置陷阱:

不要将数据库 Service 的 sessionAffinity 设置为 ClientIP,除非你明确知道自己在做什么。这会导致连接长时间绑定到同一个 Pod,在滚动更新或故障转移时造成连接中断。数据库连接池本身已经具备重连机制,无需 Kubernetes 层面的会话保持。

2.4 ConfigMap 与 Secret:配置与凭证的分离

数据库配置(my.cnf、postgresql.conf)和敏感凭证(密码、TLS 证书)必须与容器镜像解耦。Kubernetes 提供了 ConfigMap 和 Secret 来实现配置与镜像的分离。

ConfigMap 与 Secret 在数据库 Pod 中的挂载方式 ConfigMap my.cnf max_connections=200 innodb_buffer_pool_size=8G Secret MYSQL_ROOT_PASSWORD TLS 证书(base64) 复制用户密码 数据库 Pod /etc/config/my.cnf(ConfigMap) /etc/secret/password(Secret) ⚠ 数据库密码、复制账号、TLS 私钥必须使用 Secret,禁止明文写入 ConfigMap ConfigMap 仅存放非敏感配置项,如缓冲池大小、连接数限制、日志级别
数据库配置管理最佳实践:
  • ConfigMap 不可变:设置 immutable: true 可避免配置被意外修改,同时减轻 API Server 压力。
  • Secret 自动轮换:使用 reload 注解或外部工具(如 External Secrets Operator)实现密码自动轮换,无需重启 Pod。
  • 挂载方式:优先使用 subPath 挂载单个文件,而非整个卷,避免覆盖容器内的其他配置文件。
  • 大小限制:ConfigMap 和 Secret 的单个条目大小限制为 1MB,数据库配置文件通常远小于此值,但需注意 TLS 证书链可能较大。

2.5 核心概念在数据库迁移中的协同关系

在数据库迁移项目中,上述资源并非孤立使用,而是形成一个完整的部署拓扑。以下是一个典型 MySQL 主从集群在 Kubernetes 上的资源依赖关系:

资源类型 实例名称 作用 数据库场景关键属性
StatefulSet mysql-cluster 管理 3 个 MySQL Pod(1 主 2 从) serviceName: mysql-hl; volumeClaimTemplates: 100Gi SSD
Headless Service mysql-hl 提供稳定 DNS:mysql-0.mysql-hl.svc.cluster.local clusterIP: None; publishNotReadyAddresses: true
ClusterIP Service mysql-rw 应用写入流量入口,指向主节点 selector: role=master; 端口 3306
ClusterIP Service mysql-ro 应用只读流量入口,指向从节点 selector: role=replica; 端口 3306
ConfigMap mysql-config 存储 my.cnf 公共配置 immutable: true; data: max_connections, binlog 配置
Secret mysql-secret 存储 root 密码、复制账号、TLS 证书 type: Opaque; 使用 base64 编码
PersistentVolumeClaim data-mysql-cluster-0 每个 Pod 独立的数据卷 storageClassName: local-ssd; accessModes: ReadWriteOnce
迁移前的资源清单检查:
  1. 是否所有数据库 Pod 都使用 StatefulSet 而非 Deployment?
  2. Headless Service 是否已配置 publishNotReadyAddresses: true 以支持滚动更新期间的节点发现?
  3. ConfigMap 中是否误放了数据库密码或证书私钥?
  4. Secret 是否设置了 stringData 字段以便于人类可读的 YAML 管理?
  5. 每个 StatefulSet 副本是否绑定了独立的 PVC,且存储类支持所需的 IOPS?