超10万字整理完k8s的volume卷之本地卷和网络卷详细说明,代码和理论都超详细,建议跟着做一遍实验【emptyDir、hostPath、nfs共享的网络卷】【1】
文章目录说明测试环境准备临时卷本地卷说明emptyDir卷emptyDir的常规使用配置文件编辑和创建pod查看pod节点自动生成的pod存储路径数据测试同一个pod里2个容器共享数据配置【emptyDir】配置文件编辑和生成pod查看pod节点自动生成的pod存储路径数据测试### emptyDir以只读的形式创建podhostPath卷支持类型参数说明hostPath的常规使用配置文件编辑和创
说明【必看】
- 总数将近11万,存储一共有4大类,本地卷、网络卷、持久性存储和动态卷供应 ,为了能够直观理解,所以我分成了3篇来发布;
初次看的时候,建议3篇都打开,按顺序学习和使实验,有助于理解哈。 后面查阅的时候看标题,点进相应的文章哈 - 这篇是第一篇
持久性存储 【必看】【2】
看这篇博客【上面的本地卷和网络卷是基础知识,足够用了,如果想更深学习,就去这篇博客】:
超10万字整理完k8s的volume卷之持久性存储-超详细说明,代码和理论都超详细,建议跟着做一遍实验【2】
动态卷供应 【必看】【3】
看这篇博客【分开存放是因为volume内容太多了,放一起很容易导致页面卡顿,上面的本地卷和网络卷是基础知识,足够用了,如果想更深学习,就去这篇博客】【先学持久性存储】:
超10万字整理完k8s的volume卷之动态卷供应-超详细说明,代码和理论都超详细,建议跟着做一遍实验【3】
说明
- 如果基础不好的,看这边文章,可能会看不懂,我博客的k8s分类中有k8s详细的流程,建议去我分类中从第一片文章整体学习一遍。
容器磁盘上的文件的生命周期是短暂的,这就使得在容器中运行重要应用时会出现一些问题。首先,当容器崩溃 时,kubelet 会重启它,但是容器中的文件将丢失——容器以干净的状态(镜像最初的状态)重新启动。其次,在 Pod 中同时运行多个容器时,这些容器之间通常需要共享文件。Kubernetes 中的 Volume 抽象就很好的解决了 这些问题
测试环境准备
- 先搭建一套集群【没有的去我k8s分类中找到k8s集群搭建跟着搭一套】
我现在的集群是一个master和2个node节点。
[root@master volume]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
master Ready master 35d v1.21.0
node1 Ready <none> 35d v1.21.0
node2 Ready <none> 35d v1.21.0
[root@master volume]#
- 为了测试方便,新建一个volume目录,后续所有的配置文件都放在这里面,然后新建一个volume的ns空间,后续所有的pod都创建在这个ns空间下。
[root@master ~]# mkdir volume
[root@master ~]#
[root@master ~]# kubectl create ns volume
namespace/volume created
[root@master ~]# kubens volume
Context "context" modified.
Active namespace is "volume".
[root@master ~]#
[root@master ~]# kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
* context master ccx volume
context1-new master1 ccx1 default
[root@master ~]#
[root@master ~]# kubectl get pods
No resources found in volume namespace.
[root@master ~]#
- 然后在这个volume路径下生成pod文件并增加一个0秒删除pod的内容
[root@master ~]# cd volume/
[root@master volume]#
[root@master volume]# kubectl run pod1 --image=nginx --image-pull-policy=IfNotPresent --dry-run=client -o yaml > pod1.yaml
[root@master volume]#
[root@master volume]# vim pod1.yaml
[root@master volume]# cat pod1.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod1
name: pod1
spec:
terminationGracePeriodSeconds: 0
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: pod1
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
[root@master volume]#
# 如果node节点里面没有nginx镜像,创建的pod的状态就不会为running啊
[root@node2 ~]# docker images | grep nginx
nginx latest d1a364dc548d 2 months ago 133MB
[root@node2 ~]#
临时卷
-
临时卷就是创建的pod,这pod里面的数据是临时存储在本地物理机上的,无果将pod删除,那么这个本地数据也会跟着删除。
简单来说,就是本地卷的数据是随着pod的存在而存在,当我们删除了pod之后,我们往pod里缩写的数据都被删除掉了,这样的容器是不存储任何数据的——这种我们称之为:无状态的容器【stateless】
有时我们需要让pod能够存储数据,这样的容器叫做有状态的容器【statefull】 -
现在我们来创建一个pod测试
现在先创建一个pod并查看其运行在哪个node节点上
[root@master volume]# kubectl apply -f pod1.yaml
pod/pod1 created
[root@master volume]#
[root@master volume]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod1 1/1 Running 0 5s 10.244.166.164 node1 <none> <none>
[root@master volume]#
- 通过上面我们可以看到pod是运行在node1节点上的,那么我们现在去node1这台主机上,并执行
find / -name aaa.txt
【这个我们后面会在容器内创建】
可以看到是没有任何内容的
[root@node1 ~]# find / -name aaa.txt
[root@node1 ~]#
- 现在我们回到master节点上,进入到创建的这个pod里面,然后创建一个aaa.txt文件
然后再去到node1节点上执行上面命令,可以看到本地就会生成相应的文件了,这个文件就是临时的卷的文件,用来存储pod里面数据的
[root@master volume]#
[root@master volume]# kubectl exec -it pod1 -- bash
root@pod1:/#
root@pod1:/# touch aaa.txt
root@pod1:/#
# 下面是node1节点上
[root@node1 ~]# find / -name aaa.txt
/var/lib/docker/overlay2/00168907f2abaeffd3c5534c80c870e0e30725d89d51dcaba8d4643a50de08ac/diff/aaa.txt
/var/lib/docker/overlay2/00168907f2abaeffd3c5534c80c870e0e30725d89d51dcaba8d4643a50de08ac/merged/aaa.txt
[root@node1 ~]#
- 这时候我们把pod1删除,然后再去node1上查看,可以发现,这个临时生成的文件也会跟着消失
[root@master volume]# kubectl delete pod pod1
pod "pod1" deleted
[root@master volume]#
# 上面pod删除以后,node1上的文件会隔一会才会被自动删除
[root@node1 ~]# find / -name aaa.txt
/var/lib/docker/overlay2/00168907f2abaeffd3c5534c80c870e0e30725d89d51dcaba8d4643a50de08ac/diff/aaa.txt
[root@node1 ~]# find / -name aaa.txt
[root@node1 ~]#
本地卷
说明
-
在容器中的文件在磁盘上是临时存放的,当容器关闭时这些临时文件也会被一并清除。这给容器中运行的特殊应用程序带来一些问题。
-
首先,当容器崩溃时,kubelet 将重新启动容器,容器中的文件将会丢失——因为容器会以干净的状态重建。
-
其次,当在一个 Pod 中同时运行多个容器时,常常需要在这些容器之间共享文件。
Kubernetes 抽象出 Volume 对象来解决这两个问题。 -
Kubernetes Volume卷具有明确的生命周期——与包裹它的 Pod 相同。 因此,Volume比 Pod 中运行的任何容器的存活期都长,在容器重新启动时数据也会得到保留。 当然,当一个 Pod 不再存在时,Volume也将不再存在。更重要的是,Kubernetes 可以支持许多类型的Volume卷,Pod 也能同时使用任意数量的Volume卷。
-
使用卷时,Pod 声明中需要提供卷的类型 (.spec.volumes 字段)和卷挂载的位置 (.spec.containers.volumeMounts 字段).
-
定义卷格式为:
...
spec:
# 定义一个挂载
volumes:
- name: 自定义卷名称
类型:
卷的参数
#类型有emptyDir和hostPath 两种,我下面分别展示
...
# 下面是emptyDir类型
...
spec:
# 定义一个挂载
volumes:
- name: 自定义卷名称
#下面这个就是表示在pod所属的node节点上随机生成一个存储路径
emptyDir: {}
...
# 下面是hostPath类型
...
spec:
# 定义一个挂载
volumes:
- name: 自定义卷名称
hostsPath:
...
- 任何在容器里引用卷:
volumeMounts:
- name: 上面定义的卷名
mountPath: /自定义目录【这里面的数据就会同步到node节点上
# 如下
# 我多放2行代码,是让你知道这个片段是放哪里的
- image: nginx
imagePullPolicy: IfNotPresent
name: pod-emp1
resources: {}
#下面这是挂载,name是上面的name名称,mountpath是容器内的存储路径
volumeMounts:
- name: v1
mountPath: /aa
dnsPolicy: ClusterFirst
emptyDir卷
emptyDir——以内存为介质的,数据不会被永久存储,pod删除,数据就跟着删除了
emptyDir的常规使用
配置文件编辑和创建pod
- 我们先cp一份之前生成的配置文件并编辑该配置文件,最后创建一个pod
[root@master volume]# cp pod1.yaml pod-emp1.yaml
[root@master volume]# vim pod-emp1.yaml
[root@master volume]# cat pod-emp1.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod-emp1
name: pod-emp1
spec:
terminationGracePeriodSeconds: 0
# 定义一个挂载
volumes:
# 自定义卷名称
- name: v1
#下面这个就是表示在pod所属的node节点上随机生成一个存储路径
emptyDir: {}
# 如果需要定义多个,直接复制下面2行即可
- name: v2
emptyDir: {}
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: pod-emp1
resources: {}
#下面这是挂载,name是上面的name名称,mountpath是容器内的存储路径
volumeMounts:
- name: v1
mountPath: /aa
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
[root@master volume]#
[root@master volume]# kubectl apply -f pod-emp1.yaml
pod/pod-emp1 created
[root@master volume]#
[root@master volume]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-emp1 1/1 Running 0 102s 10.244.166.165 node1 <none> <none>
[root@master volume]#
查看pod节点自动生成的pod存储路径
- 可以看到该pod是运行在node1节点上的,那么我们去node1节点上查看该卷,流程如下
- 1、先用docker在pod所属节点上过滤出该pod的ID
- 2、通过pod的ID查看详细属性,使用Mount参数过滤,得到Source【主机路径】和Destination【pod挂载路径】
- 3、查看Source主机路径文件和pod内/aa文件是否一致
[root@node1 ~]# docker ps | grep pod-emp1
477b87b04edc d1a364dc548d "/docker-entrypoint.…" 3 hours ago Up 3 hours k8s_pod-emp1_pod-emp1_volume_cdf68c6e-4068-478f-bf22-f43a9a9f1345_0
9617a0a32d7b registry.aliyuncs.com/google_containers/pause:3.4.1 "/pause" 3 hours ago Up 3 hours k8s_POD_pod-emp1_volume_cdf68c6e-4068-478f-bf22-f43a9a9f1345_0
[root@node1 ~]# docker inspect 477b87b04edc | grep -A10 Mounts
"Mounts": [
{
"Type": "bind",
"Source": "/var/lib/kubelet/pods/cdf68c6e-4068-478f-bf22-f43a9a9f1345/volumes/kubernetes.io~empty-dir/v1",
"Destination": "/aa",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
},
{
"Type": "bind",
[root@node1 ~]#
# 可以看到,现在主机路径是没有内容的
[root@node1 ~]# ls /var/lib/kubelet/pods/cdf68c6e-4068-478f-bf22-f43a9a9f1345/volumes/kubernetes.io~empty-dir/v1
[root@node1 ~]#
数据测试
- 现在回到master节点上,进入到该pod里面,在/aa路径上随便创建2个文件,然后回到node的主机路径,看文件是否同步了
[root@master volume]# kubectl exec -it pod-emp1 -- bash
root@pod-emp1:/#
root@pod-emp1:/# touch /aa/aa.txt
root@pod-emp1:/#
# 回到node节点,可以看到aa.txt文件就是正常的
[root@node1 ~]# ls /var/lib/kubelet/pods/cdf68c6e-4068-478f-bf22-f43a9a9f1345/volumes/kubernetes.io~empty-dir/v1
aa.txt
[root@node1 ~]#
- 根据这个特性,同理,我们在node节点的这个存储路径上创建文件,我们在master中进入pod内部,/aa中也会出现才对
[root@node1 ~]# ls /var/lib/kubelet/pods/cdf68c6e-4068-478f-bf22-f43a9a9f1345/volumes/kubernetes.io~empty-dir/v1
aa.txt
[root@node1 ~]# cd /var/lib/kubelet/pods/cdf68c6e-4068-478f-bf22-f43a9a9f1345/volumes/kubernetes.io~empty-dir/v1
[root@node1 v1]# ls
aa.txt
[root@node1 v1]#
[root@node1 v1]# touch bb.txt
[root@node1 v1]#
[root@node1 v1]# ls
aa.txt bb.txt
[root@node1 v1]#
# 进入到该容器内部,看到/aa内有文件,则正常
[root@master volume]# kubectl exec -it pod-emp1 -- bash
root@pod-emp1:/# cd /aa
root@pod-emp1:/aa# ls
aa.txt bb.txt
root@pod-emp1:/aa#
- 前面说过,这种模式的数据是随着pod的存在而存在的,如果pod删了,那么node节点上的数据也会随着消失,现在我们删除这个pod试试
[root@node1 ~]# ls /var/lib/kubelet/pods/cdf68c6e-4068-478f-bf22-f43a9a9f1345/volumes/kubernetes.io~empty-dir/v1
aa.txt bb.txt
[root@node1 ~]#
# 现在去master节点删除pod
[root@master volume]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-emp1 1/1 Running 0 3h20m 10.244.166.165 node1 <none> <none>
[root@master volume]#
[root@master volume]# kubectl delete pod pod-emp1
pod "pod-emp1" deleted
[root@master volume]#
# 再次回到node节点,查看数据是否确实消失
[root@node1 ~]# ls /var/lib/kubelet/pods/cdf68c6e-4068-478f-bf22-f43a9a9f1345/volumes/kubernetes.io~empty-dir/v1
ls: cannot access /var/lib/kubelet/pods/cdf68c6e-4068-478f-bf22-f43a9a9f1345/volumes/kubernetes.io~empty-dir/v1: No such file or directory
[root@node1 ~]#
同一个pod里2个容器共享数据配置【emptyDir】
- 同一个pod里2个容器共享数据配置 我们称之为:
sidecar
- 简单来说,就是我们在一个pod里创建2个容器,比如:一个容器是nginx,另一个容器是fluentd用来分析日志,我们将这2个容器的挂载目录都指向同一个地址,这样就可以实现数据共享了
配置文件编辑和生成pod
一个pod中创建第二个容器,从第二个容器开始,必须自定义command,否则就会端口冲突报错,下面共享存储这个逻辑是这样的,2个容器的存储卷我们都指向的是v1,虽然容器中的挂载目录不一致,但存储卷是一致的,所以在容器c1中是可以看到容器c2写入内容的,数据时刻同步。
[root@master volume]# cat pod-emp2.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod-emp1
name: pod-emp1
spec:
terminationGracePeriodSeconds: 0
volumes:
- name: v1
emptyDir: {}
- name: v2
emptyDir: {}
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: c1
resources: {}
volumeMounts:
- name: v1
mountPath: /aa
- image: nginx
imagePullPolicy: IfNotPresent
command: ["sh","-c","sleep 10000"]
name: c2
resources: {}
volumeMounts:
- name: v1
mountPath: /xx
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
[root@master volume]#
[root@master volume]# kubectl apply -f pod-emp2.yaml
pod/pod-emp1 created
[root@master volume]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-emp1 2/2 Running 0 12s 10.244.166.166 node1 <none> <none>
[root@master volume]#
查看pod节点自动生成的pod存储路径
- 测试前,我们先找到该容器对应的节点存储位置
多容器的查询和单容器可能略有不同,如果自己不能定位 路径的,可以参考我下面的方式。
【注:查询结果可以看到,虽然容器挂载路径不一样,但主机存储的路径的一样的】
[root@node1 ~]# docker ps | grep pod-emp1
f80b7cc5b585 d1a364dc548d "sh -c 'sleep 10000'" 8 minutes ago Up 8 minutes k8s_c2_pod-emp1_volume_184bb3f1-7e37-4b5b-ba30-78a6bf64a8d6_0
da9b3ca32495 d1a364dc548d "/docker-entrypoint.…" 8 minutes ago Up 8 minutes k8s_c1_pod-emp1_volume_184bb3f1-7e37-4b5b-ba30-78a6bf64a8d6_0
3b043db3ba62 registry.aliyuncs.com/google_containers/pause:3.4.1 "/pause" 8 minutes ago Up 8 minutes k8s_POD_pod-emp1_volume_184bb3f1-7e37-4b5b-ba30-78a6bf64a8d6_0
[root@node1 ~]#
[root@node1 ~]# docker inspect f80b7cc5b585 | grep -B3 /xx
"ExecIDs": null,
"HostConfig": {
"Binds": [
"/var/lib/kubelet/pods/184bb3f1-7e37-4b5b-ba30-78a6bf64a8d6/volumes/kubernetes.io~empty-dir/v1:/xx",
--
{
"Type": "bind",
"Source": "/var/lib/kubelet/pods/184bb3f1-7e37-4b5b-ba30-78a6bf64a8d6/volumes/kubernetes.io~empty-dir/v1",
"Destination": "/xx",
[root@node1 ~]#
[root@node1 ~]# docker inspect da9b3ca32495 | grep -B3 /aa
"ExecIDs": null,
"HostConfig": {
"Binds": [
"/var/lib/kubelet/pods/184bb3f1-7e37-4b5b-ba30-78a6bf64a8d6/volumes/kubernetes.io~empty-dir/v1:/aa",
--
{
"Type": "bind",
"Source": "/var/lib/kubelet/pods/184bb3f1-7e37-4b5b-ba30-78a6bf64a8d6/volumes/kubernetes.io~empty-dir/v1",
"Destination": "/aa",
# 上面结果得知/xx对应的目录是/var/lib/kubelet/pods/184bb3f1-7e37-4b5b-ba30-78a6bf64a8d6/volumes/kubernetes.io~empty-dir/v1
# /aa对应的目录是/var/lib/kubelet/pods/184bb3f1-7e37-4b5b-ba30-78a6bf64a8d6/volumes/kubernetes.io~empty-dir/v1
# 现在这2个位置里面都是没有文件的
[root@node1 ~]# ls /var/lib/kubelet/pods/184bb3f1-7e37-4b5b-ba30-78a6bf64a8d6/volumes/kubernetes.io~empty-dir/v1
[root@node1 ~]#
[root@node1 ~]# ls /var/lib/kubelet/pods/184bb3f1-7e37-4b5b-ba30-78a6bf64a8d6/volumes/kubernetes.io~empty-dir/v1
[root@node1 ~]#
数据测试
- 现在我们分别进入到这2个容器中创建文件,然后回到主机上查看这2个文件,看文件是否同步生成呢
[root@master volume]# kubectl exec -it pod-emp1 -c c1 -- bash
root@pod-emp1:/#
root@pod-emp1:/# touch /aa/aa.txt
root@pod-emp1:/#
root@pod-emp1:/# exit
exit
[root@master volume]# kubectl exec -it pod-emp1 -c c2 -- bash
root@pod-emp1:/#
root@pod-emp1:/# touch /xx/xx.txt
root@pod-emp1:/# ls /xx
aa.txt xx.txt
root@pod-emp1:/#
# 上面可以看到在/xx里面是可以看到/aa里创建的文件的,说明文件同步是没问题的
# 回到node主机查看本地数据是否一致
[root@node1 ~]# ls /var/lib/kubelet/pods/184bb3f1-7e37-4b5b-ba30-78a6bf64a8d6/volumes/kubernetes.io~empty-dir/v1
aa.txt xx.txt
[root@node1 ~]#
- 注意,上面这种数据依然不是永久存储的,容器没了,数据和其文件也就没了
[root@master volume]# kubectl delete pod pod-emp1
pod "pod-emp1" deleted
[root@master volume]#
[root@node1 ~]# ls /var/lib/kubelet/pods/184bb3f1-7e37-4b5b-ba30-78a6bf64a8d6/volumes/kubernetes.io~empty-dir/v1
ls: cannot access /var/lib/kubelet/pods/184bb3f1-7e37-4b5b-ba30-78a6bf64a8d6/volumes/kubernetes.io~empty-dir/v1: No such file or directory
[root@node1 ~]#
emptyDir以只读的形式创建pod
这个就是在配置文件中增加一行参数:readOnly: ture
不多说,直接看实例吧
【之前忘了说,现在回来补上的,下面代码是以hostPath的文件展示的哈,不过使用方式是一样的。】
[root@master volume]# cat pod-host.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod-host
name: pod-host
spec:
terminationGracePeriodSeconds: 0
volumes:
- name: v1
hostPath:
path: /xx
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: pod-host
resources: {}
volumeMounts:
- name: v1
mountPath: /aa
#只读参数加在这
readOnly: true
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
[root@master volume]# kubectl apply -f pod-host.yaml
pod/pod-host created
[root@master volume]# kubectl exec -it pod-host -- bash
root@pod-host:/# cd /aa
root@pod-host:/aa# ls
aa.txt memload-7.0-1.r29766.x86_64.rpm
root@pod-host:/aa# touch bb
touch: cannot touch 'bb': Read-only file system
root@pod-host:/aa#
hostPath卷
-
hostPath卷的好处就是,数据会永久存在,不会随着容器的删除而消失。
-
hostPath 卷能将主机node节点文件系统上的文件或目录挂载到你的 Pod 中。 虽然这不是大多数 Pod 需要的,但是它为一些应用程序提供了强大的逃生舱。
-
hostPath 的一些用法有
- 运行一个需要访问 Docker 引擎内部机制的容器;请使用 - hostPath 挂载 /var/lib/docker 路径。
- 在容器中运行 cAdvisor 时,以 hostPath 方式挂载 /sys。
- 允许 Pod 指定给定的 hostPath 在运行 Pod 之前是否应该存在,是否应该创建以及应该以什么方式存在。
支持类型参数说明
- 定义卷格式为:
...
spec:
# 定义一个挂载
volumes:
- name: 自定义卷名称
类型:
卷的参数
#类型有emptyDir和hostPath 两种,我下面分别展示
...
# 下面是emptyDir类型
...
spec:
# 定义一个挂载
volumes:
- name: 自定义卷名称
#下面这个就是表示在pod所属的node节点上随机生成一个存储路径
emptyDir: {}
...
# 下面是hostPath类型
...
spec:
# 定义一个挂载
volumes:
- name: 自定义卷名称
hostsPath:
...
- 任何在容器里引用卷:
volumeMounts:
- name: 上面定义的卷名
mountPath: /自定义目录【这里面的数据就会同步到node节点上
# 如下
# 我多放2行代码,是让你知道这个片段是放哪里的
- image: nginx
imagePullPolicy: IfNotPresent
name: pod-emp1
resources: {}
#下面这是挂载,name是上面的name名称,mountpath是容器内的存储路径
volumeMounts:
- name: v1
mountPath: /aa
dnsPolicy: ClusterFirst
除了必需的 path 属性之外,用户可以选择性地为 hostPath 卷指定 type。支持的 type 值如下:
取值 | 行为 |
---|---|
空字符串(默认)用于向后兼容,这意味着在安装 hostPath 卷之前不会执行任何检查 | |
DirectoryOrCreate | 如果指定的路径不存在,那么将根据需要创建空目录,权限设置为 0755,具有与 Kubelet 相同的组和所有权 |
Directory | 给定的路径必须存在 |
FileOrCreate | 如果给定路径的文件不存在,那么将在那里根据需要创建空文件,权限设置为 0644,具有与 Kubelet 相同的组和所有权【前提:文件所在目录必须存在;目录不存在则不能创建文件】 |
File | 给定路径上的文件必须存在 |
Socket | 在给定路径上必须存在的 UNIX 套接字 |
CharDevice | 在给定路径上必须存在的字符设备 |
BlockDevice | 在给定路径上必须存在的块设备 |
- 注意事项
当使用这种类型的卷时要小心,因为:- 具有相同配置(例如从 podTemplate 创建)的多个 Pod 会由于节点上文件的不同而在不同节点上有不同的行为。
- 当 Kubernetes 按照计划添加资源感知的调度时,这类调度机制将无法考虑由 hostPath 卷使用的资源。
- 基础主机上创建的文件或目录只能由 root 用户写入。需要在 特权容器 中以 root 身份运行进程,或者修改主机上的文件权限以便容器能够写入 hostPath 卷。
hostPath的常规使用
配置文件编辑和创建pod
- 我们创建一个容器,创建规则看代码中2行注释内容吧,挺好理解的。
[root@master volume]# cat pod-host.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod-host
name: pod-host
spec:
terminationGracePeriodSeconds: 0
# 下面这个意思就是,我们创建一个卷v1,对应主机的/xx目录
volumes:
- name: v1
hostPath:
path: /xx
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: pod-host
resources: {}
volumeMounts:
# 这个就是容器中的位置,使用v1卷,对应容器中的/aa目录
- name: v1
mountPath: /aa
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
[root@master volume]#
[root@master volume]# kubectl apply -f pod-host.yaml
pod/pod-host created
[root@master volume]#
[root@master volume]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-host 1/1 Running 0 9s 10.244.166.167 node1 <none> <none>
[root@master volume]#
查看pod节点自动生成的pod存储路径
上面我们看到该pod运行在node1上,我们去node1上查看属性,可以看到/aa对应的路径是/xx了,而不是一堆自动生成的路径;
然后系统会自动给我们创建/xx这个目录,我下面有内容,可能是之前创建过并放了东西在里面,这个不影响哈
[root@node1 ~]# docker ps | grep pod-host
78c5a8144951 d1a364dc548d "/docker-entrypoint.…" 2 minutes ago Up 2 minutes k8s_pod-host_pod-host_volume_fc4fdba6-6d95-48bc-95ff-04f7373c2924_0
c551e76f8ab8 registry.aliyuncs.com/google_containers/pause:3.4.1 "/pause" 2 minutes ago Up 2 minutes k8s_POD_pod-host_volume_fc4fdba6-6d95-48bc-95ff-04f7373c2924_0
[root@node1 ~]#
[root@node1 ~]# docker inspect 78c5a8144951| grep -A10 Mounts
"Mounts": [
{
"Type": "bind",
"Source": "/xx",
"Destination": "/aa",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
},
{
"Type": "bind",
[root@node1 ~]#
[root@node1 ~]# ls /xx
memload-7.0-1.r29766.x86_64.rpm
[root@node1 ~]#
数据测试
接下来我们进入到容器内创建个文件,再次回到主机,看文件是否会同步
注:容器内也可以看到node节点上给该目录存放的文件内容。
[root@master volume]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-host 1/1 Running 0 9s 10.244.166.167 node1 <none> <none>
[root@master volume]#
[root@master volume]# kubectl exec -it pod-host -- bash
root@pod-host:/#
root@pod-host:/#
root@pod-host:/# ls /aa
memload-7.0-1.r29766.x86_64.rpm
root@pod-host:/#
root@pod-host:/# touch /aa/aa.txt
root@pod-host:/# ls /aa/
aa.txt memload-7.0-1.r29766.x86_64.rpm
root@pod-host:/#
# 回到node节点,查看该目录,确实可以看到有文件的
[root@node1 ~]# ls /xx
aa.txt memload-7.0-1.r29766.x86_64.rpm
[root@node1 ~]#
- 前面说过,这种方式挂载的存储卷,不会随着容器的消失而消失,现在测试
[root@master volume]# kubectl delete pod pod-host
pod "pod-host" deleted
[root@master volume]#
# 上面容器删除了,但节点上的数据依然存在
[root@node1 ~]# ls /xx
aa.txt memload-7.0-1.r29766.x86_64.rpm
[root@node1 ~]#
hostPath以只读的形式创建pod
这个就是在配置文件中增加一行参数:readOnly: ture
不多说,直接看实例吧
[root@master volume]# cat pod-host.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod-host
name: pod-host
spec:
terminationGracePeriodSeconds: 0
volumes:
- name: v1
hostPath:
path: /xx
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: pod-host
resources: {}
volumeMounts:
- name: v1
mountPath: /aa
#只读参数加在这
readOnly: true
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
[root@master volume]# kubectl apply -f pod-host.yaml
pod/pod-host created
[root@master volume]# kubectl exec -it pod-host -- bash
root@pod-host:/# cd /aa
root@pod-host:/aa# ls
aa.txt memload-7.0-1.r29766.x86_64.rpm
root@pod-host:/aa# touch bb
touch: cannot touch 'bb': Read-only file system
root@pod-host:/aa#
文件备份
- 文件备份其实就备份完整的yaml文件,我们后面可以通过该文件创建pod,创建出来的内容和备份时的node节点是一摸一样的。
备份的好处就是,该pod是运行在哪个节点上的,恢复时候就一定会运行在该节点上,数据可以始终保持一致【但不能删主机上的数据,否则容器内数据也会跟着变化】 - 语法:
kubectl get pod pod名称 -o yaml > 自定义名称.yaml
[root@master volume]# kubectl get pod pod-host -o yaml > pod-copy.yaml
[root@master volume]# cat pod-copy.yaml
apiVersion: v1
kind: Pod
metadata:
annotations:
cni.projectcalico.org/podIP: 10.244.166.169/32
cni.projectcalico.org/podIPs: 10.244.166.169/32
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"creationTimestamp":null,"labels":{"run":"pod-host"},"name":"pod-host","namespace":"volume"},"spec":{"containers":[{"image":"nginx","imagePullPolicy":"IfNotPresent","name":"pod-host","resources":{},"volumeMounts":[{"mountPath":"/aa","name":"v1"}]}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","terminationGracePeriodSeconds":0,"volumes":[{"hostPath":{"path":"/xx"},"name":"v1"}]},"status":{}}
creationTimestamp: "2021-08-19T02:30:50Z"
labels:
run: pod-host
name: pod-host
namespace: volume
resourceVersion: "5842284"
uid: 853b07b8-bbd8-4df6-8c7c-3cbeee5d5712
spec:
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: pod-host
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /aa
name: v1
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: kube-api-access-mlfvg
readOnly: true
dnsPolicy: ClusterFirst
enableServiceLinks: true
nodeName: node1
preemptionPolicy: PreemptLowerPriority
priority: 0
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
serviceAccount: default
serviceAccountName: default
terminationGracePeriodSeconds: 0
tolerations:
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 300
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 300
volumes:
- hostPath:
path: /xx
type: ""
name: v1
- name: kube-api-access-mlfvg
projected:
defaultMode: 420
sources:
- serviceAccountToken:
expirationSeconds: 3607
path: token
- configMap:
items:
- key: ca.crt
path: ca.crt
name: kube-root-ca.crt
- downwardAPI:
items:
- fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
path: namespace
status:
conditions:
- lastProbeTime: null
lastTransitionTime: "2021-08-19T02:30:51Z"
status: "True"
type: Initialized
- lastProbeTime: null
lastTransitionTime: "2021-08-19T02:30:53Z"
status: "True"
type: Ready
- lastProbeTime: null
lastTransitionTime: "2021-08-19T02:30:53Z"
status: "True"
type: ContainersReady
- lastProbeTime: null
lastTransitionTime: "2021-08-19T02:30:50Z"
status: "True"
type: PodScheduled
containerStatuses:
- containerID: docker://2a1d512a9a942867e2c58a57ee26191b6ec92bd2fc011714a5a0d4f32a1e6929
image: nginx:latest
imageID: docker://sha256:d1a364dc548d5357f0da3268c888e1971bbdb957ee3f028fe7194f1d61c6fdee
lastState: {}
name: pod-host
ready: true
restartCount: 0
started: true
state:
running:
startedAt: "2021-08-19T02:30:52Z"
hostIP: 192.168.59.143
phase: Running
podIP: 10.244.166.169
podIPs:
- ip: 10.244.166.169
qosClass: BestEffort
startTime: "2021-08-19T02:30:51Z"
[root@master volume]#
- 恢复
这个和创建pod是一样的方式,只是配置文件内容不一样而已
[root@master volume]# kubectl delete pod pod-host
pod "pod-host" deleted
[root@master volume]# kubectl apply -f pod-copy.yaml
pod/pod-host created
[root@master volume]# kubectl exec -it pod-host -- bash
root@pod-host:/# ls /aa
aa.txt memload-7.0-1.r29766.x86_64.rpm
root@pod-host:/#
注意事项
我们先做一个测试,配置文件不变,我们重新创建一个pod,可以看到也是运行在node1上的,并且进入容器可以看到之前创建的文件内容,说明该容器数据具有持久性了,但这种说法不严谨,我们能看到之前创建的文件内容是因为该pod恰好也是运行在node1节点上的,如果该 pod重新创建过程中,他的节点不在node1上了,那么数据其实也就没 了【也就是说,我们现在看到的数据,其实是node1上的,而非容器内】。
[root@master volume]# kubectl apply -f pod-host.yaml
pod/pod-host created
[root@master volume]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-host 1/1 Running 0 4s 10.244.166.168 node1 <none> <none>
[root@master volume]# kubectl exec -it pod-host -- bash
root@pod-host:/# ls /a
ls: cannot access '/a': No such file or directory
root@pod-host:/# ls /aa
aa.txt memload-7.0-1.r29766.x86_64.rpm
root@pod-host:/#
- 为了证实我刚才所说的,看到的数据是node1节点上,而非容器内的,我们现在删除该容器,并指定起节点为node1,我们再次看这个aa文件,里面就会没内容了。
[root@master volume]# cat pod-host.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod-host
name: pod-host
spec:
nodeName: node2
terminationGracePeriodSeconds: 0
volumes:
- name: v1
hostPath:
path: /xx
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: pod-host
resources: {}
volumeMounts:
- name: v1
mountPath: /aa
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
[root@master volume]# kubectl apply -f pod-host.yaml
pod/pod-host created
[root@master volume]#
[root@master volume]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-host 1/1 Running 0 12s 10.244.104.41 node2 <none> <none>
[root@master volume]# kubectl exec -it pod-host -- bash
root@pod-host:/# ls /aa
memload-7.0-1.r29766.x86_64.rpm
root@pod-host:/#
root@pod-host:/# touch /aa/bb.txt
root@pod-host:/# ls /aa
bb.txt memload-7.0-1.r29766.x86_64.rpm
root@pod-host:/#
# 创建了bb,我们去node2上查看
[root@node2 ~]# ls /xx
bb.txt memload-7.0-1.r29766.x86_64.rpm
[root@node2 ~]#
- 解决上面问题呢,我们可以使用网络卷
网络卷
说明
- 我们上面本地卷中,hostpath是映射到主机本地目录的,这样的好处就是数据不会随着容器的消失而消失,但是也会发生一些极端情况,就是说当容器重建了,但运行在其他节点上了,那么数据也会没了,解决这种办法呢,就是创建卷的时候,我们映射到网络卷上,无论pod是运行在哪个容器上,卷对应的地址都是同一个网络卷,这样,无论pod如何重建,卷都是在网络卷上,所以数据不会有任何变化【我这样说能理解我想表达的意思吗?】
- 这个网络卷,是一台单独的主机,我们在上面做一个共享目录,然后创建pod的时候卷就对应到这个共享目录上,因为卷是一台单独的主机,所以我们就不需要考虑卷是映射到哪台node节点上了。
- 支持网络数据卷
- nfs
- iscsi
- glusterfs
- awsElasticBlockStore
- cephfs
- azureFileVolume
- azureDiskVolume
- vsphereVolume
- …
- 任何共享形式都行,我这使用nfs吧,这个简单,也容易理解。
nfs中的配置文件格式如下
...
spec:
terminationGracePeriodSeconds: 0
volumes:
- name: 【自定义名称】
nfs:
server: 【nfs主机ip】
path: 【nfs共享目录】
...
nfs共享配置【服务端】
- 先准备一台主机,这个主机可以不用是集群中的某一台,只要和集群能互通就行。
我使用的是一台非集群内的虚拟机,ip是:192.168.59.156
- 先安装nfs服务:
yum install nfs-utils -y
[root@etcd1 ~]# yum install nfs-utils -y
。。。
已安装:
nfs-utils.x86_64 1:1.3.0-0.61.el7
作为依赖被安装:
gssproxy.x86_64 0:0.7.0-21.el7 keyutils.x86_64 0:1.5.8-3.el7
libbasicobjects.x86_64 0:0.1.1-32.el7 libcollection.x86_64 0:0.7.0-32.el7
libevent.x86_64 0:2.0.21-4.el7 libini_config.x86_64 0:1.3.1-32.el7
libnfsidmap.x86_64 0:0.25-19.el7 libpath_utils.x86_64 0:0.2.1-32.el7
libref_array.x86_64 0:0.1.5-32.el7 libtirpc.x86_64 0:0.2.4-0.15.el7
libverto-libevent.x86_64 0:0.2.5-4.el7 quota.x86_64 1:4.01-17.el7
quota-nls.noarch 1:4.01-17.el7 rpcbind.x86_64 0:0.2.0-47.el7
tcp_wrappers.x86_64 0:7.6-77.el7
更新完毕:
selinux-policy.noarch 0:3.13.1-229.el7
作为依赖被升级:
libselinux.x86_64 0:2.5-14.1.el7
libselinux-python.x86_64 0:2.5-14.1.el7
libselinux-utils.x86_64 0:2.5-14.1.el7
libsemanage.x86_64 0:2.5-14.el7
libsepol.x86_64 0:2.5-10.el7
policycoreutils.x86_64 0:2.5-29.el7
selinux-policy-targeted.noarch 0:3.13.1-229.el7
完毕!
[root@etcd1 ~]#
- 创建共享目录,添加权限
可以创建任意目录,我使用/data
[root@etcd1 ~]# mkdir /data
[root@etcd1 ~]# chmod 777 /data
[root@etcd1 ~]#
- 编辑配置文件:
/etc/exports
我对任何ip开放,所以用*
,*
可以替换为master节点IP,但没必要限制。
[root@etcd1 ~]# cat /etc/exports
# 添加目录给相应网段访问并添加读写权限
# no_root_squash——不修改root权限,一定要加,不加就有问题
/data *(rw,async,no_root_squash)
[root@etcd1 ~]#
- 开启rpc服务
[root@etcd1 ~]# systemctl enable rpcbind --now
- 启动服务并设置开机自启
[root@etcd1 ~]# systemctl enable nfs-server.service --now
Created symlink from /etc/systemd/system/multi-user.target.wants/nfs-server.service to /usr/lib/systemd/system/nfs-server.service.
[root@etcd1 ~]#
- 让共享立即生效:
exportfs -arv
[root@etcd1 ~]# exportfs -arv
exporting *:/data
[root@etcd1 ~]#
nfs客户端配置【node节点操作】
- 安装服务:
yum install nfs-utils -y
【所有node节点操作】
[root@node1 ~]# yum install nfs-utils -y
[root@node2 ~]# yum install nfs-utils -y
- 启动服务并设置开机自启
[root@node1 ~]# systemctl enable nfs --now
Created symlink from /etc/systemd/system/multi-user.target.wants/nfs-server.service to /usr/lib/systemd/system/nfs-server.service.
[root@node1 ~]#
[root@node2 ~]# systemctl enable nfs --now
Created symlink from /etc/systemd/system/multi-user.target.wants/nfs-server.service to /usr/lib/systemd/system/nfs-server.service.
[root@node2 ~]#
- 验证【下面的ip是nfs共享主机的ip】
[root@node1 ~]# showmount -e 192.168.59.156
Export list for 192.168.59.156:
/data *
[root@node1 ~]#
[root@node2 ~]# showmount -e 192.168.59.156
Export list for 192.168.59.156:
/data *
[root@node2 ~]#
配置文件编辑【master节点操作】
[root@master volume]# cat pod-nfs.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod-emp1
name: pod-emp1
spec:
terminationGracePeriodSeconds: 0
volumes:
# 自定义卷名称
- name: v1
nfs:
# nfs-主机ip【showmount -e nfsip可以查看的】
server: 192.168.59.156
# nfs共享目录【showmount -e nfsip可以查看的】
path: /data
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: pod-emp1
resources: {}
volumeMounts:
# 使用上面卷名称
- name: v1
# 这个是容器内的存储文件【自定义的】
mountPath: /aa
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
[root@master volume]# kubectl apply -f pod-nfs.yaml
pod/pod-emp1 created
[root@master volume]#
[root@master volume]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-emp1 1/1 Running 0 46s 10.244.166.172 node1 <none> <none>
[root@master volume]#
[root@master volume]# kubectl exec -it pod-emp1 -- bash
root@pod-emp1:/#
root@pod-emp1:/# ls
aa boot docker-entrypoint.d etc lib media opt root sbin sys usr
bin dev docker-entrypoint.sh home lib64 mnt proc run srv tmp var
root@pod-emp1:/# ls /aa
root@pod-emp1:/# touch /aa/aa.txt
root@pod-emp1:/# ls /aa
aa.txt
root@pod-emp1:/#
数据测试
我们进入该容器,创建一个文件,然后去nfs共享主机,查看共享目录,确定数据是否同步【同步以后为正常】
[root@master volume]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-emp1 1/1 Running 0 46s 10.244.166.172 node1 <none> <none>
[root@master volume]#
[root@master volume]# kubectl exec -it pod-emp1 -- bash
root@pod-emp1:/#
root@pod-emp1:/# ls
aa boot docker-entrypoint.d etc lib media opt root sbin sys usr
bin dev docker-entrypoint.sh home lib64 mnt proc run srv tmp var
root@pod-emp1:/# ls /aa
root@pod-emp1:/# touch /aa/aa.txt
root@pod-emp1:/# ls /aa
aa.txt
root@pod-emp1:/#
# 回到nfs节点,有数据为正常,同理,这里面创建的数据,容器内部也能看到才对
[root@etcd1 ~]# ls /data/
aa.txt
[root@etcd1 ~]#
[root@etcd1 ~]# touch /data/test_20210819
[root@etcd1 ~]# ls /data/
aa.txt test_20210819
[root@etcd1 ~]#
# 容器内部
root@pod-emp1:/# ls /aa
aa.txt test_20210819
root@pod-emp1:/#
更换节点测试数据是否依然存在
刚才的默认创建是在node1节点上的,现在我们自定义到node2节点上,然后进入容器查看,如果有数据则正常
[root@master volume]# kubectl delete pod pod-emp1
[root@master volume]# cat pod-nfs.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod-emp1
name: pod-emp1
spec:
nodeName: node2
terminationGracePeriodSeconds: 0
volumes:
- name: v1
nfs:
server: 192.168.59.156
path: /data
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: pod-emp1
resources: {}
volumeMounts:
- name: v1
mountPath: /aa
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
[root@master volume]# kubectl apply -f pod-nfs.yaml
pod/pod-emp1 created
[root@master volume]#
[root@master volume]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-emp1 1/1 Running 0 9s 10.244.104.42 node2 <none> <none>
[root@master volume]#
[root@master volume]# kubectl exec -it pod-emp1 -- bash
root@pod-emp1:/#
root@pod-emp1:/# ls /aa
aa.txt test_20210819
root@pod-emp1:/#
root@pod-emp1:/# touch /aa/node-test1
root@pod-emp1:/# ls /aa/
aa.txt node-test1 test_20210819
root@pod-emp1:/#
# nfs节点
[root@etcd1 ~]# ls /data/
aa.txt node-test1 test_20210819
[root@etcd1 ~]#
更多推荐
所有评论(0)