🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] `pod` 中的文件在磁盘上是临时存放的,这给容器中运行的较重要的应用程序带来一些问题。 1、当容器崩溃时文件丢失。kubelet 会重新启动容器, 但容器会以干净的状态重启。 2、在同一个 `pod` 中运行多个容器并需要共享文件。 > kubernetes解决上面的方法,提出volumes的抽象概念来解决。 `Kubernetes` 支持很多类型的卷。 `Pod` 可以同时使用任意数目的卷类型。 临时卷类型的生命周期与 `Pod` 相同,但持久卷可以比 `Pod` 的存活期长。 当 `Pod` 不再存在时,`Kubernetes` 也会销毁临时卷;不过 Kubernetes 不会销毁持久卷。对于给定 `Pod` 中任何类型的卷,在容器重启期间数据都不会丢失。 该文章围绕kubernetes如何使用NFS存储。 > 注意:应该每个k8s节点都安装nfs客户端。 > CentOS发行版:`nfs-utils`,验证 `rpm -qa nfs-utils` ## 安装nfs 1. 下载 `nfs-utils` ```shell $ yum install nfs-utils -y ``` 2. 修改配置文件 ```shll $ cat > /etc/exports <<-EOF /data/nfs 192.168.200.0/24(rw,root_squash,all_squash,sync) EOF $ mkdir -p /data/nfs $ chown nfsnobody.nfsnobody /data/nfs ``` > **参数说明:** > - rw: 读写 > - root_squash:客户端使用root用户映射成nobody用户 > - all_squash:客户端使用普通用户映射成nobody用户 > - sync:将数据同步写入内存缓冲区与磁盘中,效率低,但可以保证数据的一致性; 3. 启动nfs ```shell $ systemctl start nfs ``` 如果有开防火墙的话,请将 `nfs` 的相关端口放通。通过 `rpcinfo -p` 查看当前使用的端口。 > 启动NFS会开启如下端口: 1)portmapper 端口:111 udp/tcp; 2)nfs/nfs_acl 端口:2049 udp/tcp; 3)mountd 端口:"32768--65535" udp/tcp 4)nlockmgr 端口:"32768--65535" udp/tcp 固定上面的随机端口 ```shell $ cat >> /etc/sysconfig/nfs <<-EOF RQUOTAD_PORT=4001 LOCKD_TCPPORT=4002 LOCKD_UDPPORT=4002 MOUNTD_PORT=4003 STATD_PORT=4004 EOF $ systemctl restart nfs ``` 放通 `iptables` 规则 ```shell iptables -I INPUT -p tcp -m multiport --dports 111,2049,4001,4002,4003,4004 -m comment --comment "nfs tcp ports" -j ACCEPT iptables -I INPUT -p udp -m multiport --dports 111,2049,4001,4002,4003,4004 -m comment --comment "nfs udp ports" -j ACCEPT ``` ## volumes `volumes` 的核心是一个目录,其中可能存有数据,`Pod` 中的容器可以访问该目录中的数据。 所采用的特定的卷类型将决定该目录如何形成的、使用何种介质保存数据以及目录中存放 的内容。 使用卷时, 在 `.spec.volumes` 字段中设置为 `Pod` 提供的卷,并在 `.spec.containers[*].volumeMounts` 字段中声明卷在容器中的挂载位置。 容器中的进程看到的是由它们的 Docker 镜像和卷组成的文件系统视图。 Docker镜像位于文件系统层次结构的根部。各个卷则挂载在镜像内的指定路径上。 卷不能挂载到其他卷之上,也不能与其他卷有硬链接。 `Pod` 配置中的每个容器必须独立指定各个卷的挂载位置。 1.部署以下模板,创建pod ```yaml apiVersion: v1 kind: Pod metadata: name: test-volume namespace: default spec: containers: - name: busybox image: busybox:1.28.1 imagePullPolicy: IfNotPresent args: - /bin/sh - -c - sleep 3600 volumeMounts: - name: nfs mountPath: /nfs volumes: - name: nfs nfs: server: 192.168.31.136 path: /data/nfs ``` 2.部署pod ```shell $ kubectl apply -f test-volume.yml pod/test-volume created ``` ## Persistent Volume 存储的管理是一个与计算实例的管理完全不同的问题。`PersistentVolume` 子系统为用户 和管理员提供了一组 API,将存储如何供应的细节从其如何被使用中抽象出来。 为了实现这点,我们引入了两个新的 API 资源:`PersistentVolume` 和 `PersistentVolumeClaim`。 持久卷(PersistentVolume,PV)是集群中的一块存储,可以由管理员事先供应,或者 使用存储类(Storage Class)来动态供应。 持久卷是集群资源,就像节点也是集群资源一样。PV 持久卷和普通的 `Volume` 一样,也是使用 卷插件来实现的,只是它们拥有独立于任何使用 PV 的 Pod 的生命周期。 此 API 对象中记述了存储的实现细节,无论其背后是 `NFS、iSCSI` 还是特定于云平台的存储系统。 持久卷申领(PersistentVolumeClaim,PVC)表达的是用户对存储的请求。概念上与 `Pod` 类似。 `Pod` 会耗用节点资源,而 `PVC` 申领会耗用 `PV` 资源。`Pod` 可以请求特定数量的资源(CPU 和内存);同样 `PVC` 申领也可以请求特定的大小和访问模式 (例如,可以要求 PV 卷能够以 `ReadWriteOnce、ReadOnlyMany 或 ReadWriteMany` 模式之一来挂载,参见访问模式)。 尽管 `PersistentVolumeClaim` 允许用户消耗抽象的存储资源,常见的情况是针对不同的 问题用户需要的是具有不同属性(如,性能)的 `PersistentVolume` 卷。 集群管理员需要能够提供不同性质的 `PersistentVolume`,并且这些 PV 卷之间的差别不 仅限于卷大小和访问模式,同时又不能将卷是如何实现的这些细节暴露给用户。 为了满足这类需求,就有了 存储类(StorageClass) 资源。 PV 卷是集群中的资源。PVC 申领是对这些资源的请求,也被用来执行对资源的申领检查。PV 卷的供应有两种方式:静态供应或动态供应。 ### 静态供应 1.PersistentVolume 对象 ```yaml apiVersion: v1 kind: PersistentVolume metadata: name: nfs-pv001 spec: capacity: storage: 5Gi volumeMode: Filesystem accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Recycle storageClassName: nfs nfs: path: /data/nfs server: 192.168.31.136 ``` 2.创建pv ```shell $ kubectl apply -f pv.yml persistentvolume/nfs-pv001 created ``` 3.查看pv状态 ```shell $ kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE nfs-pv001 5Gi RWX Recycle Available nfs 23s ``` > 每个卷会处于以下阶段(Phase)之一: > Available(可用)-- 卷是一个空闲资源,尚未绑定到任何申领; > Bound(已绑定)-- 该卷已经绑定到pvc; > Released(已释放)-- 所绑定的pvc已被删除,但是资源尚未被集群回收; > Failed(失败)-- 卷的自动回收操作失败。 4.PersistentVolumeClaims对象 ```yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc-001 spec: accessModes: - ReadWriteMany volumeMode: Filesystem resources: requests: storage: 3Gi storageClassName: nfs ``` > 注意: > 1、`accessModes` 和 `storageClassName` 与 `PersistentVolume` 资源清单必须一致。 > 2、 `resources.requests.storage` 小于 `PersistentVolume` 资源清单的 `capacity.storage` 5.创建PVC ```shell $ kubectl apply -f pvc.yml persistentvolumeclaim/pvc-001 created ``` 6.查看pvc状态 ```shell $ kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE pvc-001 Bound nfs-pv001 5Gi RWX nfs 3m $ kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE nfs-pv001 5Gi RWX Recycle Bound default/pvc-001 nfs 14m ``` 7.pod使用pvc资源 ```yaml apiVersion: v1 kind: Pod metadata: name: test-volume namespace: default spec: containers: - name: busybox image: busybox:1.28.1 imagePullPolicy: IfNotPresent args: - /bin/sh - -c - sleep 3600 volumeMounts: - name: nfs mountPath: /nfs volumes: - name: nfs nfs: server: 192.168.31.136 path: /data/nfs ``` 8.创建pod ```shell $ kubectl apply -f test-pvc.yml pod/test-pvc created ``` 9.查看pod ```shell $ kubectl get pod test-volume NAME READY STATUS RESTARTS AGE test-volume 1/1 Running 1 70m $ kubectl exec test-volume -- df -h Filesystem Size Used Available Use% Mounted on overlay 40.0G 9.9G 30.0G 25% / 192.168.31.136:/data/nfs 40.0G 21.2G 18.8G 53% /nfs /dev/sda3 40.0G 9.9G 30.0G 25% /dev/termination-log /dev/sda3 40.0G 9.9G 30.0G 25% /etc/localtime /dev/sda3 40.0G 9.9G 30.0G 25% /etc/resolv.conf /dev/sda3 40.0G 9.9G 30.0G 25% /etc/hostname /dev/sda3 40.0G 9.9G 30.0G 25% /etc/hosts ... ``` ### 动态供应(storageclasses) `StorageClass` 为管理员提供了描述存储 "类" 的方法。 不同的类型可能会映射到不同的服务质量等级或备份策略,或是由集群管理员制定的任意策略。 每个 `StorageClass` 都有一个提供商(Provisioner),用来决定使用哪个卷插件创建PV。 这个使用的 nfs 存储介质。所以安装 nfs-Provisioner 。 #### 安装nfs-provisioner 1.rbac权限 ```yaml apiVersion: v1 kind: ServiceAccount metadata: name: nfs-provisioner # replace with namespace where provisioner is deployed namespace: kube-system --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: nfs-provisioner-runner rules: - apiGroups: [""] resources: ["nodes"] verbs: ["get", "list", "watch"] - 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-provisioner subjects: - kind: ServiceAccount name: nfs-provisioner # replace with namespace where provisioner is deployed namespace: kube-system roleRef: kind: ClusterRole name: nfs-provisioner-runner apiGroup: rbac.authorization.k8s.io --- kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: name: leader-locking-nfs-provisioner # replace with namespace where provisioner is deployed namespace: kube-system rules: - apiGroups: [""] resources: ["endpoints"] verbs: ["get", "list", "watch", "create", "update", "patch"] --- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: leader-locking-nfs-provisioner # replace with namespace where provisioner is deployed namespace: kube-system subjects: - kind: ServiceAccount name: nfs-provisioner # replace with namespace where provisioner is deployed namespace: kube-system roleRef: kind: Role name: leader-locking-nfs-provisioner apiGroup: rbac.authorization.k8s.io ``` 2.deployment清单 ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: nfs-provisioner labels: app: nfs-provisioner namespace: kube-system spec: replicas: 1 strategy: type: Recreate selector: matchLabels: app: nfs-provisioner template: metadata: labels: app: nfs-provisioner spec: serviceAccountName: nfs-provisioner containers: - name: nfs-provisioner image: jiaxzeng/nfs-subdir-external-provisioner:v4.0.2 env: # 可以自定义名称 - name: PROVISIONER_NAME value: k8s/nfs-provisioner - name: NFS_SERVER value: 192.168.31.136 - name: NFS_PATH value: /data/nfs volumeMounts: - name: nfs-provisioner-root mountPath: /persistentvolumes volumes: - name: nfs-provisioner-root nfs: server: 192.168.31.136 path: /data/nfs ``` 3.创建nfs-provisioner ```shell $ kubectl apply -f rbac.yaml serviceaccount/nfs-provisioner created clusterrole.rbac.authorization.k8s.io/nfs-provisioner-runner created clusterrolebinding.rbac.authorization.k8s.io/run-nfs-provisioner created role.rbac.authorization.k8s.io/leader-locking-nfs-provisioner created rolebinding.rbac.authorization.k8s.io/leader-locking-nfs-provisioner created $ kubectl apply -f deploy.yml deployment.apps/nfs-provisioner created ``` #### 创建StorageClass 1.StorageClass清单 ```yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: managed-nfs-storage provisioner: k8s/nfs-provisioner # must match deployment's env PROVISIONER_NAME' parameters: archiveOnDelete: "false" ``` 2.创建StorageClass ```shell $ kubectl apply -f class.yml storageclass.storage.k8s.io/managed-nfs-storage created ``` #### 测试 1.创建pvc清单 ```yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: test-claim spec: storageClassName: managed-nfs-storage accessModes: - ReadWriteMany resources: requests: storage: 1024Mi ``` 2.创建pod清单 ```yaml apiVersion: v1 kind: Pod metadata: name: test-pod spec: containers: - name: test-pod image: busybox:1.28.1 command: - "/bin/sh" args: - "-c" - "sleep 3600" volumeMounts: - name: nfs-pvc mountPath: "/mnt" restartPolicy: "Never" volumes: - name: nfs-pvc persistentVolumeClaim: claimName: test-claim ``` 3.创建pvc和deployment ```shell $ kubectl apply -f test-claim.yaml persistentvolumeclaim/test-claim created $ kubectl apply -f test-pod.yml pod/test-pod created ``` 4.查看pv和pvc状态 ```shell $ kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pvc-470a0959-0313-4d4a-8e1c-3543fa79e737 1Gi RWX Delete Bound default/test-claim managed-nfs-storage 84s $ kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE test-claim Bound pvc-470a0959-0313-4d4a-8e1c-3543fa79e737 1Gi RWX managed-nfs-storage 96s ``` 5.查看pod挂载情况 ```shell $ kubectl exec test-pod -- df -h Filesystem Size Used Available Use% Mounted on overlay 40.0G 10.8G 29.2G 27% / 192.168.31.136:/data/nfs/default-test-claim-pvc-470a0959-0313-4d4a-8e1c-3543fa79e737 40.0G 10.1G 29.9G 25% /mnt /dev/sda3 40.0G 10.8G 29.2G 27% /dev/termination-log /dev/sda3 40.0G 10.8G 29.2G 27% /etc/localtime /dev/sda3 40.0G 10.8G 29.2G 27% /etc/resolv.conf /dev/sda3 40.0G 10.8G 29.2G 27% /etc/hostname /dev/sda3 40.0G 10.8G 29.2G 27% /etc/hosts ``` 如上述截图,pod上挂载点有 `/mnt` 的话,那就没问题。大功告成 ## 参考文档 > volume的模式:https://kubernetes.io/zh/docs/concepts/storage/persistent-volumes/#access-modes > 安装nfs-provisioner:https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner