当前位置:   article > 正文

k8s篇-应用持久化存储(PV和PVC)_k8s pvc

k8s pvc

一、Volume

一般来说,容器一旦被删除后,容器运行时内部产生的所有文件数据也会被清理掉,因此,Docker提供了 Volume 这种方式来将数据持久化存储。

可以说,Volume 是Pod与外部存储设备进行数据传递的通道,也是Pod内部容器间、Pod与Pod间、Pod与外部环境进行数据共享的方式。

实际上,这个 Volume 也只是宿主机上本地磁盘中的一个目录,也就是说,volume方式是将容器里面的数据都保存到宿主机上。除此之外,还能保存到外部存储上。

在k8s中,支持多种类型的Volume:本地存储(emptyDir / hostPath)、外部存储(如NFS)。

1、emptyDir 

若pod使用了emptyDir类型的volume,则在创建pod时,emptyDir volume随着pod也会一同被创建出来。emptyDir volume 会在pod所在的node节点上生成一个空目录,而这个空目录的默认路径是在/var/lib/kubelet/pods/下。

emptyDir 类型相当于执行【docker run -v /CONTAINER/DIR】。

emptyDir Volume与Pod生命周期一致,只要Pod一直运行,该Volume就一直存在,而当Pod被删除时,该Volume也同时会删除,即Node上对应目录也会被删掉。

一个Volume可被Pod中的所有容器共享,且可被挂载到容器的指定路径下。

示例:

  1. 说明:创建一个Pod,Pod有两个容器,它们共享一个Volume,busybox容器负责往 Volume 中写数据,myapp容器则是从 Volume 读取数据
  2. apiVersion: v1
  3. kind: Pod
  4. metadata:
  5. name: pod-demo
  6. spec:
  7. volumes: # 定义emptyDir类型的Volume
  8. - name: myweb
  9. emptyDir: {}
  10. containers:
  11. - name: myapp
  12. image: ikubernetes/myapp:v1
  13. volumeMounts:
  14. - name: myweb
  15. mountPath: /usr/share/nginx/html/
  16. - name: busybox
  17. image: busybox:latest
  18. volumeMounts: # 将名为myweb的volume挂载到容器里的/web目录下
  19. - name: myweb
  20. mountPath: /web
  21. command: [ "/bin/sh", "-c", "while true; do echo $(date) >> /web/index.html; done" ]

查看volume信息:

  1. $ docker inspect 020799d427ae -f "{{.Mounts}}"
  2. "Mounts": [
  3. {
  4. "Type": "bind",
  5. "Source": "/var/lib/kubelet/pods/bb66b0cd-979d-4356-92a9-492d420fc613/volumes/kubernetes.io~empty-dir/html",
  6. "Destination": "/usr/share/nginx/html",
  7. "Mode": "",
  8. "RW": true,
  9. "Propagation": "rprivate"
  10. },

尝试将pod删除,node上的volume目录也会被删除:

  1. $ kubectl delete pod pod-demo
  2. $ ls /var/lib/kubelet/pods/bb66b0cd-979d-4356-92a9-492d420fc613

2、hostPath

该类型是将Node上指定的文件或目录挂载到Pod中。当Pod被删除时,Node上对应的该Volume的文件或目录不会被删除,会保留下来,从这点来看,hostPath的持久性比emptyDir强。不过一旦node节点崩溃了,hostPath也就没法访问了。

hostPath 类型相当于执行【docker run -v /HOST/DIR:/CONTAINER/DIR】。

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: pod-demo2
  5. spec:
  6. volumes:
  7. - name: myweb
  8. hostPath:
  9. path: /data/www/ # 指定node上的目录
  10. type: Directory
  11. containers:
  12. - name: myapp
  13. image: ikubernetes/myapp:v1
  14. volumeMounts:
  15. - name: myweb
  16. mountPath: /usr/share/nginx/html/ #要挂载到容器的哪个目录下

3、外部存储(以NFS为例)

除了将数据存放在本地node节点上,为了更安全,我们也可以将数据存储到外部的远程磁盘上,比如放到NFS服务器上,IP为192.168.100.172。

1)搭建NFS服务器

  1. yum -y install nfs-utils
  2. mkdir -p /data/testvol
  3. echo "NFS Test Data" > /data/testvol/index.html
  4. echo "/data/testvol 192.168.100.0/24(rw,no_root_squash)" >> /etc/exports
  5. systemctl start nfs

