3、技术选型与架构设计:容器化(K8s)、存储选型(本地盘/云盘)、网络方案(CNI/Calico)

好,咱们进入第三章。这一章我打算聊聊技术选型和架构设计。说白了,就是决定你的 PaaS 数据库服务到底用什么“料”来搭。

你想想看,一个数据库上云,最核心的三件事是什么?跑在哪、存哪、怎么连。对应过来就是:容器编排(K8s)、存储选型(本地盘 vs 云盘)、网络方案(CNI 插件)。这三样选不好,后面全是坑。我在项目中见过太多因为选型失误导致重构的案例,嗯,咱们今天就把这事聊透。

3.1 容器化:为什么非 K8s 不可?

先说说容器化。有人问我:“数据库这么重的状态应用,真适合跑在 K8s 上吗?”

我的回答是:适合,但有前提

K8s 带来的好处很明显:

  • 资源利用率高:多个数据库实例可以混部,不像物理机那样“一台一库”浪费资源。
  • 运维标准化:部署、升级、扩缩容全走声明式 API,不用再 SSH 到机器上敲命令。
  • 自愈能力:节点挂了,Pod 自动漂移,配合 Operator 能实现秒级恢复。

但要注意,数据库是“有状态”的。K8s 原生对无状态应用友好,对有状态应用需要额外设计。我个人习惯用 StatefulSet 而不是 Deployment,原因很简单:StatefulSet 保证 Pod 的稳定网络标识和有序启停,这对数据库主从切换至关重要。

核心原则:数据库容器化,Operator + StatefulSet 是标配。别图省事用 Deployment,你会后悔的。

举个例子,我做过一个 MySQL 集群,用的就是 K8s + MySQL Operator。每个实例对应一个 StatefulSet,PVC 自动绑定存储。扩缩容时,Operator 自动处理数据重分布。整个过程不需要人工介入。

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql-0
spec:
  serviceName: mysql
  replicas: 3
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:8.0
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 100Gi

这段 YAML 看着简单,但背后涉及很多细节。比如 volumeClaimTemplates 会自动为每个 Pod 创建独立的 PVC,保证数据不混在一起。

小技巧:StatefulSet 的 Pod 名称是固定的(如 mysql-0, mysql-1),配合 Headless Service,你可以直接用 Pod 名做 DNS 解析。这在配置主从复制时特别方便。

3.2 存储选型:本地盘 vs 云盘,怎么选?

存储是数据库的命根子。选错了,性能上不去,数据还可能丢。我见过不少团队在存储选型上栽跟头。

咱们先看两种主流方案:

维度 本地盘(Local SSD) 云盘(如 AWS EBS、阿里云 ESSD)
延迟 极低(微秒级) 较低(毫秒级)
吞吐 高,受限于单机 高,可弹性扩展
持久性 低,节点故障数据丢失 高,多副本冗余
弹性 差,不能动态扩容 好,可在线扩容
成本 较低 较高
适用场景 高并发、低延迟、可接受数据丢失 高可靠、弹性扩展、生产环境

我个人习惯这样选:

  • 核心生产库:用云盘。虽然贵点,但数据安全第一。你想想看,万一节点挂了,本地盘的数据就全没了,那得多崩溃。
  • 缓存、日志、临时库:用本地盘。性能好,成本低,丢了也不心疼。
  • 混合方案:有些团队用本地盘做 WAL 日志,云盘做数据文件。这样兼顾了性能和可靠性。但复杂度高,不建议新手尝试。

避坑指南:我曾经在项目中用过本地盘跑 MySQL,结果 K8s 节点 OOM 后,Pod 被调度到另一台机器,但数据还在原节点上。最后只能手动恢复。所以,用本地盘一定要配合 Local Persistent Volume节点亲和性,确保 Pod 不会漂移到没有数据的节点上。

另外,云盘也有坑。比如 AWS EBS 的 gp2 类型有 IOPS 上限,超过后性能会急剧下降。我建议用 io1io2,虽然贵,但 IOPS 可以独立配置,适合数据库这种 IO 密集型应用。

3.3 网络方案:CNI 与 Calico 的实战选择

网络是数据库的血管。选不好,延迟高、吞吐低,甚至出现网络分区。

K8s 的网络模型要求:每个 Pod 都有独立的 IP,Pod 之间可以直接通信。这个靠 CNI 插件实现。市面上主流的 CNI 有 Flannel、Calico、Cilium、Weave 等。

对于数据库场景,我首推 Calico。为什么?

  • 性能好:基于 BGP 路由,不经过 Overlay 封装,延迟低。
  • 网络策略强大:支持精细化的 ACL,可以控制哪些 Pod 能访问数据库。
  • 支持 IPIP 和 VXLAN:如果底层网络不支持 BGP,可以用 Overlay 模式。

Flannel 虽然简单,但性能不如 Calico,而且不支持网络策略。Cilium 基于 eBPF,性能更好,但学习曲线陡峭,适合有经验的团队。

我的建议:中小规模集群用 Calico,大规模集群(1000+ 节点)考虑 Cilium。别为了省事用 Flannel,数据库场景下性能差距很明显。

配置 Calico 时,有几个关键点要注意:

  1. IPIP 模式 vs 直接路由:如果节点在同一二层网络,用直接路由模式,性能最好。否则用 IPIP 或 VXLAN。
  2. MTU 设置:默认 1500,但如果底层网络有隧道,要调小。我遇到过因为 MTU 不一致导致网络丢包的问题,排查了很久。
  3. 网络策略:数据库 Pod 只允许特定应用访问,其他 Pod 一律拒绝。用 Calico 的 NetworkPolicy 实现。
apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
  name: database-access
  namespace: production
spec:
  selector: app == 'mysql'
  ingress:
  - action: Allow
    protocol: TCP
    source:
      selector: app == 'web-server'
    destination:
      ports:
      - 3306
  - action: Deny
    protocol: TCP
    source: {}
    destination:
      ports:
      - 3306

这段策略的意思是:只允许 web-server 标签的 Pod 访问 MySQL 的 3306 端口,其他一律拒绝。嗯,安全第一。

小技巧:数据库 Pod 建议用 hostNetwork 模式,直接使用宿主机网络。这样可以避免 Overlay 网络的性能损耗。但要注意端口冲突问题。

3.4 架构设计:三者的协同

好,咱们把三样东西串起来。一个典型的 PaaS 数据库服务架构是这样的:

  • K8s 集群:管理所有数据库实例。每个实例是一个 StatefulSet。
  • 存储:核心库用云盘(如 ESSD),缓存库用本地盘(如 Local SSD)。通过 StorageClass 动态分配。
  • 网络:Calico 提供 Pod 网络,配合 NetworkPolicy 做安全隔离。
  • Operator:负责数据库的创建、备份、恢复、扩缩容等自动化操作。

举个例子,用户申请一个 MySQL 实例,流程是这样的:

  1. 用户通过 API 提交请求,指定规格(如 4C8G、100G 云盘)。
  2. Operator 收到请求,创建 StatefulSet 和对应的 PVC。
  3. K8s 调度器将 Pod 调度到合适的节点上。
  4. Calico 为 Pod 分配 IP,并配置网络策略。
  5. 数据库启动,自动初始化数据目录。
  6. 用户通过 Service 访问数据库。

整个过程全自动化,不需要人工干预。这就是 PaaS 的魅力。

避坑指南:我曾经在项目中遇到一个问题:数据库 Pod 重启后,IP 变了,导致应用连接失败。后来我改用 Headless Service + StatefulSet,通过 Pod 名称访问,问题解决。记住,数据库的访问方式一定要稳定。

好了,这一章就聊到这。技术选型没有银弹,关键是根据场景做权衡。下一章咱们聊聊数据库 Operator 的设计,那才是真正的核心。