Kubernetes-持久化存储之PV PVC以及StorageClass

一、PV和PVC的引入

k8s之PV、PVC、StorageClass详解

Volume 提供了非常好的数据持久化方案,不过在可管理性上还有不足。
要使用 Volume,Pod 必须事先知道如下信息:

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

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

  1. 要么询问管理员。
  2. 要么自己就是管理员。

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

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实现持久化存储

配置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:

创建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

1)capacity 指定 PV 的容量为 1G。
2)accessModes 指定访问模式为 ReadWriteOnce,支持的访问模式有:

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

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

Retain: 需要管理员手工回收。
Recycle:清除 PV 中的数据,效果相当于执行 rm -rf /thevolume/*。
Delete: 删除 Storage Provider 上的对应存储资源,例如 AWS EBS、GCE PD、Azure Disk、- OpenStack Cinder Volume 等。

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

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

创建 mypv:

kubectl apply -f nfs-pv.yml
image.png

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

创建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
image.png

创建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

可以去官网下载这三个文件去网上下载

https://github.com/kubernetes-incubator/external-storage/tree/master/nfs-client/deploy

使用脚本批量下载:

for file in class.yaml deployment.yaml rbac.yaml; do wget https://raw.githubusercontent.com/kubernetes-incubator/external-storage/master/nfs-client/deploy/$file ; 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进行测试

vim nginx.yaml

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  type: NodePort
  ports:
  - port: 80
    nodePort: 30012
  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


image.png

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

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

  • 1、创建自己的 StorageClass 备用。
  • 2、创建 statefulset ,在 yaml 文件的volumeClaimTemplates 块,添加 StorageClass 的名字。

一直启动不起来,查看 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、按 K8S 的修改方式是 apiserver 的配置文件,重新启用 SelfLink 功能。针对 K8S,可添加如下配置:

# vim /etc/kubernetes/manifests/kube-apiserver.yaml
...
spec:
  containers:
  - command:
    - kube-apiserver
    ...
    - --feature-gates=RemoveSelfLink=false # 增加

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

# vim /etc/systemd/system/k3s.service

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

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

$ curl -sfL https://get.k3s.io | 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 集群使用calico网络报错( 0/3 nodes are available)

calico-kube-controllers 一直在创建或者pending状态

0/3 nodes are available: 1 node(s) had taints that the pod didn’t tolerate。。。。。。

起初以为是污点的问题,结果强制消除master节点上的污点不起作用

解决办法:
修改/etc/sysconfig/kubelet配置文件,添加–max-pods配置,然后重启kubelet服务,修改后文件内容如下:

KUBELET_EXTRA_ARGS="–fail-swap-on=false --max-pods=300"
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容