Kubernetes PersistentVolumes 介绍
环境、软件准备
单节点使用 CephFS
Kubernetes PV & PVC 方式使用 CephFS
测试跨节点使用 CephFS
1、Kubernetes PersistentVolumes 介绍

Kubernetes PersistentVolumes 持久化存储方案中,提供两种 API 资源方式: PersistentVolume(简称PV) 和 PersistentVolumeClaim(简称PVC)。PV 可理解为集群资源,PVC 可理解为对集群资源的请求,Kubernetes 支持很多种持久化卷存储类型。Ceph 是一个开源的分布式存储系统,支持对象存储、块设备、文件系统,具有可靠性高、管理方便、伸缩性强等特点。在日常工作中,我们会遇到使用 k8s 时后端存储需要持久化,这样不管 Pod 调度到哪个节点,都能挂载同一个卷,从而很容易读取或存储持久化数据,我们可以使用 Kubernetes 结合 Ceph 完成。

2、环境、软件准备

本次演示环境,我是在虚拟机 Linux Centos7 上操作,通过虚拟机完成 Ceph 存储集群搭建以及 Kubernetes 集群的搭建,以下是安装的软件及版本:

Centos:release 7.4.1708 (Core)
Ceph:jewel-10.2.10
Kubernetes:v1.6.2
Docker:v1.12.6
注意:这里我们着重描述一下 Kubernetes 集群如何使用 CephFS 来实现持久化存储,所以需要提前搭建好 Kubernetes 集群和 Ceph 存储集群,具体搭建过程可参考之前文章 国内使用 kubeadm 在 Centos 7 搭建 Kubernetes 集群 和 初试 Centos7 上 Ceph 存储集群搭建,这里就不在详细讲解了。同时由于本机内存限制,共开启了 3 个虚拟机节点,每个节点既是 Ceph 集群节点又是 Kubernetes 集群节点,所以功能节点图如下:

3、单节点使用 CephFS

有上一篇 初试 Kubernetes 集群使用 Ceph RBD 块存储 操作的基础,这次就更加轻车熟路了!首先我们单节点使用 CephFS,先只使用 admin 和 node0,这样就将所有的 Pod 都调度到 node0 上执行。

k8s 集群单节点使用 CephFS,我们可以使用 Kubernetes Examples Github 官方示例代码,稍加修改即可。

$ cd /home/wanyang3/k8s
$ git clone https://github.com/kubernetes/examples.git
# tree examples/staging/volumes/cephfs
|— cephfs-with-secret.yaml
|— cephfs.yaml
|— README.md
-- secret<br />— ceph-secret.yaml

跟之前使用 Ceph RBD 类似,也提供了两种方式供 k8s 集群使用 CephFS,在搭建 Ceph 集群时,默认开启了 cephx 安全认证的,所以在 k8s 集群使用 CephFS 时,也是要配置认证信息的,下边分别演示一下吧!

3.1 创建 cephfs

在正式开始演示之前,我们先在 admin 节点创建一个 cephfs,测试下 cephfs 是否可用吧!详细操作过程可参考文章 初试 Ceph 存储之块设备、文件系统、对象存储 中文件系统部分,贴下操作代码。

创建元数据服务器 MDS
$ ceph-deploy mds create admin node0 node1

$ ceph mds stat
e5: 1/1/1 up {0=node0=up:active}, 1 up:standby

创建 cephfs
$ ceph osd pool create cephfs_data 128
pool ‘cephfs_data’ created
$ ceph osd pool create cephfs_metadata 128
pool ‘cephfs_metadata’ created
$ ceph fs new cephfs cephfs_metadata cephfs_data
new fs with metadata pool 2 and data pool 1
$ ceph fs ls
name: cephfs, metadata pool: cephfs_metadata, data pools: [cephfs_data ]

创建认证文件 admin.secret
$ cat /etc/ceph/ceph.client.admin.keyring
[client.admin]
key = AQD01VVaVvzzLRAAaTURyUZwI9Ad3uYYFRL+VA==
caps mds = “allow
caps mon = “allow

