本文发布于Cylon的收藏册,转载请著名原文链接~

在Kubernetes之上,在节点级提供一个存储卷的方式来持久存储数据的逻辑,这种只具备一定程度上的持久性。为了实现更强大的持久性,应该使用脱离节点而存在的共享存储设备。 为此Kubernetes提供了不同类型的存储卷。

大多数和数据存储服务相关的应用,和有状态应用几乎都是需要持久存储数据的。容器本身是有生命周期的,为了使容器终结后可以将其删除,或者编排至其他节点上去运行。意味着数据不能存储在容器本地。一旦Pod故障就会触发重构。如果将数据放置在Pod自有的容器内名称空间中,数据随着Pod终结而结束。为了突破Pod生命周期的限制,需要将数据放置在Pod自有文件系统之外的地方。

存储卷

对Kubernetes来讲,存储卷不属于容器,而属于Pod。因此,在Kubernetes中同一个Pod内的多个容器可共享访问同一组存储卷。

Pod底部有一个基础容器, ==pause==,但是不会启动。pause是基础架构容器。创建Pod时pause时Pod的根,所有Pod,包括网络命名空间等分配都是分配给pause的。在Pod中运行的容器是pause的网络名称空间的。容器在挂载存储卷时,实际上是复制pause的存储卷。

因此为了真的实现持久性,存储卷应为宿主机挂载的外部存储设备的存储卷。如果需要实现跨节点持久,一般而言需要使用脱离节点本地的网络存储设备(ceph、glusterfs、nfs)来实现。节点如果需要使用此种存储的话,需要可以驱动相应存储设备才可以(在节点级可以访问相应网络存储设备)。

k8s之上可使用的存储卷

Kubernetes支持的存储卷类型

  • empryDir:只在节点本地使用的,用于做临时目录,或当缓存使用。一旦Pod删除,存储卷一并被删除。empryDir背后关联的宿主机目录可以使宿主机的内存。
  • hostPath:使宿主机目录与容器建立关联关系。
  • 网络存储
    • 传统的SAN(iSCSI,FC)NAS(常见用法协议 NFS,cifs,http)设备所构建的网络存储设备。
    • 分布式存储(分机系统或块级别),glusterfs,ceph(rbd ceph的块接口存储),cephfs等。
    • 云存储:EBS(弹性块存储)亚马逊 ,Azure Disk 微软。此模型只适用于Kubernetes集群托管在其公有云之上的场景。

使用kubectl explain pod.spec.volumes查看Kubernetes所支持的存储类型。

emptyDir

语法

  • emptyDir
    • medium 媒介类型 empty string (disk 默认) or memory
    • sizeLimit 空间上限

定义完存储卷之后,需要在container当中使用volumeMounts指明挂载哪个或哪些个存储卷


1
2
3
4
5
- container
    - mountPath 挂载路径
    - name 挂载那个卷
    - readOnly 是否只读挂载
    - subPath 是否挂载子路径之下
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: v1
kind: Pod
metadata:
  name: my-nginx
  namespace: default
spec:
  containers:
  - name: busybox
    image: busybox
    imagePullPolicy: IfNotPresent
    ports:
    - name: http
      containerPort: 80
    command: ["tail"]
    volumeMounts:  # 指明挂载哪一个存储卷
    - name: html
      mountPath: /data/web/html # 指明挂载到容器的哪个路径下
  volumes:
  - name: html
    emptyDir: {} # 表示空映射,都使用默认值,大小不限制,使用磁盘空间,而不是不定义

在Kubernetes中 $()是变量引用

gitRepo

将git仓库当做存储卷来使用,其实并不是Pod将git仓库当存储卷来使用。只不过是在Pod创建时,会自动连接到git仓库之上(此链连接依赖于宿主机上有git命令来完成),由宿主机驱动,将git仓库中的内容clone到本地来,并且将其作为存储卷挂载至Pod之上。

  1. ==gitRepo是建立在emptyDir之上==。所不同的在于,将所指定仓库的内容clone下来并放至空目录中。因此主容器将此目录当做服务于用户的数据来源。需要注意的是,在此处做的修改是不会同步到git仓库中去的。

  2. 如果git仓库在Pod运行过程中内容发生改变,Pod之内的存储卷内容是不会随之改变的。

