一、持久化Volumes¶
数据持久化:
容器部署过程中一般有以下三种数据:
-
启动时需要的初始数据,例如配置文件;
-
启动过程中产生的临时数据,该临时数据需要多个容器间共享;
-
应用运行过程中产生的持久化数据,例如MySQL的data目录;
数据卷的分类:
- 本地(hostPath,emptyDir等)
- 网络(NFS,Ceph,GlusterFS等)
- 公有云(阿里云OSS,NAS / AWS EBS等)
- K8S资源(configmap,secret等)
二、emptyDir(临时存储卷)¶
- 1、emptyDir存储卷是Pod对象生命周期中的一个临时目录,在Pod对象启动时即被创建,而在Pod对象被移除时会被彻底删除;
- 2、不具有持久能力的emptyDir存储卷只能用于某些特殊场景中;
- 3、emptyDir只是一个临时挂载的文件,pod删除后,该目录也会在node节点上被删除,但是容器崩溃时,该文件还存在;
案例一: 暂存空间,用作长时间计算崩溃恢复时的检查点
# 创建pod模板
[root@master01 1]# k run emptydir-nginx --image=registry.cn-hangzhou.aliyuncs.com/zq-demo/nginx:1.21.6 --dry-run=client -oyaml > emptydir-nginx.yaml
# 定义yaml文件
[root@master01 1]# vim emptydir-nginx.yaml
apiVersion: v1
kind: Pod
metadata:
name: emptydir-nginx
namespace: study
spec:
containers:
- image: registry.cn-hangzhou.aliyuncs.com/zq-demo/nginx:1.21.6
name: emptydir-nginx
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /data/ #添加容器内部的挂载点
name: emptydir-name
volumes:
- name: emptydir-name
emptyDir: #创建一个空目录
sizeLimit: 1G #目录容量大小为1G
# 应用
[root@master01 1]# kaf emptydir-nginx.yaml
# 查看
[root@master01 1]# kgp -owide -n study
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
emptydir-nginx 1/1 Running 0 3m55s 172.21.231.178 node02 <none> <none>
# 进入容器并在容器中创建文件测试
[root@master01 1]# kubectl exec -it emptydir-nginx -n study /bin/bash
root@emptydir-nginx:/# echo $(date +%Y%m%d-%H:%M:%S) >> /data/time.log
root@emptydir-nginx:/# ls /data/time.log
/data/time.log
# 在node02节点上查看临时存储的文件位置
[root@node02 ~]# find / -name "time.log"
/var/lib/kubelet/pods/6ed328ee-72fe-44ca-9720-20c25708173d/volumes/kubernetes.io~empty-dir/emptydir-name/time.log
# 在node02节点上查看并验证文件内容
[root@node02 ~]# cat /var/lib/kubelet/pods/6ed328ee-72fe-44ca-9720-20c25708173d/volumes/kubernetes.io~empty-dir/emptydir-name/time.log
20250506-03:24:14
# 在master节点上删除pod
[root@master01 1]# k delete -f emptydir-nginx.yaml
# 再次在node02节点上验证数据是否存在,观察到数据已经消失
[root@master01 ~]# ll /var/lib/kubelet/pods/6ed328ee-72fe-44ca-9720-20c25708173d/volumes/kubernetes.io~empty-dir/emptydir-name/time.log
ls: cannot access /var/lib/kubelet/pods/6ed328ee-72fe-44ca-9720-20c25708173d/volumes/kubernetes.io~empty-dir/emptydir-name/time.log: No such file or directory
# 环境复原
[root@master01 1]# k delete -f emptydir-nginx.yaml
案例二: Pod内的多个容器间文件的共享,或者作为容器数据的临时存储目录用于数据缓存系统等
# 创建pod模板
[root@master01 1]# k run producer-consumer --image=registry.cn-hangzhou.aliyuncs.com/abroad_images/busybox:1.28 --dry-run=client -oyaml > producer-consumer.yaml
# 定义yaml文件
[root@master01 1]# vim producer-consumer.yaml
apiVersion: v1
kind: Pod
metadata:
name: producer-consumer
namespace: study
spec:
containers:
- image: registry.cn-hangzhou.aliyuncs.com/abroad_images/busybox:1.28
name: producer
volumeMounts:
- name: shared-volume
mountPath: /producer_dir
command: [ "/bin/sh", "-c" ]
args:
- while true; do
echo $(hostname) $(date) >> /producer_dir/hello;
sleep 5;
done
- name: consumer
image: registry.cn-hangzhou.aliyuncs.com/abroad_images/busybox:1.28
volumeMounts:
- name: shared-volume
mountPath: /consumer_dir
args:
- /bin/sh
- -c
- cat /consumer_dir/hello ; sleep 3600
volumes:
- name: shared-volume
emptyDir: {}
# 应用
[root@master01 1]# kaf producer-consumer.yaml
# 查看
[root@master01 1]# kgp -n study -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
producer-consumer 2/2 Running 0 73s 172.21.231.179 node02 <none> <none>
# 在master01节点上新起两个窗口进行测试验证,观察到两个容器 producer和consumer,它们共享一个 Volume
[root@master01 1]# kubectl exec -it producer-consumer -c producer -n study -- tail -f /producer_dir/hello
producer-consumer Fri Nov 10 09:11:30 UTC 2023
...
...
[root@master01 11.4]# kubectl exec -it producer-consumer -c consumer -n study -- tail -f /consumer_dir/hello
producer-consumer Fri Nov 10 09:11:30 UTC 2023
...
...
# 环境复原
[root@master01 1]# k delete -f producer-consumer.yaml
案例流程:
这里我们模拟了一个 producer / consumer 场景。Pod 有两个容器 producer和 consumer,它们共享一个 Volume。producer 负责往 Volume 中写数据,其每隔5秒 生成一行信息追加到存储卷上的hello文件中, consumer 则是从 Volume 读取数据。
- 1、文件最底部 volumes 定义了一个 emptyDir 类型的 Volume shared-volume。
- 2、producer 容器将 shared-volume mount 到 /producer_dir 目录。
- 3、producer 通过 echo 将数据写到文件 hello 里。
- 4、consumer 容器将 shared-volume mount 到 /consumer_dir 目录。
- 5、consumer 通过 cat 从文件 hello 读数据。
总结:
emptyDir 是Host上创建的临时目录;
优点:能够方便地为 Pod 中的容器提供共享存储,不需要额外的配置。
缺点:不具备持久性,如果 Pod 不存在了,emptyDir 也就没有了。
三、hostPath(节点存储卷)¶
hostPath卷是指将工作节点上某文件系统的目录或文件挂载于Pod中的一种存储卷,它可独立于Pod资源的生命周期,因而具有持久性。
支持的 type 值如下:
| 取值 | 解释 |
|---|---|
| 空字符串(默认) | 在安装 hostPath 卷之前不会执行任何检查 |
| DirectoryOrCreate | 目录存在就使用,不存在就先创建,权限设置为0755 |
| Directory | 目录必须存在 |
| FileOrCreate | 文件存在就使用,不存在就先创建,权限设置为0644 |
| File | 文件必须存在 |
| Socket(不常用) | 在给定路径上必须存在的UNIX 套接字 |
| CharDevice(不常用) | 在给定路径上必须存在的字符设备 |
| BlockDevice(不常用) | 在给定路径上必须存在的块设备 |
案例一:
# 创建deploy模板
[root@master01 1]# k create deploy pod-hook-exec --image=registry.cn-hangzhou.aliyuncs.com/abroad_images/busybox:1.28 -r=1 -n study --dry-run=client -oyaml > pod-hook-exec.yaml
# 定义yaml文件
[root@master01 1]# vim pod-hook-exec.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pod-hook-exec
namespace: study
spec:
replicas: 1
selector:
matchLabels:
app: pod-hook-exec
template:
metadata:
labels:
app: pod-hook-exec
spec:
terminationGracePeriodSeconds: 8 # 设置8秒宽限时间,默认是30s
initContainers:
- name: init-containers
image: registry.cn-hangzhou.aliyuncs.com/abroad_images/busybox:1.28
command: ["sh","-c","echo init-containers...|tee -a /tmp/pod-hook-exec.log;sleep 5s"]
volumeMounts:
- name: logs
mountPath: /tmp/
containers:
- image: registry.cn-hangzhou.aliyuncs.com/abroad_images/busybox:1.28
name: main-container
command: ["sh","-c","echo main-container...|tee -a /tmp/pod-hook-exec.log;sleep 3600s"]
volumeMounts:
- name: logs
mountPath: /tmp/
startupProbe:
exec:
command: ["sh","-c","echo startupProbe...|tee -a /tmp/pod-hook-exec.log;sleep 5s"]
timeoutSeconds: 10
livenessProbe:
exec:
command: ["sh","-c","echo livenessProbe...|tee -a /tmp/pod-hook-exec.log;sleep 5s"]
timeoutSeconds: 10
readinessProbe:
exec:
command: ["sh","-c","echo readinessProbe...|tee -a /tmp/pod-hook-exec.log;sleep 5s"]
timeoutSeconds: 10
lifecycle:
postStart:
exec:
command: ["sh","-c","echo postStart...|tee -a /tmp/pod-hook-exec.log;sleep 5s"]
preStop: # 在pod停止之前执行这个命令
exec:
command: ["sh","-c","echo preStop...|tee -a /tmp/pod-hook-exec.log"]
volumes:
- name: logs #和上面保持一致 这是本地的文件路径,上面是容器内部的路径
hostPath:
path: /tmp/logs/
type: DirectoryOrCreate
# 应用
[root@master01 1]# kaf pod-hook-exec.yaml
# 查看
[root@master01 1]# kgp -n study -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-hook-exec-75cb79786d-6f2zj 1/1 Running 0 2m37s 172.21.231.180 node02 <none> <none>
# 在宿主机node02节点上查看应用的pod-hook-exec.log日志
[root@node02 logs]# more pod-hook-exec.log
init-containers...
main-container...
postStart...
startupProbe...
readinessProbe...
livenessProbe...
readinessProbe...
livenessProbe...
readinessProbe...
readinessProbe...
livenessProbe...
readinessProbe...
...
...
...
# 环境复原
[root@master01 1]# k delete -f pod-hook-exec.yaml
最终我们可以通过容器所在宿主机的 /tmp/logs/ 去查看应用的 pod-hook-exec.log 日志;