2)创建NFS存储卷

  1. # 在k8s集群的节点上,安装nfs-utils工具
  2. $ yum -y install nfs-utils
  3. # 验证是否能成功挂载
  4. $ mount -t nfs 192.168.100.172:/data/testvol /mnt
  5. # yaml文件如下:
  6. $ cat vol-nfs-demo.yaml
  7. apiVersion: v1
  8. kind: Pod
  9. metadata:
  10. name: vol-nfs
  11. spec:
  12. volumes:
  13. - name: myweb
  14. nfs:
  15. path: /data/testvol # NFS共享目录
  16. server: 192.168.100.172 # NFS服务器IP
  17. containers:
  18. - name: myapp
  19. image: ikubernetes/myapp:v1
  20. volumeMounts:
  21. - name: myweb
  22. mountPath: /usr/share/nginx/html/

二、PV与PVC

除了Volume之外,kubernetes 还提供了 Persistent Volume 的方法持久化数据。

它与普通Volume的区别是, 普通Volume和Pod之间是一种静态绑定关系,也就是,在定义pod时,同时要将pod所使用的Volume一并定义好,Volume是Pod的附属品。volume会随着pod创建而被创建,我们无法单独创建一个Volume,因为它不是一个独立的K8S资源对象。

而Persistent Volume则是一个K8S资源对象,它是独立于Pod的,能单独创建。Persistent Volume 不与Pod发生直接关系,而是通过 Persistent Volume Claim(PVC) 来与Pod绑定关系。在定义Pod时,为Pod指定一个PVC,Pod在创建时会根据PVC要求,从现有集群的PV中,选择一个合适的PV绑定,或动态建立一个新的PV,再与其进行绑定。

Persistent Volume(PV):用于定义各种存储资源的配置信息,一个PV对应一个volume,定义一个PV内容包括了 存储类型、存储大小和访问模式等。

Persistent Volume Claim(PVC):描述对PV的一个请求。请求信息包含存储大小、访问模式等。PVC只会选择符合自己要求的PV进行绑定,然后在定义pod时指定使用哪个PVC就可以了。

原理:

PVC和PV的设计,其实跟面向对象的思想完全一致,PVC是面向对象编程中的接口,PV是接口具体的实现。

用户只需通过PVC来声明自己的存储需求,比如存储大小、可读写权限等,类似于调用接口函数并传入属性参数,而不用关心后端存储实现细节,这些都交由运维人员统一管理即可。

Pod是直接与PVC绑定关系,再根据PVC存储需求,去找到对应PV。PVC只有绑定了PV之后才能被Pod使用。

PersistentVolume Controller 会不断地查看当前每一个PVC,是不是已经处于Bound(已绑定)状态。如果不是,那它就会遍历所有的、可用的PV,并尝试将其与这个PVC进行绑定。这样,Kubernetes就可以保证用户提交的每一个PVC,只要有合适的PV出现,它就能够很快进入绑定状态。

在提交PVC后,是如何找到对应的PV:先根据PVC的accessModes匹配出PV列表,再根据PVC的Capacity、StorageClassName、Label Selector进一步筛选PV。如果满足条件的PV有多个,选择PV的size最小的,accessmodes列表最短的PV,也即最小适合原则。

也就是说,PVC绑定PV的过程是有一定规则的,以下规则都满足的PV才能被PVC绑定:

VolumeMode:被消费PV的VolumeMode需要和PVC一致。

AccessMode:被消费PV的AccessMode需要和PVC一致。

StorageClassName:如果PVC定义了此字段,则PV也必须有对应字段才能进行绑定。

LabelSelector:通过标签(labels)匹配的方式从PV列表中选择合适的PV绑定。

Size:被消费PV的capacity必须大于或等于PVC的存储容量需求才能被绑定。

PV类型:

一般来说,PV又有多种类型:Static PV (静态)、Dynamic PV (动态)、Local PV (本地)。

Static/Dynamic PV:静态和动态PV

