饮墨

子安饮墨馀三斗,留与卿儿作赋来

Rook-Ceph:Kubernetes 原生分布式存储落地实战

痛点

Kubernetes 集群跑有状态服务(数据库、消息队列、对象存储)时,存储始终是最大的痛点:

  • 云厂商 EBS/EFS 成本高:大规模集群每月存储账单轻松破万美元,且 IOPS 按量计费让预算不可控
  • hostPath/local PV 无高可用:节点宕机数据丢失,运维半夜被 PagerDuty 叫醒
  • 外置 Ceph 集群运维复杂:独立于 K8s 的 Ceph 集群需要单独的运维团队,升级和扩容都是噩梦

Rook 把 Ceph 变成 Kubernetes 的一等公民——用 Operator 模式管理分布式存储,让存储和计算在同一个控制平面内声明式管理。

方案

Rook 是 CNCF 毕业项目,作为 Kubernetes 存储编排层,将 Ceph 的部署、扩容、升级、自愈全部 CRD 化。核心架构:

┌─────────────────────────────────────────┐
│           Kubernetes Cluster            │
│                                         │
│  ┌───────────┐    ┌──────────────────┐  │
│  │  Rook     │    │   Ceph Cluster   │  │
│  │  Operator │───▶│  MON / OSD / MDS │  │
│  └───────────┘    └──────────────────┘  │
│         │                    │           │
│         ▼                    ▼           │
│  ┌───────────┐    ┌──────────────────┐  │
│  │ CRDs      │    │  CSI Driver      │  │
│  │ (Cluster, │    │  (RBD/CephFS/    │  │
│  │  Pool,    │    │   Object Store)  │  │
│  │  Object)  │    └──────────────────┘  │
│  └───────────┘                          │
└─────────────────────────────────────────┘

适用场景: - 裸金属 / 自建 K8s 集群需要高可用块存储 - 需要同时提供 Block(RBD)、File(CephFS)、Object(S3 兼容) 三种存储 - 希望存储运维纳入 GitOps 流程

实操步骤

第一步:部署 Rook Operator

# 添加 Helm 仓库
helm repo add rook-release https://charts.rook.io/release
helm repo update

# 部署 Operator(建议独立 namespace)
helm install --create-namespace --namespace rook-ceph \
  rook-ceph rook-release/rook-ceph \
  --set csi.enableRbdDriver=true \
  --set csi.enableCephfsDriver=true \
  --set monitoring.enabled=true

# 确认 Operator Running
kubectl -n rook-ceph get pods -l app=rook-ceph-operator

第二步:创建 Ceph 集群

# cluster.yaml
apiVersion: ceph.rook.io/v1
kind: CephCluster
metadata:
  name: rook-ceph
  namespace: rook-ceph
spec:
  cephVersion:
    image: quay.io/ceph/ceph:v18.2  # Reef LTS
  dataDirHostPath: /var/lib/rook
  mon:
    count: 3
    allowMultiplePerNode: false
  mgr:
    count: 2
    modules:
      - name: prometheus
        enabled: true
  storage:
    useAllNodes: true
    useAllDevices: false
    devices:
      - name: "nvme1n1"   # 指定裸盘,不要用系统盘
      - name: "nvme2n1"
    config:
      osdsPerDevice: "1"
  resources:
    osd:
      requests:
        cpu: "2"
        memory: "4Gi"
      limits:
        cpu: "4"
        memory: "8Gi"
  network:
    provider: host  # 生产环境推荐 host 网络,减少网络开销
kubectl apply -f cluster.yaml

# 等待集群 Ready(约 3-5 分钟)
kubectl -n rook-ceph get cephcluster -w

第三步:创建存储池 + StorageClass

# pool.yaml
apiVersion: ceph.rook.io/v1
kind: CephBlockPool
metadata:
  name: replicapool
  namespace: rook-ceph
