参考
容器磁盘上的文件的生命周期是短暂的,这就使得在容器中运行重要应用时会出现一些问题。
首先,当容器崩溃时,kubelet 会重启它,但是容器中的文件将丢失
——容器以干净的状态
(镜像最初的状态)重新启动。
其次,在 Pod 中同时运行多个容器时,这些容器之间通常需要共享文件
Volume 就是卷的概念,它是用来管理 Kubernetes 存储的,是用来声明 在 Pod 中的容器可以访问文件目录的,一个卷可以被挂载在 Pod 中一 个或者多个容器的指定路径下面
。 而 Volume 本身是一个抽象的概念,一个 Volume 可以去支持多种的后 端的存储。比如说 Kubernetes 的 Volume 就支持了很多存储插件,它 可 以 支 持 本 地 的 存 储 , 可 以 支 持 分 布 式 的 存 储
, 比 如 说 像 ceph ,GlusterFS
;它也可以支持云存储,比如说阿里云上的云盘、AWS 上的 云盘、Google 上的云盘
等等。
常用的几种卷:
emptyDir:本地临时卷
hostPath:本地卷
nfs等:共享卷
configmap: 配置文件
https://kubernetes.io/zh/docs/concepts/storage/volumes/
当 Pod 被分配给节点时,首先创建 emptyDir 卷
,并且只要该 Pod 在该节点上运行,该卷就会存在。
同个pod里面的不同容器,共享同一个持久化目录,
当pod节点删除
时,volume的数据也会被删除。
如果仅仅是容器被销毁
,pod还在,则不会影响
volume中的数据。
总结来说:emptyDir的数据持久化的生命周期和使用的pod一致。一般是作为临时存储
使用。
emptyDir 的用法有:
崩溃恢复时的检查点
保存内容管理器容器提取的文件
[root@k8s-master emptydir]# pwd
/root/k8s_practice/emptydir
[root@k8s-master emptydir]# cat pod_emptydir.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-emptydir
namespace: default
spec:
containers:
- name: myapp-pod
image: registry.cn-beijing.aliyuncs.com/google_registry/myapp:v1
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /cache
name: cache-volume
- name: busybox-pod
image: registry.cn-beijing.aliyuncs.com/google_registry/busybox:1.24
imagePullPolicy: IfNotPresent
command: ["/bin/sh", "-c", "sleep 3600"]
volumeMounts:
- mountPath: /test/cache
name: cache-volume
volumes:
- name: cache-volume
emptyDir: {}
可以看到,两个容器都能读取到 emptyDir 中的数据
# 在容器2的 /test 目录下,创建一个 index.html 文件
$ kubectl exec test-pd -c test2-container -it -- touch /test/index.html
# 查看容器1的 /cache 目录
$ kubectl exec test-pd -c test-container -it -- ls /cache
index.html
https://kubernetes.io/zh/docs/concepts/storage/volumes/
hostPath 卷将主机节点的文件系统中的文件或目录挂载到集群中,pod删除的时候,卷不会被删除
hostPath 的用途如下:
/var/lib/docker 的 hostPath
/dev/cgroups 的 hostPath
[root@k8s-master hostpath]# pwd
/root/k8s_practice/hostpath
[root@k8s-master hostpath]# cat pod_hostpath.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-hostpath
namespace: default
spec:
containers:
- name: myapp-pod
image: registry.cn-beijing.aliyuncs.com/google_registry/myapp:v1
imagePullPolicy: IfNotPresent
volumeMounts:
- name: hostpath-dir-volume
mountPath: /test-k8s/hostpath-dir
- name: hostpath-file-volume
mountPath: /test/hostpath-file/test.conf
volumes:
- name: hostpath-dir-volume
hostPath:
# 宿主机目录
path: /k8s/hostpath-dir
# hostPath 卷指定 type,如果目录不存在则创建(可创建多层目录)
type: DirectoryOrCreate
- name: hostpath-file-volume
hostPath:
path: /k8s2/hostpath-file/test.conf
# 如果文件不存在则创建。 前提:文件所在目录必须存在 目录不存在则不能创建文件
type: FileOrCreate
进入容器生成数据:
kubectl exec -it nginx-deployment-7c759f97d4-5ql6t bash
root@nginx-deployment-7c759f97d4-5ql6t:/# echo "test data" > /cache/a.txt
到指定宿主机验证数据:
root@k8s-node2:~# cat /tmp/linux39/a.txt
test data
删除 pod 再次验证数据是否会丢失:
# kubectl delete -f deploy_hostPath.yml
deployment.apps "nginx-deployment" deleted‘
再node中查看是否创建/data/mysql目录,并且有
# cat /data/mysql/a.txt
test data
而且在容器本目录中创建,修改
在宿主机也会变化
反之也成立
但删除时,卷不会被删除
kubectl get pod -o wide
参考
PV
) 持久存储卷
是由管理员设置的存储
,它是群集的一部分。就像节点是集群中的资源一样,PV 也是集群中的资源
。 PV 是 Volume
之类的卷插件,但具有独立于使用 PV
的 Pod
的生命周期。此 API 对象包含存储实现的细节,即 NFS、iSCSI
或特定于云供应商的存储系统
PVC
): 持久存储卷声明
是用户存储的请求
。它与 Pod 相似。Pod 消耗节点资源
,PVC 消耗 PV 资源
。Pod 可以请求特定级别的资源(CPU 和内存
)。PVC 可以声明可以请求特定的大小和访问模式(例如,可以读/写一次或 只读多次模式挂载)
静态
pv:可用于消费
动态
pv:当管理员创建的静态 PV 都不匹配用户的 PersistentVolumeClaim
时,集群可能会尝试动态地为 PVC 创建卷。此配置基于 StorageClasses
:PVC 必须请求 [存储类],并且管理员必须创建并配置该类才能进行动态创建。声明该类为 " "
可以有效地禁用其动态配置
要启用基于存储级别的动态存储配置,集群管理员需要启用 API server 上的 DefaultStorageClass
[准入控制器]。例如,通过确保 DefaultStorageClass
位于 API server 组件的 --admission-control
标志,使用逗号分隔的有序值列表中,可以完成此操作
绑定:
master
中的控制环路监视新的 PVC,寻找匹配的 PV(如果可能),并将它们绑定
在一起。如果为新的 PVC 动态调配 PV,则该环路将始终将该 PV 绑定到 PVC。否则,用户总会得到他们所请求的存储,但是容量可能超出要求的数量
。
一旦 PV 和 PVC 绑定后, PersistentVolumeClaim
绑定是排他性的
,不管它们是如何绑定的。 PVC 跟 PV 绑定是一对一的映射
持久化卷声明的保护
PVC 保护的目的是确保由 Pod 正在使用的 PVC 不会从系统中移除
,因为如果被移除的话可能会导致数据丢失。
注意:当 Pod 状态为 Pending 并且 Pod 已经分配给节点或者 Pod 为 Running 状态时,PVC 处于活动状态。
当启用PVC
保护 alpha
功能时,如果用户删除了一个 pod 正在使用的 PVC,则该 PVC 不会被立即删除。PVC 的删除将被推迟,直到 PVC 不再被任何 pod 使用。
PersistentVolume
类型以插件形式实现。Kubernetes 目前支持以下插件类型:
NFS
、iSCS
、RBD(Ceph Block Device)、CephFS
Glusterfs
、VsphereVolume、Quobyte、Volumes
HostPath
、VMware
、Photon、Portworx、Volumes、ScaleIO、Volumes
、StorageOSPersistentVolume
可以以资源提供者支持的任何方式
挂载到主机上。如下表所示,供应商具有不同的功能,每个 PV 的访问模式都将被设置为该卷支持的特定模式。例如,NFS 可以支持多个读/写客户端,但特定的 NFS PV 可能以只读方式导出到服务器上。每个 PV 都有一套自己的用来描述特定功能的访问模式
ReadWriteOnce
——该卷可以被单个节点以读/写模式挂载
ReadOnlyMany
——该卷可以被多个节点以只读模式挂载
ReadWriteMany
——该卷可以被多个节点以读/写模式挂载
在命令行中,访问模式缩写为:
RWO
:ReadWriteOnce
ROX
:ReadOnlyMany
RWX
:ReadWriteMany
注意:一个卷一次只能使用一种访问模式挂载,即使它支持很多访问模式。
例如,GCEPersistentDisk
可以由单个节点作为 ReadWriteOnce
模式挂载,或由多个节点以 ReadWriteMany
模式挂载,但不能同时挂载
Retain
(保留):手动回收
Recycle
(回收):基本擦除( 相当于执行了 rm -rf /thevolume/*
)
Delete
(删除):关联的存储资产(例如 AWS EBS、GCE PD、Azure Disk 和 OpenStack Cinder 卷)将被删除
当前,只有 NFS
和 HostPath
支持回收策略。
AWS EBS、GCE PD、Azure Disk 和 Cinder 卷支持删除
策略
卷可以处于以下的某种状态:
Available
(可用):一块空闲资源还没有被任何声明绑定
Bound
(已绑定):卷已经被声明绑定
Released
(已释放):声明被删除,但是资源还未被集群重新声明
Failed
(失败):该卷的自动回收失败
命令行会显示绑定到 PV 的 PVC 的名称
apiVersion: v1
kind: PersistentVolume # 类型:PV
metadata:
name: pv0003 # 名称
spec:
capacity:
storage: 5Gi # 卷的大小:5G
volumeMode: Filesystem # 文件类型
accessModes: # 访问策略
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle # 回收策略
storageClassName: slow # 存储类的一个名称
mountOptions: # 其它说明,也可以不指定,让他自己做判断
- hard
- nfsvers=4.1
nfs:
path: /tmp # 挂载到哪个目录下
server: 172.17.0.2 # 挂载到哪个服务器
status:
phase: Bound
nfs共享存储
https://kubernetes.io/zh/docs/concepts/storage/volumes/
nfs 卷允许将现有的 NFS(网络文件系
统)共享挂载到您的容器中。不像 emptyDir,当删除 Pod 时,nfs 卷的内容被保留
,卷仅仅是被卸载。这意味着 NFS 卷可以预填充数据
,并且可以在 pod 之间“切换”数据
。 NFS 可以被多个写入者同时挂载
。
需要提前准备NFS服务器,授权规则
在本机通过showmoubt -e ip
可以看到NFS的共享文件
也就是最总将server.path
挂载到mountPath
中
新建一台虚拟机,安装 NFS,我的虚拟机 IP 为 192.168.66.200
yum install -y nfs-common nfs-utils rpcbind
mkdir /nfs
chmod 777 /nfs
chown nfsnobody /nfs
vim /etc/exports # 文件中写入以下内容
/nfs *(rw,no_root_squash,no_all_squash,sync)
systemctl start rpcbind
systemctl start nfs
在 k8s 每个节点中安装 NFS 客户端
# 安装依赖
$ yum -y install nfs-utils rpcbind
NFS的一些操作
# 查看共享目录
$ showmount -e 192.168.66.20
Export list for 192.168.66.20:
/nfs *
# 共享目录与本地目录挂载
$ mkdir ~/test
$ mount -t nfs 192.168.66.20:/nfs ~/test
# 解除挂载
$ umount ~/test
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfspv1
spec:
capacity: # 容量
storage: 10Gi
accessModes: # 访问模式
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain # 回收策略
storageClassName: nfs
nfs: # nfs服务器配置
path: /nfs # 目录
server: 192.168.66.20 # IP
创建 PV
$ kubectl apply -f nfspv.yaml
persistentvolume/nfspv1 created
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
nfspv1 10Gi RWO Retain Available nfs 7s
创建 Service 和 StatefulSet
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
selector:
matchLabels:
app: nginx
serviceName: "nginx" # 指定 Service 名称(上面创建的,一定要是个无头服务)
replicas: 3 # 副本数
template:
metadata:
labels:
app: nginx
spec:
containers: # 容器信息
- name: nginx
image: qcq-linux/myapp:v2
ports:
- containerPort: 80 # 释放的端口
name: web # 端口名字
volumeMounts: # 挂载
- name: www
mountPath: /usr/share/nginx/html # 容器内目录
volumeClaimTemplates: # 卷请求声明模板(pvc模板)
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ] # 指定要请求的卷的访问模式
storageClassName: "nfs" # 指定要请求的卷的类名,只有与 PV 中的storageClassName 相同时,才会匹配
resources:
requests:
storage: 1Gi # 指定要请求的卷大小必须满足 1G
因为前面只创建了一个 pv,所以 StatefulSet
只有一个 Pod 可以匹配,查看 Pod:
# 查看pod,只有一个因为pv,所以只有一个pod匹配成功后正常运行
# 因为是 StatefulSet,第二个没能正常启动,所以第三个Pod不会创建
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 57s
web-1 0/1 Pending 0 54s
# 查看 pv,可以看到只有一个绑定成功
# 第二个绑定不成功是因为访问模式不匹配
# 第三个绑定不成功是因为 storageClass 不匹配
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
nfspv1 10Gi RWO Retain Bound default/www-web-0 nfs 3m35s
nfspv2 5Gi ROX Retain Available nfs 34m
nfspv3 5Gi RWO Retain Available nfs1 34m
# 查看 pvc,每个Pod有一个pvc
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
www-web-0 Bound nfspv1 10Gi RWO nfs 6m6s
www-web-1 Pending nfs 6m3s
在 NFS 服务器的 /nfs
目录中创建 index.html,写入“/nfs访问成功”,然后通过 nginx 来访问
# 获取IP
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-0 1/1 Running 0 27m 10.244.1.58 k8s-node01 <none> <none>
$ curl 10.244.1.58
/nfs访问成功
删除 Pod,pv 中的数据不会丢失
$ kubectl delete pod web-0
pod "web-0" deleted
# 可以看到 IP 已经变了,说明上一个Pod删除后又建了个新的(Pod 的 name 一致)
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-0 1/1 Running 0 15s 10.244.2.60 k8s-node02 <none> <none>
# 可以看到仍然可以成功访问,数据不会丢失
$ curl 10.244.2.60
/nfs访问成功
删除 StatefulSet
后,pvc 不会自动删除,pv也不会自动释放,需要手动删除
# 删除 StatefulSet 后,pvc 仍然存在
$ kubectl delete statefulset web
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
www-web-0 Bound nfspv1 10Gi RWO nfs 13h
# 删除 pvc 后,pv 没有自动释放
$ kubectl delete pvc www-web-0
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
nfspv1 10Gi RWO Retain Released default/www-web-0 nfs 13h
# 手动释放 pv
$ kubectl edit pv nfspv1
# 将下面的 spec.claimRef 删除
spec:
claimRef:
apiVersion: v1
kind: PersistentVolumeClaim
name: www-web-0
namespace: default
resourceVersion: "619064"
uid: 99cea07e-339e-431c-bcb6-c398c884b29c
# 再次查看 pv 已经得到释放
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
nfspv1 10Gi RWO Retain Available nfs 13h
匹配 Pod name ( 网络标识 ) 的模式为:$(StatefulSet名称)-$(序号)
,比如上面的示例:web-0,web-1,web-2
StatefulSet
为每个 Pod 副本创建了一个 DNS 域名(意味着其它 Pod 可以通过这个域名来访问),这个域名的格式为: $(podname).(headless server name)
,也就意味着服务间是通过 Pod 域名来通信而非 Pod IP,因为当 Pod 所在 Node 发生故障时, Pod 会被飘移到其它 Node 上,Pod IP 会发生变化,但是 Pod 域名不会有变化
# 随便进入一个以前的 Pod,没有就新建一个,然后ping域名,可以成功 ping 通
$ ping web-0.nginx
PING web-0.nginx (10.244.2.60): 56 data bytes
64 bytes from 10.244.2.60: seq=0 ttl=62 time=0.388 ms
64 bytes from 10.244.2.60: seq=1 ttl=62 time=0.263 ms
Headless
服务来控制 Pod 的域名(意味着 Pod 外部可以通过这个域名来访问),这个域名的 FQDN(完全现定域名) 为:$(service name).(namespace).svc.cluster.local
,其中,“cluster.local
” 指的是集群的域名# 1. 查看 DNS 服务器 coredns 的 ip
$ kubectl get pod -n kube-system -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
coredns-5c98db65d4-5ztqn 1/1 Running 10 46d 10.244.0.19 k8s-master01 <none> <none>
coredns-5c98db65d4-pc62t 1/1 Running 10 46d 10.244.0.18 k8s-master01 <none> <none>
# 2. 通过 coredns 来解析域名,可以看到解析后的域名对应 StatefulSet 下的 Pod 的 IP
# 用 dig 解析域名(没有 dig 要安装:yum -y install bind-utils)
# 命令格式:dig -t A 域名 @DNS服务器IP
$ dig -t A nginx.default.svc.cluster.local @10.244.0.19
...省略
nginx.default.svc.cluster.local. 30 IN A 10.244.2.60
...省略
根据 volumeClaimTemplates
,为每个 Pod 创建一个 pvc,pvc 的命名规则匹配模式:
(volumeClaimTemplates.name)-(pod_name)
,比如上面的 volumeMounts.name=www, Pod
name=web-[0-2],因此创建出来的 PVC 是 www-web-0、www-web-1、www-web-2
删除 Pod 不会删除其 pvc,手动删除 pvc 将自动释放 pv