12.PV、PVC、StrageClass、本地持久存储

介绍StorageClass安装的一篇文章

https://huaweicloud.csdn.net/638db4d4dacf622b8df8cc0a.html#%E4%B8%80%EF%BC%9AKubeSphere%E6%A6%82%E8%BF%B0

一、PV

  PresistentVolume持久卷:

    是集群资源,不能指定namespace。

    • 定义PV容量
    • PV访问权限
      • readWriteOnce:可读可写,只能被单个节点发发,支持被单个pod挂载,replicas为=1
      • readOnlyMany:以只读方式被多个pod挂载,replicas可以>1
      • readWriteMany:以读写方式被多个pod共享,replicas可以>1
    • pv连接的存储后端地址 

 PV使用nfs:

  1.mount nfs到主机,再mount到pod里。

  PV卷阶段状态

    Available:未被claim使用。

    Bound:已绑定到claim。

    Released:claim被删除,卷处于释放状态,未被集群回收。

    Failed:卷自动回收失败。

二、PVC

  PresistenVolumeClaim

    消耗/使用pv资源

    由用户进行存储的请求。类似于pod。声明请求特定的大小和访问模式。

  pvc与pv是一一对应的。

  PVC通过pv StorageClass-name绑定PV 

PV与PVC的绑定原则

  PV和PVC中的spec关键字段要匹配,比如存储(storage)大小。

  PV和PVC中的storageClassName字段必须一致。

 

 三、生命周期

  PV是集群中的资源。PVC是对这些资源的请求。

  Provisioning----->Binding----->Using----->Releasing----->Recycling

releasing:用户删除pvc回收存储资源,pv将变成 “released”  状态。保留着之前的数据,这些数据需要依策略来处理,否则这些PV无法被其他pvc使用。

Recycling:三种策略

  Retain:允许人工处理保留的数据。

  Recycle:将删除pv和外部关联的存储资源,需要插件支持。删除pvc时同时清除pv中的数据,pv将变成available状态。 在1.14版中已弃用,推荐使用动态存储供给策略:删除与该PV关联的PVC时,自动删除该PV中的所有数屈打成招

  Delete:将执行清除操作,之后可以被新的pvc使用,需要插件支持。NFS不支持

注:目前只有NFS和HostPath类型卷支持Recycle,AWS EBS、GCE PD、Azure Disk和Cinder支持Delete。

kubectl delete pv pv-name   ##Retain回收

四、例:

NFS

4.1准备nfs服务器

cd /data/volumes/    ##创建nfs服务器上的共享卷
mkdir v{1,2,3,4,5}

echo "<h1>NFS stor 01</h1>" >v1/indes.html
.......
echo "<h1>NFS stor 05</h1>" >v5/indes.html

vim /etc/exports    ##配置nfs共享内容
/data/volumes/v1    10.146.16.0/24(rw,no_root_squash)
......
/data/volumes/v5    10.146.16.0/24(rw,no_root_squash)

exportfs -arv  ##使nfs生效

showmount -e  ##检查

4.2.在master上创建pv

vim pv-damo.yaml
apiVersion: v1 kind: PersistentVolume metadata: name: pv001 labels: name: pv001 spec:
persistentVolumeReclaimPolicy: Delete ##设置回收策略
nfs: path:
/data/volumes/v1 server: nfs accessModes: ["ReadWriteMany","ReadWriteOnce"] capacity: storage: 2Gi --- apiVersion: v1 kind: PersistentVolume metadata: name: pv002 labels: name: pv002 spec: nfs: path: /data/volumes/v2 server: nfs accessModes: ["ReadWriteOnce"] capacity: storage: 5Gi --- apiVersion: v1 kind: PersistentVolume metadata: name: pv003 labels: name: pv003 spec: nfs: path: /data/volumes/v3 server: nfs accessModes: ["ReadWriteMany","ReadWriteOnce"] capacity: storage: 20Gi --- apiVersion: v1 kind: PersistentVolume metadata: name: pv004 labels: name: pv004 spec:
  storageClassName: nfs    ##PVC用storageClassName与PV绑定
  nfs:
    path: /data/volumes/v4
    server: nfs
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 10Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv005
  labels:
    name: pv005
spec:
  nfs:
    path: /data/volumes/v5
    server: nfs
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 15Gi
kubectl apply -f pv-damo.yaml

kubectl get pv

4.3.创建PVC,绑定PV

创建一个pvc,需要6G存储,v1、v2、v3都不符合

#vim vol-pvc-demo.yaml

apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mypvc namespace: default spec:
  storageClassName: nfs    ##PVC用storageClassName与PV绑定
  accessModes: ["ReadWriteMany"]  ##它的模式必须与PV中定义的相匹配或是PV中定义的子集。
  resources:  ##定义资源要求PV满足这个PVC的要求才会被匹配到。
    requests:
      storage: 6Gi  #定义要求的空间大小
---
apiVersion: v1
kind: Pod
metadata:
  name: vol-pvc
  namespace: default
spec:
  volumes:
  - name: html
    persistentVolumeClaim:
      claimName: mypvc
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html/
kubectl apply -f vol-pvc-demo.yaml
kubectl get pvc

kubectl get pv

kubectl get pods -o wide

 

 

 在work上:挂载在work node上的目录

/var/lib/kubelet/pods/<Pod 的 ID>/volumes/kubernetes.io~<Volume 类型 >/<Volume 名字 >

 

 

五、不通过PV、PVC使用存储

5.1.emptyDir

  pod删除它也删除。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpd-demo
  labels:
    app: httpd-demo
spec:
  replicas: 3
  selector:
    matchLabels:
      app: httpd-demo
  template:
    metadata:
      labels:
        app: httpd-demo
    spec:
      containers:
      - name: httpd
        image: httpd
        ports:
        - containerPort: 80
        volumeMounts:
        - name: html
          mountPath: /usr/share/http/html/
      volumes:
      - name: html
        emptyDir: {}

5.2.hostPath

  把宿主机的目录与pod建立关系

kubectl explain Deployment.spec.template.spec.volumes
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpd-demo
  labels:
    app: httpd-demo
spec:
  replicas: 3
  selector:
    matchLabels:
      app: httpd-demo
  template:
    metadata:
      labels:
        app: httpd-demo
    spec:
      containers:
      - name: httpd
        image: httpd
        ports:
        - containerPort: 80
        volumeMounts:
        - name: html
          mountPath: /usr/share/http/html/
      volumes:
      - name: html
        path: /data/volumes
        type: DirectoryOrCreate

5.3.NFS

apiVersion: v1
kind: Pod
metadata:
  name: pvcpod
  namespace: default
spec:
  volumes:
  - name: html
    nfs:
      path: /data/volumes
      server: 10.146.16.248
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html/

5.4.GFS/ceph略

 

六、StorageClass   (动态创建PV,pod挂载存储)

  大规模集群里,大量的PVC、PV,通过StorageClass定义,管理员可以将存储资源定义为某种类型的资源(快速存储、慢速存储),用户根据storageClass描述就可以申请到PV了。StorageClass会依据需求创建出PV。

  管理不限属性的资源(如性能), 集群管理员提供各种pv的不同方式,而不仅仅是大小和访问模式。

  能够自动的去创建pv。

  工作原理:比如要使用NFS,需要一个nfs-client的自动配置程序---Provisioner(制备器),它使用已经配置好的nfs服务器,来自动创建持久卷--PV。

  1.自动创建的PV以${namespace}-${pvcName}-${pvName}格式创建在NFS服务器上的共享数据目录中。

  2.当PV被回收后以archieved-${namespace}-${pvcName}-${pvName}命名格式存在NFS服务器上。

  

 

 6.1.StorageClass资源

  每个StorageClass都包含provisioner、parameters和reclaimPolicy字段,这些字段在StorageClass动态分配PersistenVolume时会使用到。

  StorageClass对象的命名很重要,user使用这个命名来请求生成一个特定的类。当创建StorageClass对象进,管理员设置StorageClass对象的命名和其他参数,一旦创建了对象就不能再对其更新。

  通过StorageClass的名称来标识不同种类的存储,比如SSD、block-device这种名称,使用者通过StorageClass的名字来实现动态创建PV指定类弄存储的过程。

  可以为没有申请绑定到特定StorageClass的PVC指定一个默认的存储类。

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: standard
provisioner: kubernetes.io/aws-ebs   
parameters:
  type: gp2
reclaimPolicy: Retain
allowVolumeExpansion: true
mountOptions:
  - debug
volumeBindingMode: Immediate   ##立即绑定

6.2.Provisioner---制备器

  Provisioner决定使用哪个卷插件制备PV。

卷插件               内置制备器           配置例子
AWSElasticBlockStore         ✓              AWS EBS
AzureFile               ✓             Azure File
AzureDisk               ✓               Azure Disk
CephFS                -               -
Cinder                 ✓             OpenStack Cinder
FC                  -               -
Glusterfs                ✓             Glusterfs
iSCSI                 -                -
NFS                  -               -
RBD                   ✓             Ceph RBD
Local                        -             Local

  内置制备器(其名称前缀为”kubernetes.io“并打包在kubernetes中)。如NFS没有内部provisioner,可以用第三方的外部provisioner。