caps osd = “allow *”
$ vim /etc/ceph/admin.secret
AQD01VVaVvzzLRAAaTURyUZwI9Ad3uYYFRL+VA==

$ mkdir /mnt/cephfs
$ mount -t ceph 10.222.78.12:6789:/ /mnt/cephfs -o name=admin,secretfile=/etc/ceph/admin.secret
$ df -h

10.222.78.12:6789:/ 66G 32G 34G 49% /mnt/cephfs

从上边看到在 admin 节点已经成功将 cephfs 挂载到 /mnt/cephfs 啦!说明整个存储集群是没有问题的,接下来演示的时候,就不需要在各个节点再次创建 cephfs 了,pod 容器内部挂载即可。当然也创建不成功,因为目前 一个 Ceph 存储集群只支持创建一个 cephfs。

3.2 使用 cephfs.yaml

$ cat cephfs.yaml
apiVersion: v1
kind: Pod
metadata:
name: cephfs1
spec:
containers:
- name: cephfs-rw
image: kubernetes/pause
volumeMounts:
- mountPath: “/mnt/cephfs”
name: cephfs
volumes:
- name: cephfs
cephfs:
monitors:
- 10.222.78.12:6789
user: admin
secretFile: “/etc/ceph/admin.secret”
readOnly: true

修改完的配置如上,这里我们要挂载 /mnt/cephfs 目录到 Pod 容器内。在创建之前,我们要去 node0 节点生成 admin.secret (因为现在是单节点测试,Pod 只会被调度到 node0,后续加入node1 时也需要生成 admin.secret),同时这里 node0 既是 Ceph 集群 osd 节点又是 k8s 节点,所以已存在该文件,取出 Key 值即可。

$ cat /etc/ceph/ceph.client.admin.keyring
[client.admin]
key = AQD01VVaVvzzLRAAaTURyUZwI9Ad3uYYFRL+VA==
caps mds = “allow
caps mon = “allow

caps osd = “allow *”
$ vim /etc/ceph/admin.secret
AQD01VVaVvzzLRAAaTURyUZwI9Ad3uYYFRL+VA==

好了,现在可以创建使用 CephFS 作为后端存储的 Pod 了。

$ kubectl create -f cephfs.yaml
pod “cephfs1” created

$ kubectl get pods
NAME READY STATUS RESTARTS AGE
cephfs1 1/1 Running 0 12s

接下来我们去 node0 上验证一下是否正确启动并挂载该 CephFS 吧!

node0 上操作
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b9f2bac921d1 docker.io/kubernetes/pause@sha256:2088df8eb02f10aae012e6d4bc212cabb0ada93cb05f09e504af0c9811e0ca14 “/pause” 28 seconds ago Up 27 seconds k8s_cephfs-rw_cephfs1_default_5cc74af6-f5ea-11e7-be96-080027ee5979_0

$ mount

10.222.78.12:6789:/ on /var/lib/kubelet/pods/5cc74af6-f5ea-11e7-be96-080027ee5979/volumes/kubernetes.io~cephfs/cephfs type ceph (ro,relatime,name=admin,secret=,acl)

$ docker inspect b9f2bac921d1

“Mounts”: [
{
“Source”: “/var/lib/kubelet/pods/5cc74af6-f5ea-11e7-be96-080027ee5979/containers/cephfs-rw/80747a86”,
“Destination”: “/dev/termination-log”,
“Mode”: “”,
“RW”: true,
“Propagation”: “rprivate”
},
{
“Source”: “/var/lib/kubelet/pods/5cc74af6-f5ea-11e7-be96-080027ee5979/volumes/kubernetes.io~cephfs/cephfs”,
“Destination”: “/mnt/cephfs”,
“Mode”: “”,
“RW”: true,
“Propagation”: “rprivate”
},
{
“Source”: “/var/lib/kubelet/pods/5cc74af6-f5ea-11e7-be96-080027ee5979/volumes/kubernetes.io~secret/default-token-jg3f8”,
“Destination”: “/var/run/secrets/kubernetes.io/serviceaccount”,
“Mode”: “ro”,
“RW”: false,
“Propagation”: “rprivate”
},
{
“Source”: “/var/lib/kubelet/pods/5cc74af6-f5ea-11e7-be96-080027ee5979/etc-hosts”,
“Destination”: “/etc/hosts”,
“Mode”: “”,
“RW”: true,
“Propagation”: “rprivate”
}
],

