通过学习前五章,我们已经可以部署一个安全稳定的临时应用程序了。为什么说是临时的应用程序呢?
众所周知,程序=代码+数据,现在代码已经可以运行并提供服务,同时可以产生或读取数据了,但每次重启服务或者 K8S 帮助我们调度 Pod 导致容器重启以后,之前运行的一些有价值无价值的数据也就不存在了,在本章,我们将介绍怎么处理这份有价值的数据。
Kubernetes 通过定义 Volume 来满足这个需求,Volume 被定义为 Pod 这类顶级资源的一部分,并和 Pod 共享生命周期。
也就是 Pod 启动时创建卷,Pod 删除时销毁卷,期间卷的内容不会消失,所以 Pod 因为各种原因重启容器都不会影响卷的内容,如果一个 Pod 内包含多个容器,多个容器共享此卷。
虽然 emptyDir 只能作为临时数据存储,不过利用容器共享卷的这一特性,在 Pod 的多个容器中共享文件还是很有效的。
apiVersion: v1
kind: Pod
metadata:
name: fortune
spec:
containers:
- image: luksa/fortune
name: html-generator
volumeMounts:
- name: html # 使用名为 html 的卷
mountPath: /var/htdocs # 卷挂载位置
- image: nginx:alpine
name: html-server
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
readOnly: true
ports:
containerPort: 80
protocol: TCP
volumes:
- name: html # 声明一个名为 html 的卷,Pod 创建时创建,自动绑定 Pod 的生命周期
emptyDir: {} # 空配置,配置项下面说
empytDir 的存储介质由运行 Pod 的节点提供,并且默认实在磁盘上创建一个空目录。我们可以通过修改emptyDir
的配置项,实现在工作节点的内存创建目录。
apiVersion: v1
kind: Pod
metadata:
name: fortune
spec:
...
volumes:
- name: html
emptyDir:
medium: Memory
gitRepo 是 emptyDir 的进化版,它通过克隆一个 Git 仓库的特定分支版本来初始化目录的内容(不会同步更新仓库内容)。
apiVersion: v1
kind: Pod
metadata:
name: fortune-for-gitRepo
spec:
...
volumes:
- name: html
gitRepo:
repository: https://github.com/clockq/***.git # 一个 git 仓库的路径
reversion: master # 检出 git 的主分支
directory: . # git clone 后放在 volume 的根目录,如果不写,会在根目录下创建一个 *** 项目名的子文件夹,实际仓库内容会放到 *** 子文件夹内
上述例子主要实现的功能是 Pod 内的容器重启后,卷中数据可复用。以及 Pod 内的多个容器利用卷共享卷中数据。但因为上述 Volume 的生命周期和 Pod 同步,也导致了 Volume 无法做到真正的持久化。
什么是真正的持久化呢?
当 Pod 重启后数据依旧存在,不论 Pod 是再次调度到上次的工作节点还是其他工作节点都可以加载到之前生成的持久化数据。
注意:如果需要 Volume 可以跨工作节点访问,就需要存储介质可以在所有工作节点可以访问。
只是将持久化数据放到工作节点的存储介质中,如果 Pod 重新调度后出现在别的工作节点,那么之前持久化的数据就没有用到,所以谨慎使用。
一般用来某些系统级别的应用(比如由 DaemonSet 管理的 Pod)读取工作节点的文件系统时使用。即便可以但也不建议用来做多个 Pod 之间的文件同步。
apiVersion: v1
kind: Pod
metadata:
name: fortune-for-hostPath
spec:
...
volumes:
- name: html
hostPath:
path: /data # 工作节点的目录位置
type: Directory # 挂载的文件类型
GCE(Google Compute Engine)是 Google 提供的云计算平台,对于使用方式日新月异,但国内不方便,所以有兴趣的看官网吧。
https://kubernetes.io/zh/docs/concepts/storage/volumes/#gcepersistentdisk
工作原理大致如下:
其他云平台大同小异。常见的云平台和接口如下:
需要了解更多的支持和配置信息,可以使用
$ kubectl explain
自行查询
掌握上面的内容可以绑定大多数的持久化存储了,但一个 Pod 的发布者(或者说服务的开发者)其实并不需要知道所使用的的存储介质,以及存储介质的具体配置,这些应该交给集群管理员来处理。
利用 Kubernetes Volume 屏蔽实际的存储技术不就是 K8S 所推崇的吗!否则就导致一个 Pod 与某种云平台产生了强依赖关系。
理想情况是,当开发人员需要一定一定数量的持久化存储是,向 K8S 请求,就好像请求 CPU,Memory等资源一样。而集群管理员的工作只需将存储介质配置好,并加入到集群的资源池中。
为了使应用能够正常请求存储资源,同时避免处理基础设施细节,所以引入了持久卷和持久卷声明。
apiVersion: v1
kind: PersistentVolume
metadata:
name: demo-pv
spec:
capacity:
storage: 1Gi # 定义存储卷大小
accessModes: # 定义存储访问模式
- ReadWriteOnce # 可以被一个用户绑定为读写模式
- ReadOnlyMany # 也可以被多个节点(而非Pod)绑定为只读模式
persistentVolumeReclaimPolicy: Retain # 定义回收策略
# Retain => 当 PV 被删除后 PV 的内容会被保留。相应策略还有 Delete 和 Recycle,回收策略可以动态改变
local: # 持久卷类型,使用本地存储
path: /tmp/hostpath_pv/demo-pv # 本地目录位置
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: demo-pvc
namespace: demo-ns # pvc 归属于某个 ns
spec:
accessModes:
- ReadWriteOnce # 可以被一个用户绑定为读写模式
storageClassName: "" # 动态类配置
resources:
requests:
storage: 1Gi # 申请1Gi的存储空间
如上图一样,K8S 根据 PVC 申请的资源,去所有 PV 中找到能满足所有要求的 PV,然后两者绑定。此时列举 PVC 打印信息如下:
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESSMODES AGE
demo-pvc Bound demo-pv 1Gi RWO 13s
apiVersion: v1
kind: Pod
metadata:
name: fortune-for-pvc
spec:
...
volumes:
- name: html
persistentVolumeClaim:
claimName: demo-pv # 通过名字引用之前创建的 pvc
持久化的回收,so easy,删就完了
$ kubectl delete pvc demo-pvc
$ kubectl delete pv demo-pv
PV、PVC、Pod 三者的生命周期如下:
两种持久卷的使用方式对比如下图。
好处显而易见,通过中间加了一层抽象,使开发者的持久化工作更加简单,且对存储介质解耦。虽然需要集群管理员做更多的配置工作,但这是值得的。
通过上面章节,我们已经可以很好很方便的使用存储资源,但每次使用都需要集群管理员配置好 PV 来支持实际的存储。
不过还好,K8S 提供 持久卷配置
来自动创建 PV。集群管理员只需定义一个或多个 SC(StorageClass)资源,用户在创建 PVC 时就可以指定 SC,K8S 就会使用 SC 的置备程序(provisioner)自动创建 PV。
Kubernetes 包括最流行的云服务提供商的置备程序(provisioner),所以管理员不需手动创建。但如果 K8S 部署在本地就需要配置一个定制的置备程序。
下面是 minikube 环境下使用 hostpath PV 的一个 SC,对于其他云平台的 SC,内容会更简单,但大同小异。
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast
annotations:
storageclass.kubernetes.io/is-default-class: "false" # 是否设置为默认 SC
creationTimestamp: "2019-12-18"
resourceVersion: "v0.1"
provisioner: kubernetes.io/minikube-hostpath # 调用此 SC 时使用的置备程序,对于不同的云平台选择不同内容
reclaimPolicy: Retain # Supported policies: Delete, Retain
parameters: # 传递给 provisioner 的参数
type: pd-ssd
当创建下面 PVC 时就会引入上面定义的 SC,因此调用里面配置的 provisioner 来自动创建一个 PV。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: demo-pvc
namespace: demo-ns
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast # 选择之前创建的 SC
resources:
requests:
storage: 1Gi
如果引入的 SC 不存在,则 PV 配置失败(ProvisioningFailed)
上面使用 SC 的例子,在某些场景下,又一次的提高了应用 Pod 和 PVC 的移植性,因为不需要集群管理员一个一个的创建 PV 了。
在上面例子中,我们显式的指定了 storageClassName: fast
才实现了我们要的效果,如果我们把这句话删除,则 PVC 会使用默认的 SC (使用 $ kubectl get sc
列出所有 SC 就会看到默认的)。
如果想要 PVC 不通过 provisioner 创建,而是绑定到之手动配置的 PV 时,就要想最开始的例子一样,storageClassName: ""
将动态类显式的配置为空字符串。
最后,附上动态持久卷的完整图例
关于 local PV,推荐两篇不错的文章,看的我清醒不少
Kubernetes in Action中文版.pdf 观后笔记 二
原文:https://www.cnblogs.com/clockq/p/12297728.html