赞
踩
一个 Pod 可以设置一组共享的存储卷。 Pod 中的所有容器都可以访问该共享卷,从而允许这些容器共享数据。 卷还允许 Pod 中的持久数据保留下来,即使其中的容器需要重新启动。 以下介绍有关 Kubernetes 如何在 Pod 中实现共享存储并将其提供给 Pod 的更多信息。
容器中的文件在磁盘上是临时存放的,这给在容器中运行较重要的应用带来一些问题。 当容器崩溃或停止时会出现一个问题。此时容器状态未保存, 因此在容器生命周期内创建或修改的所有文件都将丢失。 在崩溃期间,kubelet 会以干净的状态重新启动容器。 当多个容器在一个 Pod 中运行并且需要共享文件时,会出现另一个问题。 跨所有容器设置和访问共享文件系统具有一定的挑战性。Kubernetes 卷这一抽象概念能够解决这两个问题。
Docker 也有卷的概念,但对它只有少量且松散的管理。 Docker 卷是磁盘上或者另外一个容器内的一个目录。 Docker 提供卷驱动程序,但是其功能非常有限。
Kubernetes 支持很多类型的卷。 Pod可以同时使用任意数目的卷类型。临时卷类型的生命周期与 Pod 相同, 但持久卷可以比 Pod 的存活期长。 当 Pod 不再存在时,Kubernetes 也会销毁临时卷;不过 Kubernetes 不会销毁持久卷。 对于给定 Pod 中任何类型的卷,在容器重启期间数据都不会丢失。
卷的核心是一个目录,其中可能存有数据,Pod 中的容器可以访问该目录中的数据。 所采用的特定的卷类型将决定该目录如何形成的、使用何种介质保存数据以及目录中存放的内容。
使用卷时, 在 .spec.volumes
字段中设置为 Pod 提供的卷,并在 .spec.containers[*].volumeMounts
字段中声明卷在容器中的挂载位置。 容器中的进程看到的文件系统视图是由它们的容器镜像的初始内容以及挂载在容器中的卷(如果定义了的话)所组成的。 其中根文件系统同容器镜像的内容相吻合。 任何在该文件系统下的写入操作,如果被允许的话,都会影响接下来容器中进程访问文件系统时所看到的内容。
卷挂载在镜像中的指定路径下。 Pod 配置中的每个容器必须独立指定各个卷的挂载位置。
卷不能挂载到其他卷之上,也不能与其他卷有硬链接。
Kubernetes 支持下列类型的卷:
emptyDir是定义在Pod空间内的一种存储卷类型, 它无须指定宿主机上对应的目录文件,当 Pod 分派到某个节点上时,emptyDir
卷会被创建,并且在 Pod 在该节点上运行期间,卷一直存在。 就像其名称表示的那样,卷最初是空的。 Pod 中的容器挂载 emptyDir
卷的路径可能相同也可能不同,这些容器都可以读写 emptyDir
卷中相同的文件。 当 Pod 因为某些原因被从节点上删除时,emptyDir
卷中的数据也会被永久删除。
说明:
容器崩溃并不会导致 Pod 被从节点上移除,因此容器崩溃期间
emptyDir
卷中的数据是安全的。
emptyDir
的一些用途:
- 作为某些应用程序运行时的临时目录, 且无须永久保留。
- 为耗时较长的计算任务提供检查点,以便任务能方便地从崩溃前状态恢复执行。
- Pod内一个容器需要从另一个容器内获取数据。 ( 多容器共享目录)
hostPath
卷能将主机节点文件系统上的文件或目录挂载到你的 Pod 中。 虽然这不是大多数 Pod 需要的,但是它为一些应用程序提供了强大的逃生舱。
例如,
hostPath
的一些用法有:
- 运行一个需要访问 Docker 内部机制的容器;可使用
hostPath
挂载/var/lib/docker
路径。- 在容器中运行 cAdvisor 时,以
hostPath
方式挂载/sys
。- 当容器应用程序生成的日志需要永久保存时, 可以使用该类型挂载宿主机上的高速文件系统。
警告:
HostPath 卷存在许多安全风险,最佳做法是尽可能避免使用 HostPath。 当必须使用 HostPath 卷时,它的范围应仅限于所需的文件或目录,并以只读方式挂载。
注意:
- HostPath 卷可能会暴露特权系统凭据(例如 Kubelet)或特权 API(例如容器运行时套接字),可用于容器逃逸或攻击集群的其他部分。
- 具有相同配置的多个 Pod 会由于节点上文件的不同而在不同节点上对Volume的访问结果不同。
- 下层主机上创建的文件或目录只能由 root 用户写入。 你需要在特权容器中以 root 身份运行进程,或者修改主机上的文件权限以便容器能够写入
hostPath
卷。
nfs
卷能将 NFS (网络文件系统) 挂载到你的 Pod 中。 不像 emptyDir
那样会在删除 Pod 的同时也会被删除,nfs
卷的内容在删除 Pod 时会被保存,卷只是被卸载。 这意味着 nfs
卷可以被预先填充数据,并且这些数据可以在 Pod 之间共享。
说明:
在使用 NFS 卷之前,你必须运行自己的 NFS 服务器并将目标 share 导出备用。
还需要注意,不能在 Pod spec 中指定 NFS 挂载可选项。 可以选择设置服务端的挂载可选项,或者使用 /etc/nfsmount.conf。 此外,还可以通过允许设置挂载可选项的持久卷挂载 NFS 卷。
configMap
卷提供了向 Pod 注入配置数据的方法。 ConfigMap 对象中存储的数据可以被 configMap
类型的卷引用,然后被 Pod 中运行的容器化应用使用。引用 configMap 对象时,你可以在卷中通过它的名称来引用。 也自定义 ConfigMap 中特定条目所要使用的路径。
说明:
persistentVolumeClaim
卷用来将持久卷(PersistentVolume)挂载到 Pod 中。 持久卷申领(PersistentVolumeClaim)是用户在不知道特定云环境细节的情况下“申领”持久存储的一种方法。
fc (光纤通道):将现有的光纤通道块存储卷挂载到 Pod 中
fc
卷类型允许将现有的光纤通道块存储卷挂载到 Pod 中。 可以使用卷配置中的参数 targetWWNs
来指定单个或多个目标 WWN(World Wide Names)。 如果指定了多个 WWN,targetWWNs 期望这些 WWN 来自多路径连接。
iscsi:在Pod上挂载iSCSI存储设备上的目录。
iscsi
卷能将 iSCSI 卷挂载到你的 Pod 中。iscsi
卷的内容在删除 Pod 时会被保留,卷只是被卸载。 这意味着 iscsi
卷可以被预先填充数据,并且这些数据可以在 Pod 之间共享。
local:代表的是某个被挂载的本地存储设备,例如磁盘、分区或者目录。
local
卷只能用作静态创建的持久卷。不支持动态配置。
与 hostPath
卷相比,local
卷能够以持久和可移植的方式使用,而无需手动将 Pod 调度到节点。系统通过查看 PersistentVolume 的节点亲和性配置,就能了解卷的节点约束。
cephfs:允许将现存的 CephFS 卷挂载到 Pod 中:
在使用 Ceph 卷之前,你的 Ceph 服务器必须已经运行并将要使用的 share 导出(exported)。
rbd:块设备共享存储。
在使用 RBD 之前,你必须安装运行 Ceph。
secret:用来给 Pod 传递敏感信息,例如密码。
可以将 Secret 存储在 Kubernetes API 服务器上,然后以文件的形式挂载到 Pod 中,无需直接与 Kubernetes 耦合。
gcePersistentDisk: Google公司提供的公有云永久磁盘。
GlusterFS: 开源的网络文件系统
Kubernetes 1.27 不包含 glusterfs
卷类型。
awsElasticBlockStore: 可以将Amazon Web 服务(AWS)EBS卷挂载到 Pod 中。
cinder:用于将 OpenStack Cinder 卷挂载到 Pod 中。
gitRepo:是一个卷插件的例子,挂载一个空目录,并将一个 Git 代码仓库克隆到这个目录中供 Pod 使用。
PersistentVolume 子系统为用户和管理员提供了一组 API, 将存储如何制备的细节从其如何被使用中抽象出来。 为了实现这点,我们引入了两个新的 API 资源:PersistentVolume 和 PersistentVolumeClaim。
持久卷(PersistentVolume,PV) 是集群中的一块存储,可以由管理员事先制备, 或者使用存储类来动态制备。 持久卷是集群资源,就像节点也是集群资源一样。PV 持久卷和普通的 Volume 一样, 也是使用卷插件来实现的,只是它们拥有独立于任何使用 PV 的 Pod 的生命周期。 此 API 对象中记述了存储的实现细节,无论其背后是 NFS、iSCSI 还是特定于云平台的存储系统。
持久卷申领(PersistentVolumeClaim,PVC) 表达的是用户对存储的请求。概念上与 Pod 类似。 Pod 会耗用节点资源,而 PVC 申领会耗用 PV 资源。Pod 可以请求特定数量的资源(CPU 和内存);同样 PVC 申领也可以请求特定的大小和访问模式 )。
一个 projected
卷可以将若干现有的卷源映射到同一个目录之上。
目前,以下类型的卷源可以被投射:
secret
downwardAPI
configMap
serviceAccountToken
所有的卷源都要求处于 Pod 所在的同一个名字空间内。
介绍:
有些应用程序需要额外的存储,但并不关心数据在重启后是否仍然可用。 例如,缓存服务经常受限于内存大小,而且可以将不常用的数据转移到比内存慢的存储中,对总体性能的影响并不大。
另有些应用程序需要以文件形式注入的只读数据,比如配置数据或密钥。
临时卷 就是为此类用例设计的。因为卷会遵从 Pod 的生命周期,与 Pod 一起创建和删除, 所以停止和重新启动 Pod 时,不会受持久卷在何处可用的限制。
五、配置 Pod 以使用卷进行存储
本练习将创建一个运行 Pod,该 Pod 仅运行一个容器并拥有一个类型为 emptyDir的卷, 在整个 Pod 生命周期中一直存在,即使 Pod 中的容器被终止和重启。以下是 Pod 的配置:
apiVersion: v1
kind: Pod
metadata:
name: redis
spec:
containers:
- name: redis
image: redis
volumeMounts:
- name: redis-storage
mountPath: /data/redis
volumes:
- name: redis-storage
emptyDir: {}
1、创建pod
$ kubectl apply -f https://k8s.io/examples/pods/storage/redis.yaml
或者手动创建yaml文件,使用apply命令创建pod
lin@k8smaster:~$ kubectl apply -f redis.yaml #注意路径
pod/redis created
2、验证 Pod 中的容器是否正在运行,留意 Pod 的更改:
kubectl get pod redis --watch
输出如下:
NAME READY STATUS RESTARTS AGE
redis 1/1 Running 0 13s
3、使用另一个终端,用 Shell 连接正在运行的容器:
$ kubectl exec -it redis -- /bin/bash
4、切换到 /data/redis
目录下,然后创建一个文件:
lin@k8smaster:~$ kubectl exec -it redis -- /bin/bash
root@redis:/data# cd /data/redis/
root@redis:/data/redis# echo Hello > test-file
root@redis:/data/redis# apt-get update
5、在你的 Shell 中,列出正在运行的进程:
$ apt-get update
$ apt-get install procps
$ ps aux
输出类似:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
redis 1 0.2 0.1 53688 7892 ? Ssl 07:39 0:00 redis-server
root 19 0.0 0.0 4160 3388 pts/0 Ss 07:39 0:00 /bin/bash
root 354 0.0 0.0 6756 2904 pts/0 R+ 07:41 0:00 ps aux
6、结束 Redis 进程
#root@redis:/data/redis# kill <pid>
root@redis:/data/redis# kill 1
root@redis:/data/redis# command terminated with exit code 137
其中 是 Redis 进程的 ID (PID),在原先终端中,留意 Redis Pod 的更改,会看到和下面类似的输出:
lin@k8smaster:~$ kubectl get pod redis --watch
NAME READY STATUS RESTARTS AGE
redis 1/1 Running 0 88s
redis 0/1 Completed 0 3m39s
redis 1/1 Running 1 (5s ago) 3m43s
此时,容器已经终止并重新启动。这是因为 Redis Pod 的 restartPolicy
为 Always
。
7、用 Shell 进入重新启动的容器中,进入到 /data/redis
目录下,并确认 test-file
文件是否仍然存在。
lin@k8smaster:~$ kubectl exec -it redis -- /bin/bash
root@redis:/data# cd /data/redis/
root@redis:/data/redis# ls
test-file
8、删除为此练习所创建的 Pod
lin@k8smaster:~$ kubectl delete pod redis
pod "redis" deleted
留意原终端变化
lin@k8smaster:~$ kubectl get pod redis --watch
NAME READY STATUS RESTARTS AGE
redis 1/1 Running 0 88s
redis 0/1 Completed 0 3m39s
redis 1/1 Running 1 (5s ago) 3m43s
redis 1/1 Terminating 1 (93s ago) 5m11s
redis 0/1 Terminating 1 (94s ago) 5m12s
redis 0/1 Terminating 1 (94s ago) 5m12s
本练习将配置一个使用 PersistentVolumeClaim作为存储的pod
#创建一个/mnt/data 目录:
lin@k8smaster:~/Desktop$ sudo mkdir /mnt/data
#在 /mnt/data 目录中创建一个 index.html 文件:
lin@k8smaster:~/Desktop$ sudo sh -c "echo 'Hello from Kubernetes storage' > /mnt/data/index.html"
#测试 index.html 文件确实存在
lin@k8smaster:~/Desktop$ cat /mnt/data/index.html
Hello from Kubernetes storage
创建一个 hostPath 类型的 PersistentVolume, hostPath PersistentVolume 的配置文件:
apiVersion: v1
kind: PersistentVolume
metadata:
name: task-pv-volume
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/data"
(1)创建 PersistentVolume:
kubectl apply -f https://k8s.io/examples/pods/storage/pv-volume.yaml
同样也可复制以上配置文件手动创建yaml文件,使用apply命令创建pod。
(2)查看 PersistentVolume 的信息:
kubectl get pv task-pv-volume
输出结果显示该 PersistentVolume 的状态(STATUS)
为 Available
。 这意味着它还没有被绑定给 PersistentVolumeClaim。
创建一个 PersistentVolumeClaim。 Pod 使用 PersistentVolumeClaim 来请求物理存储。请求至少 3 GB 容量的卷, 该卷至少可以为一个节点提供读写访问:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: task-pv-claim
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
(1)创建PersistentVolumeClaim:
kubectl apply -f https://k8s.io/examples/pods/storage/pv-claim.yaml
同样也可复制以上配置文件手动创建yaml文件,使用apply命令创建pod。
(2)再次查看 PersistentVolume 信息
kubectl get pv task-pv-volume
可见输出的状态为Bound
(3)查看 PersistentVolumeClaim:
lin@k8smaster:~$ kubectl get pvc task-pv-claim
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
task-pv-claim Bound task-pv-volume 10Gi RWO manual 4m26s
输出结果表明该 PersistentVolumeClaim 绑定了你的 PersistentVolume task-pv-volume
。
下面是此 Pod 的配置文件:
apiVersion: v1 kind: Pod metadata: name: task-pv-pod spec: volumes: - name: task-pv-storage persistentVolumeClaim: claimName: task-pv-claim containers: - name: task-pv-container image: nginx ports: - containerPort: 80 name: "http-server" volumeMounts: - mountPath: "/usr/share/nginx/html" name: task-pv-storage
注意 Pod 的配置文件指定了 PersistentVolumeClaim,但没有指定 PersistentVolume。 对 Pod 而言,PersistentVolumeClaim 就是一个存储卷。
(1)创建 Pod:
kubectl apply -f https://k8s.io/examples/pods/storage/pv-pod.yaml
(2)检查 Pod 中的容器是否运行正常:
kubectl get pod task-pv-pod
#以下输出为正常,需等待一段时间
NAME READY STATUS RESTARTS AGE
task-pv-pod 1/1 Running 0 38s
(3)访问 Pod 中的容器:验证 Nginx 是否正在从 hostPath 卷提供 index.html
文件:
kubectl exec -it task-pv-pod -- /bin/bash
(4)验证 Nginx 是否正在从 hostPath 卷提供 index.html
文件:
apt update
apt install curl
curl http://localhost/
#输出
Hello from Kubernetes storage
(5)删除为此练习所创建的 Pod、PV、PVC
kubectl delete pod task-pv-pod
kubectl delete pvc task-pv-claim
kubectl delete pv task-pv-volume
sudo rm /mnt/data/index.html
sudo rmdir /mnt/data
本练习将使用本地文件来创建用户名和密码 Secret, 然后创建运行一个容器的 Pod, 该 Pod 使用projected
卷将 Secret 挂载到相同的路径下。
下面是 Pod 的配置文件:
apiVersion: v1 kind: Pod metadata: name: test-projected-volume spec: containers: - name: test-projected-volume image: busybox:1.28 args: - sleep - "86400" volumeMounts: - name: all-in-one mountPath: "/projected-volume" readOnly: true volumes: - name: all-in-one projected: sources: - secret: name: user - secret: name: pass
1、创建 Secret:
# 创建包含用户名和密码的文件:
echo -n "admin" > ./username.txt
echo -n "1f2d1e2e67df" > ./password.txt
# 在 Secret 中引用上述文件
kubectl create secret generic user --from-file=./username.txt
kubectl create secret generic pass --from-file=./password.txt
2、创建Pod
kubectl apply -f https://k8s.io/examples/pods/storage/projected.yaml
3、确认 Pod 中的容器运行正常,然后监视 Pod 的变化,输出结果和下面类似:
lin@k8smaster:~$ kubectl get --watch pod test-projected-volume
NAME READY STATUS RESTARTS AGE
test-projected-volume 1/1 Running 0 79s
4、在另外一个终端中进入容器,在 shell 中,确认 projected-volume
目录包含你的投射源:
$ kubectl exec -it test-projected-volume -- /bin/sh
$ ls /projected-volume/
5、删除为此练习所创建的Pod 和 Secret:
kubectl delete pod test-projected-volume
kubectl delete secret user pass
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。