天天看点

K8S-数据持久化PV、PVC、StorageClass的关系

一、引言

在使用K8S时总绕不开的话题就是K8S持久化存储,要实现持久化数据,就要把数据存储到硬盘上。在K8S持久化的过程中会有许多的概念PV、PVC、StorageClass、Provisioner等等。我只想存储一个文件有么有简单的方式呢?答案是有的。

K8S环境下,Container 中的文件在磁盘上是临时存放的,当容器崩溃时文件丢失。kubelet 会重新启动容器, 但容器会以干净的状态重启。所以我们要使用 Volume 来持久化数据。

Docker 也有 卷(Volume) 的概念,但对它只有少量且松散的管理。 Docker 卷是磁盘上或者另外一个容器内的一个目录 Docker 提供卷驱动程序,但是其功能非常有限。

Kubernetes 支持很多类型的卷。 Pod 可以同时使用任意数目的卷类型。

临时卷类型的生命周期与 Pod 相同,但持久卷可以比 Pod 的存活期长。 当 Pod 不再存在时,Kubernetes 也会销毁临时卷;不过 Kubernetes 不会销毁 持久卷。对于给定 Pod 中任何类型的卷,在容器重启期间数据都不会丢失。

卷的核心是一个目录,其中可能存有数据,Pod 中的容器可以访问该目录中的数据。 所采用的特定的卷类型将决定该目录如何形成的、使用何种介质保存数据以及目录中存放 的内容。
使用卷时,在 .spec.volumes 字段中设置为 Pod 提供的卷,并在 .spec.containers[*].volumeMounts 字段中声明卷在容器中的挂载位置。 各个卷则挂载在镜像内的指定路径上。 卷不能挂载到其他卷之上,也不能与其他卷有硬链接。Pod 配置中的每个容器必须独立指定各个卷的挂载位置。

二、K8S存储的分类

2.1、Emptydir---临时存储

当pod的存储方案设定为emptydir的时候,pod启动时,就会在pod所在节点的磁盘空间开辟出一块空卷,最开始里面是什么都没有的,pod启动后容器产生的数据会存放到那个空卷中。空卷变成了一个临时卷供pod内的容器读取和写入数据,一旦pod容器消失,节点上开辟出的这个临时卷就会随着pod的销毁而销毁。

Emptydir的用途

  1. 缓存空间,例如基于磁盘的归并排序。
  2. 为耗时较长的计算任务提供检查点,以便任务能方便地从崩溃前状态恢复执行。
  3. 在 Web 服务器容器服务数据时,保存内容管理器容器获取的文件。

emptyDir配置示例

Go

[root@k8s-master emptydir]# cat emptydir.yaml

apiVersion: v1

kind: Pod

metadata:

name: test-pd

spec:

containers:

- image: nginx

name: test-container

volumeMounts:

- mountPath: /usr/share/nginx/html

name: cache-volume

volumes:

- name: cache-volume

emptyDir: {}

创建和验证

Go

[root@k8s-master emptydir]# kubectl apply -f emptydir.yaml

pod/test-pd created

[root@k8s-master emptydir]# kubectl get pod -owide |grep test-pd

test-pd 1/1 Running 0 29s 10.244.2.172 k8s-node2 <none> <none>

#1、在K8S-node2上进行操作

[root@k8s-node2 cache-volume]# docker ps -a|grep test-pd

ec7a0faec6bb nginx "/docker-entrypoint.…" 3 minutes ago Up 3 minutes k8s_test-container_test-pd_default_0e27bcf4-7ebe-4dc3-b48a-3230cf7641ae_0

#2、查看容器的详细信息

[root@k8s-node2 ~]# docker inspect f87f032bbaf8

....

