Kubernetes StorageClass实践

手动创建PV不仅繁琐,还可能造成资源浪费。比如某个PV定义的存储空间为10Gi,该PV被某个声明需要8Gi内存的PVC绑定上了,这时候该PV处于Bound状态,无法再和别的PVC进行绑定,PV上剩下的2Gi内存实际上浪费的。StorageClass可以根据PVC的声明,动态创建对应的PV,这样不仅省去了创建PV的过程,还实现了存储资源的动态供应。

StorageClass构成

StorageClass的定义主要包括名称、后端存储的提供者(provisioner)和后端存储的相关参数配置。StorageClass一旦被创建出来,则将无法修改。如需更改,则只能删除原StorageClass的定义重建。

举个简单的StorageClass配置:

1
2
3
4
5
6
7
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: managed-nfs-storage # StorageClass名称
provisioner: fuseim.pri/ifs # 指定具体存储的提供者
parameters: # 后端存储相关参数配置
archiveOnDelete: "false"

不同的StorageClass主要区别在于:不同的存储提供者需要填写不同的参数配置,下面实现个NFS作为动态StorageClass存储的例子。

基于NFS存储类型的实践

NFS环境在上一节已经搭建好了,IP为192.168.33.13,路径为/nfs。

在master节点上克隆相关代码:

1
git clone https://github.com/kubernetes-incubator/external-storage.git

切换到external-storage/nfs-client/deploy目录

1
cd external-storage/nfs-client/deploy

创建RBAC:

1
kubectl create -f rbac.yml

QQ截图20191126155024.png

接着部署NFS Client Provisioner,部署前修改deployment.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
labels:
app: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: nfs-client-provisioner
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
image: quay.io/external_storage/nfs-client-provisioner:latest
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: mrbird.cc/nfs # 名称随你定义
- name: NFS_SERVER
value: 192.168.33.13 # NFS 服务IP
- name: NFS_PATH
value: /nfs # NFS 目录
volumes:
- name: nfs-client-root
nfs:
server: 192.168.33.13 # NFS 服务IP
path: /nfs # NFS 目录

然后创建该deployment:

1
kubectl create -f deployment.yml

修改class.yaml配置:

1
2
3
4
5
6
7
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: managed-nfs-storage # StorageClass名称,随你定义
provisioner: mrbird.cc/nfs # 和上面deployment里定义的一致
parameters:
archiveOnDelete: "false"

创建这个StorageClass:

1
kubectl create -f class.yml

QQ截图20191126160143.png

创建PVC,PVC的定义对应test-claim.yml:

1
2
3
4
5
6
7
8
9
10
11
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: test-claim
spec:
storageClassName: managed-nfs-storage # 指定StorageClass名称,即我们上面定义的StorageClass
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Mi

创建这个PVC:

1
kubectl create -f test-claim.yml

QQ截图20191126160628.png

状态为Bound,说明已经成功绑定上了存储。

查看PV,会看到系统自动创建了PV: QQ截图20191126161957.png

自动创建的PV名称为pvc-3b8c5364-aadb-4b07-a3b5-fddad280fc98。

最后,修改test-pod.yml配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
kind: Pod
apiVersion: v1
metadata:
name: test-pod
spec:
containers:
- name: test-pod
image: busybox # 修改为这个,原先的镜像地址需要科学上网
command:
- "/bin/sh"
args:
- "-c"
- "touch /mnt/SUCCESS && exit 0 || exit 1"
volumeMounts:
- name: nfs-pvc
mountPath: "/mnt"
restartPolicy: "Never"
volumes:
- name: nfs-pvc
persistentVolumeClaim:
claimName: test-claim # 指定PVC名称,对应上面创建的PVC

该Pod主要就是通过busybox在/mnt目录下创建了个SUCCESS文件。

创建该Pod:

1
kubectl create -f test-pod.yml

QQ截图20191126160933.png

状态为Completed,说明busybox成功执行完了命令并结束了。如果一切顺利的话,在192.168.33.13的/nfs目录下会看到busybox pod创建的SUCCESS文件:

QQ截图20191126161215.png

可以看到/nfs目录下新增了一个目录,目录名称格式为:[namespace]-[pvc名称]-[pv名称]。

我们还可以玩一下另一个测试,新建一个test-pod-rc.yml文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
apiVersion: v1
kind: ReplicationController
metadata:
name: busybox
spec:
replicas: 2 # 副本数为2,为了测试共享存储
selector:
name: busybox
template:
metadata:
labels:
name: busybox
spec:
containers:
- image: busybox
command:
- sh
- -c
- 'while true; do sleep $(($RANDOM % 5 + 5)); done'
imagePullPolicy: IfNotPresent
name: busybox
volumeMounts:
- name: nfs
mountPath: "/mnt"
volumes:
- name: nfs
persistentVolumeClaim:
claimName: test-claim # 指定PVC名称,对应上面创建的PVC

上面busybox做的事情很简单,就是无限期休眠。创建该rc:

QQ截图20191126164355.png

可以看到,pod分别部署到了node1和node2上。到node1节点上,进入busybox容器内部的/mnt目录,在该目录下创建一个hello文件:

QQ截图20191126164821.png

然后到node2节点上,进入busybox容器内部/mnt目录,观察刚刚在node1节点busybox容器内部创建的hello文件是否已经同步过来了:

QQ截图20191126165107.png

可以看到,文件同步成功。并且前面例子创建SUCCESS也同步过来了,这是因为它们指定了同一个PVC。

回到NFS服务器的/nfs目录下,可以看到刚刚创建的hello文件:

QQ截图20191126165229.png

请作者喝瓶肥宅水🥤

0