6.3.回收策略

  reclaimPolicy字段中指定回收策略,可以是Delete或者Retain。没有指定默认为Delete;

  通过StorageClass手动创建并管理的PersistentVolume会使用这些回收策略。

6.4.允许卷扩展

  PersistenVolume可以配置为可扩展。allowVolumeExpansion: true

  只能扩不能收。Azure File、Azure Disk、glusterfs、rbc、Cinder支持。

6.5.卷绑定模式

  volumeBindingMode字段控制了卷绑定和动态制备是时应该的动作。

  默认:immediate模式表示一旦创建了PersistentVolumeClaim也就完成了卷绑定和动态制备。

  可以通过WaitForFirstConsumer模式来解决此问题。它将延迟persistenVolume的绑定和制备,直到使用该PVC的Pod被创建。PV会根据Pod调度约束指定的拓扑来选择或制备。

6.6.例

StorageClass + NFS

6.6.1.准备NFS Server

6.6.2.创建Service Account,用于管理NFS Provisioner在K8S集群中运行的权限;NFS Provisioner需要单独下截,并以DP方式部署pod。

RBAC授权

# rbac.yaml:#唯一需要修改的地方只有namespace,根据实际情况定义
apiVersion: v1
kind: ServiceAccount #  创建一个账户,主要用来管理NFS provisioner在k8s集群中运行的权限
metadata:
  name: nfs-client-provisioner
  namespace: default
---
kind: ClusterRole # 创建集群角色
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-client-provisioner-runner # 角色名
rules: # 角色权限
  - apiGroups: [""]
    resources: ["persistentvolumes"] # 操作的资源
    verbs: ["get", "list", "watch", "create", "delete"] # 对该资源的操作权限
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get", "list", "watch", "update"]
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["create", "update", "patch"]
---
kind: ClusterRoleBinding # 集群角色绑定
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-client-provisioner
subjects: # 角色绑定对象
  - kind: ServiceAccount
    name: nfs-client-provisioner
    namespace: default
roleRef:
  kind: ClusterRole # 哪个角色
  name: nfs-client-provisioner-runner
  apiGroup: rbac.authorization.k8s.io
---
kind: Role # 创建角色
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner # 角色名
  namespace: default # Role需要指定名称空间,ClusterRole 不需要
rules: # 角色权限
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding # 角色绑定
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
subjects: # 角色绑定对象
  - kind: ServiceAccount
    name: nfs-client-provisioner
    namespace: default
roleRef: # 绑定哪个角色
  kind: Role
  name: leader-locking-nfs-client-provisioner
  apiGroup: rbac.authorization.k8s.io

6.6.3.创建StorageClass,指定Provisioner。负责建立PVC并调用NFS Provisioner进行预定的工作,并让PV与PVC建立关联。注意StorageClass provisioner需要与6.6.4.NFS provisioner中的PROVISIONER_NAME相同

# 创建NFS资源的StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass # 创建StorageClass
metadata:
  name: managed-nfs-storage   ##通过这个名字来定义不同类的存储
provisioner: qgg-nfs-storage #这里的名称要和provisioner配置文件中的环境变量PROVISIONER_NAME保持一致
parameters:  
   archiveOnDelete: "false"

附:如果有内置的provisioner就不需要手动创建6.6.4步的provisioner

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: slow   ##通过这个名字来定义不同类的存储
provisioner: kubernetes.io/aws-ebs  #可以只写aws
parameters:
  type: io1
  iopsPerGB: "10"
  fsType: ext4

6.6.4.创建NFS Provisioner DP,它有两个功能:1.在NFS共享目录下创建挂载点(volume);2.建立PV并将PV与NFS的挂载点建立关联。# 创建NFS provisioner

apiVersion: apps/v1
kind: Deployment # 部署nfs-client-provisioner
metadata:
  name: nfs-client-provisioner
  labels:
    app: nfs-client-provisioner
  namespace: default #与RBAC文件中的namespace保持一致
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 # 指定serviceAccount!
      containers:
        - name: nfs-client-provisioner
          image: quay.io/external_storage/nfs-client-provisioner:latest #镜像地址
      ###image: registry.cn-beijing.aliyuncs.com/docker-dhbm/nfs-subdir-external-provisioner ##k8s 1.28image用这个,用上面的会报错,pvc会一直处于pending状态。 volumeMounts: # 挂载数据卷到容器指定目录