3.3 使用 cephfs-with-secret & ceph-secret

接下来使用 cephfs-with-secret 和 ceph-secret 方式,这种方式跟上边直接使 admin.secret 文件认证最大的区别就是使用 k8s secret 对象,该 secret 对象用于 k8s volume 插件通过 cephx 认证访问 ceph 存储集群。不过要提一下的是,k8s secret 认证 key 需要使用 base64 编码。

获取 Ceph ceph.client.admin.keyring 并生成 secret key
$ ceph auth get-key client.admin |base64
QVFEMDFWVmFWdnp6TFJBQWFUVVJ5VVp3STlBZDN1WVlGUkwrVkE9PQ==

修改 ceph-secret.yaml 文件中,key 字段替换成上边生成的字符串。

$ cat ceph-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: ceph-secret
data:
key: QVFEMDFWVmFWdnp6TFJBQWFUVVJ5VVp3STlBZDN1WVlGUkwrVkE9PQ==

创建名称为 ceph-secret 的 Secret。

$ kubectl create -f ceph-secret.yaml
secret “ceph-secret” created
$ kubectl get secret
NAME TYPE DATA AGE
ceph-secret Opaque 1 11s
default-token-jg3f8 kubernetes.io/service-account-token 3 28m

接下来,需要修改一下 cephfs-with-secret.yaml 文件,主要修改 cephfs 相关信息,修改完成后如下。

cat cephfs-with-secret.yaml
apiVersion: v1
kind: Pod
metadata:
name: cephfs2
spec:
containers:
- name: cephfs-rw
image: kubernetes/pause
volumeMounts:
- mountPath: “/mnt/cephfs”
name: cephfs
volumes:
- name: cephfs
cephfs:
monitors:
- 10.222.78.12:6789
user: admin
secretRef:
name: ceph-secret
readOnly: true

配置跟上边类似,唯一区别就是 secretRef 换成刚创建的 ceph-secret 对象,接下来创建该 Pod 啦!

$ kubectl create -f cephfs-with-secret.yaml
pod “cephfs2” created
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
cephfs1 1/1 Running 0 11m
cephfs2 1/1 Running 0 25s

跟上边示例一样,妥妥没问题,可以去 node0 上查看 mount 信息验证一下,这里就不在演示了。

4、Kubernetes PV & PVC 方式使用 CephFS

上一篇文章中指出,k8s 支持的 PV 类型有很多,其中就有我们熟悉的 Ceph RBD 和 CephFS,接下来就演示一下如何使用 PV & PVC 结合 CephFS 完成上边演示操作。这里使用上边创建的同一个 cephfs,因为一个 Cephfs 存储集群只支持创建一个 cephfs。还有就是,这里我们可以加入 node1 了,组成一个多节点的 k8s 集群,测试跨节点使用 CephFS。

4.1 创建 PV

还记得上边提过 Ceph 认证的 secret,这里也是需要的,这次就不用再创建了,直接复用上边的即可。新建 PV 文件 rbd-pv.yaml 如下:
$ vim cephfs-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: cephfs-pv
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
cephfs:
monitors:
- 10.222.78.12:6789
user: admin
secretRef:
name: ceph-secret
readOnly: false
persistentVolumeReclaimPolicy: Recycle

注意,这里我们使用的 accessModes 模型为 ReadWriteMany,kubernetes 官网 Persistent Volumes 文档中指出 CephFS 文件存储对三种 Mode 方式都支持,CephFS 支持读写多次,也是实际工作中需要的。接下来创建 PV。

