k8s之StorageClass+NFS

参考:https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner

参考:https://jimmysong.io/kubernetes-handbook/practice/using-nfs-for-persistent-storage.html

一、什么是StorageClass

参考:https://kubernetes.io/zh/docs/concepts/storage/storage-classes/

StorageClass翻译过来是存储类的意思。

PV这个对象的创建,是由运维人员完成的。但是在大规模的生产环境里面,这是一个非常麻烦的操作。

这是因为,一个大规模的kubernetes集群里面很可能有成千上万个PVC,这就意味着运维人员必须事先创建出成千上万个PV。更麻烦的是,随着PVC不断被提交,运维人员就不得不继续添加新的、能满足条件的PV,否则新的Pod就会因为PVC绑定不到PV而失败,在实际操作中,这几乎没办法靠人工做到。

所以,kubernetes为我们提供了一套可以自动创建PV的机制,即:Dynamic Privisioning。

相比之下,人工管理的PV就叫做Static Provisioning。

Dynamic Provisioning机制工作的核心,在于一个名叫StorageClass的API对象。

而StorageClass对象的作用,其实就是创建PV的模板。

具体的说,StorageClass对象会定义如下两个部分的内容:

  • 第一,PV的属性,比如存储类型,Volume大小等等。
  • 第二:创建PV需要用到的存储插件。比如,Ceph等。

有了这两个信息之后,Kubernetes就能根据用户提交的PVC,找到一个对应的StorageClass了。然后,Kubernetes就会调用该StorageClass声明的存储插件,创建出需要的PV。

举个例子,如果我们使用的GCE的Persistent Disk,需要定义这样一个StorageClass:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: block-service
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-ssd

这个YAML文件里面,我们定义了一个名叫block-service的StorageClass。

provisioner字段值为kubernetes.io/gce-pd,这个正是Kubernetes内置的GCE PD存储插件的名字,其他可以参考这里

parameters字段是PV的参数,上面type=pd-ssd指的是这个PV的类型是“SSD格式的GCE远程磁盘。”

这时候,作为应用开发者,只需要在PVC里面指定要使用的StorageClass名字即可。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: claim1
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: block-service
  resources:
    requests:
      storage: 30Gi

当然,如果kubernetes官方文档里面没有找到想要的插件,则可以通过external-storage这个库来编写一个外部插件来完成这个工作。

就例如我们今天使用的NFS,k8s官方就没有内置的存储插件,不过有已经做好的例子,参考nfs-subdir-external-provisioner

二、部署nfs-server

随便找一个节点部署nfs-server即可,我放到k8s-master节点了

我是ubuntu系统

[root@k8s ~]# apt install nfs-kernel-server rpcbind -y
[root@k8s ~]# mkdir /data/nfs -p
[root@k8s ~]# chmod 777 /data/nfs/
[root@k8s ~]# vim /etc/exports 
[root@k8s ~]# tail -1 /etc/exports
/data/nfs 192.168.66.0/24(rw,sync,no_subtree_check,no_root_squash)
[root@k8s ~]# systemctl restart nfs-kernel-server.service rpcbind
[root@k8s ~]# systemctl enable nfs-kernel-server.service rpcbind
[root@k8s ~]# showmount -e 192.168.66.143
Export list for 192.168.66.143:
/data/nfs 192.168.66.0/24

注意,node节点需要安装nfs-common,不然后面挂载pvc会失败

三、下载yaml清单文件

修改deployment.yaml文件里面的nfs地址和路径

https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner/tree/master/deploy
[root@k8s nfs-sc]# cat deployment.yaml 
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
  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: shuangchenyue/nfs-subdir-external-provisioner:v4.0.2
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: k8s-sigs.io/nfs-subdir-external-provisioner
            - name: NFS_SERVER
              value: 192.168.66.143
            - name: NFS_PATH
              value: /data/nfs
      volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.66.143
            path: /data/nfs

四、创建rbac,deployment,storageclass

创建好后要确认pod正常运行,不正常的话需要自己排查下,大都是nfs挂载失败等

[root@k8s nfs-sc]# kubectl create -f rbac.yaml 
serviceaccount/nfs-client-provisioner created
clusterrole.rbac.authorization.k8s.io/nfs-client-provisioner-runner created
clusterrolebinding.rbac.authorization.k8s.io/run-nfs-client-provisioner created
role.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created
rolebinding.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created
[root@k8s nfs-sc]# kubectl create -f deployment.yaml 
deployment.apps/nfs-client-provisioner created
[root@k8s nfs-sc]# kubectl create -f class.yaml 
storageclass.storage.k8s.io/managed-nfs-storage created

五、设置默认存储

参考:https://kubernetes.io/zh/docs/tasks/administer-cluster/change-default-storage-class/

[root@k8s nfs-sc]# kubectl patch storageclass managed-nfs-storage -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'
storageclass.storage.k8s.io/managed-nfs-storage patched

六、创建pod测试

[root@k8s nfs-sc]# cat test-pod.yaml 
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pv-volume
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: managed-nfs-storage
  resources:
    requests:
      storage: 1Mi
---
apiVersion: v1
kind: Pod
metadata:
  name: web-nginx
spec:
  volumes:
  - name: web
    persistentVolumeClaim:
      claimName: pv-volume
  containers:
  - name: web-nginx
    image: nginx:alpine
    volumeMounts:
    - name: web
      mountPath: /usr/share/nginx/html
[root@k8s nfs-sc]# kubectl create -f test-pod.yaml 
persistentvolumeclaim/pv-volume created
pod/web-nginx created
[root@k8s nfs-sc]# kubectl exec -it web-nginx -- sh -c "echo hello > /usr/share/nginx/html/index.html"
[root@k8s nfs-sc]# curl --noproxy "*" http://172.20.0.49/
hello
#删掉pod重建数据不变,说明pvc挂载成功
[root@k8s nfs-sc]# kubectl delete po web-nginx 
pod "web-nginx" deleted
[root@k8s nfs-sc]# kubectl create -f test-pod.yaml 
pod/web-nginx created
Error from server (AlreadyExists): error when creating "test-pod.yaml": persistentvolumeclaims "pv-volume" already exists
[root@k8s nfs-sc]# kubectl get po web-nginx -owide
NAME        READY   STATUS            RESTARTS   AGE   IP            NODE             NOMINATED NODE   READINESS GATES
web-nginx   0/2     PodInitializing   0          12s   172.20.0.50   192.168.66.143   <none>           <none>
[root@k8s nfs-sc]# curl --noproxy "*" http://172.20.0.50
hello