CSI (容器存储接口) 已经成为了容器存储编排接口规范。Ceph CSI 插件实现了容器存储编排与 Ceph 集群交互的接口,为容器编排系统提供存储服务。Ceph CSI 3 版本增加了很多特性,比如为 RBD 块存储提供了快照的创建和删除、从快照中创建持久卷等。
文中使用的环境
Kubernetes
# kubectl get nodes
NAME STATUS ROLES AGE VERSION
izwz90brjjogtbgisvc5bnz Ready <none> 6d23h v1.20.6
izwz90brjjogtbgisvc5boz Ready <none> 6d23h v1.20.6
izwz90brjjogtbwd0sw5gvz Ready <none> 6d20h v1.20.6
Ceph
# ceph version
ceph version 15.2.13 (c44bc49e7a57a87d84dfff2a077a2058aa2172e2) octopus (stable)
Ceph Pool
# rados lspools
device_health_metrics
rbd
Ceph-CSI 版本信息,本文只使用 RBD
Ceph CSI RBD 安装
创建 ceph-csi 配置 ConfigMap 对象,ceph-csi 目前只支持旧版 V1 协议,即 6789 端口。
clusterID 是 ceph fsid
# cat <<EOF > csi-config-map.yaml
---
apiVersion: v1
kind: ConfigMap
data:
config.json: |-
[
{
"clusterID": "bd241c5a-d012-11eb-8215-e977530c8b31",
"monitors": [
"172.16.x.x:6789",
"172.16.x.x:6789",
"172.16.x.x:6789"
]
}
]
metadata:
name: ceph-csi-config
namespace: kube-system
EOF
# kubectl create -f csi-config-map.yaml
ceph-csi 的最新版本还需要一个额外的 ConfigMap 对象来定义密钥管理服务 (KMS) 提供程序的详细信息。加密 KMS 配置是为了进一步提高安全稳健性,可以理解成 PVC 加密,目前 HashiCorp Vault 是唯一支持的 KMS。
如果不设置 KMS,在 csi-kms-config-map.yaml 文件中放置一个空配置即可。
# cat <<EOF > csi-kms-config-map.yaml
---
apiVersion: v1
kind: ConfigMap
data:
config.json: |-
{}
metadata:
name: ceph-csi-encryption-kms-config
namespace: kube-system
EOF
# kubectl create -f csi-kms-config-map.yaml
ceph-csi 需要 cephx 凭据才能与 Ceph 集群通信,这里使用的是 admin 用户。
# USER_KEY=$(ceph auth get-key client.admin)
# cat <<EOF > csi-rbd-secret.yaml
---
apiVersion: v1
kind: Secret
metadata:
name: csi-rbd-secret
namespace: kube-system
stringData:
userID: admin
userKey: ${USER_KEY}
EOF
# kubectl create -f csi-rbd-secret.yaml
ceph-csi 默认使用开发版本 (quay.io/cephcsi/cephcsi:canary), 这里换成 v3.3.1 版本。
# 下载 yaml 文件
wget https://raw.githubusercontent.com/ceph/ceph-csi/release-v3.3/deploy/rbd/kubernetes/csi-provisioner-rbac.yaml
wget https://raw.githubusercontent.com/ceph/ceph-csi/release-v3.3/deploy/rbd/kubernetes/csi-nodeplugin-rbac.yaml
wget https://raw.githubusercontent.com/ceph/ceph-csi/release-v3.3/deploy/rbd/kubernetes/csi-rbdplugin-provisioner.yaml
wget https://raw.githubusercontent.com/ceph/ceph-csi/release-v3.3/deploy/rbd/kubernetes/csi-rbdplugin.yaml
ceph-csi 默认部署在 default 命名空间,这里改到 kube-system 。
创建 rbac
sed -i "s/namespace: default/namespace: kube-system/g" $(grep -rl "namespace: default" ./)
kubectl -n kube-system create -f csi-provisioner-rbac.yaml
kubectl -n kube-system create -f csi-nodeplugin-rbac.yaml
部署 ceph-csi rbd ,有些镜像的仓库是 k8s.gcr.io , 网络有问题的需要更换仓库。
sed -i 's/v3.3-canary/v3.3.1/g' $(grep -rl "v3.3-canary" ./)
kubectl -n kube-system create -f csi-rbdplugin-provisioner.yaml
kubectl -n kube-system create -f csi-rbdplugin.yaml
创建 StorageClass
# cat <<EOF > csi-rbd-sc.yaml
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: ceph-csi
provisioner: rbd.csi.ceph.com
parameters:
clusterID: bd241c5a-d012-11eb-8215-e977530c8b31
pool: rbd
imageFeatures: layering
csi.storage.k8s.io/provisioner-secret-name: csi-rbd-secret
csi.storage.k8s.io/provisioner-secret-namespace: kube-system
csi.storage.k8s.io/controller-expand-secret-name: csi-rbd-secret
csi.storage.k8s.io/controller-expand-secret-namespace: kube-system
csi.storage.k8s.io/node-stage-secret-name: csi-rbd-secret
csi.storage.k8s.io/node-stage-secret-namespace: kube-system
reclaimPolicy: Delete
allowVolumeExpansion: true
mountOptions:
- discard
EOF
# kubectl create -f csi-rbd-sc.yaml
创建 PVC
# cat <<EOF > pvc.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: rbd-pvc
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
# volumeMode: Block
resources:
requests:
storage: 1Gi
storageClassName: ceph-csi
EOF
# kubectl apply -f pvc.yaml
创建 pod
# cat <<EOF > pod.yaml
---
apiVersion: v1
kind: Pod
metadata:
name: nginx-ceph-csi-test
spec:
containers:
- name: nginx
image: nginx:1.21.0-alpine
volumeMounts:
- name: mypvc
mountPath: /data
volumes:
- name: mypvc
persistentVolumeClaim:
claimName: rbd-pvc
readOnly: false
EOF
# kubectl apply -f pod.yaml
加入 pod 查看
# kubectl exec -it nginx-ceph-csi-test -- sh
/ # df -Th | grep /data
/dev/rbd1 ext4 975.9M 2.5M 957.4M 0% /data
PVC 扩容
PVC 支持扩容的文件系统是 XFS、Ext3 和 Ext4 ,且在 StorageClass 声明 allowVolumeExpansion: true
,Kubernetes 1.15 版本开始默认开启在线扩容,由 --feature-gates
参数中的 ExpandInUsePersistentVolumes=true
控制。
扩容前为 1G
# kubectl get pvc rbd-pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
rbd-pvc Bound pvc-937e9588-95ff-475f-9473-513e18a96f37 1Gi RWO ceph-csi 7m56s
# kubectl describe pv pvc-937e9588-95ff-475f-9473-513e18a96f37 | grep imageName
imageName=csi-vol-b16dd8bf-d364-11eb-9891-56a6d268783b
# rbd info csi-vol-b16dd8bf-d364-11eb-9891-56a6d268783b
rbd image 'csi-vol-b16dd8bf-d364-11eb-9891-56a6d268783b':
size 1 GiB in 256 objects
order 22 (4 MiB objects)
snapshot_count: 0
id: 8f71d10b5f20
block_name_prefix: rbd_data.8f71d10b5f20
format: 2
features: layering
op_features:
flags:
create_timestamp: Tue Jun 22 22:18:19 2021
access_timestamp: Tue Jun 22 22:18:19 2021
modify_timestamp: Tue Jun 22 22:18:19 2021
扩容比较简单,直接修改 PVC 的容量即可。
# kubectl edit pvc rbd-pvc
扩容到 2G
# kubectl get pvc rbd-pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
rbd-pvc Bound pvc-937e9588-95ff-475f-9473-513e18a96f37 2Gi RWO ceph-csi 12m
# rbd info csi-vol-b16dd8bf-d364-11eb-9891-56a6d268783b
rbd image 'csi-vol-b16dd8bf-d364-11eb-9891-56a6d268783b':
size 2 GiB in 512 objects
order 22 (4 MiB objects)
snapshot_count: 0
id: 8f71d10b5f20
block_name_prefix: rbd_data.8f71d10b5f20
format: 2
features: layering
op_features:
flags:
create_timestamp: Tue Jun 22 22:18:19 2021
access_timestamp: Tue Jun 22 22:18:19 2021
modify_timestamp: Tue Jun 22 22:18:19 2021
# kubectl exec -it nginx-ceph-csi-test -- sh
/ # df -Th | grep /data
/dev/rbd1 ext4 1.9G 3.0M 1.9G 0% /data
/ #
卷快照
从 Kubernetes 1.17 开始卷快照(Volume Snapshot)功能默认开启,由 --feature-gates
参数中的 VolumeSnapshotDataSource=true
控制。卷快照功能不属于 Kubernetes 核心 API。它的 API 对象 VolumeSnapshot
,VolumeSnapshotContent
和 VolumeSnapshotClass
是通过 CRDs 实现的。
卷快照控制器 (snapshot controller) 是独立于 CSI 之外的,目前由 external-snapshotter 项目提供支持,无论 Kubernetes 中部署了多少个 CSI ,都需要部署一个卷快照控制器。
// 拷贝源码到本地
# git clone https://github.com/kubernetes-csi/external-snapshotter
# cd external-snapshotter
// 安装快照 CRD
kubectl create -f client/config/crd
// 安装通用快照控制器
kubectl create -f deploy/kubernetes/snapshot-controller
// 安装 CSI 驱动程序, 这里安装到 kube-system 命名空间
sed -i "s/namespace: default/namespace: kube-system/g" .deploy/kubernetes/csi-snapshotter/*
kubectl create -f deploy/kubernetes/csi-snapshotter -n kube-system
创建 snapshotclass
# cat <<EOF > snapshotclass.yaml
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
name: csi-rbdplugin-snapclass
driver: rbd.csi.ceph.com
parameters:
clusterID: bd241c5a-d012-11eb-8215-e977530c8b31
csi.storage.k8s.io/snapshotter-secret-name: csi-rbd-secret
csi.storage.k8s.io/snapshotter-secret-namespace: kube-system
deletionPolicy: Delete
EOF
# kubectl create -f snapshotclass.yaml
# kubectl get volumesnapshotclass
NAME DRIVER DELETIONPOLICY AGE
csi-rbdplugin-snapclass rbd.csi.ceph.com Delete 46s
加入 pod 写入一些数据进行测试
# kubectl exec -it nginx-ceph-csi-test -- sh
/ # cd /data/
/data # for i in $(seq 1 100); do echo $i; echo $RANDOM >>./datatest.db; done
创建快照
# cat <<EOF > rbd-pvc-snapshot.yaml
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: rbd-pvc-snapshot
spec:
volumeSnapshotClassName: csi-rbdplugin-snapclass
source:
persistentVolumeClaimName: rbd-pvc
EOF
# kubectl create -f rbd-pvc-snapshot.yaml
# kubectl get volumesnapshot
NAME READYTOUSE SOURCEPVC SOURCESNAPSHOTCONTENT RESTORESIZE SNAPSHOTCLASS SNAPSHOTCONTENT CREATIONTIME AGE
rbd-pvc-snapshot true rbd-pvc 2Gi csi-rbdplugin-snapclass snapcontent-b0dfbe62-0b11-4aa9-a473-ddb07866fdb3 14s 15s
查看快照
# rbd ls
csi-snap-6e94b74c-d368-11eb-84a2-82c03fe56569
csi-vol-b16dd8bf-d364-11eb-9891-56a6d268783b
# rbd snap ls csi-snap-6e94b74c-d368-11eb-84a2-82c03fe56569 -p rbd
SNAPID NAME SIZE PROTECTED TIMESTAMP
5 csi-snap-6e94b74c-d368-11eb-84a2-82c03fe56569 2 GiB Tue Jun 22 22:45:06 2021
删除测试数据
# kubectl exec -it nginx-ceph-csi-test -- sh
/ # cd /data/
/data # ls -lrt
total 20
drwx------ 2 root root 16384 Jun 22 14:21 lost+found
-rw-r--r-- 1 root root 564 Jun 22 14:23 datatest.db
/data # rm -f datatest.db
/data # ls -lrt
total 16
drwx------ 2 root root 16384 Jun 22 14:21 lost+found
恢复快照
# cat <<EOF > rbd-pvc-snapshot.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: rbd-pvc-restore
spec:
storageClassName: ceph-csi
dataSource:
name: rbd-pvc-snapshot
kind: VolumeSnapshot
apiGroup: snapshot.storage.k8s.io
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
EOF
# kubectl create -f rbd-pvc-snapshot.yaml
从快照中创建了一个新的 PVC
# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
rbd-pvc Bound pvc-937e9588-95ff-475f-9473-513e18a96f37 2Gi RWO ceph-csi 36m
rbd-pvc-restore Bound pvc-ffbb56a4-be3f-47fb-b92e-c2db1df92a97 2Gi RWO ceph-csi 12s
# rbd ls
csi-snap-6e94b74c-d368-11eb-84a2-82c03fe56569
csi-vol-b16dd8bf-d364-11eb-9891-56a6d268783b
csi-vol-c4785c04-d369-11eb-9891-56a6d268783b
编辑 pod ,把新创建的 PVC 挂载上去
# cat pod.yaml
---
apiVersion: v1
kind: Pod
metadata:
name: nginx-ceph-csi-test
spec:
containers:
- name: nginx
image: nginx:1.21.0-alpine
volumeMounts:
- name: mypvc
mountPath: /data
volumes:
- name: mypvc
persistentVolumeClaim:
claimName: rbd-pvc-restore
readOnly: false
# kubectl create -f pod.yaml
# kubectl exec -it nginx-ceph-csi-test -- sh
/ # cd /data/
/data # ls -lrt
total 20
drwx------ 2 root root 16384 Jun 22 14:21 lost+found
-rw-r--r-- 1 root root 564 Jun 22 14:23 datatest.db
/data # more datatest.db
17542
26974
29622
32377
9929
29734
26492
...
也可以使用 ceph CLI 进行操作。
pod 使用原来的 rbd-pvc ,然后写入数据数据
# kubectl exec -it nginx-ceph-csi-test -- sh
/ # cd /data/
/data # for i in $(seq 1 100); do echo $i; echo $RANDOM >>./datatest.db; done
创建快照
# rbd snap create rbd/csi-vol-b16dd8bf-d364-11eb-9891-56a6d268783b@snapshot1
# rbd snap ls rbd/csi-vol-b16dd8bf-d364-11eb-9891-56a6d268783b
SNAPID NAME SIZE PROTECTED TIMESTAMP
6 snapshot1 2 GiB Tue Jun 22 23:14:24 2021
删除测试数据
# kubectl exec -it nginx-ceph-csi-test -- sh
/ # cd data/
/data # ls -lrt
total 20
drwx------ 2 root root 16384 Jun 22 14:21 lost+found
-rw-r--r-- 1 root root 574 Jun 22 15:12 datatest.db
/data # rm -f datatest.db
/data # ls -lrt
total 16
drwx------ 2 root root 16384 Jun 22 14:21 lost+found
恢复快照
# rbd snap rollback rbd/csi-vol-b16dd8bf-d364-11eb-9891-56a6d268783b@snapshot1
Rolling back to snapshot: 100% complete...done.
重启 pod 后验证。
# kubectl exec -it nginx-ceph-csi-test -- sh
/ # cd /data/
/data # ls -lrt
total 20
drwx------ 2 root root 16384 Jun 22 14:21 lost+found
-rw-r--r-- 1 root root 574 Jun 22 15:12 datatest.db
写在最后
在 Ceph 中 object (对象)是最小存储单元,默认是 4MB,无论使用对象存储、块存储还是文件系统挂载的存储方式,存储的数据都会被切分成对象,由 PG (Place Group) 管理,object 通过 Hash 映射到 PG 中,一个 PG 可以包含多个 object ,PG 再通过 CRUSH 计算映射到 OSD 中。如果是三副本的,则每个 PG 都会映射到三个 OSD ,保证了数据的冗余。数据迁移或拷贝时,也是以 PG 作为基本单位进行的,Ceph 不会直接操作对象。
数据是一家企业的基石,数据安全的重要性毋庸置疑。在 Kubernetes 中卷快照功能,velero 也可以做到,还可以备份 Kubernetes 其他资源对象的数据,感兴趣的读者可以去了解下。