$ kubectl create -f cephfs-pv.yaml
persistentvolume “cephfs-pv” created
$ kubectl get pv
NAME CAPACITY ACCESSMODES RECLAIMPOLICY STATUS CLAIM STORAGECLASS REASON AGE
cephfs-pv 1Gi RWX Recycle Available

4.2 创建 PVC

好了,上边 PV 资源已经创建好了,接下来创建对资源的请求 PVC,新建 PVC 文件 cephfs-pv-claim.yaml 如下:

$ cat cephfs-pv-claim.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: cephfs-pv-claim
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi

这里提一下的是,因为接下来验证测试需要跨节点读取和写入文件,所以使用 ReadWriteMany 模型,接下来创建 PVC。

$ kubectl create -f cephfs-pv-claim.yaml
persistentvolumeclaim “cephfs-pv-claim” created
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESSMODES STORAGECLASS AGE
cephfs-pv-claim Bound cephfs-pv 1Gi RWX 10s

4.3 创建挂载 CephFS 的 Pod

PV 和 PVC 都创建好了,接下来就需要创建挂载该 CephFS 的 Pod 了,这里我使用官方示例中的 busybox 容器测试吧!新建 Pod 文件 cephfs-pvc-pod1.yaml 如下:

vim cephfs-pvc-pod1.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
test: cephfs-pvc-pod
name: cephfs-pv-pod1
spec:
containers:
- name: cephfs-pv-busybox1
image: busybox
command: [“sleep”, “60000”]
volumeMounts:
- mountPath: “/mnt/cephfs”
name: cephfs-vol1
readOnly: false
volumes:
- name: cephfs-vol1
persistentVolumeClaim:
claimName: cephfs-pv-claim

从文件可以看出,我们要将上边创建的 cephfs-pv-claim 请求的资源挂载到容器的 /mnt/cephfs 目录。接下来创建一下该 Pod,看是否能够正常运行吧!创建之前,我们先造点数据到 /mnt/cephfs 目录,方便后边读取测试。

提前写入数据
$ vim /mnt/cephfs/test.txt
This is cephfs test file.

创建 cephfs-pv-pod1
$ kubectl create -f cephfs-pvc-pod.yaml
pod “cephfs-pv-pod1” created
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
cephfs-pv-pod1 1/1 Running 0 47s
$ kubectl get pod —all-namespaces -o wide
NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE
default cephfs-pv-pod1 1/1 Running 0 26s 10.96.2.2 node1

我们看到 Pod 能够正常启动,并且分配到了 node1,接下来我们去 node1 验证一下吧!

node1 上操作
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3cc605bed890 docker.io/busybox@sha256:436bbf48aa1198ebca8eac0ad9a9c80c8929d9242e02608f76ce18334e0cfe6a “sleep 60000” 12 seconds ago Up 12 seconds k8s_cephfs-pv-busybox1_cephfs-pv-pod1_default_a2397f5d-f677-11e7-be96-080027ee5979_0

$ docker exec -it 3cc605bed890 /bin/sh
/ # df -h
Filesystem Size Used Available Use% Mounted on
10.222.78.12:6789:/ 65.4G 34.1G 31.3G 52% /mnt/cephfs

/ # ls /mnt/cephfs
test.txt
/ # cat /mnt/cephfs/test.txt
This is cephfs test file.

创建一个新文件,测试下边跨节点看能否挂载的上,并且能够读取出来吧。
/ # vi /mnt/cephfs/cephfs-pv-pod1.txt
/ # cat /mnt/cephfs/cephfs-pv-pod1.txt
This message write by cephfs-pv-pod1 pod.

OK 我们看到 Pod 容器内能够正确挂载 CephFS 并成功读取到之前创建的文件。

5、测试跨节点使用 CephFS

