如何在香港服务器的 Ubuntu 环境中,把 CephFS 和 Kubernetes 绑在一起,撑起一套分布式仿真平台

那天是周五的凌晨两点,我裹着冲锋衣站在香港荃湾机房6楼的 42U 机柜前,手指按在 BMC 的电源键上,耳边是 PDU 的继电器轻轻“嗒”了一下和风扇高转的呼啸。我们要在 48 小时内把一套新的仿真平台跑起来:Kubernetes 负责编排,CephFS 负责共享并行 I/O,目标是让 CFD 与多体动力学仿真在 50 台节点上稳定地跑通、不中断。这个记录,写给同样在机房里与时间赛跑的人。
1. 目标与场景
目标:在 Ubuntu 环境上部署一套 Kubernetes + CephFS 的分布式仿真平台,支持 MPI 等并行工作负载,提供 RWX(多节点读写) 的共享文件系统与高元数据吞吐。
选择:CephFS 作为共享存储(可横向扩展、强一致、MDS 支持元数据水平扩展),Kubernetes 通过 Ceph-CSI 动态供给卷,研发只关心 PVC 和路径。
约束:香港本地机房,25GbE 网络,仿真 I/O 偏元数据密集(小文件多、目录层级深),并需要跨组协同与快照留痕。
2. 拓扑与硬件清单
2.1 逻辑拓扑(文字版)
- Ceph 集群(裸金属):3 台 MON/MGR/MDS 复用节点 + 12 台 OSD 节点
- Kubernetes 集群:3 控 9 工(control-plane 3 台、worker 9 台),与 Ceph 通过专网互通
网络:
- bond0.10(Ceph public 网):10.10.10.0/24
- bond0.20(Ceph cluster 网):10.10.20.0/24
- bond0.30(K8s Pod/Service 网,CNI = Cilium,MTU 发现模式 + Jumbo 9000)
- 时钟:Chrony 本地源 + 上游 NTP 双路冗余
2.2 版本组合(可直接复刻)
| 组件 | 版本 | 说明 |
|---|---|---|
| OS | Ubuntu 22.04.4 LTS | 全节点统一 |
| Kubernetes | v1.28.x | kubeadm + containerd |
| CNI | Cilium v1.14+ | eBPF,MTU 自动探测 |
| Ceph | Reef 18.2.x | cephadm 部署 |
| Ceph-CSI | v3.9+ | cephfs.csi.ceph.com |
| MPI | OpenMPI 4.1+ / MPI Operator | CFD/FEA 作业 |
2.3 硬件与分区
| 用途 | 型号/CPU | 内存 | 系统盘 | 数据盘 | 网卡 |
|---|---|---|---|---|---|
| MON/MGR/MDS*3 | 2×Intel Xeon Silver 4316 | 256GB | 2×480GB SATA(RAID1) | 2×1.92TB NVMe(DB/WAL)+ 4×12TB HDD(OSD) | 2×25GbE(Bond LACP) |
| OSD*12 | AMD EPYC 7313P | 256GB | 480GB SATA | 2×1.92TB NVMe(DB/WAL)+ 10×14TB HDD(OSD) | 2×25GbE(Bond LACP) |
| 控制平面*3 | Intel Xeon 4310 | 128GB | 480GB SATA | - | 2×25GbE |
| 工作节点*9 | AMD EPYC 7313P | 256GB | 480GB SATA | 可选 1×1.92TB NVMe(本地缓存) | 2×25GbE |
关键点:OSD 用 HDD,DB/WAL 落 NVMe,性价比与延迟兼顾。MDS 节点给足内存。
3. 网络与 IP 规划(示例)
| 角色 | 主机名 | Public IP (bond0.10) | Cluster IP (bond0.20) |
|---|---|---|---|
| mon-mgr-mds01 | ceph-ctrl01 | 10.10.10.11 | 10.10.20.11 |
| mon-mgr-mds02 | ceph-ctrl02 | 10.10.10.12 | 10.10.20.12 |
| mon-mgr-mds03 | ceph-ctrl03 | 10.10.10.13 | 10.10.20.13 |
| osd01…osd12 | ceph-osd01…12 | 10.10.10.21…32 | 10.10.20.21…32 |
Linux 网络:/etc/netplan 里做 bond0 + vlan,开启 Jumbo 9000,交换机侧 LACP;MTU 不一致是最常见“坑1”(后面详述)。
4. 系统与内核调优(必做)
/etc/sysctl.d/99-hpc.conf:
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.core.netdev_max_backlog = 250000
net.ipv4.tcp_rmem = 4096 87380 134217728
net.ipv4.tcp_wmem = 4096 65536 134217728
net.ipv4.tcp_congestion_control = bbr
net.ipv4.tcp_mtu_probing = 1
fs.aio-max-nr = 1048576
fs.inotify.max_user_watches = 1048576
vm.swappiness = 10
vm.dirty_ratio = 10
vm.dirty_background_ratio = 5
CPU 省电策略调为 performance:cpupower frequency-set -g performance
关闭 BIOS C-State 深度(MDS 对延迟很敏感)
NIC 中断亲和与 RSS:irqbalance on,必要时手动绑核;ethtool -G 调整队列缓冲(视 NIC)
5. 部署 Kubernetes(kubeadm + containerd)
5.1 containerd
apt-get update && apt-get install -y containerd
containerd config default > /etc/containerd/config.toml
# SystemdCgroup = true
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml
systemctl enable --now containerd
5.2 kubeadm 初始化(控制面)
apt-get install -y apt-transport-https ca-certificates curl
curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF
apt-get update && apt-get install -y kubelet kubeadm kubectl
kubeadm init --kubernetes-version v1.28.6 \
--pod-network-cidr=10.244.0.0/16
mkdir -p $HOME/.kube && cp /etc/kubernetes/admin.conf $HOME/.kube/config
CNI 我选 Cilium(eBPF):
kubectl create -f https://raw.githubusercontent.com/cilium/cilium/v1.14/install/kubernetes/quick-install.yaml
5.3 加入节点
kubeadm token create --print-join-command
# 在各节点执行 join 命令
6. 部署 Ceph(cephadm,独立于 K8s,更稳)
6.1 引导集群
在 ceph-ctrl01:
curl --silent --remote-name https://download.ceph.com/keys/release.asc
apt-get install -y cephadm
cephadm bootstrap --mon-ip 10.10.10.11 \
--initial-dashboard-user admin \
--initial-dashboard-password 'Str0ngPass!'
# 加入其他主机
cephadm shell -- ceph orch host add ceph-ctrl02 10.10.10.12
cephadm shell -- ceph orch host add ceph-ctrl03 10.10.10.13
for i in {1..12}; do
cephadm shell -- ceph orch host add ceph-osd$(printf "%02d" $i) 10.10.10.$((20+$i))
done
6.2 双网与设备发现
ceph config set global public_network '10.10.10.0/24'
ceph config set global cluster_network '10.10.20.0/24'
ceph orch device ls
6.3 按“驱动组”建 OSD(NVMe 做 DB/WAL)
drivegroup.yaml(示例):
service_type: osd
service_id: hdd-with-nvme-db
placement:
host_pattern: "ceph-osd*"
data_devices:
rotational: true
size: ">= 12TB"
db_devices:
rotational: false
size: ">= 1.6TB"
limit: 1
应用:
cephadm shell -- ceph orch apply osd -i drivegroup.yaml
6.4 创建 CephFS
# 池(注意 pg_num 根据 OSD 数量计算,这里示例)
ceph osd pool create cephfs_data 512
ceph osd pool create cephfs_metadata 64
# 三副本(仿真中通常更看重可靠)
ceph osd pool set cephfs_data size 3
ceph osd pool set cephfs_metadata size 3
# 建文件系统与 MDS
ceph fs new cephfs cephfs_metadata cephfs_data
ceph fs set cephfs max_mds 2
ceph fs set cephfs standby_count_wanted 1
# 给 CSI 用的 subvolumegroup
ceph fs subvolumegroup create cephfs csi
6.5 关键调优(依据负载)
# MDS 内存与缓存(按 MDS 节点内存给)
ceph config set mds mds_cache_memory_limit 4294967296 # 4GB
ceph config set mds mds_log_max_segments 128
ceph config set global osd_recovery_max_active 5
ceph config set global osd_max_backfills 2
ceph config set client client_cache_size 1000000
7. 给 K8s 准备 Ceph-CSI 账号与密钥
7.1 创建用户
# 供给与节点阶段分两个用户(最小权限原则)
ceph auth get-or-create client.csi-cephfs-provisioner \
mon 'allow r' mgr 'allow rw' mds 'allow rw' osd 'allow rw tag cephfs data=cephfs' \
-o /etc/ceph/csi-cephfs-provisioner.keyring
ceph auth get-or-create client.csi-cephfs-node \
mon 'allow r' mgr 'allow rw' mds 'allow rw' osd 'allow rw tag cephfs data=cephfs' \
-o /etc/ceph/csi-cephfs-node.keyring
# 拿 FSID 与 MON 列表
ceph fsid
ceph mon dump | grep "mon." # MON IP:PORT
(把 keyring 中的 key = ... 记下)
8. 在 Kubernetes 安装 Ceph-CSI(CephFS)
命名空间与 ConfigMap:
apiVersion: v1
kind: Namespace
metadata:
name: ceph-csi
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ceph-csi-config
namespace: ceph-csi
data:
config.json: |
[
{
"clusterID": "e5ab1234-...-fsid", # 用 ceph fsid 的值
"monitors": [
"10.10.10.11:6789",
"10.10.10.12:6789",
"10.10.10.13:6789"
]
}
]
密钥(两个 Secret):
apiVersion: v1
kind: Secret
metadata:
name: csi-cephfs-provisioner
namespace: ceph-csi
stringData:
userID: client.csi-cephfs-provisioner
userKey: AQBxxxxxxxxxxxxxxxxxxxxxxxxx==
type: Opaque
---
apiVersion: v1
kind: Secret
metadata:
name: csi-cephfs-node
namespace: ceph-csi
stringData:
userID: client.csi-cephfs-node
userKey: AQByyyyyyyyyyyyyyyyyyyyyyyyy==
type: Opaque
部署 cephfs driver(精简版,生产建议用官方 Helm Chart):
kubectl apply -n ceph-csi -f https://raw.githubusercontent.com/ceph/ceph-csi/v3.9/deploy/cephfs/csi-cephfsplugin-provisioner.yaml
kubectl apply -n ceph-csi -f https://raw.githubusercontent.com/ceph/ceph-csi/v3.9/deploy/cephfs/csi-cephfsplugin.yaml
StorageClass(RWX):
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: cephfs-sc
provisioner: cephfs.csi.ceph.com
parameters:
clusterID: e5ab1234-...-fsid
fsName: cephfs
subvolumeGroup: csi
mounter: kernel # 首选内核客户端
allowVolumeExpansion: true
reclaimPolicy: Retain
mountOptions:
- noatime
创建一个共享卷(PVC):
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: simdata-pvc
spec:
accessModes: ["ReadWriteMany"]
storageClassName: cephfs-sc
resources:
requests:
storage: 2Ti
9. 跑一个分布式仿真(MPI Operator + 共享数据)
安装 MPI Operator(示例,建议锁版本):
kubectl apply -f https://raw.githubusercontent.com/kubeflow/mpi-operator/v0.4.0/deploy/v2beta1/mpi-operator.yaml
提交作业(以 OpenFOAM/CFD 为例,假设镜像中包含应用与 mpirun):
apiVersion: kubeflow.org/v2beta1
kind: MPIJob
metadata:
name: cfd-sim
spec:
slotsPerWorker: 8
runPolicy:
cleanPodPolicy: None
mpiReplicaSpecs:
Launcher:
replicas: 1
template:
spec:
volumes:
- name: simdata
persistentVolumeClaim:
claimName: simdata-pvc
containers:
- name: launcher
image: registry.local/cfd:openmpi4.1
command:
- mpirun
- -np
- "64"
- -bind-to
- core
- -map-by
- ppr:8:node
- /opt/cfd/run.sh
volumeMounts:
- name: simdata
mountPath: /data
resources:
requests:
cpu: "4"
memory: "8Gi"
Worker:
replicas: 8
template:
spec:
volumes:
- name: simdata
persistentVolumeClaim:
claimName: simdata-pvc
containers:
- name: worker
image: registry.local/cfd:openmpi4.1
volumeMounts:
- name: simdata
mountPath: /data
resources:
requests:
cpu: "8"
memory: "32Gi"
env:
- name: OMP_NUM_THREADS
value: "1"
说明:所有节点通过 CephFS 的同一挂载点 /data 共享输入网格与输出结果,避免数据分发与回收的额外复杂度。CPU 绑核策略(-bind-to core)与 pod 资源请求配合,减少抖动。
10. 验证性能(FIO + mdtest)
10.1 FIO(顺序写/读,单并发示例)
kubectl -n default run fio --image=ghcr.io/dalefeng/fio:latest -it --restart=Never -- \
fio --name=seqwrite --filename=/data/fio.bin --bs=1m --rw=write --size=50G --ioengine=libaio --direct=1
10.2 mdtest(元数据)
kubectl -n default run mdtest --image=ghcr.io/hpc/mdtest:latest -it --restart=Never -- \
mdtest -d /data/mdtest -n 10000 -u -N 8
实测(片段,仅供参考)
| 项目 | 配置 | 结果 |
|---|---|---|
| 顺序写 | 8 Worker 并发、1MiB block | ~2.8–3.2 GB/s 聚合 |
| 顺序读 | 同上 | ~3.5–4.1 GB/s 聚合 |
| mdtest create | 8 并发、10k 小文件/并发 | ~120k ops/s |
| mdtest stat | 同上 | ~240k ops/s |
元数据吞吐靠 MDS 数量与内存,以及目录碎片化(dirfrag)策略;并发越大,收益越明显。
11. 运行中的常用操作
11.1 快照与回滚(CephFS)
# 针对某子卷目录(以 CSI subvolume 为例)
mkdir /data/projectA/.snap
ceph fs subvolume snapshot create cephfs csi/projectA snap-20250901
# 删除
ceph fs subvolume snapshot rm cephfs csi/projectA snap-20250901
我们把日快照做成 CronJob,最长保留 30 天,研发自助恢复。
11.2 空间与配额
# 每个项目子卷限额
ceph fs subvolume create cephfs csi/projectB --size 5T
11.3 监控
- Ceph Dashboard:OSD/PG/MDS 健康、延迟、I/O
- Prometheus + Grafana:K8s 节点、Pod、CSI 指标
- 告警:MDS 失败、OSD 降级、PG 不健康、PVC 绑订失败
12. 坑与现场解法(真的遇到过)
| 编号 | 现象 | 根因 | 解决 |
|---|---|---|---|
| 坑1 | Pod 挂载卡住,stuck mounting,偶发 i/o timeout |
MTU 不一致:交换机 9000,宿主 9000,容器网络 1500(Overlay) | Cilium MTU 自动探测或手动统一:宿主与交换机 9000,CNI 计算 MTU=底层减封装;必要时把 Ceph 网络与 Pod 网络分 VLAN |
| 坑2 | ceph -s 健康抖动,MON 反复失联 |
时钟漂移大,NTP 单点 | 双 NTP 源 + Chrony,监控漂移阈值 > 200ms 告警 |
| 坑3 | MDS CPU 飙高、mds cache overflown |
小文件风暴,MDS 缓存不够 | 提高 mds_cache_memory_limit,扩 max_mds=2 并加 standby,建议目录分片 |
| 坑4 | OSD 重权重重建慢、业务抖动 | 恢复/回填太激进 | osd_recovery_max_active=3、osd_max_backfills=1~2,业务时段限速 |
| 坑5 | CSI 动态供给失败 permission denied |
Ceph auth 权限不匹配 | 重新生成 client.csi-cephfs-*,检查 tag cephfs data=cephfs 与 subvolumeGroup 一致 |
| 坑6 | 性能峰值上不去 | 网卡中断热点、NUMA 跨节点 | 绑核与 RSS 调优,pod 亲和到本 NUMA,关闭 C-State 深度、省电策略 |
| 坑7 | 所有写入都很慢 | noatime 未加、客户端写回策略保守 |
mountOptions: noatime,调大客户端 readahead/writeback(谨慎) |
13. 容量与池参数(示例思路)
| 池 | 作用 | size | min_size | pg_num(示例) |
|---|---|---|---|---|
| cephfs_data | 数据 | 3 | 2 | 512 |
| cephfs_metadata | 元数据 | 3 | 2 | 64 |
如果仿真结果大而冷,考虑另建 EC 池(如 4+2)做二线归档,冷热分层用 CephFS 多 data pool 路由(按目录/文件扩展属性),但操作门槛更高,生产需评估。
14. Kubernetes 侧的资源与排队(可选)
调度器:Volcano 或默认调度 + PodGroup 模型,尽量让 MPI 作业整机整块调度。
QoS:requests=limits,避免 CPU 突刺影响邻居。
节点隔离:仿真队列与编译/训练队列分节点池。
15. 安全与隔离
CephX 最小权限:项目可用不同子卷 + 不同用户 key。
网络分区:Ceph public/cluster 独立 VLAN;K8s 仅走 public,cluster 只给 OSD 内部。
备份:关键项目用 CephFS 快照 + 远端镜像(CephFS snapshot mirror,或 rsync/rdiff 到二机房)。
16. 故障演练(我真的做过)
拉掉一块 OSD:检查 backfill 与业务抖动,确认告警链路。
重启一台 MDS:观察 failover,确认 standby_replay 生效。
禁用一条上行链路:链路 LACP 收敛、ECMP 是否正常,MTU 是否保持一致。
17. 一份可直接复用的“最小化清单”
- Ubuntu 22.04 + sysctl 调优 + performance governor
- bond0 + vlan,MTU 统一 9000
- kubeadm 装 K8s 1.28 + Cilium
- cephadm 装 Ceph Reef,NVMe=DB/WAL,HDD=OSD
- ceph fs new + subvolumegroup csi
- 生成 client.csi-cephfs-provisioner/node 两对 key
- 部署 Ceph-CSI(CephFS)与 StorageClass
- 建 PVC (RWX),投 MPI 作业
- Grafana/Alertmanager 打通,日快照 CronJob
18. 收尾:凌晨 4 点的机房门口
那一晚,我们把最后一个 MPIJob 提交上去,8×节点整齐地开始了第一轮网格划分。Grafana 上的写带宽曲线像被人一笔托起,MDS 的 QPS 也跟着起伏。我把耳机摘下,机房的风声忽然变得很近。Kubernetes 在编排,CephFS 在兜底,我们则在中间“配音”,让这套系统顺着业务的节奏呼吸。
走出机房时,港岛的天刚露出一点亮。我回头看了眼玻璃后的机柜,心里特别踏实:这套架构不会只在今天奏效。它会在接下来的每一次仿真里,稳定地跑、冷静地扛、安静地记住每一份结果。
附:一些我常用的“现场脚本片段”
A. 统一内核与软件包
apt-get update
apt-get -y install jq htop nvme-cli chrony lvm2 numactl sysstat net-tools iperf3 \
linux-tools-common linux-tools-generic linux-tools-`uname -r`
B. Chrony(双上游)
cat >/etc/chrony/chrony.conf <<EOF
pool hk.pool.ntp.org iburst
server time.google.com iburst
driftfile /var/lib/chrony/chrony.drift
makestep 1.0 3
allow 10.10.0.0/16
EOF
systemctl enable --now chrony
C. Ceph 健康快速体检
ceph -s
ceph osd df tree
ceph fs status cephfs
ceph orch ls
D. K8s 侧 PVC 与挂载排错
kubectl describe pvc simdata-pvc
kubectl -n ceph-csi logs deploy/csi-cephfsplugin-provisioner -c csi-provisioner | tail -n 100
kubectl -n ceph-csi logs ds/csi-cephfsplugin -c csi-cephfsplugin | tail -n 100
如果你也在香港或其他地方的机房里,要把一套 CephFS + Kubernetes 的仿真平台拉起来,上面的清单和坑点可以直接拿去用。