Volume 的生命周期独立于容器,Pod 中的容器可能被销毁和重建,但 Volume 会被保留。
本质上,Kubernetes Volume 是一个目录。当Volume被mount到Pod,Pod中的所有容器都可以访问这个Volume。
Kubernetes Volume 也支持多种 backend 类型,完整列表可参考 Volumes | Kubernetes,包括 emptyDir、hostPath、GCE Persistent Disk、AWS Elastic Block Store、NFS、Ceph 等,

一、emptyDir Volume

一个emptyDir Volume是Host上的一个空目录。
emptyDir Volume对于容器来说是持久的(在容器重新启动时能保留数据),对于Pod则不是。当Pod从节点删除,Volume的内容随之删除。但如果只是容器被销毁而Pod还在,则Volume不受影响。 emptyDir Volume 的生命周期与 Pod 一致
emptyDir.yaml:
模拟了一个 producer-consumer 场景。Pod 有两个容器 producer和 consumer,它们共享一个 Volume。producer 负责往 Volume 中写数据,consumer 则是从 Volume 读取数据。
apiVersion: v1
kind: Pod
metadata:
  name: producer-consumer
  namespace: default # 命名空间
spec:
  containers:
  - name: producer
    image: busybox
    imagePullPolicy: IfNotPresent # 优先使用本地镜像,没有则下载
    volumeMounts:    # 2、producer容器将shared-volume mount到/producer_dir目录;
    - mountPath: /producer_dir
      name: shared-volume
    args:            # 3、producer 通过 echo 将数据写到文件 /producer_dir/hello 里。
    - /bin/sh
    - -c
    - echo "hello world" > /producer_dir/hello; sleep 30000
 
  - name: consumer
    image: busybox
    imagePullPolicy: IfNotPresent # 优先使用本地镜像,没有则下载
    volumeMounts:    # 4、consumer 容器将 shared-volume mount 到 /consumer_dir 目录。
    - mountPath: /consumer_dir
      name: shared-volume
    args:            # 5、 consumer 通过 cat 从文件 hello 读数据
    - /bin/sh
    - -c
    - cat /consumer_dir/hello; sleep 30000
 
  volumes:           # 1、文件最底部volumes定义一个emptyDir类型的Volume shared-volume
  - name: shared-volume
    emptyDir: {}

执行如下命令创建Pod:

1ee6449ebda5464fbceaff233d12d046.png

kubectl logs pod-name container-name显示容器consumer成功读到了producer 写入的数据,验证了两个容器共享emptyDir Volume。
因为emptyDir是Docker Host文件系统里的目录,其效果等同于执行 docker run -v /producer_dirdocker run -v /consumer_dir。通过 docker inspect container_id 查看容器的详细配置信息,我们发现两个容器都 mount 了同一个目录:
"/var/lib/kubelet/pods/b5b4093f-0791-430b-836e-32fc374690b2/volumes/kubernetes.io~empty-dir/shared-volume"就是emptyDir在Host上真正路径。
emptyDir是Host上创建的临时目录,优点是能够方便的为Pod中的容器提供共享存储,不需要额外的配置,但它不具备持久性,若Pod不在了,emptyDir也就没有了。
根据这个特性,emptyDir 特别适合 Pod 中的容器需要临时共享存储空间的场景,比如前面的生产者消费者用例。

二、hostPath Volume

hostPath Volume的作用是将Host文件系统中 已经存在的目录mount给Pod的容器。
大部分应用不会使用hostPath Volume,因为这个实际增加了Pod与节点的耦合,限制了Pod的使用。不过那些需要访问 Kubernetes 或 Docker 内部数据(配置文件和二进制库)的应用则需要使用 hostPath。
kubectl edit -- namespace = kube - system pod kube - apiserver - master
358031822c2e485487fcffb0cec179ce.png
type取值:
  • DirectoryOrCreate:宿主机上不存在创建此目录
  • Directory 必须存在挂载目录  
  • FileOrCreate:宿主机上不存在挂载文件就创建
  • File:必须存在文件
  • 空字符串( 默认)用于向后兼容,这意味着在安装 hostPath 卷之前不会执行任何检查。