上一篇文中 K8s 集群跨节点使用 Ceph RBD 存在 rbd: image ceph-rbd-pv-test is locked by other nodes 错误信息,说明 Ceph RBD 仅能被 k8s 中的一个 node 挂载,也就是不支持跨节点挂载同一 Ceph RBD。那么 CephFS 也会存在这个问题吗?接下来我们验证一下。

我们再在另外一个 node 上创建挂载同一个 CephFS 的 Pod,看是否可以跨节点挂载吧!

创建挂载 cephfs 的 pod2,配置同 pod1
$ vim cephfs-pvc-pod2.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
test: cephfs-pvc-pod
name: cephfs-pv-pod2
spec:
containers:
- name: cephfs-pv-busybox2
image: busybox
command: [“sleep”, “60000”]
volumeMounts:
- mountPath: “/mnt/cephfs”
name: cephfs-vol2
readOnly: false
volumes:
- name: cephfs-vol2
persistentVolumeClaim:
claimName: cephfs-pv-claim

接下来创建一下该 Pod,看能够创建成功吧!

$ kubectl create -f cephfs-pvc-pod2.yaml
pod “cephfs-pv-pod2” created

$ kubectl get pods —all-namespaces -o wide
NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE
default cephfs-pv-pod1 1/1 Running 0 9m 10.96.2.3 node1
default cephfs-pv-pod2 1/1 Running 0 16s 10.96.1.4 node0

OK 我们看到 cephfs-pv-pod2 被分配到了 node0 并且正常运行起来了。说明 K8s 支持 CephFS 跨节点挂载。接下来,我们去 node0 验证一下是否正确挂载该 CephFS 吧!

node0 上操作
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f8e544e9f497 docker.io/busybox@sha256:436bbf48aa1198ebca8eac0ad9a9c80c8929d9242e02608f76ce18334e0cfe6a “sleep 60000” About a minute ago Up About a minute k8s_cephfs-pv-busybox2_cephfs-pv-pod2_default_faf6f3fd-f678-11e7-be96-080027ee5979_0

$ docker exec -it f8e544e9f497 /bin/sh
/ # df -h
Filesystem Size Used Available Use% Mounted on
10.222.78.12:6789:/ 65.4G 34.1G 31.3G 52% /mnt/cephfs

/ # ls -l /mnt/cephfs
total 1
-rw-r—r— 1 root root 42 Jan 11 02:36 cephfs-pv-pod1.txt
-rw-r—r— 1 root root 26 Jan 10 09:39 test.txt

/ # cat /mnt/cephfs/cephfs-pv-pod1.txt
This message write by cephfs-pv-pod1 pod.

OK 一切正常,Pod 容器内能够正确挂载同一 CephFS 并成功读取到 pod1 创建的文件。下边我们在pod2 中写入文件,看下 pod1 中是否能够读取的到吧!

node0 pod2 中写入文件
$ docker exec -it f8e544e9f497 /bin/sh
/ # vi /mnt/cephfs/cephfs-pv-pod2.txt
/ # cat /mnt/cephfs/cephfs-pv-pod2.txt
this message write by cephfs-pv-pod2 pod from node0.

node1 pod1 读取文件
$ docker exec -it 3cc605bed890 /bin/sh
/ # ls /mnt/cephfs/
cephfs-pv-pod1.txt cephfs-pv-pod2.txt test.txt

依旧妥妥没问题哒!再次说明,k8s 集群支持跨节点挂载 CephFS 文件存储。同时 kubernetes 官网 Persistent Volumes 文档 中指出,当静态 PV 都不匹配用户的 PVC 请求时,k8s 集群还支持 Dynamic 动态提供 Volume 给 PVC,不过这种方式需要配置 StorageClasses,查看 Kubernetes 官网 Storage Classes 文档 Provisioner 部分指出支持 Ceph RBD,下一篇我们继续研究 Kubernetes 集群使用 RBD 作为 StorageClass PVC,来实现分布式存储。
原文链接:https://blog.csdn.net/aixiaoyang168/article/details/79056864
参考资料