PV创建虽是由运维人员完成的,但在一个大规模的Kubernetes集群里,很可能有成千上万个PVC,这就意味着运维人员必须得事先创建出成千上万个PV,如果单纯靠人工来管理,会存在一定的困难。

Kubernetes提供了一套可以自动创建PV的机制,即Dynamic Volume Provisioning(动态PV)。而手动创建并管理的PV叫做Static Volume Provisioning(静态PV)。

Dynamic PV创建机制的核心,在于一个名为StorageClass的API对象,它是一个用于创建PV的模板。

在YAML文件中定义PVC时,需要指定一个StorageClass名称,然后等到用户要创建这个PVC时,系统会根据PVC定义的需求,并参考StorageClass的存储细节,最后通过调用StorageClass声明的存储插件(Provisioner),动态创建出需要的PV。

所以,在声明一个PVC时,如果在PVC中添加了StorageClassName字段,那就意味着,当PVC在集群中找不到匹配的PV时,它会根据StorageClassName的定义,触发相应的Provisioner插件创建出合适的PV进行绑定。

也就是说,现在无需事先创建好将来要用到的PV,只要通过StorageClass准备好一些PV模板,等到将来要使用时,PVC再直接使用StorageClass定义好的PV模板,调用存储插件将PV一并创建出来就可以了。

Local-PV:本地PV

一是,不应该随便把node上的任何一个目录当作PV使用,因为不安全,应该额外挂载一个外部磁盘到node上,也就是,一个PV对应一块外部数据盘。

二是,调度器要保证Pod始终能被正确地调度到它所请求的Local PV所在的节点上,那调度器就要知道所有node与local pv的关联关系,即PV的位置分布信息(也叫存储拓扑信息),然后根据这个位置信息来调度Pod。

流程图:

1:先准备好外部存储资源;
2:然后通过static或dynamic方式,将存储资源定义成PV;
3:定义PVC资源请求,PVC会根据配置描述选择合适的PV;
4:最后Pod指定使用哪个PVC,最终是由PVC将Pod与匹配的PV绑定在一起;

  1. ————————————————————————————————
  2. |namespace |
  3. | |
  4. | [pod1] [pod2] |
  5. | ↓ ↓ |
  6. | [volume1] [volume2] |
  7. | ↑ ↑ |
  8. | | / |
  9. | ↓ ↓ |
  10. | [pvc] [pvc] [pvc] |
  11. ——————↑—————————↑————————↑——————
  12. / \ |_ _ _ _ _ __
  13. / \ ↓
  14. ↓ ↓ ↓
  15. [pv] [pv] | | [pv] [pv] [pv] | [pv] [pv] [pv]
  16. | | |
  17. static | | storageClass | storageClass
  18. ——————————— —————————————— ————————————————
  19. ↑↑ ↑↑
  20. ↑↑ ↑↑
  21. ———————————————— ——————————————————————
  22. [NFS] [ISCSI] [Ceph RDB] [Glusterfs]

PV状态:

Create PV ---> pending ---> available ---> bound ---> released ---> deleted或failed

Available:创建PV后,会短暂处于pending状态,等真正创建好后,就会进入available状态,只有处于该状态下的PV才能够被PVC绑定。

Bound:用户在提交PVC后,并找到相应PV,此时PV与PVC已绑在一起,两者都处于BOUND状态。

Released:如果PV设置了ReclaimPolicy策略为retain,也就是当用户在使用完PVC,将其删除后,对应的这个PV就会处于released状态。

当PV已经处在released状态时,它是无法直接回到available状态,也就是说,接下来这个PV无法被一个新的PVC去做绑定。

有两种方式复用处于released状态的PV:

一种是对之前released的PV做好数据备份,然后重新创建一个PV,并将之前released的PV相关字段的信息填到这个PV中。另一种是在删除Pod后,不要删除PVC,将PVC保留下来供其他Pod直接复用。

1、Static PV

这里还是以NFS服务器作为PV存储为例。

1)先搭建好NFS服务器(192.168.100.172)

