目录
- 前言
- 一、Volume
- 1.1 emptyDir
- 1.1.1 基本概念
- 1.1.2 应用案例
- 1.2 hostPath
- 1.2.1 基本概念
- 1.2.2 应用案例
- 1.3 外部 Storage Provider
- 二、Persistent Volume
- 2.1 基本概念
- 2.1.1 PersistentVolume
- 2.1.2 PersistentVolumeClaim
- 2.2 NFS PersistentVolume
前言
与 Docker 类似,也是通过 Volume 为容器提供存储。一般地,我们会将应用服务分为有状态和无状态,对于 K8s 中容器和 Pod 来说,其生命周期是短暂的,因为他们随时可能被销毁和创建。容器销毁时,其内部文件系统中的数据也会被删除。
如何解决这个问题?K8s 提供了 Volume 数据卷,Volume 的生命周期独立于容器,它不会因为容器的销毁而销毁。当 Volume 被 mount 到 Pod 中时,Pod 中的所有容器都可以访问这个 Volume。K8s 支持多种 backend 类型,如 emptyDir、hostPath、GCE Persistent Disk 等。
一、Volume
1.1 emptyDir
1.1.1 基本概念
从字面意思来看,它似乎表示一个空目录,其实它就是 Host 上的一个空目录(即卷最初是空的),而且是最基础的 Volume 类型。需要注意的是:当 Pod 因为某些原因被从节点上删除时,emptyDir
卷中的数据也会被永久删除。但如果只是容器被销毁,而 Pod 还在的情况下,这个 Volume 是不受影响的。
1.1.2 应用案例
Pod 中的所有容器都可以共享 Volume,它们可以指定各自的 mount 路径。
vim emptyDir.yml
apiVersion: v1
kind: Pod
metadata:
name: pro-dev
spec:
containers:
- image: busybox
name: pro
volumeMounts:
- mountPath: /pro_dir
name: shared-volume
args:
- /bin/sh
- -c
- echo "hello world" > /pro_dir/hello; sleep 30000
- image: busybox
name: dev
volumeMounts:
- mountPath: /dev_dir
name: shared-volume
args:
- /bin/sh
- -c
- cat /dev_dir/hello; sleep 30000
volumes:
- name: shared-volume
emptyDir: {}
该案例中 Pod 有两个容器 pro 和 dev,它们共享一个 volume,pro 负责往 volume 写数据,dev 则是从 volume 中读取数据。
整体流程就是:
- 定义了一个 emptyDir 类型的 Volume - - > shared-volume;
- pro 容器将 shared-volume mount 到 /pro_dir 目录下;
- pro 将数据写入 /pro_dir/hello 文件里;
- dev 容器将 shared-volume mount 到 /dev_dir 目录下;
- dev 通过 cat 从文件 hello 读取数据。
开始创建 Pod:
kubectl apply -f emptyDir.yml
查看 dev 输出日志:
kubectl logs pro-dev dev
从日志可看到 dev 成功读到了 pro 写入的数据,验证了同个 Pod 中的多个容器共享了 emptyDir Volume。
emptyDir 是 Docker Host 文件系统里的目录,其效果相当于执行了 docker run -v /pro_dir
和 docker run -v /dev_dir
。通过 docker inspect
来查看文件 Mount 情况。
docker inspect k8s_pro_pro-dev_default_479982fc-0a53-45e8-806c-38994e3f6f08_0
docker inspect k8s_dev_pro-dev_default_479982fc-0a53-45e8-806c-38994e3f6f08_0
看看 Host 是否有 Source 文件:
可看到,shared-volume 已经被创建于 Host 中,且两个容器都 Mount 到了同一个目录,因此,/var/lib/kubelet/pods/479982fc-0a53-45e8-806c-38994e3f6f08/volumes/kubernetes.io~empty-dir/shared-volume/ 就是 emptyDir 在 Host 上的实际路径。
由此可看出,emptyDir 能够很方便地为 Pod 中的容器提供共享存储,不需要额外的配置。但 emptyDir 不具备持久性,只要 Pod 被销毁,那 emptyDir 也就会被销毁。因此,emptyDir 只适用于临时共享存储的场景。
1.2 hostPath
1.2.1 基本概念
hostPath Volume
的作用就是将 Docker Host 文件系统中已经存在的目录 mount 给 Pod 的容器,但大部分应用都不会使用 hostPath Volume
,因为这会增加 Pod 与节点的耦合,限制了 Pod 的使用。不过那些需要访问 k8s 或 Docker 的内部数据的应用则需要使用到 hostPath
。
1.2.2 应用案例
有哪些典型的应用案例呢?比如 kube-apiserver、kube-controller-manager
就是这样的应用,我们可以查看一下 kube-apiserver
的 Pod 配置。
kubectl edit pod kube-apiserver-k8s-master --namespace=kube-system
上图中定义了三个 hostPath
:ca-certs、etc-pki 和 k8s-certs,他们分别对应了 Host 目录下的 /etc/ssl/certs
、/etc/pki
、/etc/kubernetes/pki
。如果 Pod 被销毁了,hostPath 对应的目录还是会保留,相比于 emptyDir 来说持久性更强,但如果 Host 崩溃,hostPath 也就无法访问了。
1.3 外部 Storage Provider
当然,K8s 也可以使用主流的分布式存储,如 Ceph、GlusterFS 等,如使用 Ceph 分布式存储。
apiVersion: v1
kind: Pod
metadata:
name: Ceph-Storage
spec:
containers:
- image: busybox
name: Ceph-Storage
mountPath: /test-ceph
volumes:
- name: ceph-volume
cephfs:
path: /home/data/cephfs
monitors: "192.168.56.190:6789"
secretFile: "/etc/ceph/admin.secret"
这种外部分布式存储的特点就是:它们不依赖于 K8s,Volume 的底层基础设施由独立的存储系统管理,与 K8s 集群是分离,数据持久化后,即使整个 K8s 集群崩溃了,持久化的数据也不会受到影响。
二、Persistent Volume
2.1 基本概念
尽管 Volume 已经为 K8s 提供了更好的数据持久化方案,但在存储管理上任然是欠缺的,尤其是在大规模集群环境下,需要考虑到效率和安全性问题。K8s 给出的解决方案就是 PersistentVolume,PV
和 PersistentVolumeClaim,PVC
。
2.1.1 PersistentVolume
持久卷(PersistentVolume,PV)
是集群中的一块存储,可以由管理员事先制备, 或者使用存储类(Storage Class)来动态制备。 持久卷是集群资源,就像节点也是集群资源一样。PV 持久卷和普通的 Volume 一样, 也是使用卷插件来实现的,只是它们拥有独立于任何使用 PV 的 Pod 的生命周期。
2.1.2 PersistentVolumeClaim
持久卷申领(PersistentVolumeClaim,PVC)
表达的是用户对存储的请求。概念上与 Pod 类似。Pod 会耗用节点资源,而 PVC 申领会耗用 PV 资源。Pod 可以请求特定数量的资源(CPU 和内存);同样 PVC 申领也可以请求特定的大小和访问模式 (例如,可以要求 PV 卷能够以 ReadWriteOnce、ReadOnlyMany 或 ReadWriteMany 模式之一来挂载)。
有了 PersistentVolumeClaim
,用户只需要告诉 K8s 需要什么样的存储资源,而不必真正关心真正的空间是从哪里分配、如何访问底层信息等。
2.2 NFS PersistentVolume
K8s 支持多种类型的 PersistentVolume,比如 Ceph、AWS EBS、NFS 等,我们就通过 NFS 来实验 PersistentVolume
。
1、安装 NFS
在 K8s-Master 上部署 NFS
# 安装工具/服务
yum -y install rpcbind
yum -y install nfs-utils
# 启动服务
systemctl start nfs-server
systemctl enable nfs-server
systemctl start rpcbind
systemctl enable rpcbind
# 编辑共享文件
vim /etc/exports
/home/data/app 192.168.56.0/24(rw,no_root_squash,sync)
# 创建共享目录
mkdir -p /home/data/app
# 重启NFS
systemctl restart nfs-server
查看 NFS 共享目录
2、K8s 创建 PV 对象
vim pv-test.yml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-test
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
storageClassName: nfs
nfs:
path: /home/data/app
server: 192.168.56.160
参数说明
capacity: 指定PV的容量为1GB
accessModes: 指定访问模式为ReadWriteOnce,支持三种访问模式
ReadWriteOnce: 表示PV能以读写模式mount到单个节点
ReadOnlyMany: 表示PV能以只读模式mount到多个节点
ReadWriteMany: 表示PV能以读写模式mount到多个节点
persistentVolumeReclaimPolicy: 指定PV回收策略为Recycle,支持三种回收策略
Retain: 表示需要管理员手动回收
Recycle: 表示清除PV中的数据,相当于 rm -rf
Delete: 表示删除Storage Provide 上对应的存储资源
storageClassName: 指定PV的class为NFS,相当于为PV设置了一个分类,PVC可以指定class申请相应class的PV
nfs.path: NFSf服务的共享目录
nfs.server: NFS服务IP地址
创建 PV 对象
kubectl apply -f pv-test.yml
查看 PV 资源
kubectl get pv
上图中,STATUS 状态为 Available,表示该 PV 资源已经就绪,可以被 PVC 申请。
3、创建 PVC
对于 PVC 来说,只需要指定 PV 的容量、访问模式和 class 即可。
vim pvc-test.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-test
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: nfs
selector:
matchLabels:
pv: pv-test
# selector标签选择器,选择pv资源的标签:pv-test
创建 PVC 对象
kubectl apply -f pvc-test.yml
查看 PVC 资源
从输出结果来看,PVC 已经 Bound 到 PV 了,申请成功。
4、Pod 中使用存储
PVC 申请成功后,接下来就可以在 Pod 中使用存储了,配置 Pod 文件。
vim pod-test.yml
apiVersion: v1
kind: Pod
metadata:
name: pod-test
spec:
containers:
- name: pod-test
image: busybox
args:
- /bin/sh
- -c
- sleep 30000
volumeMounts:
- mountPath: "/mydata"
name: mydata
volumes:
- name: mydata
persistentVolumeClaim:
claimName: pvc-test
其实就与普通的 Volume 格式类似,在 volume 中通过 persistentVolumeClaim 指定使用 pvc-test 申请的 Volume。
创建 Pod
kubectl apply -f pod-test.yml
查看 Pod 状态
5、验证
在 Pod pod-test
容器中创建一个测试文件testfile
,看看 NFS 共享文件中是否数据同步。
在 NFS 共享目录中创建一个测试文件,在去 Pod 对应容器中看看是否有刚刚创建的 helloWrld
。
如果都同步了,证明 Pod 共享了 NFS 的资源。
从实验结果来看,Pod 中创建的文件同步到了 NFS 共享目录,同样,在 NFS 共享目录中创建的文件也同步到了 Pod 中了。