Kubernetes学习笔记
Kubernetes的存储资源对象
Volume数据卷
为什么要用Volumes
- k8s中容器中的磁盘的生命周期是短暂的,这就带来了一些列的问题:
- 当一个容器损坏之后,kubelet会重启这个容器,但是容器中的文件将丢失,容器以干净的状态重新启动。
- 当很多容器运行在同一个pod中时,很多时候需要数据文件的共享。
- 在 K8S 中,由于 POD 分布在各个不同的节点之上,并不能实现不同节点之间持久性数据的共享,并且在节点故障时,可能会导致数据的永久性丢失。
- Volumes就是用来解决以上问题的:Volume 的生命周期独立于容器,Pod 中的容器可能被销毁和重建,但 Volume 会被保留。
Volume数据结构
- Volume 是 POD 内部容器共享的数据卷,被 Pause 容器所创建的,当 POD 内部容器宕机、崩溃的时候,Volume 数据卷不会消失,一旦 POD 宕机、崩溃,Volume 数据卷就消失了。
- 问题:Volume 数据本身是否能够存储数据?
- 答案:不可以,Volume 本身是 K8S 抽象出来的资源对象,此对象本身不能存储数据,而是用来把数据挂载到容器内部,因此 Volume 数据卷仅仅是进行数据挂载的(从磁盘、网络、文件系统把数据挂载到容器内部)。
数据卷类型
- kubenetes卷的类型:
- 第一种就是本地卷:像 hostPath 类型与 docker 里面的 bind mount 类型,就是直接挂载到宿主机文件的类型;像 emptyDir 是种本地卷,也就是类似于 volume 类型,这两个都是绑定node节点的。
- 第二种就是网络数据卷:比如 Nfs、ClusterFs、Ceph,这些都是外部的存储都可以挂载到k8s上。
- 第三种就是云盘:比如AWS、微软(azuredisk)。
- 第四种就是k8s自身的资源:比如 secret、configmap、downwardAPI。
emptyDir
- 先来看一下本地卷,像 emptyDir 类似于 docker 的 Volume,而 docker 删除容器,数据卷还会存在,而 emptyDir 删除容器,数据卷也会丢失,一般这个只做临时数据卷来使用。
- 创建一个空卷,挂载到 Pod 中的容器。Pod 删除,该卷也会被删除。
- 应用场景:Pod 中容器之间数据共享。
- 当 Pod 被分配给节点时,首先创建 emptyDir 卷,并且只要该 Pod 在该节点上运行,该卷就会存在。正如卷的名字所述,它最初是空的,Pod 中的容器可以读取和写入 emptyDir 卷中的相同文件,尽管该卷可以挂载到每个容器中的相同或者不同路径上。当出于任何原因从节点删除 Pod 时,emptyDir 中的数据将被永久删除。
- 注意:容器崩溃不会从节点中移除 Pod,因此 emptyDir 卷中的数据在容器崩溃时是安全的。
- emptyDir的用法:
1、暂存空间,例如用于基于磁盘的合并排序
2、用作长时间计算崩溃恢复时候的检查点
3、web服务器容器提供数据时,保存内容管理器容器提取的文件
- 使用:定义 POD 的 yaml文件
# emptyDir-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: test-pod1
spec:
containers:
- name: test-container
image: hub.veli.com/library/nginx:latest
volumeMounts:
- name: cache-volume
mountPath: /cache
volumes:
- name: cache-volume
emptyDir: {}
---
apiVersion: v1
kind: Pod
metadata:
name: test-pod2
spec:
containers:
- image: hub.veli.com/library/nginx:latest
name: test-container
volumeMounts:
- mountPath: /cache
name: cache-volume
- name: test-1
image: hub.veli.com/library/nginx:latest
command: ["/bin/sh","-c","sleep 3600s"]
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /cache
name: cache-volume
volumes:
- name: cache-volume
emptyDir: {}
- 创建执行:
kubectl apply -f emptyDir-pod.yaml
- 分别打开两个 Session,进入到这两个容器中:
kubectl exec -it test-pod2 -c test-container
kubectl exec -it test-pod2 -c test-1 -- sh
Hostpath
- 挂载 Node文 件系统上的文件或者目录到Pod中的容器。
- 应用场景:Pod 中容器需要访问宿主机文件。
apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
containers:
- image: hub.veli.com/library/nginx:latest
name: test-container
volumeMounts:
- mountPath: /cache
name: cache-volume
volumes:
- name: cache-volume
hostPath:
path: /data
type: Directory
- 除了所需的path属性职位,用户还可以为hostPath卷指定type:
- 创建执行:
kubectl apply -f hotspath-pod.yaml
- 需要先在 centos110 这个节点创建 /data 目录,创建好目录后,再次查看:已经OK了
- 测试一下:
网络卷
准备NFS环境
- 安装NFS:
yum install nfs-utils -y
- 暴露目录,让是让其他服务器能挂载这个目录
mkdir /opt/k8s
vim /etc/exports
# 给这个网段加上权限,可读可写
/opt/k8s 192.168.254.0/24(rw,no_root_squash)
- 启动NFS:
systemctl start nfs
# 关防火墙
systemctl stop firewalld
测试NFS挂载
- 找个节点去挂载测试一下,只要去共享这个目录就要都去安装这个客户端
yum install nfs-utils -y
mount -t nfs 192.168.254.114:/opt/k8s /mnt
cd mnt
df -h
远程网络文件挂载
- NFS服务端 centos114 创建目录:mkdir /opt/k8s/wwwroot
- 定义 nfs-pod.yaml:
# vim nfs.yaml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: nfs
spec:
replicas: 1
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: hub.veli.com/library/nginx:latest
volumeMounts:
- name: wwwroot
mountPath: /usr/share/nginx/html
ports:
- containerPort: 80
volumes:
- name: wwwroot
nfs:
server: 192.168.254.114
path: /opt/k8s/wwwroot
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
labels:
app: nginx
spec:
ports:
- port: 80
targetPort: 80
selector:
app: nginx
type: NodePort
- 将容器中的目录(/usr/share/nginx/html:nginx 的根目录)挂载到远程NFS服务端的目录(/opt/k8s/wwwroot),并且该POD设置了Service的IP类型为NodePort,这样外网可以访问。
- 执行创建:
kubectl apply -f nfs-pod.yaml
- 测试一下
- 外网访问一下:
存储介质
- 如下图所示: Volume数据卷直接挂载存储介质,是否存在问题?
- Volume 数据卷直接和底层的存储介质直接进行交互的,耦合性较高。
- Kubernetes 无法控制底层存储介质(存储设备多种多样:高速设备、慢速设备,不同厂商提供的服务存储服务),也就是说底层存储介质是复杂多样的,Volume 进行数据卷挂载的时候,都需要做出适当的适配,要做出调整。
- 也不能控制需要多大空间,不能自由分配和调度。
- 访问模式:RMO、RWM ,不能进行自由控制。
- 一旦 POD 宕机,Volume 数据会丢失,影响数据完整性。
提出你的解决方案:解耦,屏蔽掉对底层复杂操作,让上层操作更简单、高效。
PV与PVC
PV是什么?
- PV(Persistent Volume) 持久化的数据卷,Kubernetes 为了部署有状态服务(存储持久化的数据,让数据操作更简单高效)而抽象出的一个持久化的数据存储对象。其作用就是屏蔽掉对底层的复杂操作,使得 Kubernetes 更好的管理存储资源。
- 说明: PV 屏蔽掉 POD 对底层的存储介质的复杂操作,使得 POD 在数据存储管理将会更加方便。PV 对象生命周期是独立的:POD 宕机了,PV 对象不会消失,数据不会丢失。
PV原理解析
- PV 资源对象由运维先创建好:按照规划需求,创建多大的空间,访问方式等等。
- 可以把 PV 资源对象看做一个个的存储块(对底层物理存储介质抽象),使得 Kubernetes 服务部署的时候使用存储空间就更加自由了,也就是说使用哪个存储块(存储大小自由控制,访问方式)进行自由匹配。
- PV 对象把底层存储介质(物理存储硬件,不可分割状态)抽象成一个一个虚拟化的存储块,使得 Kubernetes 可以自由的管理存储介质。PV 对象都是根据业务需求事先创建好的,因此 PV 资源对象创建必须经过合理规划、编排。
问题:如何去获取 PV 存储块呢?
创建 POD 对象的时候,需要存储数据,向 PV 对象发出请求,绑定一个合适的 PV 对象即可。
问题:当创建 POD 的时候,需要存储数据(10GB),向 PV 发出请求,匹配一个合适的大小的 PV 对象,但是 PV 对象有成千上万个,如何匹配?
解决方案: 使用 PVC 接受请求,帮助 POD 匹配一个合适的 PV 对象(相当于做了请求转发)。
PVC是什么
- PVC(Persistent Volume Claim 持久卷声明,简称PVC)是用户的一种存储请求,PVC 代理 POD 发送对存储资源申请的一个请求,且匹配一个合适的 PV 存储块(资源对象)。
- 它和 POD 类似,POD 消耗 Node 资源,而 PVC 消耗 PV 资源。POD 能够请求特定的资源(如CPU和内存),PVC能够请求指定的大小和访问的模式的PV(可以被映射为一次读写或者多次只读)。
PVC原理
- 创建 POD 服务,申请存储空间,存储数据:
- 向 PVC 发出请求,让 PVC 绑定一个合适的 PV 存储块对象。
- PVC 发送申请请求,申请一个合适的 PV 存储块,根据以下方式的进行匹配绑定的
- 访问方式: RWO、RWM
- 访问大小 :5GB
- PVC根据以上2点进行匹配到一个合适的pv资源对象的,首先匹配的是访问模式,然后匹配的是访问大小。
生命周期
- Volume 和 claim 的生命周期:PV 是集群中的资源,PVC 是对这些资源的请求,同时也是这些资源的“提取证”。PV 和 PVC 的交互遵循以下生命周期:
供给
- 有两种 PV 提供的方式:静态和动态。
- 静态:集群管理员创建多个 PV,它们携带着真实存储的详细信息,这些存储对于集群用户是可用的。它们存在于 Kubernetes API 中,并可用于存储使用。
- 动态:当管理员创建的静态 PV 都不匹配用户的 PVC 时,集群可能会尝试专门地供给 Volume 给 PVC。这种供给基于 StorageClass:PVC 必须请求这样一个等级,而管理员必须已经创建和配置过这样一个等级,以备发生这种动态供给的情况。请求等级配置为“”的 PVC,有效地禁用了它自身的动态供给功能。
绑定
- 用户创建一个 PVC(或者之前就已经就为动态供给创建了),指定要求存储的大小和访问模式。Master 中有一个控制回路用于监控新的 PVC,查找匹配的 PV(如果有),并把 PVC 和 PV 绑定在一起。如果一个 PV 曾经动态供给到了一个新的 PVC,那么这个回路会一直绑定这个 PV 和 PVC。另外,用户总是至少能得到它们所要求的存储,但是 Volume 可能超过它们的请求。一旦绑定了,PVC 绑定就是专属的,无论它们的绑定模式是什么。
- 如果没找到匹配的 PV,那么 PVC 会无限期得处于 unbound 未绑定状态,一旦 PV 可用了,PVC 就会又变成绑定状态。比如,如果一个供给了很多 50G 的 PV 集群,不会匹配要求 100G 的 PVC。直到 100G 的 PV 添加到该集群时,PVC 才会被绑定。
使用
- POD 使用 PVC 就像使用 Volume 一样。集群检查 PVC,查找绑定的 PV,并映射 PV 给 POD。对于支持多种访问模式的 PV,用户可以指定想用的模式。一旦用户拥有了一个 PVC,并且 PVC 被绑定,那么只要用户还需要,PV 就一直属于这个用户。用户调度POD,通过在 POD 的 Volume 块中包含 PVC 来访问 PV。
释放
- 当用户使用 PV 完毕后,他们可以通过 API 来删除 PVC 对象。当 PVC 被删除后,对应的 PV 就被认为是已经是“released”了,但还不能再给另外一个 PVC 使用。前一个 PVC 的属于还存在于该 PV 中,必须根据策略来处理掉。
回收
- PV 的回收策略告诉集群,在 PV 被释放之后集群应该如何处理该 PV。当前,PV 可以被 Retained(保留)、 Recycled(再利用)或者Deleted(删除)。保留允许手动地再次声明资源。对于支持删除操作的 PV 卷,删除操作会从 Kubernetes 中移除 PV 对象,还有对应的外部存储(如AWS EBS,GCE PD,Azure Disk,或者Cinder volume)。动态供给的卷总是会被删除。
PVC&PV案例
- 定义两个 PV:
# vim pv1.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: my-pv1
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
nfs:
path: /opt/k8s/pv-demo1
server: 192.168.254.114
# vim pv2.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: my-pv2
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteMany
nfs:
path: /opt/k8s/pv-demo2
server: 192.168.254.114
- 执行创建 PV:
kubectl apply -f pv1.yaml
kubectl apply -f pv2.yaml
- 定义一个 POD,并通过 PVC 挂载我们的 PV:
# vim pod-pv.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-pv
spec:
containers:
- name: nginx
image: hub.veli.com/library/nginx:latest
ports:
- containerPort: 80
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumes:
- name: www
persistentVolumeClaim:
claimName: my-pvc
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 2Gi
- 执行创建 PVC和POD:
kubectl apply -f pv1.yaml
kubectl apply -f pv2.yaml
- 测试一下: