1.PV ReadWriteMany的需求
最近一个正在开发的服务对PersistentVolume的Access Mode有ReadWriteMany的需求。 PersistentVolume持久化卷(即PV)是Kubernetes对存储的抽象,PV可以是网络存储,不属于任何Node,但可以在每个Node上访问。PV有以下三种访问模式(Access Mode):
- ReadWriteOnce:只可被一个Node挂载,这个Node对PV拥有读写权限
- ReadOnlyMany: 可以被多个Node挂载,这些Node对PV只有只读权限
- ReadWriteMany: 可以被多个Node挂载,这些Node对PV拥有读写权限
我们之前使用Ceph RBD作为Kubernetes集群的PV,在使用场景上基于Kubernetes的Dynamic Storage Provision特性,使用provisioner: kubernetes.io/rbd的StorageClass来动态创建PV并由volumeClaimTemplates中声明自动创建的PVC去绑定,当前这种形式很稳定的支撑了我们业务的需求。
但RBD的访问模式是ReadWriteOnce的,面对最近的ReadWriteMany的需求,我们需要引入新的存储类型。在 Persistent Volumes文档中以表格的形式列出了当前Kubernetes个Volume Plugin支持的访问模式:
| Volume Plugin | ReadWriteOnce | ReadOnlyMany | ReadWriteMany |
|---|---|---|---|
| AWSElasticBlockStore | ✓ | - | - |
| AzureFile | ✓ | ✓ | - |
| AzureDisk | ✓ | - | - |
| CephFS | ✓ | ✓ | ✓ |
| Cinder | ✓ | - | - |
| FC | ✓ | ✓ | - |
| FlexVolume | ✓ | ✓ | - |
| Flocker | ✓ | - | - |
| GCEPersistentDisk | ✓ | ✓ | - |
| Glusterfs | ✓ | ✓ | ✓ |
| HostPath | ✓ | - | - |
| iSCSI | ✓ | ✓ | - |
| PhotonPersistentDisk | ✓ | - | - |
| Quobyte | ✓ | ✓ | ✓ |
| NFS | ✓ | ✓ | ✓ |
| RBD | ✓ | ✓ | - |
| VsphereVolume | ✓ | - | -(works when pods are collocated) |
| PortworxVolume | ✓ | - | ✓ |
| ScaleIO | ✓ | ✓ | - |
| StorageOS | ✓ | - | - |
选择不是很多,有CephFS、Glusterfs、NFS等。由于我们已经在使用Ceph的块存储RBD和对象存储,而且当前这个正在开发的服务也不是很关键,所以这里选择使用CephFS。
2.在Ceph集群上创建CephFS
CephFS是一个支持POSIX接口的文件系统。文件系统对于客户端来说可以方便的挂载到本地使用。之前整理过一篇《Ceph文件系统存储之Ceph FS》。
使用ceph-deploy为集群添加MDS:
ceph-deploy --overwrite-conf mds create node1 node2 node3Ceph当前的版本还不支持一个集群中创建多个CephFS,会报Error EINVAL: Creation of multiple filesystems is disabled. To enable this experimental feature, use 'ceph fs flag set enable_multiple true'的错误,即正式环境不建议使用多文件系统,改特性当前只能体验。所以这里我们只创建一个名称为cephfs的CephFS。
一个CephFS需要两个pool来存储数据和元数据:
ceph osd pool create cephfs.data 8
ceph osd pool create cephfs.metadata 8创建CephFS:
ceph fs new cephfs cephfs.metadata cephfs.data创建一个cephfs.client用户,并授权。cephfs默认不会限制客户端用户对路径的访问权限,可以使用下面的命令设置权限同时创建好用户:
ceph fs authorize cephfs client.cephfs / r /data rw这里设置cephfs用户对/有读权限,对/data有读写权限。
接下来我们使用admin用户挂载/,并创建好对应的目录:
mkdir /mnt/mycephfs
mount -t ceph node1:6789,node2:6789,node3:6789:/ /mnt/mycephfs -o name=admin,secret=thesecret
cd /mnt/mycephfs
mkdir data
cd data
mkdir myservice此时cephfs的/data/myservice目录将作为我们正在开发的这个服务的数据目录。
3.在Kubernetes上挂载CephFS
基于使用RBD时使用了Kubernetes的Dynamic Storage Provision特性,在使用CephFS时首先想到了也要使用这个特性。 遗憾的是当前版本的Kubernetes还不支持CephFS作为Internal Provisioner。
| Volume Plugin | Internal Provisioner |
|---|---|
| AWSElasticBlockStore | ✓ |
| AzureFile | ✓ |
| AzureDisk | ✓ |
| CephFS | - |
| Cinder | ✓ |
| FC | - |
| FlexVolume | - |
| Flocker | ✓ |
| GCEPersistentDisk | ✓ |
| Glusterfs | ✓ |
| iSCSI | - |
| PhotonPersistentDisk | ✓ |
| Quobyte | ✓ |
| NFS | - |
| RBD | ✓ |
| VsphereVolume | ✓ |
| PortworxVolume | ✓ |
| ScaleIO | ✓ |
| StorageOS | ✓ |
| Local | - |
所以这里我们手动创建PV和PVC,并在服务的Deployment中使用PVC挂载数据卷。
将client.cephfs的keyring转成base64编码:
ceph auth get-key client.cephfs | base64
QVFBemNZVlo2c2JKTGhBQTdxQ0J5d00raVBSZ0FHOTdGdG9YSXc9PQ==创建Secret ceph-secret-client-cephfs
apiVersion: v1
kind: Secret
metadata:
name: ceph-secret-client-cephfs
namespace: dev
type: kubernetes.io/rbd
data:
key: QVFBemNZVlo2c2JKTGhBQTdxQ0J5d00raVBSZ0FHOTdGdG9YSXc9PQ==创建PV和PVC:
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: myservice-pv
labels:
name: myservice-pv
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteMany
cephfs:
monitors:
- node1:6789
- node2:6789
- node3:6789
path: /data/myservice
user: cephfs
secretRef:
name: ceph-secret-client-cephfs
readOnly: false
persistentVolumeReclaimPolicy: Retain
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myservice-pvc
namespace: dev
spec:
accessModes:
- ReadWriteMany
storageClassName: ""
resources:
requests:
storage: 2Gi
selector:
matchLabels:
name: myservice-pv注意这里的storageClassName要给"",这是因为我们的集群中已经创建了默认的StorageClass,如果不给"“的话,将会按默认的StorageClass来。
最后在Deployment中使用myservice-pvc这个PVC即可:
---
kind: Deployment
apiVersion: apps/v1beta2
metadata:
labels:
app: myservice
name: myservice
namespace: dev
spec:
replicas: 1
selector:
matchLabels:
app: myservice
template:
metadata:
labels:
app: myservice
spec:
containers:
- name: myservice
image: harbor.frognew.com/public/myservice:latest
imagePullPolicy: Always
resources:
requests:
memory: "2Gi"
cpu: "250m"
limits:
memory: "2Gi"
cpu: "500m"
volumeMounts:
- name: data
mountPath: /data
ports:
- containerPort: 8080
protocol: TCP
volumes:
- name: data
persistentVolumeClaim:
claimName: myservice-pvc