参考官方文档:k8s-卷
一、卷的概念
Container 中的文件在磁盘上是临时存放的,这给 Container 中运行的较重要的应用程序带来一些问题。
问题一:当容器崩溃时文件丢失,kubelet 会重新启动容器, 但容器会以干净的状态重启。
问题二:会在同一 Pod 中运行多个容器并共享文件时出现。 Kubernetes 卷(Volume) 这一抽象概念能够解决这两个问题。
1.1 卷的概念
k8s 的最小单位是pod,pod 是由一个或者多个容器组成的,最终卷还是作用于Container ,Docker 也有卷(Volume) 的概念,但是Docker 卷是磁盘上或者另外一个容器内的一个目录。 Docker 提供卷驱动程序,但是其功能非常有限。
Kubernetes 支持很多类型的卷。 Pod可以同时使用任意数目的卷类型。 临时卷类型的生命周期与 Pod 相同,但持久卷可以比 Pod 的存活期长。 当 Pod 不再存在时,Kubernetes 也会销毁临时卷;不过 Kubernetes 不会销毁持久卷。 对于给定 Pod 中任何类型的卷,在容器重启期间数据都不会丢失。
卷的核心是一个目录,其中可能存有数据,Pod 中的容器可以访问该目录中的数据。 所采用的特定的卷类型将决定该目录如何形成的、使用何种介质保存数据以及目录中存放的内容。
1.2 卷类型
数据卷大致被分为以下三种类型:
临时卷(Ephemeral Volume):,pod内的容器发生重启不会造成数据的丢失,但是当pod被重启后,emptyDir数据会丢失,也就是说emptyDir与pod的生命周期是一致的(hostPath,emptyDir等)
持久卷(Persistent Volume):真正的持久化,K8s将自己不擅长的存储实现,交给了别的擅长于存储的服务商或软件;——这也是我们以后学习的重点难点!(NFS,NAS,Ceph,GlusterFS,Iscsi,RDB,local等)
投射卷(Projected Volumes):类似于独立环境变量文件的方式,将文件的键值对挂载到容器内,或者讲一类重要token 密钥存放在容器这种往往是临时存储。(configmap,secret等)
二、持久卷
持久卷(PersistentVolume,PV) 是集群中的一块存储,可以由管理员事先制备, 或者使用存储类(Storage Class)来动态制备。 持久卷是集群资源,就像节点也是集群资源一样。PV 持久卷和普通的 Volume 一样, 也是使用卷插件来实现的,只是它们拥有独立于任何使用 PV 的 Pod 的生命周期。 此 API 对象中记述了存储的实现细节,无论其背后是 NFS、iSCSI 还是特定于云平台的存储系统。
持久卷申领(PersistentVolumeClaim,PVC) 表达的是用户对存储的请求。概念上与 Pod 类似。 Pod 会耗用节点资源,而 PVC 申领会耗用 PV 资源。Pod 可以请求特定数量的资源(CPU 和内存);同样 PVC 申领也可以请求特定的大小和访问模式 (例如,可以要求 PV 卷能够以 ReadWriteOnce、ReadOnlyMany 或 ReadWriteMany 模式之一来挂载,参见访问模式)。
通常在企业中,Volume是由存储系统的运维人员来维护,他们来提供pv,pv具有持久性,生命周期独立于Pod;Pod则是由应用的开发人员来维护,如果要进行一卷挂载,那么就写一个pvc来消费pv就可以了,K8s会查找并提供满足条件的pv。
2.1 创建PV
我们这里以NFS 为例,我在一台192.168.232.50 安装安装NFS server,把192.168.232.50:/data-nfs 目录挂载到两个node 节点。
在192.168.232.50上安装nfs server
yum -y install nfs-nutils
mkdir /data-nfs/
echo "/data-nfs/ 192.168.232.0/24(insecure,rw,sync,no_root_squash)" > /etc/exports
systemctl enable --now rpcbind
systemctl enable --now nfs-server
exportfs -r
##exportfs 检查是否生效
在需要共享nfs server的机器上,安装NFS Client
#执行以下命令检查 nfs 服务器端是否有设置共享目录
# showmount -e $(nfs服务器的IP)
showmount -e 192.168.232.50
#执行以下命令挂载 nfs 服务器上的共享目录到本机路径 /data
mkdir /data
mount 192.168.232.50:/data-nfs/ /data
node1 和node2 上我的node节点是两个,这个根据需求来选择
mkdir /data/pv-test01
mkdir /data/pv-test02
[root@k8s-master01 ~]# cat pv-01.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-01
labels:
type: test
spec:
capacity:
storage: 1Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs
nfs:
path: /data-nfs/pv-test01
server: 192.168.232.50
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-02
labels:
type: test
spec:
capacity:
storage: 50m
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs
nfs:
path: /data-nfs/pv-test02
server: 192.168.232.50
查看创建pv 并查看信息:
[root@k8s-master01 ~]# kubectl apply -f pv-01.yaml
persistentvolume/pv-01 created
persistentvolume/pv-02 created
[root@k8s-master01 ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE## 存活时间
pv-01 1Gi RWO Retain Available nfs 21s
pv-02 50m RWO Retain Available nfs 21s
2.2 pv 参数
①accessModes:pv访问模式
-
ReadWriteOnce:卷可以被一个节点以读写方式挂载。 ReadWriteOnce 访问模式也允许运行在同一节点上的多个 Pod 访问卷。
-
ReadOnlyMany:卷可以被多个节点以只读方式挂载。
-
ReadWriteMany:卷可以被多个节点以读写方式挂载。
-
ReadWriteOncePod:卷可以被单个 Pod 以读写方式挂载。 如果你想确保整个集群中只有一个 Pod 可以读取或写入该 PVC, 请使用 ReadWriteOncePod 访问模式。这只支持 CSI 卷以及需要 Kubernetes 1.22 以上版本
在命令行接口(CLI)中,ACCESS MODES也使用以下缩写形式:
- RWO - ReadWriteOnce
- ROX - ReadOnlyMany
- RWX - ReadWriteMany
- RWOP - ReadWriteOnceP
② persistentVolumeReclaimPolicy:pv回收策略 **
- Retain -- 手动回收,清除pv中的数据需要运维手动去操作,最为安全
- Recycle -- 基本擦除 (
rm -rf /thevolume/*
) - Delete -- 诸如 AWS EBS、GCE PD、Azure Disk 或 OpenStack Cinder 卷这类关联存储资产也被删除,类如删除pvc 对应的pv 也被删除了。
目前,仅 NFS 和 HostPath 支持回收(Recycle)。 AWS EBS、GCE PD、Azure Disk 和 Cinder 卷都支持删除(Delete)。
③STATUS :pv状态
-
Available(可用)– 卷是一个空闲资源,尚未绑定到任何申领;
-
Bound(已绑定)– 该卷已经绑定到某申领;
-
Released(已释放)– 所绑定的PVC已被删除,但是资源尚未被集群回收;
-
Failed(失败)– 卷的自动回收操作失败。
④CLAIM:当前正被哪个PVC绑定 —— 当某个PV被PVC申领之后,该列就会有数值了!
⑤STORAGECLASS:存储类——动态供应的重点,每个StorageClass都对应着自己的一套自己的存储实现
2.3 创建PVC
上面已经说过pv是运维人员创建的持久化卷,而开发要使用这个持久化卷就需要创建这个卷对应的申领/申请,这个就是pvc。以下是具体的调度过程:
手动创建一个pvc ,cat pvc-01.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-01
namespace: mytest
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 50m
storageClassName: nfs
selector:
matchLabels:
type: test
[root@k8s-master01 ~]# kubectl apply -f pvc-01.yaml
Error from server (NotFound): error when creating "pvc-01.yaml": namespaces "mytest" not found
查看k8s 集群所有namespace ns 是所以也是使用全称
[root@k8s-master01 ~]# kubectl get ns --show-labels
NAME STATUS AGE LABELS
default Active 252d <none>
ingress-nginx Active 132d app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx
kube-node-lease Active 252d <none>
kube-public Active 252d <none>
kube-system Active 252d <none>
kubernetes-dashboard Active 251d <none>
我们默认没有mytest 的namespace,手动创建一个 kubectl create ns mytest,再次创建pvc
[root@k8s-master01 ~]# kubectl apply -f pvc-01.yaml
persistentvolumeclaim/pvc-01 created
[root@k8s-master01 ~]# kubectl get pvc --all-namespaces
NAMESPACE NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mytest pvc-01 Bound pv-02 50m RWO nfs 7s
[root@k8s-master01 ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv-01 1Gi RWO Retain Available nfs 28s
pv-02 50m RWO Retain Bound mytest/pvc-01 nfs 28s
创建一个nginx 来使用使将web 项目放到 nfs 上
#deploy
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-demo
namespace: mytest
labels:
app: nginx-demo
spec:
replicas: 2
selector:
matchLabels:
app: nginx-demo
template:
metadata:
labels:
app: nginx-demo
spec:
containers:
- image: registry.cn-hangzhou.aliyuncs.com/jyl_docker/nginx
name: nginx-demo
volumeMounts: # 我们这里将nginx容器默认的页面目录挂载
- name: html-files
mountPath: "/usr/share/nginx/html"
volumes:
- name: html-files
persistentVolumeClaim: # 卷类型使用pvc,同时下面名称处填先创建好的pvc1
claimName: pvc-01
---
#service
apiVersion: v1
kind: Service
metadata:
name: nginx-demo-svc
namespace: mytest
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx-demo
type: ClusterIP
---
#ingress 如果没有配置ingress 可以把这部分删除
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginx-demo
namespace: mytest
spec:
rules:
- host: nginx.jylx.net
http:
paths:
- path: /
backend:
serviceName: nginx-demo-svc
servicePort: 80
查看创建信息
[root@k8s-master01 ~]# kubectl get svc -o wide -n mytest
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
nginx-demo-svc ClusterIP 10.105.164.251 <none> 80/TCP 4m36s app=nginx-demo
[root@k8s-master01 ~]# curl 10.244.58.211
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.21.5</center>
</body>
</html>
在 echo "sunccess_web_pv-02" > mkdir /data/pv-test02/index.html
再次测试
[root@k8s-master01 ~]# curl 10.105.164.251
sunccess_web_pv-02
[root@k8s-master01 ~]# kubectl get ingress -n mytest
NAME CLASS HOSTS ADDRESS PORTS AGE
nginx-demo <none> nginx.jylx.net 192.168.232.71,192.168.232.72 80 7m4s
当然可以本地配置 hosts解析访问:nginx.jylx.net
2.4 StorageClass 动态存储
上面pv 和pvc 创建我们提到的pv 创建我们是让运维工程师或者存储工程师创建,我们只管使用pvc就好,但是实际业务中这个方式管理起来比较麻烦,运维人员可以提前配置好Stroagreclass,这样开发就可以直接使用pvc从而动态创建pv。
2.4.1 执行整合文件
可以参考文档:https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner
选择deploy 目录里得class.yaml、deployment.yaml、rbac.yaml ,以下是整合后的文件StorageClass-deploy.yaml
安装后使用案例验证StorageClass 参考: test-pod.yaml,test-claim.yaml
修改StorageClass 添加 annotations:storageclass.kubernetes.io/is-default-class: "true" 设置为默认存储
####class
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-client
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner # or choose another name, must match deployment's env PROVISIONER_NAME'
parameters:
archiveOnDelete: "true"
---
###deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
labels:
app: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: mynfs
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
image: registry.cn-hangzhou.aliyuncs.com/jyl_docker/nfs-subdir-external-provisioner:v4.0.2
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: k8s-sigs.io/nfs-subdir-external-provisioner ##供应者得名称提供storageclass 调用
- name: NFS_SERVER
value: 192.168.232.50
- name: NFS_PATH
value: /data-nfs
volumes:
- name: nfs-client-root
nfs:
server: 192.168.232.50
path: /data-nfs
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: mynfs
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-client-provisioner-runner
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "update", "patch"]
---
###rbac
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: mynfs
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: mynfs
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: mynfs
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: mynfs
roleRef:
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.io
执行StorageClass-deploy.yaml
[root@k8s-master01 ~]# kubectl apply -f StorageClass-deploy.yaml
storageclass.storage.k8s.io/nfs-client created
deployment.apps/nfs-client-provisioner created
serviceaccount/nfs-client-provisioner created
clusterrole.rbac.authorization.k8s.io/nfs-client-provisioner-runner created
clusterrolebinding.rbac.authorization.k8s.io/run-nfs-client-provisioner created
role.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created
rolebinding.rbac.authorization.k8s.io/leader-locking-nfs-client-provisioner created
[root@k8s-master01 ~]# kubectl get storageclass
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
nfs-client k8s-sigs.io/nfs-subdir-external-provisioner Delete Immediate false 2m5
2.4.2 使用StorageClass
创建一个pod 来验证StorageClass
[root@k8s-master01 ~]# cat StorageClass-nginx-test.yaml
##service
apiVersion: v1
kind: Service
metadata:
name: nginx-sc
namespace: mytest
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx-sc
type: ClusterIP
---
##deploy
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-sc
namespace: mytest
labels:
app: nginx-sc
spec:
replicas: 2
selector:
matchLabels:
app: nginx-sc
template:
metadata:
labels:
app: nginx-sc
spec:
containers:
- image: registry.cn-hangzhou.aliyuncs.com/jyl_docker/nginx
name: nginx-sc
volumeMounts: # 我们这里将nginx容器默认的页面目录挂载
- name: html-files
mountPath: "/usr/share/nginx/html"
volumes:
- name: html-files
persistentVolumeClaim:
claimName: test-claim ##
---
##StorageClass
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: test-claim
spec:
storageClassName: nfs-client
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Mi
执行StorageClass-nginx-test.yaml 文件
[root@k8s-master01 ~]# kubectl apply -f StorageClass-nginx-test.yaml
service/nginx-sc created
deployment.apps/nginx-sc created
persistentvolumeclaim/test-claim created
可以看到最终创建对应了pvc
root@k8s-master01 ~]# kubectl get svc -nmytest
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-sc ClusterIP 10.109.35.206 <none> 80/TCP 24s
[root@k8s-master01 ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv-02 500m RWO Retain Bound mytest/pvc-01 nfs 3d15h
pvc-e4318826-a127-4913-839a-b53a79683369 1Mi RWX Delete Bound default/test-claim nfs-client 52s
[root@k8s-master01 ~]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
test-claim Bound pvc-e4318826-a127-4913-839a-b53a79683369 1Mi RWX nfs-client 72s
Comments | NOTHING