k8s-----(| 三 |)存储Volume卷,PV / PVC,nfs持久化

文章目录

  • 一. 存储Volume 卷
    • 1. emptyDir本地临时卷
    • 2. hostPath 本地卷
  • 二 . PV/PVC
    • 1. 概念
    • 2. PV 一些概念
      • (1)PV 的类型(插件)
      • (2)访问模式
      • (3)回收策略
      • (4)状态
      • (5)模板
    • 3. NFS 持久化示例
      • (1)安装 NFS
      • (2)创建 PV 和 StatefulSet
    • 4. PV 的一些说明

一. 存储Volume 卷

参考
容器磁盘上的文件的生命周期是短暂的,这就使得在容器中运行重要应用时会出现一些问题。

  1. 首先,当容器崩溃时,kubelet 会重启它,但是容器中的文件将丢失——容器以干净的状态(镜像最初的状态)重新启动。

  2. 其次,在 Pod 中同时运行多个容器时,这些容器之间通常需要共享文件

Volume 就是卷的概念,它是用来管理 Kubernetes 存储的,是用来声明 在 Pod 中的容器可以访问文件目录的,一个卷可以被挂载在 Pod 中一 个或者多个容器的指定路径下面。 而 Volume 本身是一个抽象的概念,一个 Volume 可以去支持多种的后 端的存储。比如说 Kubernetes 的 Volume 就支持了很多存储插件,它 可 以 支 持 本 地 的 存 储 , 可 以 支 持 分 布 式 的 存 储 , 比 如 说 像 ceph ,GlusterFS ;它也可以支持云存储,比如说阿里云上的云盘、AWS 上的 云盘、Google 上的云盘等等。

常用的几种卷:
emptyDir:本地临时卷
hostPath:本地卷
nfs等:共享卷
configmap: 配置文件

1. emptyDir本地临时卷

https://kubernetes.io/zh/docs/concepts/storage/volumes/
当 Pod 被分配给节点时,首先创建 emptyDir 卷,并且只要该 Pod 在该节点上运行,该卷就会存在。

同个pod里面的不同容器,共享同一个持久化目录,
pod节点删除时,volume的数据也会被删除。
如果仅仅是容器被销毁,pod还在,则不会影响volume中的数据。
总结来说:emptyDir的数据持久化的生命周期和使用的pod一致。一般是作为临时存储使用。

emptyDir 的用法有:

  1. 缓存空间,例如用于基于磁盘的合并排序
  2. 用作长时间计算崩溃恢复时的检查点
  3. Web 服务器容器提供数据时,保存内容管理器容器提取的文件
[root@k8s-master emptydir]# pwd
/root/k8s_practice/emptydir
[root@k8s-master emptydir]# cat pod_emptydir.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-emptydir
  namespace: default
spec:
  containers:
  - name: myapp-pod
    image: registry.cn-beijing.aliyuncs.com/google_registry/myapp:v1
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  - name: busybox-pod
    image: registry.cn-beijing.aliyuncs.com/google_registry/busybox:1.24
    imagePullPolicy: IfNotPresent
    command: ["/bin/sh", "-c", "sleep 3600"]
    volumeMounts:
    - mountPath: /test/cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir: {}

可以看到,两个容器都能读取到 emptyDir 中的数据

# 在容器2/test 目录下,创建一个 index.html 文件
$ kubectl exec test-pd -c test2-container -it -- touch /test/index.html

# 查看容器1/cache 目录
$ kubectl exec test-pd -c test-container -it -- ls /cache
index.html

2. hostPath 本地卷

https://kubernetes.io/zh/docs/concepts/storage/volumes/

hostPath 卷将主机节点的文件系统中的文件或目录挂载到集群中,pod删除的时候,卷不会被删除
k8s-----(| 三 |)存储Volume卷,PV / PVC,nfs持久化_第1张图片

hostPath 的用途如下

  1. 运行需要访问 Docker 内部的容器;使用 /var/lib/docker 的 hostPath
  2. 在容器中运行 cAdvisor;使用 /dev/cgroups 的 hostPath
  3. 允许pod指定给hostPath在运行pod之前是否存在

type支持类型
k8s-----(| 三 |)存储Volume卷,PV / PVC,nfs持久化_第2张图片