spec:
  failureDomain: host       # 按 host 级别做副本分散
  replicated:
    size: 3                  # 三副本
    requireSafeReplicaSize: true
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: rook-ceph-block
provisioner: rook-ceph.rbd.csi.ceph.com
parameters:
  clusterID: rook-ceph
  pool: replicapool
  imageFormat: "2"
  imageFeatures: layering,fast-diff,object-map,deep-flatten
  csi.storage.k8s.io/provisioner-secret-name: rook-csi-rbd-provisioner
  csi.storage.k8s.io/provisioner-secret-namespace: rook-ceph
  csi.storage.k8s.io/node-stage-secret-name: rook-csi-rbd-node
  csi.storage.k8s.io/node-stage-secret-namespace: rook-ceph
  csi.storage.k8s.io/fstype: ext4
reclaimPolicy: Retain
allowVolumeExpansion: true
kubectl apply -f pool.yaml

# 验证:创建一个测试 PVC
kubectl apply -f - <<EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: test-rbd-pvc
spec:
  accessModes: ["ReadWriteOnce"]
  storageClassName: rook-ceph-block
  resources:
    requests:
      storage: 10Gi
EOF

kubectl get pvc test-rbd-pvc  # 应为 Bound 状态

第四步:监控与运维工具箱

# 部署 Ceph Toolbox(集群内调试)
kubectl apply -f https://raw.githubusercontent.com/rook/rook/release-1.14/deploy/examples/toolbox.yaml

# 进入 Toolbox 检查集群状态
kubectl -n rook-ceph exec -it deploy/rook-ceph-tools -- ceph status
kubectl -n rook-ceph exec -it deploy/rook-ceph-tools -- ceph osd tree
kubectl -n rook-ceph exec -it deploy/rook-ceph-tools -- ceph df

# Prometheus 指标(Operator 自动暴露 ServiceMonitor)
kubectl -n rook-ceph get servicemonitor

避坑指南

1. 裸盘选择:绝不使用已分区/已挂载的磁盘

Rook OSD 需要未格式化的裸设备。如果磁盘上有分区表或文件系统,OSD 创建会静默失败:

# 检查磁盘是否干净
lsblk -f /dev/nvme1n1
# 输出中 FSTYPE 应为空

# 清理磁盘(危险操作,确认无数据)
wipefs -a /dev/nvme1n1
sgdisk --zap-all /dev/nvme1n1

2. Mon 数量必须为奇数,且分散到不同节点

Mon 是 Ceph 的大脑,使用 Paxos 协议。偶数个 Mon 会导致脑裂风险。生产环境固定 3 或 5 个,并用 allowMultiplePerNode: false 强制分散:

# 确保集群至少 3 个节点有 mon 标签
kubectl label node node1 node2 node3 ceph-mon=enabled

3. 网络带宽是存储性能瓶颈

Ceph 副本复制走网络,10Gbps 是最低要求。25Gbps+ 生产推荐。部署前务必用 iperf3 验证节点间带宽:

# 节点 A
iperf3 -s

# 节点 B
iperf3 -c <node-A-IP> -t 30 -P 4
# 期望:单流 > 9Gbps(10G网卡)或 > 20Gbps(25G网卡)

如果用 Pod 网络(Calico/Cilium),务必开启 eBPF datapath 并设置 MTU 9000(Jumbo Frame)。

总结

维度 Rook-Ceph 云厂商 EBS 外置 Ceph
运维复杂度 中(CRD 声明式)
成本(100TB) ~$3k/月(硬件摊销) ~$10k+/月 ~$3k/月
高可用 三副本 + 自动恢复 跨 AZ 依赖手动配置
存储类型 Block/File/Object Block/File Block/File/Object
GitOps 友好 ✅ 原生 CRD

落地建议

  1. 小规模试水:先在 3 节点测试集群验证,每节点 1 块 NVMe 即可
  2. 生产配置:至少 3 节点 × 2 NVMe,网络 25Gbps+,Mon/OSD 分开调度
  3. 监控先行:部署前先确保 Prometheus + Grafana 就绪,导入 Ceph Dashboard
  4. 备份兜底:Rook-Ceph 解决的是高可用,不是备份——关键数据仍需 Velero 定期快照到对象存储

存储是 Kubernetes 有状态负载的地基,Rook-Ceph 让这个地基可以用 kubectl apply 管理,这才是云原生存储该有的样子。

您还没有登录,请登录后发表评论。