"Mounts": [

{

"Type": "bind",

"Source": "/home/k8s/kubelet/pods/0e27bcf4-7ebe-4dc3-b48a-3230cf7641ae/volumes/kubernetes.io~empty-dir/cache-volume",

"Destination": "/usr/share/nginx/html",

"Mode": "",

"RW": true,

"Propagation": "rprivate"

},

......

###Source是宿主机的地址

###Destination是挂载到容器的地址

#3、进入宿主机的目录并创建文件

[root@k8s-node2 cache-volume]# ls /home/k8s/kubelet/pods/0e27bcf4-7ebe-4dc3-b48a-3230cf7641ae/volumes/kubernetes.io~empty-dir/cache-volume

[root@k8s-node2 cache-volume]# cd /home/k8s/kubelet/pods/0e27bcf4-7ebe-4dc3-b48a-3230cf7641ae/volumes/kubernetes.io~empty-dir/cache-volume

[root@k8s-node2 cache-volume]# echo date > index.html

[root@k8s-node2 cache-volume]# cat index.html

date

##访问该pod的IP地址

[root@k8s-node2 cache-volume]# curl 10.244.2.173

date

[root@k8s-node2 cache-volume]# date > index.html

[root@k8s-node2 cache-volume]# curl 10.244.2.173

Sun Jun 5 17:02:30 CST 2022

###创建其他的访问文件

[root@k8s-node2 cache-volume]# echo 2022-06-05 > test.html

[root@k8s-node2 cache-volume]# curl 10.244.2.173/test.html

2022-06-05

#4、删除对应的pod清除实验。

[root@k8s-master emptydir]# kubectl delete -f emptydir.yaml

pod "test-pd" deleted

###再次在node2计算节点上查看

[root@k8s-node2 cache-volume]# ls /home/k8s/kubelet/pods/0e27bcf4-7ebe-4dc3-b48a-3230cf7641ae/volumes/kubernetes.io~empty-dir/cache-volume

ls: cannot access /home/k8s/kubelet/pods/0e27bcf4-7ebe-4dc3-b48a-3230cf7641ae/volumes/kubernetes.io~empty-dir/cache-volume: No such file or directory

#emptydir是随pod创建而创建,然后再随pod删除而删除

2.2、hostPath

hostPath 卷能将主机节点文件系统上的文件或目录挂载到你的 Pod 中, 虽然这不是大多数 Pod 需要的,但是它为一些应用程序提供了强大的逃生舱。

hostPath的用途

  1. 运行一个需要访问 Docker 内部机制的容器;可使用 hostPath 挂载/var/lib/docker 路径。
  2. 在容器中运行 cAdvisor 时,以 hostPath 方式挂载/sys。
  3. 允许 Pod 指定给定的 hostPath 在运行 Pod 之前是否应该存在,是否应该创建以及应该以什么方式存在。

Type的值

除了必需的path 属性之外,用户可以选择性地为hostPath 卷指定type。

支持的type 值如下:

取值 行为
空字符串(默认)用于向后兼容,这意味着在安装 hostPath 卷之前不会执行任何检查。
DirectoryOrCreate 如果在给定路径上什么都不存在,那么将根据需要创建空目录,权限设置为 0755,具有与 kubelet 相同的组和属主信息。
Directory 在给定路径上必须存在的目录。
FileOrCreate 如果在给定路径上什么都不存在,那么将在那里根据需要创建空文件,权限设置为 0644,具有与 kubelet 相同的组和所有权。
File 在给定路径上必须存在的文件。
Socket 在给定路径上必须存在的 UNIX 套接字。
CharDevice 在给定路径上必须存在的字符设备。
BlockDevice 在给定路径上必须存在的块设备。

hostPath配置示例

Go

[root@k8s-master hostpath]# cat hostpath.yaml

apiVersion: v1

kind: Pod

metadata:

name: test-pd

spec:

containers:

- image: nginx

name: test-container

volumeMounts:

- mountPath: /usr/share/nginx/html

name: test-hostpath

volumes:

- name: test-hostpath

hostPath:

path: /data

type: DirectoryOrCreate

创建和配置示例

Go

#1、创建pod

[root@k8s-master hostpath]# kubectl apply -f hostpath.yaml

pod/test-pd created

[root@k8s-master hostpath]# kubectl get pod -owide|grep test-pd

test-pd 1/1 Running 0 6m54s 10.244.2.174 k8s-node2 <none> <none>

#2、在K8S-node2节点上查看内部

[root@k8s-node2 data]# docker ps -a|grep test-container

b4ff1de7e371 nginx "/docker-entrypoint.…" 8 minutes ago Up 8 minutes k8s_test-container_test-pd_default_e48b2791-54d6-40d6-bc77-56328ca4c038_0

[root@k8s-node2 data]# docker inspect b4ff1de7e371

[

....

"Mounts": [

{

"Type": "bind",

"Source": "/data",

"Destination": "/usr/share/nginx/html",

"Mode": "",

"RW": true,

"Propagation": "rprivate"

},

....

]

##"Source": "/data", # 宿主机的目录

## "Destination": "/usr/share/nginx/html", # 容器中的目录

#3、在master节点上进入pod

[root@k8s-master hostpath]# kubectl exec -it pod/test-pd /bin/bash

kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.

root@test-pd:/# cd /usr/share/nginx/html

root@test-pd:/usr/share/nginx/html# ls

index.html

root@test-pd:/usr/share/nginx/html# echo date >> index.html

root@test-pd:/usr/share/nginx/html# cat index.html

Sun Jun 5 17:23:17 CST 2022 test hostpath

date

[root@k8s-master hostpath]# curl 10.244.2.174

Sun Jun 5 17:23:17 CST 2022 test hostpath

date

#4、删除pod

[root@k8s-master hostpath]# kubectl delete -f hostpath.yaml

pod "test-pd" deleted

[root@k8s-master hostpath]# kubectl delete -f hostpath.yaml

Error from server (NotFound): error when deleting "hostpath.yaml": pods "test-pd" not found

#5、重启创建pod

[root@k8s-master hostpath]# kubectl apply -f hostpath.yaml

pod/test-pd created

[root@k8s-master hostpath]# kubectl get pod -owide|grep test-pd

test-pd 1/1 Running 0 63s 10.244.2.175 k8s-node2 <none> <none>

[root@k8s-master hostpath]# curl 10.244.2.175

Sun Jun 5 17:23:17 CST 2022 test hostpath

date

###这种方式如果宿主机一直在的话,或者该pod一直被调度到该节点的话,可以实现pod的存储,如果该node停机维护或其他

问题,该数据就无法保持了。

2.3、pv、pvc

既然Host类型的持久化存储无法解决节点宕机或者pod在另外的机器上拉起导致数据无法恢复的Bug,那我们就应该思考一个问题:既然我无法在宿主机持久化,那我在集群之外的服务器上存储数据,让我的Pod关联到这个数据存储服务器上,同时我对这个数据存储服务器做高可用,岂不美哉? 想法很好,那我们来介绍一下K8S给我们的解决方案: PersistentVolumes 简称PV

  1. PV(PersistentVolume): 持久化卷。PV 是对底层共享存储的一种抽象,由管理员进行创建和配置,它和具体的底层的共享存储技术的实现方式有关,比如 Ceph、GlusterFS、NFS、hostPath 等,都是通过插件机制完成与共享存储的对接。
  2. PVC(PersistentVolumeClaim): 持久化卷声明。PVC 是用户存储的一种声明,PVC 和 Pod 比较类似,Pod 消耗的是节点,PVC 消耗的是 PV 资源,Pod 可以请求 CPU 和内存,而 PVC 可以请求特定的存储空间和访问模式。对于真正使用存储的用户不需要关心底层的存储实现细节,只需要直接使用 PVC 即可。

hostpath创建pv、pvc

Go

#1、创建pv

[root@k8s-master volumes]# cat pv.yaml

apiVersion: v1

kind: PersistentVolume

metadata:

name: nginx-pv-volume

labels:

type: local

spec:

storageClassName: manual-nginx

capacity:

storage: 5Gi

accessModes:

- ReadWriteOnce

hostPath:

path: "/home/nfs"

[root@k8s-master volumes]# kubectl apply -f pv.yaml

persistentvolume/nginx-pv-volume created

[root@k8s-master ~]# kubectl get pv -owide

NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE VOLUMEMODE

nginx-pv-volume 5Gi RWO Retain Available manual-nginx 10s Filesystem

##可以看到这个PV为10G,访问模式为RWO,卸载后保留,目前的STATUS为可用状态。

#2、创建pvc

[root@k8s-master volumes]# kubectl apply -f pvc.yaml

persistentvolumeclaim/nginx-pv-claim created

[root@k8s-master volumes]# cat pvc.yaml

apiVersion: v1

kind: PersistentVolumeClaim

metadata:

name: nginx-pv-claim

spec:

storageClassName: manual-nginx

accessModes:

- ReadWriteOnce

resources:

requests:

storage: 3Gi

[root@k8s-master volumes]# kubectl get pv,pvc -owide|grep pv-claim

persistentvolume/nginx-pv-volume 5Gi RWO Retain Bound default/nginx-pv-claim manual-nginx 5m54s Filesystem

persistentvolumeclaim/nginx-pv-claim Bound nginx-pv-volume 5Gi RWO manual-nginx 97s Filesystem

3、创建pod

[root@k8s-master volumes]# cat pvc-pod.yaml

apiVersion: v1

kind: Pod

metadata:

name: task-pv-pod

spec:

volumes:

- name: nginx-pv-volume

persistentVolumeClaim:

claimName: nginx-pv-claim

containers:

- name: task-pv-container

image: nginx:1.7.9

ports:

- containerPort: 80

name: "http-server"

volumeMounts:

- mountPath: "/usr/share/nginx/html"

name: nginx-pv-volume

nodeSelector:

kubernetes.io/hostname: k8s-node6

[root@k8s-master volumes]# kubectl apply -f pvc-pod.yaml

pod/task-pv-pod created

[root@k8s-master volumes]# kubectl get pod -owide|grep task-pv-pod

task-pv-pod 1/1 Running 0 20s 10.244.4.117 k8s-node6 <none> <none>

#3、在k8s-node3中执行

[root@k8s-node6 ~]# hostname > /home/nfs/index.html

[root@k8s-node6 ~]# curl 10.244.4.117

k8s-node6

NFS创建pv pvc

大多数情况下,持久化 Volume 的实现,往往依赖于一个远程存储服务,比如:远程文件存储(比如,NFS、GlusterFS)、远程块存储(比如,公有云提供的远程磁盘)等等。

Go

1、搭建nfs服务器

yum -y install nfs-utils rpcbind

[root@k8s-master ~]# vim /etc/exports

/data/k8s *(rw,sync,no_root_squash)

[root@k8s-master ~]# systemctl enable rpcbind

[root@k8s-master ~]# systemctl start rpcbind

[root@k8s-master ~]# systemctl status rpcbind

####在启动nfs

[root@k8s-master ~]# systemctl enable nfs

Created symlink from /etc/systemd/system/multi-user.target.wants/nfs-server.service to /usr/lib/systemd/system/nfs-server.service.

[root@k8s-master ~]# systemctl start nfs

[root@k8s-master ~]# systemctl status nfs

客户端挂在

[root@k8s-node1 ~]# mkdir /data/k8s/ -p

[root@k8s-node1 ~]# mount -t nfs 172.16.4.169:/data/k8s /data/k8s

2、创建pv和pvc

[root@k8s-master volumes]# kubectl apply -f nfs-pv.yaml

persistentvolume/nginx-nfs-pv created

[root@k8s-master volumes]# cat nfs-pv.yaml

apiVersion: v1

kind: PersistentVolume # 类型为PV

metadata:

name: nginx-nfs-pv # pv的名称

spec:

accessModes: # 访问模式

- ReadWriteMany # PV以read-write挂载到多个节点

capacity: # 容量

storage: 2Gi # pv可用的大小

nfs:

path: /home/k8s/nfs # NFS的路径

server: 172.16.4.169 # NFS服务器地址

##创建pvc

[root@k8s-master volumes]# cat nfs-pvc.yaml

apiVersion: v1

kind: PersistentVolumeClaim # 类型

metadata:

name: nginx-nfs-pvc # PVC的名称

spec:

accessModes: # 访问模式

- ReadWriteMany # PVC以read-write挂载到多个节点

resources:

requests:

storage: 2Gi # PVC允许申请的大小

[root@k8s-master volumes]# kubectl apply -f nfs-pvc.yaml

persistentvolumeclaim/nginx-nfs-pvc created

[root@k8s-master ~]# kubectl get pv,pvc -owide|grep nfs

persistentvolume/nginx-nfs-pv 2Gi RWX Retain Bound default/nginx-nfs-pvc 4m37s Filesystem

persistentvolumeclaim/nginx-nfs-pvc Bound nginx-nfs-pv 2Gi RWX 53s Filesystem

##c创建pod

[root@k8s-master volumes]# cat nfs-pod.yaml

apiVersion: apps/v1

kind: Deployment

metadata:

name: nginx-nfs-pvc

spec:

selector:

matchLabels:

app: nginx-pvc

template:

metadata:

labels:

app: nginx-pvc

spec:

containers:

- name: nginx-test-pvc

image: nginx

imagePullPolicy: IfNotPresent

ports:

- name: web-port

containerPort: 80

protocol: TCP

volumeMounts:

- name: nginx-persistent-storage # 取个名字,与下面的volumes的名字要一致

mountPath: /usr/share/nginx/html # 容器中的路径

volumes:

- name: nginx-persistent-storage

persistentVolumeClaim:

claimName: nginx-nfs-pvc # 引用前面声明的PVC

[root@k8s-master volumes]# kubectl apply -f nfs-pod.yaml

deployment.apps/nginx-nfs-pvc created

[root@k8s-master volumes]# kubectl get pod -owide|grep nfs

nginx-nfs-pvc-577dbd6d5-jsjqf 1/1 Running 0 2m32s 10.244.2.183 k8s-node2 <none> <none>

[root@k8s-master volumes]# curl 10.244.2.183

Thu May 12 19:26:48 CST 2022

this is mater

4、回收的顺序为:先删除pod,在删除pvc,查看pv的时候pv的状态为:Released ,如果变为avaiable,

则需要删除claimRef:字段下的所有配置。

2.4、StorageClass

创建PV有两种方式:一种是人工管理 PV 的方式就叫作 Static Provisioning;另一种是Dynamic Provisioning动态创建存储卷,动态供给的关键就是StorageClass,它的作用就是创建PV模板,Provision意为”提供者。
创建StorageClass里面需要定义PV属性比如存储类型、大小等;另外创建这种PV需要用到存储插件。最终效果是,用户提交PVC,里面指定存储类型,如果符合我们定义的StorageClass,则会为其自动创建PV并进行绑定。

什么是StorageClass

StorageClass 为管理员提供了描述存储 "class(类)" 的方法。 不同的 class 可能会映射到不同的服务质量等级或备份策略,或由群集管理员确定的任意策略。

Kubernetes提供了一套可以自动创建PV的机制,即:Dynamic Provisioning。而这个机制的核心在于StorageClass这个API对象。

StorageClass对象会定义下面两部分内容:

  1. PV的属性。比如,存储类型,Volume的大小等。
  2. 创建这种PV需要用到的存储插件,即存储制备器,比如,Ceph 等等。

有了这两个信息之后,Kubernetes就能够根据用户提交的PVC,找到一个对应的StorageClass,之后Kubernetes就会调用该StorageClass声明的存储插件,进而创建出需要的PV。

StorageClass 为管理员提供了描述存储 “类” 的方法。 不同的类型可能会映射到不同的服务质量等级或备份策略,或是由集群管理员制定的任意策略。 Kubernetes 本身并不清楚各种类代表的什么。这个类的概念在其他存储系统中有时被称为 “配置文件”。

StorageClass的功能

StorageClass 的作用,则是充当 PV 的模板。并且,只有同属于一个 StorageClass 的 PV 和 PVC,才可以绑定在一起。StorageClass 的另一个重要作用,是指定 PV 的 Provisioner(存储插件)。这时候,如果你的存储插件支持 Dynamic Provisioning 的话,Kubernetes 就可以自动为你创建 PV 了。

通过StorageClass创建pv和pvc例子

Go

1、创建pvc和storageclass的yaml文件

[root@k8s-master storageclass]# cat storageclass-pvc.yaml

apiVersion: v1

kind: PersistentVolumeClaim

metadata:

name: storageclass-claim1

spec:

accessModes:

- ReadWriteOnce

# 指定所使用的存储类,此存储类将会自动创建符合要求的 PV

storageClassName: fast

resources:

requests:

storage: 30Gi

---

apiVersion: storage.k8s.io/v1

kind: StorageClass

metadata:

name: fast

provisioner: kubernetes.io/gce-pd

parameters:

type: pd-ssd

[root@k8s-master storageclass]# kubectl apply -f storageclass-pvc.yaml

persistentvolumeclaim/storageclass-claim1 created

storageclass.storage.k8s.io/fast created

2、查看该pvc处于pending原因,由于没有没有gce插件

[root@k8s-master storageclass]# kubectl describe persistentvolumeclaim/storageclass-claim1

Name: storageclass-claim1

Namespace: default

StorageClass: fast

Status: Pending

Volume:

Labels: <none>

Annotations: volume.beta.kubernetes.io/storage-provisioner: kubernetes.io/gce-pd

Finalizers: [kubernetes.io/pvc-protection]

Capacity:

Access Modes:

VolumeMode: Filesystem

Used By: <none>

Events:

Type Reason Age From Message

---- ------ ---- ---- -------

Warning ProvisioningFailed 14m persistentvolume-controller storageclass.storage.k8s.io "fast" not found

Warning ProvisioningFailed 68s (x10 over 13m) persistentvolume-controller Failed to provision volume with StorageClass "fast": failed to get GCE GCECloudProvider with error <nil>

StorageClass 的作用,则是充当 PV 的模板。并且,只有同属于一个 StorageClass 的 PV 和 PVC,才可以绑定在一起。StorageClass 的另一个重要作用,是指定 PV 的 Provisioner(存储插件)。这时候,如果你的存储插件支持 Dynamic Provisioning 的话,Kubernetes 就可以自动为你创建 PV 了。

2.5、Local PV(local-volume-provisioner)

Kubernetes 依靠 PV、PVC 实现了一个新的特性,这个特性的名字叫作:Local Persistent Volume,也就是 Local PV。

Local PV 实现的功能就非常类似于 hostPath 加上 nodeAffinity,比如,一个 Pod 可以声明使用类型为 Local 的 PV,而这个 PV 其实就是一个 hostPath 类型的 Volume。如果这个 hostPath 对应的目录,已经在节点 A 上被事先创建好了,那么,我只需要再给这个 Pod 加上一个 nodeAffinity=nodeA,不就可以使用这个 Volume 了吗?理论上确实是可行的,但是事实上,我们绝不应该把一个宿主机上的目录当作 PV 来使用,因为本地目录的存储行为是完全不可控,它所在的磁盘随时都可能被应用写满,甚至造成整个宿主机宕机。所以,一般来说 Local PV 对应的存储介质是一块额外在挂载在宿主机的磁盘或者块设备,我们可以认为就是“一个 PV 一块盘”。

local-volume-provisioner创建local PV

3.1、准备本地存储

Go

##依次在K8S的各个计算节点依次执行

1、创建对应的目录

mkdir /home/data/{data1,data2,data3}

2、依次挂载对应的节点

mount --bind /home/data/data1/ /home/data/data1/

mount --bind /home/data/data2/ /home/data/data2/

mount --bind /home/data/data3/ /home/data/data3/

3、把相关的配置信息写道/etc/fstab

echo UUID=`sudo blkid -s UUID -o value /dev/sda2` /home/data/data1 ext4 defaults 0 2 | sudo tee -a /etc/fstab

echo UUID=`sudo blkid -s UUID -o value /dev/sda2` /home/data/data2 ext4 defaults 0 2 | sudo tee -a /etc/fstab

echo UUID=`sudo blkid -s UUID -o value /dev/sda2` /home/data/data3 ext4 defaults 0 2 | sudo tee -a /etc/fstab

###

上述的/home/data/data1、/home/data/data2、/home/data/data13是 local-volume-provisioner

使用的发现目录(discovery directory),local-volume-provisioner 会为发现目录下的每一个

子目录创建对应的 PV

3.2、下载和配置 local-volume-provisioner

Go

1、下载

wget https://raw.githubusercontent.com/pingcap/tidb-operator/master/examples/local-pv/local-volume-provisioner.yaml

2、修改不同的路径。如果你使用与上一步中不同路径的发现目录,需要修改 ConfigMap 和 DaemonSet 定义。

#cat local-volume-provisioner.yaml

apiVersion: storage.k8s.io/v1

kind: StorageClass

metadata:

name: "local-storage"

provisioner: "kubernetes.io/no-provisioner"

volumeBindingMode: "WaitForFirstConsumer"

---

apiVersion: v1

kind: ConfigMap

metadata:

name: local-provisioner-config

namespace: kube-system

data:

setPVOwnerRef: "true"

nodeLabelsForPV: |

- kubernetes.io/hostname

storageClassMap: |

local-storage:

hostDir: /home/data

mountDir: /data

---

apiVersion: apps/v1

kind: DaemonSet

metadata:

name: local-volume-provisioner

namespace: kube-system

labels:

app: local-volume-provisioner

spec:

selector:

matchLabels:

app: local-volume-provisioner

template:

metadata:

labels:

app: local-volume-provisioner

spec:

serviceAccountName: local-storage-admin

containers:

- image: "quay.io/external_storage/local-volume-provisioner:v2.3.4"

name: provisioner

securityContext:

privileged: true

env:

- name: MY_NODE_NAME

valueFrom:

fieldRef:

fieldPath: spec.nodeName

- name: MY_NAMESPACE

valueFrom:

fieldRef:

fieldPath: metadata.namespace

- name: JOB_CONTAINER_IMAGE

value: "quay.io/external_storage/local-volume-provisioner:v2.3.4"

resources:

requests:

cpu: 100m

memory: 100Mi

limits:

cpu: 100m

memory: 100Mi

volumeMounts:

- mountPath: /etc/provisioner/config

name: provisioner-config

readOnly: true

- mountPath: /data

name: local-disks

mountPropagation: "HostToContainer"

volumes:

- name: provisioner-config

configMap:

name: local-provisioner-config

- name: local-disks

hostPath:

path: /home/data

---

apiVersion: v1

kind: ServiceAccount

metadata:

name: local-storage-admin

namespace: kube-system

---

apiVersion: rbac.authorization.k8s.io/v1

kind: ClusterRoleBinding

metadata:

name: local-storage-provisioner-pv-binding

namespace: kube-system

subjects:

- kind: ServiceAccount

name: local-storage-admin

namespace: kube-system

roleRef:

kind: ClusterRole

name: system:persistent-volume-provisioner

apiGroup: rbac.authorization.k8s.io

---

apiVersion: rbac.authorization.k8s.io/v1

kind: ClusterRole

metadata:

name: local-storage-provisioner-node-clusterrole

namespace: kube-system

rules:

- apiGroups: [""]

resources: ["nodes"]

verbs: ["get"]

---

apiVersion: rbac.authorization.k8s.io/v1

kind: ClusterRoleBinding

metadata:

name: local-storage-provisioner-node-binding

namespace: kube-system

subjects:

- kind: ServiceAccount

name: local-storage-admin

namespace: kube-system

roleRef:

kind: ClusterRole

name: local-storage-provisioner-node-clusterrole

apiGroup: rbac.authorization.k8s.io

如果你使用与上一步中不同路径的发现目录,需要修改 ConfigMap 和 DaemonSet 定义。

3.3、部署和检查 local-volume-provisioner 程序

Go

1、部署

[root@k8s-master tidb]# kubectl apply -f local-volume-provisioner.yaml

storageclass.storage.k8s.io/local-storage unchanged

configmap/local-provisioner-config unchanged

daemonset.apps/local-volume-provisioner unchanged

serviceaccount/local-storage-admin unchanged

clusterrolebinding.rbac.authorization.k8s.io/local-storage-provisioner-pv-binding unchanged

clusterrole.rbac.authorization.k8s.io/local-storage-provisioner-node-clusterrole unchanged

clusterrolebinding.rbac.authorization.k8s.io/local-storage-provisioner-node-binding unchanged

2、检查pv和pod状态

[root@k8s-master tidb]# kubectl get po -n kube-system -l app=local-volume-provisioner && kubectl get pv

NAME READY STATUS RESTARTS AGE

local-volume-provisioner-9gp9x 1/1 Running 0 29h

local-volume-provisioner-kghc7 1/1 Running 0 29h

local-volume-provisioner-v2vvt 1/1 Running 0 29h

NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE

local-pv-264b0ff0 446Gi RWO Delete Available local-storage 62m

local-pv-27bc7b00 446Gi RWO Delete Available local-storage 62m

local-pv-4653df42 446Gi RWO Delete Available local-storage 62m

local-pv-993f4e47 446Gi RWO Delete Available local-storage 62m

local-pv-ad7b1fa4 446Gi RWO Delete Available local-storage 62m

local-pv-b9e5d531 446Gi RWO Delete Available local-storage 62m

local-pv-bfe87b7 446Gi RWO Delete Available local-storage 62m

local-pv-dc8fa7ee 446Gi RWO Delete Available local-storage 62m

local-pv-f12d96bb 446Gi RWO Delete Available local-storage 62m

三、总结

本文讨论了 k8s 存储的几种常见的存储类型类型,有临时存储如:hostPath、emptyDir。也有真正的持久化存储,还讨论了相关的概念,如:PVC、PV、StorageClass等,下图是对这些概念的一个概括:

K8S-数据持久化PV、PVC、StorageClass的关系

继续阅读