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 上限,超过后性能会急剧下降。我建议用 io1 或 io2,虽然贵,但 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 时,有几个关键点要注意:
- IPIP 模式 vs 直接路由:如果节点在同一二层网络,用直接路由模式,性能最好。否则用 IPIP 或 VXLAN。
- MTU 设置:默认 1500,但如果底层网络有隧道,要调小。我遇到过因为 MTU 不一致导致网络丢包的问题,排查了很久。
- 网络策略:数据库 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 实例,流程是这样的:
- 用户通过 API 提交请求,指定规格(如 4C8G、100G 云盘)。
- Operator 收到请求,创建 StatefulSet 和对应的 PVC。
- K8s 调度器将 Pod 调度到合适的节点上。
- Calico 为 Pod 分配 IP,并配置网络策略。
- 数据库启动,自动初始化数据目录。
- 用户通过 Service 访问数据库。
整个过程全自动化,不需要人工干预。这就是 PaaS 的魅力。
避坑指南:我曾经在项目中遇到一个问题:数据库 Pod 重启后,IP 变了,导致应用连接失败。后来我改用 Headless Service + StatefulSet,通过 Pod 名称访问,问题解决。记住,数据库的访问方式一定要稳定。
好了,这一章就聊到这。技术选型没有银弹,关键是根据场景做权衡。下一章咱们聊聊数据库 Operator 的设计,那才是真正的核心。