[root@k8s-master hostpath]# pwd
/root/k8s_practice/hostpath
[root@k8s-master hostpath]# cat pod_hostpath.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-hostpath
  namespace: default
spec:
  containers:
  - name: myapp-pod
    image: registry.cn-beijing.aliyuncs.com/google_registry/myapp:v1
    imagePullPolicy: IfNotPresent
    volumeMounts:
    - name: hostpath-dir-volume
      mountPath: /test-k8s/hostpath-dir
    - name: hostpath-file-volume
      mountPath: /test/hostpath-file/test.conf
  volumes:
  - name: hostpath-dir-volume
    hostPath:
      # 宿主机目录
      path: /k8s/hostpath-dir
      # hostPath 卷指定 type,如果目录不存在则创建(可创建多层目录)
      type: DirectoryOrCreate
  - name: hostpath-file-volume
    hostPath:
      path: /k8s2/hostpath-file/test.conf
      # 如果文件不存在则创建。 前提:文件所在目录必须存在  目录不存在则不能创建文件
      type: FileOrCreate

进入容器生成数据:
kubectl exec -it nginx-deployment-7c759f97d4-5ql6t bash
root@nginx-deployment-7c759f97d4-5ql6t:/# echo "test data" > /cache/a.txt


到指定宿主机验证数据:
root@k8s-node2:~# cat /tmp/linux39/a.txt 
test data

删除 pod 再次验证数据是否会丢失:
# kubectl delete -f deploy_hostPath.yml 
deployment.apps "nginx-deployment" deleted‘

再node中查看是否创建/data/mysql目录,并且有
# cat /data/mysql/a.txt 
test data

而且在容器本目录中创建,修改
在宿主机也会变化

反之也成立

但删除时,卷不会被删除
kubectl get pod -o wide 

二 . PV/PVC

参考