gitRepo 卷示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: v1
kind: Pod
metadata:
  name: my-git
  namespace: default
spec:
  containers:
  - name: git
    image: nginx
    imagePullPolicy: IfNotPresent
    ports:
    - name: http
      containerPort: 80
    volumeMounts:
    - name: git-volumes
      mountPath: /data/web/html
  volumes:
  - name: git-volumes
    gitRepo:
      repository: "https://github.com/potester/test-k8s.git"
      revision: "master"

查看Pod内容器挂载的目录

1
2
3
4
5
6
$ kubectl exec -it my-git -- ls -l /data/web/html/test-k8s/
total 16
nginx.svc
redis.svc
svc-redis
test.yaml

hostPath

hostPath 将Pod所在宿主机之上、脱离Pod中容器名称空间之外的宿主机的文件系统的某一目录与Pod建立关联关系。在Pod被删除时,此存储卷是不会被删除的。

hostPath在一定程度上拥有持久的特性,但这种持久只是节点级的持久,在被跨节点调度时,这些数据还是会丢失的。

Volumes - Kubernetes

type

  • DirectoryOrCreate 挂载路径在宿主机上是已存在的目录,如目录不存在则创建此目录。
  • Directory 挂载路径在宿主机上必须已存在的目录。
  • FileOrCreate 文件或创建新的空文件。
  • File 必须存在此文件,将其挂载至容器中。
  • Socket 必须是socket类型的文件。
  • CharDevice 必须是一个字符类型的设备文件。
  • BlockDevice 块类型的设备文件。

hostPath 卷示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
apiVersion: v1
kind: Pod
metadata:
  name: pod-hostPath
  namespace: default
spec:
  containers:
  - name: myapp
    image: nginx:1.8
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html/ # 挂载路径
  volumes: # 定义存储卷
  - name: html
    hostPath:
      path: /data/html
      type: DirectoryOrCreate

NFS,对于nfs存储卷来讲

  • path -required- nfs服务器导出路径,
  • readOnly 只读 true or false 默认false
  • server [required] 服务器地址
1
2
3
4
5
volumes:
- name: html
  nfs:
    path: /data/volumes/
    server: nfs01.test.com

PVC使用逻辑

在Pod中只需定义存储卷,定义时只需指定使用存储卷大小,这个存储卷叫PVC类型的存储卷。PVC存储卷必须与当前名称空间中的PVC建立直接绑定关系,而PVC必须与PV建立绑定关系,而PV是某个真实存储设备上的存储空间。所以PV和PVC是kubernetes系统之上的抽象的标准资源。PV和PVC之间的关系,在PVC不被调用时是空载的。

对于PV类型的资源的使用

PVC语法

PVC是标准K8S资源,也有自己所属的属组。

kubectl explain pvc

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
apiVersion: v1
kind: pvc
metadata:
  name: k8s-pvc
  namespaces: default
spec:
  accessModes # 访问模型
  resources # 资源限制,至少多少G
  selector # 标签选择器,必须要选择哪个PV建立关联关系
  storageClassName # 存储类名称
  volumeMode # 后端存储卷模式。
  volumeName # 存储卷名称,指后端PersistentVolume

PVC选择的模式

  • 使用存储卷名称,一对一绑定了(精确选择),
  • 选择器选定
  • 如不指定名称,会从大量符合条件的PV选一个。
  • 类型限制,volumeMode,那一类型的PV可以被当前claim所使用。

在Pod中使用当前名称空间已经存在的PVC

exportfs -arv kubectl explain pods.spec.volumes.persistentVolumeClaim,PV和PVC的关联是一对一的,一旦被使用(状态为banding)

1
2
3
4
volmes
persistentVolumeClaim
  claimName # pvc名称
  readOnly # 要不要只读