创建4个NFS共享目录:

  1. yum -y install nfs-utils
  2. mkdir -p /data/volumes/v{1..5}
  3. echo "<h1>NFS stor 01</h1>" > /data/volumes/v1/index.html
  4. echo "<h1>NFS stor 02</h1>" > /data/volumes/v2/index.html
  5. echo "<h1>NFS stor 03</h1>" > /data/volumes/v3/index.html
  6. echo "<h1>NFS stor 04</h1>" > /data/volumes/v4/index.html
  7. cat > /etc/exports << EOF
  8. /data/volumes/v1 192.168.100.0/24(rw,no_root_squash)
  9. /data/volumes/v2 192.168.100.0/24(rw,no_root_squash)
  10. /data/volumes/v3 192.168.100.0/24(rw,no_root_squash)
  11. /data/volumes/v4 192.168.100.0/24(rw,no_root_squash)
  12. EOF
  13. systemctl start nfs
  14. exportfs -rv # 重新加载配置
  15. showmount -e # 查看本地有哪些共享目录

2)创建PV

创建4个pv,都使用nfs共享的目录,存储大小各不相同,是否可读也不相同。

  1. apiVersion: v1
  2. kind: PersistentVolume
  3. metadata:
  4. name: pv0001
  5. labels:
  6. name: pv0001
  7. spec:
  8. nfs:
  9. path: /data/volumes/v1
  10. server: 192.168.100.172
  11. accessModes: ["ReadWriteMany","ReadWriteOnce"]
  12. capacity:
  13. storage: 2Gi
  14. ---
  15. apiVersion: v1
  16. kind: PersistentVolume
  17. metadata:
  18. name: pv0002
  19. labels:
  20. name: pv0002
  21. spec:
  22. nfs:
  23. path: /data/volumes/v2
  24. server: 192.168.100.172
  25. accessModes: ["ReadWriteOnce"]
  26. capacity:
  27. storage: 7Gi
  28. ---
  29. apiVersion: v1
  30. kind: PersistentVolume
  31. metadata:
  32. name: pv0003
  33. labels:
  34. name: pv0003
  35. spec:
  36. nfs:
  37. path: /data/volumes/v3
  38. server: 192.168.100.172
  39. accessModes: ["ReadWriteMany","ReadWriteOnce"]
  40. capacity:
  41. storage: 10Gi
  42. ---
  43. apiVersion: v1
  44. kind: PersistentVolume
  45. metadata:
  46. name: pv0004
  47. labels:
  48. name: pv0004
  49. spec:
  50. nfs:
  51. path: /data/volumes/v4
  52. server: 192.168.100.172
  53. accessModes: ["ReadWriteMany","ReadWriteOnce"]
  54. capacity:
  55. storage: 15Gi

字段说明:

capacity:设置PV的存储属性,比如存储大小。

accessModes:设置对Volume的访问模式
ReadWriteOnce – the volume can be mounted as read-write by a single node
ReadOnlyMany – the volume can be mounted read-only by many nodes
ReadWriteMany – the volume can be mounted as read-write by many nodes

persistentVolumeReclaimPolicy:当PVC被删除时,对应PV的回收策略
Retain - 当PVC被删除时,PV会保留,但被标识为released状态
Delete - 当PVC被删除时,PV也同时被删除
Recycle - 已废弃

查看PV信息:

  1. $ kubectl get pv
  2. NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
  3. pv0001 2Gi RWO,RWX Retain Available 6s
  4. pv0002 7Gi RWO Retain Available 6s
  5. pv0003 10Gi RWO,RWX Retain Available 6s
  6. pv0004 15Gi RWO,RWX Retain Available 6s

3)创建PVC

创建一个PVC,创建好后,该PVC会根据要求请求合适的PV资源。

  1. 如:下面的PVC会绑定到名为pv0003的PV上
  2. apiVersion: v1
  3. kind: PersistentVolumeClaim
  4. metadata:
  5. name: mypvc
  6. spec:
  7. accessModes: ["ReadWriteMany"] #匹配PV的accessModes要包含ReadWriteMany
  8. resources:
  9. requests:
  10. storage: 6Gi #且匹配PV要大于6G

4)创建Pod

