k8s存储器

pod容器共享Volume
同一个pod中的多个容器能够共享Pod级别的存储卷Volume。即定义一个卷,然后将该卷挂在为多个容器的内部目录。
如下pod实例,pod-volume.yml内容如下:
apiVersion: v1
kind: Pod
metadata:
 name: volume-pod
spec:
 containers:
 - name: tomcat
  image: tomcat
  ports:
  - containerPort: 8080
  volumeMounts:
  - name: app-logs #使用名称为app-logs的卷
   mountPath: /usr/local/tomcat/logs #将卷挂载到该路径,这个路径是容器内的绝对路径
 - name: busybox
  image: busybox
  command: ["sh", "-c", "tail -f /logs/catalinz*.log"]
  volumeMounts:
  - name: app-logs
   mountPath: /logs
 volumse: #定义pod中的公共volume
 - name: app-logs #volume名称为app-logs
  emptyDir: {} #
说明:pod中定义了名称为app-logs的卷,然后应用到多个pod中。

emptyDir:k8s不显式声明宿主机目录的 Volume。所以,Kubernetes 也会在宿主机上创建一个临时目录,这个目录将来就会被绑定挂载到容器所声明的 Volume 目录上。此时并不关心卷定义在宿主机的哪个目录,主要是通过该目录,实现Pod中多个容器对卷的共享。

Kubernetes 也提供了显式的 Volume 定义,它叫作 hostPath。比如下面的这个 YAML 文件:
 ...   
    volumes:
      - name: nginx-vol
        hostPath: 
          path:  " /var/data"

Kubernetes 中,有几种特殊的 Volume:
存在的意义不是为了存放容器里的数据,也不是用来进行容器和宿主机之间的数据交换。这些特殊 Volume 的作用,是为容器提供预先定义好的数据,现在支持四种:
Secret; pod中创建保密信息
ConfigMap; 获取一般配置
Downward API;让 Pod 里的容器能够直接获取到这个 Pod API 对象本身的信息。
ServiceAccountToken。
 

pv pvc storageClass
PVC 是 Pod 所希望使用的持久化存储的属性。
PV 是持久化存储数据卷。定义的是一个持久化存储在宿主机上的目录,比如一个 NFS 的挂载目录。

pvc定义
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: manual
  resources:
    requests:
      storage: 1Gi
pv定义:
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs
spec:
  storageClassName: manual
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteMany
  nfs:
    server: 10.244.1.4
    path: "/"
pod中使用pvc:
apiVersion: v1
kind: Pod
metadata:
  labels:
    role: web-frontend
spec:
  containers:
  - name: web
    image: nginx
    ports:
      - name: web
        containerPort: 80
    volumeMounts:
        - name: nfs
          mountPath: "/usr/share/nginx/html"
  volumes:
  - name: nfs
    persistentVolumeClaim:
      claimName: nfs #pvc名称
Pod创建之后,kubelet 就会把这个 PVC 所对应的 PV,也就是一个 NFS 类型的 Volume,挂载在这个 Pod 容器内的目录上。

pv和pvc的设计思想:
pvc:持久化存储的描述,类似“接口”
pv:持久化存储的实现部分,类似”实现类“
好处:作为开发者,我们只需要跟 PVC 这个“接口”打交道,而不必关心具体的实现是 NFS 还是 Ceph。

PVC 要真正被容器使用起来,就必须先和某个符合条件的 PV 进行绑定。
这里要检查的条件,包括两部分:
第一个条件,当然是 PV 和 PVC 的 spec 字段。比如,PV 的存储(storage)大小,就必须满足 PVC 的要求。
第二个条件,则是 PV 和 PVC 的 storageClassName 字段必须一样。这个机制我会在本篇文章的最后一部分专门介绍。

如果系统里并没有合适的 PV 跟它定义的 PVC 绑定,那会发生什么?
Pod 的启动就会报错。
然后如果创建了符合条件的PV,Kubernetes 能够再次完成 PVC 和 PV 的绑定操作,从而启动 Pod。