定义PV时一定不要加名称空间,PV是集群级别的,不属于名称空间,但==PVC是属于名称空间级别==的。PVC并不属于节点(node),PVC是标准的Kubernetes资源,它存储在etcd当中。只有Pod才需要运行在节点之上,所有其他资源基本都是保存在集群状态存储(apiserver的存储)etcd当中。

在Kubernetes新版本中,只要PV还被PVC绑定,就不支持删除。

定义accessMode时需要注意存储设备,有些存储设备不支持多路读写与多路只读,只支持单路读写。

  • accessMode []string accessMode可定义多个参数
    • ReadWriteOnce 单路读写,可简写为RWO
    • ReadOnlyMany 多路只读,ROX
    • ReadWriteMany 多路读写操作 RWX
  • capacity 用来指定存储空间的大小,需要使用资源访问模型来定义。
  • persistentVolumeReclaimPolicy 回收策略 Persistent Volumes
    • Retain 保留。
    • Recycle 回收,将数据删除,并且把PV置为空闲状态可以让其他设备绑定。
    • delete 删除PV
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv01
  labels:
    name: pv01
    type: ssd
spec:
  nfs:
    path: /data/pv01
    server: 192.168.1.2
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity:
    storage: 200Mi

PVC

  • spec
    • accessModes PVC也需要定义accessModes,此accessModes模式要求一定是PV的accessModes的子集才可以被匹配到。
    • resources 如指定,PV一定要满足(大于、等于)PVC此值才能被使用。
    • requests []map此处是与PV不一样之处,需要要求有多大空间

PVC的使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv01
  labels:
    name: pv-test1
spec:
  accessModes: 
  - ReadWriteOnce
  capacity: 
    storage: 2Gi
  nfs:
    path: /data/v1
    server: 10.0.0.11
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv02
  labels:
    name: pv-test2
spec:
  accessModes:
  - ReadWriteOnce
  capacity: 
    storage: 2Gi
  nfs:
    path: /data/v2
    server: 10.0.0.11
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv03
  labels:
    name: pv-test3
spec:
  accessModes:
  - ReadWriteOnce
  capacity: 
    storage: 2Gi
  nfs:
    path: /data/v3
    server: 10.0.0.11

查看PV和PVC

1
2
3
4
5
6
7
8
9
$ kubectl get pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM          STORAGECLASS   REASON   AGE
pv01   2Gi        RWO            Retain           Bound       default/pvc1                           5h45m
pv02   2Gi        RWO            Retain           Available                                          5h45m
pv03   2Gi        RWO            Retain           Available                                          5h45m

$ kubectl get pvc
NAME   STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc1   Bound    pv01     2Gi        RWO                           5h11m

StorageClass

在PVC申请时,未必就有现成的PV能正好符合PVC在申请中指定的条件,为此Kubernetes设计了一种工作逻辑,能够让PVC在申请PV时不针对某个PV进行。可以针对某个存储类(Kubernetes之上的标准资源之一)StorageClass,借助此资源层,来完成资源分配的。

StorageClass可以理解为,事先把众多的存储设备当中所提供好的现有可用存储空间(尚未做成PV的存储空间)进行分类(根据综合服务质量、IO性能等)。定义好存储类之后,当PVC再去申请PV时,不针对某个PV直接进行,而是针对存储了进行。必须让存储设备支持Restful风格的请求创建接口,用户可以通过Restful风格的请求1.在磁盘上划分刚好符合PVC大小的分区。2 编辑/etc/export文件,将分区挂载至本地某个目录上。3. 动态创建PV去绑定之前动态导出的空间。

nfs 动态

external-storage/nfs-client at master · kubernetes-incubator/external-storage · GitHub

Docker(二十九)k8s 创建动态存储,基于nfs 的storageclass-洒脱,是云谈风轻的态度-51CTO博客

通过consul、confd,动态为prometheus添加监控目标和告警规则

使用Prometheus建设Kubernetes的监控告警系统

本文发布于Cylon的收藏册,转载请著名原文链接~

链接:https://www.oomkill.com/2018/09/k8s-volumes/

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」 许可协议进行许可。