Kubernetes 数据持久化实现

Posted by 彭楷淳 on 2021-03-01
Estimated Reading Time 8 Minutes
Words 2.1k In Total
Viewed Times

概述


存储管理与计算管理是两个不同的问题。Persistent Volume 子系统,对存储的供应和使用做了抽象,以 API 形式提供给管理员和用户使用。要完成这一任务,我们引入了两个新的 API 资源:Persistent Volume(持久卷)Persistent Volume Claim(持久卷消费者)

Persistent Volume(PV)是集群之中的一块网络存储。跟 Node 一样,也是集群的资源。PV 跟 Volume (卷) 类似,不过会有独立于 Pod 的生命周期。这一 API 对象包含了存储的实现细节,例如 NFS、iSCSI 或者其他的云提供商的存储系统。Persistent Volume Claim (PVC) 是用户的一个请求。跟 Pod 类似,Pod 消费 Node 的资源,PVC 消费 PV 的资源。Pod 能够申请特定的资源(CPU 和内存);Claim 能够请求特定的尺寸和访问模式(例如可以加载一个读写,以及多个只读实例)

PV 与 PVC


PV 是集群的资源。PVC 是对这一资源的请求,也是对资源的所有权的检验。PV 和 PVC 之间的互动遵循如下的生命周期。

  • 供应:集群管理员会创建一系列的 PV。这些 PV 包含了为集群用户提供的真实存储资源,它们可利用 Kubernetes API 来消费。
  • 绑定:用户创建一个包含了容量和访问模式的持久卷申请。Master 会监听 PVC 的产生,并尝试根据请求内容查找匹配的 PV,并把 PV 和 PVC 进行绑定。用户能够获取满足需要的资源,并且在使用过程中可能超出请求数量。如果找不到合适的卷,这一申请就会持续处于非绑定状态,一直到出现合适的 PV。例如一个集群准备了很多的 50G 大小的持久卷,(虽然总量足够)也是无法响应 100G 的申请的,除非把 100G 的 PV 加入集群。
  • 使用:Pod 把申请作为卷来使用。集群会通过 PVC 查找绑定的 PV,并 Mount 给 Pod。对于支持多种访问方式的卷,用户在使用 PVC 作为卷的时候,可以指定需要的访问方式。一旦用户拥有了一个已经绑定的 PVC,被绑定的 PV 就归该用户所有了。用户的 Pods 能够通过在 Pod 的卷中包含的 PVC 来访问他们占有的 PV。
  • 释放:当用户完成对卷的使用时,就可以利用 API 删除 PVC 对象了,而且他还可以重新申请。删除 PVC 后,对应的卷被视为 “被释放”,但是这时还不能给其他的 PVC 使用。之前的 PVC 数据还保存在卷中,要根据策略来进行后续处理。
  • 回收:PV 的回收策略向集群阐述了在 PVC 释放卷的时候,应如何进行后续工作。目前可以采用三种策略:保留,回收或者删除。保留策略允许重新申请这一资源。在持久卷能够支持的情况下,删除策略会同时删除持久卷以及 AWS EBS/GCE PD 或者 Cinder 卷中的存储内容。如果插件能够支持,回收策略会执行基础的擦除操作(rm -rf /thevolume/*),这一卷就能被重新申请了。

定义 PV


持久卷插件

持久卷是以插件方式实现的,目前支持的插件如下:

  • GCEPersistentDisk
  • AWSElasticBlockStore
  • NFS(我们采用的是该方案)
  • iSCSI
  • RBD (Ceph Block Device)
  • Glusterfs
  • HostPath (单节点测试使用)
  • 本地持久卷

YAML 配置

创建一个名为 nfs-pv-mysql.yml 的配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv-mysql
spec:
# 设置容量
capacity:
storage: 5Gi
# 访问模式
accessModes:
# 该卷能够以读写模式被多个节点同时加载
- ReadWriteMany
# 回收策略,这里是基础擦除 `rm-rf/thevolume/*`
persistentVolumeReclaimPolicy: Recycle
nfs:
# NFS 服务端配置的路径
path: "/usr/local/kubernetes/volumes"
# NFS 服务端地址
server: 192.168.141.140
readOnly: false
1
2
3
4
5
6
7
8
# 部署
$ kubectl create -f nfs-pv-mysql.yml
# 删除
$ kubectl delete -f nfs-pv-mysql.yml
# 查看
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
nfs-pv-mysql 5Gi RWX Recycle Available 29m

配置说明

Capacity(容量)

一般来说,PV 会指定存储容量。这里需要使用 PV 的 capcity 属性。目前存储大小是唯一一个能够被申请的指标,今后会加入更多属性,例如 IOPS,吞吐能力等。

AccessModes(访问模式)

只要资源提供者支持,持久卷能够被用任何方式加载到主机上。每种存储都会有不同的能力,每个 PV 的访问模式也会被设置成为该卷所支持的特定模式。例如 NFS 能够支持多个读写客户端,但是某个 NFS PV 可能会在服务器上以只读方式使用。每个 PV 都有自己的一系列的访问模式,这些访问模式取决于 PV 的能力。访问模式的可选范围如下:

  • ReadWriteOnce:该卷能够以读写模式被加载到一个节点上
  • ReadOnlyMany:该卷能够以只读模式加载到多个节点上
  • ReadWriteMany:该卷能够以读写模式被多个节点同时加载

在 CLI 下,访问模式缩写为:

  • RWO:ReadWriteOnce
  • ROX:ReadOnlyMany
  • RWX:ReadWriteMany

另外,一个卷不论支持多少种访问模式,同时只能以一种访问模式加载。例如一个 GCE Persistent Disk 既能支持 ReadWriteOnce,也能支持 ReadOnlyMany。

RecyclingPolicy(回收策略)

当前的回收策略可选值包括:

  • Retain:人工重新申请
  • Recycle:基础擦除(rm-rf/thevolume/*
  • Delete:相关的存储资产例如 AWS EBS,GCE PD 或者 OpenStack Cinder 卷一并删除

目前,只有 NFS 和 HostPath 支持 Recycle 策略,AWS EBS、GCE PD 以及 Cinder 卷支持 Delete 策略。

阶段(Phase)

一个卷会处于如下阶段之一:

  • Available:可用资源,尚未被绑定到 PVC 上
  • Bound:该卷已经被绑定
  • Released:PVC 已经被删除,但该资源尚未被集群回收
  • Failed:该卷的自动回收过程失败

定义 PVC


创建一个名为 nfs-pvc-mysql-myshop.yml 的配置文件

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc-mysql-myshop
spec:
accessModes:
# 需要使用和 PV 一致的访问模式
- ReadWriteMany
# 按需分配资源
resources:
requests:
storage: 1Gi
1
2
3
4
5
6
# 部署
$ kubectl create -f nfs-pvc-mysql-myshop.yml
$ # 删除
$ kubectl delete -f nfs-pvc-mysql-myshop.yml
# 查看
$ kubectl get pvc

部署 MySQL8


注意: 要确保每台 Node 都安装了 NFS 客户端,apt-get install -y nfs-common

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
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: mysql-myshop
spec:
replicas: 1
template:
metadata:
labels:
name: mysql-myshop
spec:
containers:
- name: mysql-myshop
image: mysql
# 只有镜像不存在时,才会进行镜像拉取
imagePullPolicy: IfNotPresent
ports:
- containerPort: 3306
# 同 Docker 配置中的 environment
env:
- name: MYSQL_ROOT_PASSWORD
value: "123456"
# 容器中的挂载目录
volumeMounts:
- name: nfs-vol-myshop
mountPath: /var/lib/mysql
volumes:
# 挂载到数据卷
- name: nfs-vol-myshop
persistentVolumeClaim:
claimName: nfs-pvc-mysql-myshop
---
apiVersion: v1
kind: Service
metadata:
name: mysql-myshop
spec:
ports:
- port: 3306
targetPort: 3306
type: LoadBalancer
selector:
name: mysql-myshop

解决权限问题

当你使用 kubectl create -f <YAML> 部署后,你会发现 Pod 状态为 Error,容器无法正常启动的情况,我们可以使用 kubectl logs <Pod Name> 看到一条日志

1
chown: changing ownership of '/var/lib/mysql/': Operation not permitted

解决方案是在 NFS 服务端配置中增加一个参数 no_root_squash,即将配置修改为:/usr/local/kubernetes/volumes *(rw,sync,no_subtree_check,no_root_squash)

测试运行

部署成功后可以使用 kubectl get service 查看我们 MySQL 的运行端口,再使用连接工具连接会报错误:无法使用密码的方式登录,在 Docker 部署时我们可以在 YAML 中配置相关参数解决这个问题;下一节我们讲解在 Kubernetes 中采用 ConfigMap 的方式配置 MySQL

附:ImagePullPolicy


支持三种 ImagePullPolicy

  • Always:不管镜像是否存在都会进行一次拉取
  • Never:不管镜像是否存在都不会进行拉取
  • IfNotPresent:只有镜像不存在时,才会进行镜像拉取

注意

  • 默认为 IfNotPresent,但 :latest 标签的镜像默认为 Always
  • 拉取镜像时 Docker 会进行校验,如果镜像中的 MD5 码没有变,则不会拉取镜像数据
  • 生产环境中应该尽量避免使用 :latest 标签,而开发环境中可以借助 :latest 标签自动拉取最新的镜像

If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. If the images used in the blog infringe your copyright, please contact the author to delete them. Thank you !