1. 概念

  1. PersistentVolume (PV 持久存储卷

是由管理员设置的存储,它是群集的一部分。就像节点是集群中的资源一样,PV 也是集群中的资源。 PV 是 Volume 之类的卷插件,但具有独立于使用 PVPod 的生命周期。此 API 对象包含存储实现的细节,即 NFS、iSCSI 或特定于云供应商的存储系统

  1. PersistentVolumeClaim (PVC): 持久存储卷声明

是用户存储的请求。它与 Pod 相似。Pod 消耗节点资源PVC 消耗 PV 资源。Pod 可以请求特定级别的资源(CPU 和内存)。PVC 可以声明可以请求特定的大小和访问模式(例如,可以读/写一次或 只读多次模式挂载)

生命周期
k8s-----(| 三 |)存储Volume卷,PV / PVC,nfs持久化_第3张图片
PV的分类

  • 静态 pv:
    集群管理员创建一些 PV。它们带有可供群集用户使用的实际存储的细节,一般保存访问至后端存储的细节(怎么连接,地址多少…)。它们存在于 Kubernetes API 中,可用于消费

k8s-----(| 三 |)存储Volume卷,PV / PVC,nfs持久化_第4张图片

  • 动态 pv:

当管理员创建的静态 PV 都不匹配用户的 PersistentVolumeClaim 时,集群可能会尝试动态地为 PVC 创建卷。此配置基于 StorageClasses :PVC 必须请求 [存储类],并且管理员必须创建并配置该类才能进行动态创建。声明该类为 " " 可以有效地禁用其动态配置

要启用基于存储级别的动态存储配置,集群管理员需要启用 API server 上的 DefaultStorageClass [准入控制器]。例如,通过确保 DefaultStorageClass 位于 API server 组件的 --admission-control 标志,使用逗号分隔的有序值列表中,可以完成此操作

k8s-----(| 三 |)存储Volume卷,PV / PVC,nfs持久化_第5张图片

绑定:
master 中的控制环路监视新的 PVC,寻找匹配的 PV(如果可能),并将它们绑定在一起。如果为新的 PVC 动态调配 PV,则该环路将始终将该 PV 绑定到 PVC。否则,用户总会得到他们所请求的存储,但是容量可能超出要求的数量

一旦 PV 和 PVC 绑定后, PersistentVolumeClaim 绑定是排他性的不管它们是如何绑定的。 PVC 跟 PV 绑定是一对一的映射

持久化卷声明的保护
PVC 保护的目的是确保由 Pod 正在使用的 PVC 不会从系统中移除,因为如果被移除的话可能会导致数据丢失。

注意:当 Pod 状态为 Pending 并且 Pod 已经分配给节点或者 Pod 为 Running 状态时,PVC 处于活动状态。

当启用PVC 保护 alpha 功能时,如果用户删除了一个 pod 正在使用的 PVC,则该 PVC 不会被立即删除。PVC 的删除将被推迟,直到 PVC 不再被任何 pod 使用

2. PV 一些概念

(1)PV 的类型(插件)

PersistentVolume 类型以插件形式实现。Kubernetes 目前支持以下插件类型:

  • GCEPersistentDisk、AWSElasticBlockStore、AzureFile、AzureDisk、FC (Fibre Channel)
  • FlexVolume、Flocker、NFSiSCS、RBD(Ceph Block Device)、CephFS
  • Cinder(OpenStack block storage)、Glusterfs、VsphereVolume、Quobyte、Volumes
  • HostPathVMware、Photon、Portworx、Volumes、ScaleIO、Volumes、StorageOS

(2)访问模式

PersistentVolume 可以以资源提供者支持的任何方式挂载到主机上。如下表所示,供应商具有不同的功能,每个 PV 的访问模式都将被设置为该卷支持的特定模式。例如,NFS 可以支持多个读/写客户端,但特定的 NFS PV 可能以只读方式导出到服务器上。每个 PV 都有一套自己的用来描述特定功能的访问模式

ReadWriteOnce——该卷可以被单个节点以读/写模式挂载
ReadOnlyMany——该卷可以被多个节点以只读模式挂载
ReadWriteMany——该卷可以被多个节点以读/写模式挂载
在命令行中,访问模式缩写为:
RWO:ReadWriteOnce
ROX:ReadOnlyMany
RWX:ReadWriteMany
注意:一个卷一次只能使用一种访问模式挂载,即使它支持很多访问模式。
例如,GCEPersistentDisk 可以由单个节点作为 ReadWriteOnce 模式挂载,或由多个节点以 ReadWriteMany 模式挂载,但不能同时挂载

(3)回收策略

Retain(保留):手动回收
Recycle(回收):基本擦除( 相当于执行了 rm -rf /thevolume/*
Delete(删除):关联的存储资产(例如 AWS EBS、GCE PD、Azure Disk 和 OpenStack Cinder 卷)将被删除

当前,只有 NFSHostPath 支持回收策略
AWS EBS、GCE PD、Azure Disk 和 Cinder 卷支持删除策略

(4)状态

卷可以处于以下的某种状态:

Available(可用):一块空闲资源还没有被任何声明绑定
Bound(已绑定):卷已经被声明绑定
Released(已释放):声明被删除,但是资源还未被集群重新声明
Failed(失败):该卷的自动回收失败

命令行会显示绑定到 PV 的 PVC 的名称

(5)模板

apiVersion: v1
kind: PersistentVolume		# 类型:PV
metadata:
  name: pv0003				# 名称
spec:
  capacity:
    storage: 5Gi			# 卷的大小:5G
  volumeMode: Filesystem	# 文件类型
  accessModes:				# 访问策略
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle	# 回收策略
  storageClassName: slow	# 存储类的一个名称
  mountOptions:				# 其它说明,也可以不指定,让他自己做判断
    - hard
    - nfsvers=4.1
  nfs:
    path: /tmp				# 挂载到哪个目录下
    server: 172.17.0.2		# 挂载到哪个服务器
 status:
   phase: Bound	

3. NFS 持久化示例

nfs共享存储
https://kubernetes.io/zh/docs/concepts/storage/volumes/
nfs 卷允许将现有的 NFS(网络文件系统)共享挂载到您的容器中。不像 emptyDir,当删除 Pod 时,nfs 卷的内容被保留,卷仅仅是被卸载。这意味着 NFS 卷可以预填充数据,并且可以在 pod 之间“切换”数据。 NFS 可以被多个写入者同时挂载

需要提前准备NFS服务器,授权规则

在本机通过showmoubt -e ip可以看到NFS的共享文件

也就是最总将server.path挂载到mountPath

(1)安装 NFS

新建一台虚拟机,安装 NFS,我的虚拟机 IP 为 192.168.66.200

yum install -y nfs-common nfs-utils rpcbind
mkdir /nfs
chmod 777 /nfs
chown nfsnobody /nfs

vim /etc/exports	# 文件中写入以下内容
	/nfs *(rw,no_root_squash,no_all_squash,sync)

systemctl start rpcbind
systemctl start nfs

在 k8s 每个节点中安装 NFS 客户端

# 安装依赖
$ yum -y install nfs-utils rpcbind

NFS的一些操作

# 查看共享目录
$ showmount -e 192.168.66.20
Export list for 192.168.66.20:
/nfs *

# 共享目录与本地目录挂载
$ mkdir ~/test
$ mount -t nfs 192.168.66.20:/nfs ~/test

# 解除挂载
$ umount ~/test

(2)创建 PV 和 StatefulSet

k8s-----(| 三 |)存储Volume卷,PV / PVC,nfs持久化_第6张图片
创建一个 pv.yaml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfspv1
spec:
  capacity:				# 容量
    storage: 10Gi
  accessModes:			# 访问模式
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain		# 回收策略
  storageClassName: nfs
  nfs:						# nfs服务器配置
    path: /nfs				# 目录
    server: 192.168.66.20	# IP

创建 PV

$ kubectl apply -f nfspv.yaml
persistentvolume/nfspv1 created

$ kubectl get pv
NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
nfspv1   10Gi       RWO            Retain           Available           nfs                  7s

创建 Service 和 StatefulSet

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
    - port: 80
      name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx
  serviceName: "nginx"		# 指定 Service 名称(上面创建的,一定要是个无头服务)
  replicas: 3		# 副本数
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:		# 容器信息
        - name: nginx
          image: qcq-linux/myapp:v2
          ports:
            - containerPort: 80		# 释放的端口
              name: web		# 端口名字
          volumeMounts:		# 挂载
            - name: www
              mountPath: /usr/share/nginx/html	 # 容器内目录
  volumeClaimTemplates:		# 卷请求声明模板(pvc模板)
    - metadata:
        name: www
      spec:
        accessModes: [ "ReadWriteOnce" ]	# 指定要请求的卷的访问模式
        storageClassName: "nfs"		# 指定要请求的卷的类名,只有与 PV 中的storageClassName 相同时,才会匹配
        resources:
          requests:
            storage: 1Gi	# 指定要请求的卷大小必须满足 1G

因为前面只创建了一个 pv,所以 StatefulSet 只有一个 Pod 可以匹配,查看 Pod:

# 查看pod,只有一个因为pv,所以只有一个pod匹配成功后正常运行
# 因为是 StatefulSet,第二个没能正常启动,所以第三个Pod不会创建
$ kubectl get pod
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          57s
web-1   0/1     Pending   0          54s

# 查看 pv,可以看到只有一个绑定成功
# 第二个绑定不成功是因为访问模式不匹配
# 第三个绑定不成功是因为 storageClass 不匹配
$ kubectl get pv
NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM               STORAGECLASS   REASON   AGE
nfspv1   10Gi       RWO            Retain           Bound       default/www-web-0   nfs                     3m35s
nfspv2   5Gi        ROX            Retain           Available                       nfs                     34m
nfspv3   5Gi        RWO            Retain           Available                       nfs1                    34m

# 查看 pvc,每个Pod有一个pvc
$ kubectl get pvc
NAME        STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
www-web-0   Bound     nfspv1   10Gi       RWO            nfs            6m6s
www-web-1   Pending                                      nfs            6m3s

在 NFS 服务器的 /nfs 目录中创建 index.html,写入“/nfs访问成功”,然后通过 nginx 来访问

# 获取IP
$ kubectl get pod -o wide
NAME    READY   STATUS    RESTARTS   AGE   IP            NODE         NOMINATED NODE   READINESS GATES
web-0   1/1     Running   0          27m   10.244.1.58   k8s-node01   <none>           <none>

$ curl 10.244.1.58
/nfs访问成功

删除 Pod,pv 中的数据不会丢失

$ kubectl delete pod web-0
pod "web-0" deleted

# 可以看到 IP 已经变了,说明上一个Pod删除后又建了个新的(Pod 的 name 一致)
$ kubectl get pod -o wide
NAME    READY   STATUS    RESTARTS   AGE   IP            NODE         NOMINATED NODE   READINESS GATES
web-0   1/1     Running   0          15s   10.244.2.60   k8s-node02   <none>           <none>

# 可以看到仍然可以成功访问,数据不会丢失
$ curl 10.244.2.60
/nfs访问成功

删除 StatefulSet 后,pvc 不会自动删除,pv也不会自动释放,需要手动删除

# 删除 StatefulSet 后,pvc 仍然存在
$ kubectl delete statefulset web
$ kubectl get pvc
NAME        STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
www-web-0   Bound     nfspv1   10Gi       RWO            nfs            13h

# 删除 pvc 后,pv 没有自动释放
$ kubectl delete pvc www-web-0
$ kubectl get pv
NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM               STORAGECLASS   REASON   AGE
nfspv1   10Gi       RWO            Retain           Released    default/www-web-0   nfs                     13h

# 手动释放 pv
$ kubectl edit pv nfspv1
# 将下面的 spec.claimRef 删除
	spec:
	  claimRef:
	    apiVersion: v1
	    kind: PersistentVolumeClaim
	    name: www-web-0
	    namespace: default
	    resourceVersion: "619064"
	    uid: 99cea07e-339e-431c-bcb6-c398c884b29c

# 再次查看 pv 已经得到释放
$ kubectl get pv
NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
nfspv1   10Gi       RWO            Retain           Available           nfs                     13h

4. PV 的一些说明

  • 匹配 Pod name ( 网络标识 ) 的模式为:$(StatefulSet名称)-$(序号),比如上面的示例:web-0,web-1,web-2

  • StatefulSet 为每个 Pod 副本创建了一个 DNS 域名(意味着其它 Pod 可以通过这个域名来访问),这个域名的格式为: $(podname).(headless server name),也就意味着服务间是通过 Pod 域名来通信而非 Pod IP,因为当 Pod 所在 Node 发生故障时, Pod 会被飘移到其它 Node 上,Pod IP 会发生变化,但是 Pod 域名不会有变化

# 随便进入一个以前的 Pod,没有就新建一个,然后ping域名,可以成功 ping 通
$ ping web-0.nginx
PING web-0.nginx (10.244.2.60): 56 data bytes
64 bytes from 10.244.2.60: seq=0 ttl=62 time=0.388 ms
64 bytes from 10.244.2.60: seq=1 ttl=62 time=0.263 ms

  • StatefulSet 使用 Headless 服务来控制 Pod 的域名(意味着 Pod 外部可以通过这个域名来访问),这个域名的 FQDN(完全现定域名) 为:$(service name).(namespace).svc.cluster.local,其中,“cluster.local” 指的是集群的域名
# 1. 查看 DNS 服务器 coredns 的 ip
$ kubectl get pod -n kube-system -o wide
NAME                                   READY   STATUS    RESTARTS   AGE   IP              NODE           NOMINATED NODE   READINESS GATES
coredns-5c98db65d4-5ztqn               1/1     Running   10         46d   10.244.0.19     k8s-master01   <none>           <none>
coredns-5c98db65d4-pc62t               1/1     Running   10         46d   10.244.0.18     k8s-master01   <none>           <none>

# 2. 通过 coredns 来解析域名,可以看到解析后的域名对应 StatefulSet 下的 Pod 的 IP
# 用 dig 解析域名(没有 dig 要安装:yum -y install bind-utils)
# 命令格式:dig -t A 域名 @DNS服务器IP
$ dig -t A nginx.default.svc.cluster.local @10.244.0.19
...省略
nginx.default.svc.cluster.local. 30 IN	A	10.244.2.60
...省略

  • 根据 volumeClaimTemplates,为每个 Pod 创建一个 pvc,pvc 的命名规则匹配模式:
    (volumeClaimTemplates.name)-(pod_name),比如上面的 volumeMounts.name=www, Pod
    name=web-[0-2],因此创建出来的 PVC 是 www-web-0、www-web-1、www-web-2

  • 删除 Pod 不会删除其 pvc,手动删除 pvc 将自动释放 pv

你可能感兴趣的:(k8s,k8s)