您的位置 首页 java

K8s持久化存储

一、PV和PVC的引入

Volume 提供了非常好的数据持久化方案,不过在可管理性上还有不足。

要使用 Volume, Pod 必须事先知道如下信息:

  • 当前 Volume 来自哪一台机器。
  • EBS Volume 已经提前创建,并且知道确切的情况 volume-id

Pod 通常是由应用的开发人员维护,而 Volume 则通常是由存储系统的管理员维护。开发人员要获得上面的信息:

要么询问管理员。

要么自己就是管理员。

这样就带来一个管理上的问题:应用开发人员和系统管理员的职责耦合在一起了。如果系统规模较小或者对于 开发环境 这样的情况还可以接受。但当集群规模变大,特别是对于生成环境,考虑到效率和安全性,这就成了必须要解决的问题。

Kubernetes 给出的解决方案是什么 PersistentVolume (PV)和 PersistentVolumeClaim(PVC)。

PersistentVolume (PV) 是外部存储系统中的一块存储空间,由管理员创建和维护。与 Volume 一样,PV 具有持久性, 生命周期 独立于 Pod。

PersistentVolumeClaim (PVC) 是对 PV 的申请 (Claim)。 PVC 通常由普通用户创建和维护。需要为 Pod 分配存储资源时,用户可以创建一个 PVC,指明存储资源的容量大小和访问模式(比如只读)等信息,Kubernetes 查找并提供满足条件的 PV。

有了 PersistentVolumeClaim,用户只需要告诉 Kubernetes 需要什么样的存储资源,而不必关心真正的空间从哪里分配,如何访问等底层细节信息。这些 Storage Provider 的底层信息交给管理员来处理,只有管理员才应该关心创建 PersistentVolume 的细节信息。

二、通过 NFS 实现持久化存储

1、配置nfs

需要安装

k8s-master:nfs-server

k8s-node1:nfs-client

k8s-node2:nfs-client

所有节点安装nfs

 yum install -y nfs-common nfs-utils  

在master节点创建共享目录

  mkdir  /nfsdata
chmod 666 /nfsdata  

编辑exports文件

 cat /etc/exports

/nfsdata *(rw,no_ root _squash,no_all_squash,sync)  

启动 rpc 和nfs(注意顺序)

 systemctl start rpcbind
systemctl start nfs  

作为准备工作,我们已经在 k8s-master 节点上搭建了一个 NFS 服务器,目录为 /nfsdata:

2、创建PV

下面创建一个 PV mypv,配置文件 nfs-pv.yml 如下:

 vim nfs-pv1.yml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: mypv
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: nfs
  nfs:
    path: /nfsdata
    server: 10.128.1.117  

① capacity 指定 PV 的容量为 1G。

② accessModes 指定访问模式为 ReadWriteOnce,支持的访问模式有:

ReadWriteOnce:PV 能以 read-write 模式 mount 到单个节点。

ReadOnly Many:PV 能以 read-only 模式 mount 到多个节点。

ReadWriteMany :PV 能以 read-write 模式 mount 到多个节点。

③ persistentVolumeReclaimPolicy 指定当 PV 的回收策略为 Recycle,支持的策略有:

Retain: 需要管理员手工回收。

Recycle:清除 PV 中的数据,效果相当于执行 rm -rf /thevolume/*。

Delete: 删除 Storage Provider 上面的对应存储资源,例如 AWS EBS、GCE PD、Azure Disk、- OpenStack Cinder Volume 等。

④ storageClassName 指定 PV 的 class 为 nfs。相当于为 PV 设置了一个分类,PVC 可以指定 class 申请相应 class 的 PV。

⑤ 指定 PV 在 NFS 服务器上对应的目录。

创建 mypv:

 kubectl apply -f nfs-pv.yml  

STATUS 为 Available,表示 mypv 就绪,可以被 PVC 申请。

3、创建PVC

接下来创建 PVC mypvc,配置文件 nfs-pvc.yml 如下:

 vi nfs-pvc.yml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mypvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: nfs  

部署pvc

 kubectl apply -f nfs-pvc.yml  

4、创建pod

上面已经创建好了pv和pvc,pod中直接使用这个pvc即可

 vi pod.yml

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
    - name: mypod
      image: busybox
      args:
      - /bin/sh
      - -c
      - sleep 30000
      volumeMounts:
      - mountPath: "/mydata"
        name: mydata
  volumes:
    - name: mydata
      persistentVolumeClaim:
        claimName: mypvc  

与使用普通 Volume 的格式类似,在 volumes 中通过 persistentVolumeClaim 指定使用 mypvc 申请的 Volume。

通过命令创建mypod:

 kubectl apply -f pod.yml  

在这里,可以尝试在任何一方删除文件,文件在两端都会消失;

三、PV的回收

当 PV 不再需要时,可通过删除 PVC 回收。未删除pvc之前 pv的状态是Bound

删除pod

 kubectl delete pod mypod  

删除pvc

 kubectl delete pvc mypvc  

再次查看pv的状态

 kubectl get pv  

删除pvc之后pv的状态变为Available,,此时解除绑定后则可以被新的 PVC 申请。

/nfsdata文件中的文件被删除了

因为 PV 的回收策略设置为 Recycle,所以数据会被清除,

但这可能不是我们想要的结果。如果我们希望保留数据,可以将策略设置为 Retain

虽然 mypv 中的数据得到了保留,但其 PV 状态会一直处于 Released,不能被其他 PVC 申请。为了重新使用存储资源,可以删除并重新创建 mypv。删除操作只是删除了 PV 对象,存储空间中的数据并不会被删除。

PV 还支持 Delete 的回收策略,会删除 PV 在 Storage Provider 上对应存储空间。NFS 的 PV 不支持 Delete,支持 Delete 的 Provider 有 AWS EBS、GCE PD、Azure Disk、 OpenStack Cinder Volume 等。

四、PV的动态供给

前面的例子中,我们提前创建了 PV,然后通过 PVC 申请 PV 并在 Pod 中使用,这种方式叫做静态供给(Static Provision)。

与之对应的是动态供给(Dynamical Provision),即如果没有满足 PVC 条件的 PV,会动态创建 PV。相比静态供给,动态供给有明显的优势:不需要提前创建 PV,减少了管理员的工作量,效率高。

基于NFS的PV动态供给(StorageClass)

静态:pod–>pvc–>pv

动态:pod –>pvc–>storageclass

去官网下载三个文件

这三个文件去网上下载

使用脚本批量下载:

 for file in class.yaml deployment.yaml rbac.yaml; do wget  ; done  

其中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: gcr.io/k8s-staging-sig-storage/nfs-subdir-external-provisioner:v4.0.0 #这里需要修改,因为最新版本存在 SelfLink 问题
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: fuseim.pri/ifs
            - name: NFS_SERVER
              value: 10.128.1.117 #这里需要修改
            - name: NFS_PATH
              value: /nfsdata #这里需要修改
      volumes:
        - name: nfs-client-root
          nfs:
            server: 10.128.1.117 #这里需要修改
            path: /nfsdata #这里需要修改  

然后分别去应用这三个文件

 kubectl create -f rbac.yaml
kubectl create -f class.yaml
kubectl create -f deployment.yaml  

创建pod进行测试

 vi nginx.yaml

apiVersion: v1
kind: Service
metadata:
  name:  nginx 
  labels:
    app: nginx
spec:
  type: NodePort
  ports:
  - port: 80
    nodePort: 30012
    name: web
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx
  serviceName: "nginx"
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: wangyanglinux/myapp:v1 
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteMany" ]
      storageClassName: "managed-nfs-storage"
      resources:
        requests:
          storage: 1Gi  

查看pv和pvc

四、K3S/K8S 中动态创建 PVC 时 SelfLink 问题解决

在部署 statefulset 类型的工作负载时,动态创建 PV/PVC 是一种比较常用的配置方式,动态创建 PV/PVC 的方法基本如下:

 volumeClaimTemplates
  

一直启动不起来,查看 pvc 和 pods 信息如下:

  • 1、PVC 一直处于 pending 状态;
  • 2、 mysql pods 显示:0/3 nodes are available: 3 pod has unbound immediate PersistentVolumeClaims.

原因分析

从上边的现象来看,是 PVC 没有创建成功,动态 PVC 中,是 provisioner 中来负责创建,查看其日志,看到如下错误信息:

 I0214 10:22:35.436913 1 controller.go:1068] scheduleOperation[provision-mysql-sts/mysql-pvc-mysql-0[ac333031-e705-48d9-8180-4d2d583bb559]]
E0214 10:22:35.444757 1 controller.go:766] Unexpected error getting claim reference to claim "mysql-sts/mysql-pvc-mysql-0": selfLink was empty, can't make reference  

Google 之后,找到主要原因是,官方在 k8s 1.20 中基于对性能和统一 apiserver 调用方式的初衷,移除了对 SelfLink 的支持,而 nfs-provisioner 需要 SelfLink 该项功能。具体计划和原因可查看这个 issue[2] KEP[3]

K3S 为兼容 K8S 应该也继承了该项修改,按 K8S 的方式修改测试了下,完美解决。

解决方案

解决问题主要有下边两种方式:

1、修改 apiserver 的配置文件,重新启用 SelfLink 功能。针对 K8S,可添加如下配置:

 # /etc/kubernetes/manifests/kube-apiserver.yaml

spec:
  containers:
  - command:
    - kube-apiserver
    ...
    - --feature-gates=RemoveSelfLink=false # 增加  

K3S 中没有 apiserver 的配置文件,可通过 systemd 的启动文件添加该参数,如下:

 # /etc/systemd/system/k3s.service

 Exec Start=/usr/local/bin/k3s 
    server 
        ...
        '--kube-apiserver-arg'    # 新增
        'feature-gates=RemoveSelfLink=false'   # 新增  

若为新安装,可如下启用:

 $ curl -sfL  | sh -s - --kube-apiserver-arg "feature-gates=RemoveSelfLink=false"  

2、使用新的不基于 SelfLink 功能的 provisioner 镜像,重新创建 provisioner 容器。

若你能科学上网,可使用这个镜像:

 gcr.io/k8s-staging-sig-storage/nfs-subdir-external-provisioner:v4.0.0  

国内可使用这个镜像【若不可用自己查找】:

  registry .cn-beijing.aliyuncs.com/pylixm/nfs-subdir-external-provisioner:v4.0.0  

文章来源:智云一二三科技

文章标题:K8s持久化存储

文章地址:https://www.zhihuclub.com/186577.shtml

关于作者: 智云科技

热门文章

网站地图