实现原理:k8s存在一个Volume Controller,查看当前每一个PVC,是不是已经处于 Bound(已绑定)状态。如果不是,那它就会遍历所有的、可用的 PV,并尝试将其与这个“单身”的 PVC 进行绑定。


PV 对象,又是如何变成容器里的一个持久化存储的呢?
容器的 Volume,其实就是将一个宿主机上的目录,跟一个容器里的目录绑定挂载在了一起。
“持久化 Volume”,指的就是这个宿主机上的目录,具备“持久性”,既不会因为容器的删除而被清理掉,也不会跟当前的宿主机绑定。这样,当容器被重启或者在其他节点上重建出来之后,它仍然能够通过挂载这个 Volume,访问到这些内容。
所以 hostPath 和 emptyDir 类型的 Volume不是持久化的。
持久化 Volume 的实现,往往依赖于一个远程存储服务,比如:远程文件存储(比如,NFS、GlusterFS)、远程块存储(比如,公有云提供的远程磁盘)等等。

以远程块存储为实例看pv实现原理
1)kubelet为Pod在宿主机上创建Volume目录,路径:
/var/lib/kubelet/pods/<Pod的ID>/volumes/kubernetes.io~<Volume类型>/<Volume名字>
2)将 远程块存储 挂载到pod所在的宿主机
以Google Cloud 的 Persistent Disk为例,调用如下api:
$ gcloud compute instances attach-disk <虚拟机名字> --disk <远程磁盘名字>
3)格式化这个磁盘设备,然后将它挂载到宿主机指定的挂载点上,这个挂载点就是步骤一中volume的宿主机目录
4)kubelet 只要把这个 Volume 目录通过 CRI 里的 Mounts 参数,传递给 Docker;类似执行如下指令:
$ docker run -v /var/lib/kubelet/pods/<Pod的ID>/volumes/kubernetes.io~<Volume类型>/<Volume名字>:/<容器内的目标目录> 我的镜像 ...


StorageClass
一个大规模的 Kubernetes 集群里很可能有成千上万个PVC,这就意味着运维人员必须得事先创建出成千上万个 PV。浪费资源,且难以维护。

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

storageClass的对象定义包含两部分:
第一,PV 的属性。比如,存储类型、Volume 的大小等等。
第二,创建这种 PV 需要用到的存储插件。比如,Ceph 等等。
Kubernetes 就能够根据用户提交的 PVC,找到一个对应的 StorageClass 了。然后,Kubernetes 就会调用该 StorageClass 声明的存储插件,创建出需要的 PV。

实例如下:sc.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: block-service
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-ssd
provisioner 字段的值是:kubernetes.io/gce-pd,这正是 Kubernetes 内置的 GCE PD 存储插件的名字。
parameters 字段,就是 PV 的参数。比如:上面例子里的 type=pd-ssd,指的是这个 PV 的类型是“SSD 格式的 GCE 远程磁盘”。

创建StorageClass
$ kubectl create -f sc.yaml

PVC 里指定要使用的 StorageClass 名字,pvc定义如下:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: claim1
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: block-service
  resources:
    requests:
      storage: 30Gi
Google Cloud 为例。当我们通过 kubectl create 创建上述 PVC 对象之后,Kubernetes 就会调用 Google Cloud 的 API,创建出一块 SSD 格式的 Persistent Disk。然后,再使用这个 Persistent Disk 的信息,自动创建出一个对应的 PV 对象。

有了 Dynamic Provisioning 机制,运维人员只需要在 Kubernetes 集群里创建出数量有限的 StorageClass 对象就可以了。当开发人员提交了包含 StorageClass 字段的 PVC 之后,Kubernetes 就会根据这个 StorageClass 创建出对应的 PV。

pod pv pvc storageClass之间的关系如下图:

在这个体系中:
PVC 描述的,是 Pod 想要使用的持久化存储的属性,比如存储的大小、读写权限等。
PV 描述的,则是一个具体的 Volume 的属性,比如 Volume 的类型、挂载目录、远程存储服务器地址等。
而 StorageClass 的作用,则是充当 PV 的模板。并且,只有同属于一个 StorageClass 的 PV 和 PVC,才可以绑定在一起。还有是指定 PV 的 Provisioner(存储插件)。这时候,如果你的存储插件支持 Dynamic Provisioning 的话,Kubernetes 就可以自动为你创建 PV 了。


本地持久化存储
好处:Volume 直接使用的是本地磁盘,尤其是 SSD 盘,它的读写性能相比于大多数远程存储来说,要好得多。
Local Persistent Volume 并不适用于所有应用。事实上,它的适用范围非常固定,比如:高优先级的系统应用,需要在多个不同节点上存储数据,并且对 I/O 较为敏感。典型的应用包括:分布式数据存储比如 MongoDB、Cassandra 等,分布式文件系统比如 GlusterFS、Ceph 等,以及需要在本地磁盘上进行大量数据缓存的分布式应用。

本地模拟过程:
1)在名叫 node-1 的宿主机上创建一个挂载点,比如 /mnt/disks;然后,用几个 RAM Disk 来模拟本地磁盘
# 在node-1上执行
$ mkdir /mnt/disks
$ for vol in vol1 vol2 vol3; do
    mkdir /mnt/disks/$vol
    mount -t tmpfs $vol /mnt/disks/$vol
done

2)为这些本地磁盘定义对应的 PV 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: example-pv
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: local-storage
  local:
    path: /mnt/disks/vol1
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - node-1
local 字段,指定了它是一个 Local Persistent Volume;
而 path 字段,指定的正是这个 PV 对应的本地磁盘的路径,即:/mnt/disks/vol1;

Pod要想使用这个PV,那它就必须运行在 node-1 上。所以,在这个 PV 的定义里,需要有一个 nodeAffinity 字段指定 node-1 这个节点的名字。
调度器在调度 Pod 的时候,就能够知道一个 PV 与节点的对应关系,从而做出正确的选择

3)创建pv
$ kubectl create -f local-pv.yaml 
persistentvolume/example-pv created

$ kubectl get pv
NAME         CAPACITY   ACCESS MODES   RECLAIM POLICY  STATUS      CLAIM             STORAGECLASS    REASON    AGE
example-pv   5Gi        RWO            Delete           Available                     local-storage             16s

4)创建一个 StorageClass 来描述这个 PV
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

说明:
provisioner 字段,我们指定的是 no-provisioner。这是因为 Local Persistent Volume 目前尚不支持 Dynamic Provisioning,
StorageClass 还定义了一个 volumeBindingMode=WaitForFirstConsumer 的属性。它是 Local Persistent Volume 里一个非常重要的特性,即:延迟绑定。

因为按照一般流程: PV 和 PVC 的 YAML 文件之后,Kubernetes 就会根据它们俩的属性,以及它们指定的 StorageClass 来进行绑定。只有绑定成功后,Pod 才能通过声明这个 PVC 来使用对应的 PV。
但 Local Persistent Volume 的话,就会发现,这个流程根本行不通,实例如下:
pod只能运行在node2上,pod使用pvc名称是pvc-1。
第一个pv名称是pv-1,在node1上;第二个pv名称是pv-2,在node2上。且pv-1和pv-2都符合pvc-1的要求;
如果提前绑定,则pvc-1可以和pv-1绑定;
那使用pvc-1的pod只能裕兴在node1上了。
最终不符合我们要求。

解决方案就是延迟绑定:即被声明了WaitForFirstConsumer的pv等到实际需要的时候再和pvc绑定,按照上面的实例即:pod被创建到了node2上,此时该pod需要使用pvc-1,pvc-1再和node2上的pv-2绑定.

k8s支持编写自己的存储插件
FlexVolume 和 CSI

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值