创建一个Pod,并指定使用哪个PVC,PVC会决定将哪个PV绑定到此Pod上。

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: pod-demo
  5. namespace: default
  6. spec:
  7. volumes:
  8. - name: myvol
  9. persistentVolumeClaim:
  10. claimName: mypvc #为此Pod指定使用哪个PVC
  11. containers:
  12. - name: myapp
  13. image: ikubernetes/myapp:v1
  14. volumeMounts:
  15. - name: myvol
  16. mountPath: /usr/share/nginx/html/

创建Pod后,查看PV和PVC状态:

  1. $ kubectl get pv
  2. NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
  3. pv0001 2Gi RWO,RWX Retain Available 18m
  4. pv0002 7Gi RWO Retain Available 18m
  5. pv0003 10Gi RWO,RWX Retain Bound default/mypvc 18m
  6. pv0004 15Gi RWO,RWX Retain Available 18m
  7. $ kubectl get pvc
  8. NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
  9. mypvc Bound pv0003 10Gi RWO,RWX 17s

测试访问:

  1. $ kubectl get pods pod-demo -o wide
  2. NAME READY STATUS RESTARTS AGE IP NODE
  3. pod-demo 1/1 Running 0 3m21s 10.244.1.73 node1
  4. $ curl 10.244.1.73
  5. <h1>NFS stor 03</h1>

2、Dynamic PV

步骤1:定义2个StorageClass(创建生成PV的模板文件),一个为普通磁盘,一个为SSD磁盘。

  1. apiVersion: storage.k8s.io/v1
  2. kind: StorageClass
  3. metadata:
  4. name: slow
  5. provisioner: kubernetes.io/aws-ebs # 指定一个volume plugin,即应该用哪个存储插件来去创建PV
  6. parameters:
  7. type: pd-standard
  8. reclaimPolicy: Delete # PV的回收策略,默认为delete
  9. ---
  10. apiVersion: storage.k8s.io/v1
  11. kind: StorageClass
  12. metadata:
  13. name: fast
  14. provisioner: kubernetes.io/gce-pd
  15. parameters:
  16. type: pd-ssd
  17. ---
  18. apiVersion: storage.k8s.io/v1
  19. kind: StorageClass
  20. metadata:
  21. name: csi-disk
  22. parameters:
  23. regionId: cn-hangzhou
  24. zoneId: cn-hangzhou-b
  25. fsType: ext4
  26. type: cloud_ssd
  27. provisioner: diskplugin.csi.alibabacloud.com
  28. reclaimPolicy: Delete

步骤2:创建PVC,并指定storageClassName名,即到底用哪一个模板文件来生成PV,Kubernetes只会将StorageClass相同的PVC和PV绑定起来。

  1. apiVersion: v1
  2. kind: PersistentVolumeClaim
  3. metadata:
  4. name: disk-pvc
  5. spec:
  6. accessModes:
  7. - ReadWriteOnce
  8. storageClassName: csi-disk # 为PVC指定使用哪个StorageClass
  9. resources:
  10. requests:
  11. storage: 30Gi

步骤3:创建Pod,并指定要调用的PVC

  1. kind: Pod
  2. apiVersion: v1
  3. metadata:
  4. name: mypod
  5. spec:
  6. containers:
  7. - name: myfrontend
  8. image: dockerfile/nginx
  9. volumeMounts:
  10. - mountPath: "/var/www/html"
  11. name: mypd
  12. volumes:
  13. - name: mypd
  14. persistentVolumeClaim:
  15. claimName: disk-pvc

三、部署NFS动态存储卷(案例)

1、master和node节点安装nfs服务

yum -y install nfs-utils rpcbind
systemctl start nfs && systemctl enable nfs
systemctl start rpcbind && systemctl enable rpcbind

# master上创建并配置好共享挂载目录(node节点不需要,只需安装好nfs服务就行)
mkdir -pv /data/volumes/{v1,v2,v3}
cat > /etc/exports <<EOF
/data/volumes/v1  *(rw,no_root_squash,no_all_squash)
/data/volumes/v2  *(rw,no_root_squash,no_all_squash)
/data/volumes/v3  *(rw,no_root_squash,no_all_squash)
EOF

# 发布并查看
exportfs -arv
showmount -e

2、部署 NFS Provisioner插件(master)