- name: nfs-client-root mountPath: /persistentvolumes env: - name: PROVISIONER_NAME # 配置provisioner的Name value: qgg-nfs-storage # 确保该名称与 StorageClass 资源中的provisioner名称保持一致 - name: NFS_SERVER #绑定的nfs服务器 value: 10.146.16.248  #NFS服务器IP - name: NFS_PATH #绑定的nfs服务器目录 value: /data/scvolumes # NFS共享目录
volumes:  
##申明nfs数据卷
- name: nfs-client-root
          nfs:
            server: 10.146.16.248  ##NFS服务器IP
            path: /data/scvolumes  ##NFS共享目录

6.6.5.创建pod测试

  先创建一个PVC

# 申明一个PVC,指定StorageClass
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: test-claim
  annotations: 
    # 通过annotations注解,和storage-class进行关联,为什么不使用storageClassName,因为版本比较低
    # 这里指定的名字就是上面创建的StorageClass的名字,让它帮我们创建PV
    volume.beta.kubernetes.io/storage-class: "managed-nfs-storage"  ##用哪一类存储。
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Mi

 

 kubectl get pv

 

在1.20版本以后需要修改/etc/kubernetes/manifests/kube-apiserver.yaml文件,否则会出现以下错误:

#kubectl describe pvc test-claim查看

waiting for a volume to be created, either by external.....

添加一行:

- --feature-gates=RemoveSelfLink=false

# 创建测试pod,查看是否可以正常挂载    
kind: Pod
apiVersion: v1
metadata:
  name: test-pod
spec:
  containers:
  - name: test-pod
    image: busybox:1.24
    command:
      - "/bin/sh"
    args:
      - "-c"
      - "touch /mnt/SUCCESS && exit 0 || exit 1"   #创建一个SUCCESS文件后退出
    volumeMounts:
      - name: nfs-pvc # 挂载数据卷
        mountPath: "/mnt"
  restartPolicy: "Never"
  volumes:
    - name: nfs-pvc
      persistentVolumeClaim: # 数据卷挂载的是pvc
        claimName: test-claim  #与PVC名称保持一致

在NFS服务器上可以看到SUCCESS这个文件

  以上实验删除pod,pv不会被删除。删除PVC,PV也被删除,NFS上的文件也会被删除。

 

7、本地持久化存储

  数据存在POD运行的宿主机上。

  不支持动态创建。

  宿主机有hostPath和emptyDir两种方式,但它们不适用于本地持久存储。要保证pod被调到具有本地持久化存储的节点上。使用这种方式的持久化主要是为了解决对超高IO性能的磁盘的要求,用本地SSD,通过网络会削弱性能。

  使用这种方式要解决POD的调度问题,保证POD一定会调度到拥有这种PV的node上。定义PV是要声明节点亲和。

7.1.定义PV

apiVersion: v1
kind: PersistentVolume
metadata:
  name: example-pv
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: local-storage
  local: # local类型
    path: /data/vol1  # 节点上的具体路径
  nodeAffinity: # 这里就设置了节点亲和
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - node01 # 这里我们使用node01节点,该节点有/data/vol1路径

7.2.定义StorageClass

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

  volumeBindingMode: WaitForFirstConsumer很关键,延迟绑定,当有符合PVC要求的PV不立即绑定。因为POD使用PVC,而绑定之后,POD被调度到其他节点,但其他节点很有可能没有那个PV所以POD就挂起了。另外就算该节点有合适的PV,而POD被设置成不能运行在该节点,POD同样会被挂起。延迟绑定的好处是,POD的调度要参考卷的分布。当开始调度POD的时候看看它要求的PV在哪里,然后就调度到该节点,然后进行PVC的绑定,最后在挂载到POD中,这样就保证了POD所在的节点就一定是PV所在的节点。所以让PVC延迟绑定,就是等到使用这个PVC的POD出现在调度器上之后(真正被调度之前),然后根据综合评估再来绑定这个PVC。

7.3.定义PVC

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: local-claim
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
  storageClassName: local-storage

7.4.定义POD

apiVersion: apps/v1
kind: Deployment
metadata:
  name: tomcat-deploy
spec:
  replicas: 1
  selector:
    matchLabels:
      appname: myapp
  template:
    metadata:
      name: myapp
      labels:
        appname: myapp
    spec:
      containers:
      - name: myapp
        image: tomcat:8.5.38-jre8
        ports:
        - name: http
          containerPort: 8080
          protocol: TCP
        volumeMounts:
          - name: tomcatedata
            mountPath : "/data"
      volumes:
        - name: tomcatedata
          persistentVolumeClaim:
            claimName: local-claim

 

posted @ 2021-11-29 12:02  天涯160  阅读(717)  评论(0)    收藏  举报