如果Pod被销毁,hostPath对应的目录还会保留。从这点看,hostPath 的持久性比 emptyDir 强。不过一旦 Host 崩溃,hostPath 也就没法访问了。

三、外部 Storage Provider

如果 Kubernetes 部署在诸如 AWS、GCE、Azure 等公有云上,可以直接使用云硬盘作为 Volume,下面是 AWS Elastic Block Store 的例子:
495475f32d504ce2999b3ee1c4668ce0.png
要在Pod中使用ESB volume,必须先在AWS中创建,然后通过volume-id引用。
Kubernetes Volume 也可以使用主流的分布式存,比如 Ceph、GlusterFS 等,下面是 Ceph 的例子:
7962175bada743ad8f4da4dd52e24337.png
Ceph 文件系统的 /some/path/in/side/cephfs 目录被 mount 到容器路径 /test-ceph。
最大特点就是不依赖 Kubernetes。Volume 的底层基础设施由独立的存储系统管理,与 Kubernetes 集群是分离的。数据被持久化后,即使整个 Kubernetes 崩溃也不会受损。

四、NFS挂载

NFS挂载是在其中一个node上将共享的目录发布出来,并且在各个node上都安装好nfs-utils工具,只要在创建Pod的时候指定要挂载的文件系统为nfs和挂载点、权限,即可自动完成挂载操作,并且能实现数据同步。
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis   # Deployment名称
spec:
  selector:
    matchLabels: # 通过标签选择被控制的pod
      app: redis
  template: # pod信息
    metadata:
      labels:
        app: redis # 给pod打上标签app=redis
    spec:
      containers:
      - image: redis
        name: redis
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 6379 # 对service暴露端口
          name: redis6379
        env:
        - name: ALLOW_EMPTY_PASSWORD # 本地Key
          value: "yes"
        - name: REDIS_PASSWORD
          value: "redis"
        volumeMounts:
        - name: redis-persistent-storage
          mountPath: /data
      volumes:
      - name: redis-persistent-storage
        nfs:
        path: /k8s-nfs/redis/data  # 共享出来的目录
        server: 192.168.126.112    # 提前做好解析

五、PV & PVC

a942368e6b7e45bca38d86eb1e30451b.png
PersistentVolume(PV):外部存储系统中的一块存储空间,由 管理员创建与维护。 PV具有持久性,生命周期独立于Pod
PersistentVolumeClaim(PVC):是对PV的申请,PVC通常由 普通用户创建和维护。需要为Pod分配存储资源时,用户可以创建一个PVC,指明存储资源的容量大小和访问模式(比如只读)等信息,Kubernetes会查找并提供满足条件的PV。
有了 PersistentVolumeClaim,用户只需要告诉 Kubernetes 需要什么样的存储资源,而不必关心真正的空间从哪里分配,如何访问等底层细节信息。这些 Storage Provider 的底层信息交给管理员来处理,只有管理员才应该关心创建 PersistentVolume 的细节信息。

8318fb47ea814856968483c3780e39f7.jpeg

5.1、 生命周期

fe79773571984ccf8966fb78aee2c53b.png

5.2、PV类型

GCEPersistentDisk
AWSElasticBlockStore
AzureFile
AzureDisk
FC (Fibre Channel)
Flexvolume
Flocker
NFS
iSCSI
RBD (Ceph Block Device)
CephFS
Cinder (OpenStack block storage)
Glusterfs
VsphereVolume
Quobyte Volumes
HostPath (Single node testing only – local storage is not supported in any
way and WILL NOT WORK in a multi-node cluster)
Portworx Volumes
ScaleIO Volumes
StorageOS

5.3、PV卷阶段状

  • AVailable: 资源尚未被pvc使用
  • Bound: 卷已经被pvc绑定
  • Released: pvc被删除,卷处于释放状态,但未被集群回收
  • Failed:卷自动回收失败

5.4、 NFS PersistentVolume--通过NFS实现持久化存储

已经在master节点搭建了一个NFS服务器,目录为/nas:  showmount -e [ip]
d167750e66154afbbcae4bf1224bf7b8.png
5.4.1、创建一个PV,配置文件nfs-pv1.yaml:
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mypv1
  namespace: default
  labels: # 对 pv 打 label,关联pvc到特定的pv
    pv: nfs-pv1
spec:
  capacity: # 指定 PV 的容量为 1G
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: nfs # 指定PV的class为nfs,等同于为PV设置一个分类,PVC可以指定class申请相应的class的PV
  nfs:
    path: /nas/pv1 # 指定 PV 在 NFS 服务器上对应的目录,/nas/pv1这个目录得存在
    server: 192.168.11.45

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

  • ReadWriteOnce – PV 能以 read-write 模式 mount 到单个节点
  • ReadOnlyMany – PV 能以 read-only 模式 mount 到多个节点
  • ReadWriteMany – PV 能以 read-write 模式 mount 到多个节点
persistentVolumeReclaimPolicy: 指定PV的回收策略,支持的策略有:
  • Retain - 管理员手工回收
  • Recycle - 清除PV中的数据,效果相等于rm -rf /thevolume/*
  • Delete - 删除Storage Provider上的对应存储资源,例如 AWS EBS、OpenStack Cinder Volume 等,NFS 的 PV 不支持 Delete
创建 mypv1与查看PV:  
e1a15d89a02f4140858b41e3a048b3a7.png
kubectl get pv 获取PV,STATUS为Avaliable,表明mypv1已就绪,可以被PVC申请。
5.4.2、创建 PVC mypvc1,配置文件 nfs-pvc1.yaml:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mypvc1
namespace: default
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi   # 只需要指定 PV 的容量,访问模式和 class
  storageClassName: nfs
  selector:
    matchLabels: # 使用 matchLabel 来关联刚创建的 pv:nfs-pv1,绑定到特定的pv
      pv: nfs-pv1

创建 mypvc1: bb4689ec33154bd6b4fc40331b079682.png

kubectl get pvc 和 kubectl get pv 的输出,可以看出mypvc1已经Bound到mypv1,申请成功。
5.4.3、在 Pod 中使用存储,Pod 配置文件 pod1.yaml:
apiVersion: v1
kind: Pod
metadata:
  name: mypod1
spec:
  containers:
    - name: mypod1
      image: busybox
      args:
      - /bin/sh
      - -c
      - sleep 30000
      volumeMounts:
      - mountPath: "/mydata"
        name: mydata
  volumes:
    - name: mydata
      persistentVolumeClaim:
        claimName: mypvc1

创建 mypod1:

ef7e336560bf476b83da30307ffc7840.png

验证PV是否可用:
3035a80071e743658d263153a5dc74e4.png
可见在Pod中创建的文件 /mydata/hello 已经保存到NFS服务器的目录/nas/pv1中。

5.5、 回收PV

如果不再需要使用 PV,可用删除 PVC 回收 PV。
需要先删除之前创建的 mypod1
13f86e914c714227a1398943a2dd9c96.png
当 PVC mypvc1 被删除后,我们发现 Kubernetes 启动了一个新 Pod recycler-for-mypv1,这个 Pod 的作用就是清除 PV mypv1 的数据。此时 mypv1 的状态为 Released,表示已经解除了与 mypvc1 的 Bound,正在清除数据,不过此时还不可用
当数据清除完毕,mypv1 的状态重新变为 Available,此时则可以被新的 PVC 申请。
/nas/pv1/目录下的文件hello也被删除了,因为  PV 的回收策略设置为 Recycle,所以数据会被清除,但这可能不是我们想要的结果。如果我们希望保留数据,可以将策略设置为 Retain。
1c2085b096844a33954b88bfab1d8ecc.png

5.6、PV动态供给

提前创建了PV,然后通过PVC申请PV在Pod中使用,这种方式叫做 静态供给
动态供给:如果没有满足PVC条件的PV,会动态创建PV。相比静态供给,动态供给有明显的优势: 不需要提前创建 PV,减少了管理员的工作量,效率高。
动态供给是通过 StorageClass 实现的,StorageClass 定义了如何创建 PV。

0900a24e3ec04564a3a50a0f56cacbeb.jpeg

StorageClass standard:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: standard
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp2
reclaimPolicy: Retain # StorageClass 支持 Delete 和 Retain 两种 reclaimPolicy,默认是 Delete

StorageClass slow

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: slow
provisioner: kubernetes.io/aws-ebs
parameters:
type: io1
zones: us-east-1d, us-east-1c
iopsPerGB: "10"

这两个 StorageClass 都会动态创建 AWS EBS,不同在于 standard 创建的是 gp2 类型的 EBS,而 slow 创建的是 io1 类型的 EBS。不同类型的 EBS 支持的参数可参考 AWS 官方文档。

与之前一样,PVC 在申请 PV 时,只需要指定 StorageClass 和容量以及访问模式,比如:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mypvc1
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi 
  storageClassName: standard

六、MySQL使用PV与PVC

怎样为MySQL数据库持久化存储,步骤如下:
  • 创建PV与PVC
  • 部署MySQL
  • 向MySQL添加数据
  • 模拟节点宕机故障,Kubernetes自动将MySQL迁移到其他节点
  • 验证数据一致性

6.1、创建PV与PVC

mysql-pv.yaml:
apiVersion: v1
kind: PersistentVolume
metadata:
  name: mysql-pv
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: nfs
  nfs:
    path: /nas/mysql-pv # 指定 PV 在 NFS 服务器上对应的目录,/nas/mysql-pv这个目录得存在
    server: 192.168.11.45

mysql-pvc.yaml:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pvc1
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi   # 只需要指定 PV 的容量,访问模式和 class
  storageClassName: nfs

创建mysql-pv与mysql-pvc:

6e0bf89b8fa14a728bc327c404719acd.png

aaf2594f70e440478187550528acf9e5.png
如果此时机器上有相同容量的其他PV,那怎样将PVC绑定到特定的PV上???--- k8s 如何关联pvc到特定的pv?_强哥之神的博客-CSDN博客_pvc指定pv   k8s 如何关联pvc到特定的pv?

6.2、部署MySQL

mysql.yaml:
apiVersion: apps/v1         # 当前配置格式的版本
kind: Deployment            # kind 是要创建的资源类型
metadata:                   # metadata 是该资源的元数据,name 是必需的元数据项
  name: mysql-deployment    # deployment名称
spec:                       # spec 部分是该 Deployment 的规格说明
  selector:                 # 通过标签选择被控制的pod
    matchLabels:
      app: mysql
  replicas: 1               # 通过replicas声明pod个数是3
  template:                 # template 定义 Pod 的模板,这是配置文件的重要部分
    metadata:               # metadata 定义 Pod 的元数据,至少要定义一个 label。label 的 key 和 value 可以任意指定
      labels:               # 给pod打上标签app=httpd
        app: mysql
    spec:                   # spec 描述 Pod 的规格,此部分定义 Pod 中每一个容器的属性,name 和 image 是必需的。
      containers:           # 声明容器的名称、镜像、端口
      - name: mysql
        image: mysql:5.7
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: password
        ports:              # 需要暴露的端口库号列表
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: mysql-pvc1
      
---
apiVersion: v1
kind: Service
metadata:
  name: mysql-service
spec:
  selector:
    app: mysql # 将选择具有app: mysql标签的Pod作为管理范围
  ports:
  - port: 3306    # service监听端口

4c49f8b446ba4db8bccd824e9abdd5d3.png

MySQL被部署到node2节点上,通过客户端访问Service Mysql:
kubectl run -it --rm --image=mysql:5.7 --restart=Never mysql-client -- mysql -h mysql-service -ppassword

6.3、添加数据

b1b956e1b8e74ff496e4c8e18b7ee5ee.png
8e80ff5d673e4e40b1d0e4f7b222d064.png

6.4、模拟故障

关闭node2,模拟node2节点宕机故障:  shutdown now
耐心等待一段时间后(5分钟 pod-eviction-timeout值),kubernetes将MySQL迁移到node1节点
9601f4b0c50d4712a9567186a93359b6.png

6.5、验证数据的一致性

aa3cc79a55564bfa9706475673da5371.png
MySQL服务恢复,数据完整

七、参考

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