git clone https://github.com/kubernetes-incubator/external-storage.git
cd external-storage/nfs-client/deploy/
cp class.yaml deployment.yaml rbac.yaml test-claim.yaml /var/lib/k8s/storage

cd /var/lib/k8s/storage

a. 配置rbac授权(默认是存放在default空间,可修改为kube-system)
kubectl apply -f rbac.yaml

b. 部署 NFS Provisioner插件

# 修改deployment.yaml文件(设置nfs服务器相关信息)

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: nfs-client-provisioner
  5. labels:
  6. app: nfs-client-provisioner
  7. namespace: kube-system #默认是default,修改为kube-sytem
  8. spec:
  9. replicas: 1
  10. strategy:
  11. type: Recreate
  12. selector:
  13. matchLabels:
  14. app: nfs-client-provisioner
  15. template:
  16. metadata:
  17. labels:
  18. app: nfs-client-provisioner
  19. spec:
  20. serviceAccountName: nfs-client-provisioner
  21. containers:
  22. - name: nfs-client-provisioner
  23. # image: quay.io/external_storage/nfs-client-provisioner:latest
  24. image: registry.cn-hangzhou.aliyuncs.com/open-ali/nfs-client-provisioner:latest
  25. volumeMounts:
  26. - name: nfs-client-root
  27. mountPath: /persistentvolumes
  28. env:
  29. - name: PROVISIONER_NAME
  30. value: nfs-client #可自定义,要与class.yaml中的provisioner的名称一致,否则部署不成功
  31. - name: NFS_SERVER
  32. value: x.x.x.x #修改为nfs服务器地址
  33. - name: NFS_PATH
  34. value: /data/volumes/v1 #NFS服务器中的共享挂载目录
  35. volumes:
  36. - name: nfs-client-root
  37. nfs:
  38. server: x.x.x.x #修改为nfs服务器地址
  39. path: /data/volumes/v1 #NFS服务器中的共享挂载目录

# 部署NFS Provisioner插件
kubectl apply -f deployment.yaml

# 查看安装情况
kubectl get pod -o wide -n kube-system -l app=nfs-client-provisioner

  1. [root@jdmaster ~]# kubectl get pod -o wide -n kube-system -l app=nfs-client-provisioner
  2. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
  3. nfs-client-provisioner-7f75997fb6-tchxb 1/1 Running 0 21h 10.244.1.8 jdnode <none> <none>

kubectl get deployment -n kube-system

  1. [root@jdmaster ~]# kubectl get deployment -n kube-system
  2. NAME READY UP-TO-DATE AVAILABLE AGE
  3. coredns 2/2 2 2 3d
  4. nfs-client-provisioner 1/1 1 1 21h

3、创建storageclass

# 修改class.yaml

  1. apiVersion: storage.k8s.io/v1
  2. kind: StorageClass
  3. metadata:
  4. name: managed-nfs-storage
  5. annotations:
  6. storageclass.kubernetes.io/is-default-class: "true" #设置为默认的storageclass(如果不设置默认,在创建pvc时,需要手动指定storageclass的名称,否则会处于pending)
  7. provisioner: nfs-client #可自定义,要与 deployment 中 env PROVISIONER_NAME 一致
  8. parameters:
  9. archiveOnDelete: "false"
  10. # 也命令方式设置默认的StorageClass(NAME后面会多出一个default字样)
  11. # kubectl patch storageclass managed-nfs-storage -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

# 创建storageclass
kubectl apply -f class.yaml
# 查看(storageclass所有命名空间都可以看到,不需要指定某个namespace)
kubectl get sc

  1. [root@jdmaster ~]# kubectl get sc
  2. NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
  3. managed-nfs-storage (default) nfs-client Delete Immediate false 21h

4、创建PVC

# 修改test-claim.yaml

  1. kind: PersistentVolumeClaim
  2. apiVersion: v1
  3. metadata:
  4. name: test-claim
  5. #annotations:
  6. # volume.beta.kubernetes.io/storage-class: "managed-nfs-storage" #若这里指定storageclass了,可以不用手动指定storageClassName字段
  7. spec:
  8. storageClassName: managed-nfs-storage #若没有设置默认的storageclass,必须手动指定使用哪个storageclass
  9. accessModes:
  10. - ReadWriteMany
  11. resources:
  12. requests:
  13. storage: 1Gi

# 查看pvc状态(会处于Bound状态,若处于pending是不正常的)
kubectl get pvc

  1. [root@jdmaster ~]# kubectl get pvc
  2. NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
  3. test-claim Bound pvc-bcbea3c8-4a37-47c1-8aa6-2c2da6964108 1Gi RWX managed-nfs-storage 20h

# 只要创建好NFS Provisioner deployment 和 storageclass后,再创建PVC时,就会自动创建出PV
kubectl get pv

  1. [root@jdmaster ~]# kubectl get pv
  2. NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
  3. pvc-bcbea3c8-4a37-47c1-8aa6-2c2da6964108 1Gi RWX Delete Bound default/test-claim managed-nfs-storage 20h

5、创建一个测试pod

kubectl apply -f test-pod.yaml

  1. cat > test-pod.yaml <<EOF
  2. kind: Pod
  3. apiVersion: v1
  4. metadata:
  5. name: test-pod
  6. spec:
  7. containers:
  8. - name: test-pod
  9. image: busybox:latest
  10. command:
  11. - "/bin/sh"
  12. args:
  13. - "-c"
  14. - "touch /mnt/SUCCESS && exit 0 || exit 1"
  15. volumeMounts:
  16. - name: nfs-pvc
  17. mountPath: "/mnt"
  18. restartPolicy: "Never"
  19. volumes:
  20. - name: nfs-pvc
  21. persistentVolumeClaim:
  22. claimName: test-claim
  23. EOF

# POD会成功创建好SUCCESS文件,就退出了,状态为Completed
# 进入到NFS共享目录(/data/volumes/v1/<namespace名称>-<pvc名称>-<pv名称>/),查看SUCCESS文件是否存在

四、架构设计


PV和PVC的处理流程:
【配图-待补充】


csi全称是container storage interface,它是K8s社区后面对存储插件实现(out of tree)的官方推荐方式。

csi的实现大体可分为两部分:

第一部分:是由k8s社区驱动实现的通用的部分,如图中的csi-provisioner和csi-attacher controller;

第二部分:由云存储厂商实践的,对接云存储厂商的OpenApi,主要是实现真正的create/delete/mount/unmount 存储的相关操作,对应到图中的csi-controller-server和csi-node-server。

用户在提交PVC yaml时,首先会在集群中生成一个PVC对象,然后PVC对象会被csi-provisioner controller watch到,csi-provisioner会结合PVC对象及PVC对象中声明的 storageClass,通过GRPC调用csi-controller-server。然后,到云存储服务这边去创建真正的存储,并最终创建出来PV对象。最后,由集群中的PV controller将PVC和PV对象做bound 之后,这个PV就可以被使用了。

用户在提交pod之后,首先会被scheduler调度选中某一个合适的node,然后node上的kubelet在创建pod时,会先通过csi-node-server将之前创建的PV挂载到pod指定路径。
然后kubelet开始 create && start pod 中的所有container。


PV、PVC及通过csi使用存储流程:
【配图-待补充】

第一个阶段:create阶段,主要是创建存储。

用户提交完PVC,由csi-provisioner创建存储,并生成PV对象,之后PV controller将PVC及生成的PV对象做bound,bound之后,create阶段就完成了。

第二个阶段:attach阶段,将对应的存储挂载到node上。

用户在提交pod之后,首先会被scheduler调度选中某一个合适的node,node被选出来后,AD Controller会watch到该node,它会根据Pod使用了哪些PV,生产一个内部的VolumeAttachment对象,从而去触发csi-attacher去调用csi-controller-server去做真正的attach操作,attach操作调到云存储厂商OpenAPI,并将存储attach到pod将会运行的node上面。

第三个阶段:mount阶段,将对应的存储进一步挂载到pod里。

在kubelet创建pod的过程中,会先做一个mount操作,这是为了将已经attach到这个node上的那块盘,进一步mount到pod可使用的一个具体路径,之后kubelet才开始创建并启动容器。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/171320
推荐阅读
相关标签
  

闽ICP备14008679号