假设有如下三个节点的 K8S 集群:
k8s31master 是控制节点
k8s31node1、k8s31node2 是工作节点
容器运行时是 containerd
一、理论介绍
1.1、Volumes 卷
Kubernetes 的卷是 pod 的⼀个组成部分,因此像容器⼀样在 pod 的规范(pod.spec)中就定义了。它们不是独立的 Kubernetes 对象,也不能单独创建或删除。pod 中的所有容器都可以使用卷,但必须先将它挂载在每个需要访问它的容器中。在每个容器中,都可以在其文件系统的任意位置挂载卷。
通过上面的描述,我们知道了使用卷的两个步骤:
- 在 .spec.volumes 字段中设置为 Pod 提供的卷。
- 在 .spec.containers[*].volumeMounts 字段中声明卷在容器中的挂载位置。
1.2、为什么需要?
总结:
- 数据持久性。容器的文件在磁盘上是临时存放的,当容器崩溃重启或被删除时,数据也会随之丢失。
- 共享存储。多个容器之间共享文件数据。
1.3、常用卷类型
emptyDir、hostPath、nfs、persistentVolumeClaim、configMap、secret。
kubectl explain pod.spec.volumes
二、emptyDir 卷类型
- emptyDir 类型的 Volumes 是在 Pod 被指派到某节点时被创建。
- 就像其名称所表示的那样,emptyDir 卷最初是空的。
- Kubernetes 会在节点上自动分配一个目录,因此无需指定节点上对应的目录文件。
- 当 Pod 因为某些原因被从节点上删除时,emptyDir 卷中的数据也会被永久删除。
2.1、实践
定义一个 Pod,里面有两个容器:一个 nginx,一个 alpine。
alpine 每隔3秒往容器目录 /data 生成一个 index.html。
nginx 读取这个 index.html 作为首页。
emptydir.yaml:
apiVersion: v1
kind: Pod
metadata:
name: emptydir-pod
spec:
volumes:
- name: sharedir
emptyDir: {}
containers:
- name: html-generator
image: alpine:latest
imagePullPolicy: IfNotPresent
command: ["/bin/sh", "-c", "while true; do echo \"您好,现在时间是 $(date)\" > /data/index.html; sleep 3; done"]
volumeMounts:
- name: sharedir
mountPath: /data
- name: nginx
image: nginx:1.14.2
imagePullPolicy: IfNotPresent
volumeMounts:
- name: sharedir
mountPath: /usr/share/nginx/html
readOnly: true
ports:
- containerPort: 80
protocol: TCP
- emptyDir: {} 表示这是一个空卷。
- command: ["/bin/sh", "-c", "while true; do echo \"您好,现在时间是 $(date)\" > /data/index.html; sleep 3; done"] 表示容器启动的时候执行命令,该命令每隔3秒往 /data/index.html 打印当前系统时间。
- date:这是一个用于显示或设置系统日期和时间的命令。当不带任何参数使用时,它会显示当前的日期和时间。
- $(command):这是命令替换的语法,在 Shell 中用来获取命令执行的结果。这里的command 是指任何有效的命令。在本例中,就是 date 命令。Shell 会先运行括号内的命令,并将输出结果替换掉这个整个表达式。
- 容器 html-generator 和容器 nginx 都挂载了相同的卷 sharedir。
- 每隔3秒请求 Pod IP
Pod IP 是 10.244.9.1 ; Pod 被调度到 node2。
2.2、查看本机临时目录
- 查看 pod 的 uid
kubectl get pod emptydir-pod -oyaml | grep uid
- 进入 node2 /var/lib/kubelet/pods/pod-uid/volumes 下
2.3、Pod 删除后消失
emptyDir 类型的卷只存在于 Pod 的生命周期,Pod 被删除后卷也随之消失。
kubectl delete -f emptydir.yaml
三、hostPath 卷类型
- hostPath 允许将节点上的文件系统路径挂载到 Pod 中。
- 当 Pod 被删除时,这个存储卷还是存在的,不会被删除;所以只要同一个 Pod 再次被调度到同一个节点上时,对应的数据依然是存在的。
- 因为是节点级别的存储,所以一般配合 nodeName 节点选择器来使用。
3.1、实践
hostpath.yaml:
apiVersion: v1
kind: Pod
metadata:
name: hostpath-pod
spec:
nodeName: k8s31node1
volumes:
- name: hostpath-volume
hostPath:
path: /tmp/data # 节点上的路径
type: DirectoryOrCreate
containers:
- name: test-container
image: alpine:latest
imagePullPolicy: IfNotPresent
command: [ "sh", "-c", "while true; do echo $(date) >> /mnt/hostpath/date.txt; sleep 10; done" ]
volumeMounts:
- name: hostpath-volume # 卷的名字
mountPath: /mnt/hostpath # 挂载到容器的哪个路径下
- nodeName:指定 Pod 被调度到 k8s31node1 这个节点上。
- hostPath.path 指定了节点上要挂载的路径,这里是 /tmp/data。
- hostPath.type:是一个可选字段,用于指定路径的类型:
DirectoryOrCreate:如果路径不存在,将创建一个空目录。 Directory:路径必须是一个已存在的目录。 FileOrCreate:如果路径不存在,将创建一个空文件。 File:路径必须是一个已存在的文件。 Socket:路径必须是一个已存在的 Unix 套接字。 CharDevice:路径必须是一个已存在的字符设备。 BlockDevice:路径必须是一个已存在的块设备。
test-container 容器往容器内路径 /mnt/hostpath 生成一个 date.txt 文件。
3.2、查看节点路径
进入 node1/tmp/data 路径
3.3、查看容器路径
kubectl exec -it hostpath-pod -- /bin/sh
3.4、删除 Pod
kubectl delete -f hostpath.yaml
- 查看节点路径
3.5、重新运行 Pod
kubectl apply -f hostpath.yaml
kubectl exec -it hostpath-pod -- /bin/sh
四、configMap 卷类型
可以看这篇文章 5.4 小节。
五、secret 卷类型
可以看这篇文章 5.2 小节。