第10章 Kubernetes存储
1. 为什么需要存储卷?
2. 数据卷概述
3. 临时数据卷,节点数据卷,网络数据卷
4. 持久数据卷概述
5. PV与PVC使用流程
6. PV生命周期
7. PV动态供给
8. 有状态应用部署:StatefulSet工作负载控制器
9. 应用程序配置文件存储:ConfigMap
10.敏感数据存储:Secret
10.1 为什么需要数据卷?
容器中的文件在磁盘上是临时存放的,这给容器中运行比较重要的应用程序带来一些问题。
问题1:当容器崩溃时,kubelet会重建容器,容器内文件丢失
问题2:一个pod中运行多个容器并需要共享文件
Kubernetes卷(volume)这一抽象概念能够解决这两个问题
常用的数据卷:
• 节点本地(hostPath,emptyDir)
• 网络(NFS,Ceph,GlusterFS)
• 公有云(AWS EBS)
• K8S资源(configmap,secret)
10.2临时数据卷:emptyDir
emptyDir卷:是一个临时存储卷,与pod生命周期绑定一起,如果Pod删除了卷也会被删除。
应用场景:Pod中容器之间数据共享。
示例:Pod内容器之前共享数据
apiVersion: v1 kind: Pod metadata: name: my-pod spec: containers: - name: write image: centos command: ["bash","-c","for i in {1..100};do echo $i >> /data/hello;sleep 1;done"] volumeMounts: - name: data mountPath: /data - name: read image: centos command: ["bash","-c","tail -f /data/hello"] volumeMounts: - name: data mountPath: /data volumes: - name: data emptyDir: {}
验证结果:
root@k8s-master1 ~]# kubectl exec -it my-pod -c write -- bash [root@my-pod /]# tail -f /data/hello 10 11 12 13 [root@k8s-node1 ~]# kubectl logs my-pod -c read -f 7 8 9 10 11 12 13 14
10.3节点数据卷:hostpath
Hostpath卷:挂载Node文件系统(pod所在节点)上传或者目录到pod中的容器。
应用场景:Pod中容器需要访问宿主机文件
示例:将宿主机/tmp目录挂载到容器/data目录
apiVersion: v1 kind: Pod metadata: name: hostpath-pod spec: containers: - name: busybox image: busybox args: - /bin/sh - -c - sleep 36000 volumeMounts: - name: data mountPath: /data volumes: - name: data hostPath: path: /tmp type: Directory
10.4 网络数据卷:NFS
NFS卷:提供对NFS挂载支持,可以自动将NFS共享路径挂载到pod中
NFS:是一个主流的文件共享服务器。 # yum install nfs-utils # vi /etc/exports /ifs/kubernetes *(rw,no_root_squash) # mkdir -p /ifs/kubernetes # systemctl start nfs # systemctl enable nfs 注:每个Node上都要安装nfs-utils包 mount -t nfs 192.168.31.71:/ifs/kubernetes /mnt showmount -e
示例:将Nginx网站程序根目录持久化到NFS存储,为多个Pod提供网站程序文件

apiVersion: apps/v1 kind: Deployment metadata: name: web spec: selector: matchLabels: app: nginx replicas: 3 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx volumeMounts: - name: wwwroot mountPath: /usr/share/nginx/html ports: - containerPort: 80 volumes: - name: wwwroot nfs: server: 192.168.31.63 path: /ifs/kubernetes --- apiVersion: v1 kind: Service metadata: labels: app: web name: web-nfs spec: ports: - port: 80 protocol: TCP targetPort: 80 selector: app: nginx project: ms ############## Master主机的mnt为挂载目录,node2机器为远程挂载目录 Mount -t nfs 192.168.31.73: /ifs/kubernetes /mnt
10.5 Pv和PVC持久数据卷概述
- PersistentVolume(PV):对存储资源创建和使用的抽象,使得存储作为集群中的资源管理
- PersistentVolumeClaim(PVC):让用户不需要关心具体的Volume实现细节
- Pod申请PVC作为卷来使用,Kubernetes通过PVC查找绑定的PV,并Mount给pod。
10.5.1 PV与PVC使用流程
容器应用:
apiVersion: v1 kind: Pod metadata: name: my-pod spec: containers: - name: nginx image: nginx:latest ports: - containerPort: 80 volumeMounts: - name: www mountPath: /usr/share/nginx/html volumes: - name: www persistentVolumeClaim: claimName: my-pvc
数据卷定义:
apiVersion: v1 kind: PersistentVolume metadata: name: my-pv spec: capacity: storage: 5Gi accessModes: - ReadWriteMany nfs: path: /ifs/kubernetes server: 192.168.31.71
卷需求模板:
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: my-pvc spec: accessModes: - ReadWriteMany resources: requests: storage: 5Gi
10.6PV生命周期
10.6.1AccessModes(访问模式):
AccessModes是用来对PV进行访问模式的设置,用于描述用户应用对存储资源的访问权限,访问权限包括下面几种方式:
- ReadWriteOnce(RWO):读写权限,但是只能被单个节点挂载
- ReadOnlyMany(ROX):只读权限,可以被多个节点挂载
- ReadWriteMany(RWX):读写权限,可以被多个节点挂载
10.6.2RECLAIM POLICY(回收策略):
目前PV支持的策略有三种:
- Retain(保留):保留数据,需要管理员手工清理数据
- Recycle(回收):清除PV中的数据,效果相当于执行rm -rf /ifs/kubernetes/*
- Delete(删除):与PV相连的后端存储同时删除
10.6.3Status状态
一个PV的生命周期中,可能会处于4种不同的阶段
- Available)(可用):表示可用状态,还未被任何PVC绑定
- Bound(已绑定):表示PV已经被PVC绑定
- Released(已释放):PVC被删除,但是资源还未被集群重新声明
- Failed(失败):表示该pv的自动回收失败
10.7PV的静态供给
现在PV使用方式称为静态供给,需要K8s运维工程师提前创建一堆PV,供开发者使用。
10.8PV动态供给(StorageClass)
PV静态供给明显的缺点是维护成本太高了!
因此,k8s开始支持PV动态供给,使用StorageClass对象实现。
SC是storageClass的缩写,表示存储类;这种资源主要用来对pv资源的自动供给提供接口;所谓自动供给是指用户无需手动创建pv,而是在创建pvc时对应pv会由persistentVolume-controller自动创建并完成pv和pvc的绑定;使用sc资源的前提是对应后端存储必须
支持restful类型接口的管理接口,并且pvc必须指定对应存储类名称来引用SC;简单讲SC资源就是用来为后端存储提供自动创建pv并关联对应pvc的接口;
10.8.1 基于NFS实现动态供给流程图
10.8.2 PV动态供给(StorageClass)
K8s默认不支持NFS动态供给,需要单独部署社区开发的插件。
项目地址:https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner
部署基于SC创建动态存储
cat << EOF > nginx-sc.yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: nginx-nfs-storage namespace: nginx-ss provisioner: fuseim.pri/ifs # or choose another name, must match deployment's env PROVISIONER_NAME' reclaimPolicy: Retain #回收策略:Retain(保留)、 Recycle(回收)或者Delete(删除) volumeBindingMode: Immediate #volumeBindingMode存储卷绑定策略 allowVolumeExpansion: true #pvc是否允许扩容 EOF
volumeBindingMode存储卷绑定策略
- Immediate:创建PVC后立即创建存储卷,并且立即绑定新创建的pv和pvc。
- WaitForFirstConsumer:当pvc被pod使用时,才触发pv和后端存储的创建,同时时间pvc/pv绑定,启用该配置后,Storage Class 中的Zone 和Region将不再生效,而是使用pod调度所在节点的zone和region创建文件系统,保证文件系统能被pod挂载。
StorageClass的定义包含四个部分:
-
provisioner:该字段指定使用存储卷类型,不同的存储卷提供者类型这里要修改成对应的值。【注意】provisioner必须和上面得Deployment的YAML文件中PROVISIONER_NAME的值保持一致。
-
parameters:指定 provisioner 的选项,比如 glusterfs 支持 resturl、restuser 等参数。
-
mountOptions:指定挂载选项,当 PV 不支持指定的选项时会直接失败。比如 NFS 支持 hard 和 nfsvers=4.2 等选项。
-
reclaimPolicy:指定回收策略,同 PV 的回收策略。Retain(保留)、 Recycle(回收)或者Delete(删除)
cd deploy
kubectl apply -f rbac.yaml # 授权访问apiserver
kubectl apply -f deployment.yaml # 部署插件,需修改里面NFS服务器地址与共享目录
kubectl apply -f class.yaml # 创建存储类
kubectl get sc # 查看存储类
10.8.3 测试:在创建pvc时指定存储类名称
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: test-claim spec: storageClassName: "managed-nfs-storage" accessModes: - ReadWriteMany resources: requests: storage: 1Gi --- apiVersion: v1 kind: Pod metadata: name: test-pod spec: containers: - name: test-pod image: nginx volumeMounts: - name: nfs-pvc mountPath: "/usr/share/nginx/html" volumes: - name: nfs-pvc persistentVolumeClaim: claimName: test-claim
View Code
10.9PV与PVC可能存在疑问
1、PV与PVC什么关系 一对一关系 2、pv与pvc什么匹配条件 - 存储容量 - 访问模式 3、容量匹配策略 - 匹配最接近的PV容量(匹配大的) - 如果都满足不了,pvc处于pending 4、容量是实际限制? 不能是用于限制,主要匹配的一种标记,实际取决于后端的存储。 5、一个deployment能不能使用多个pvc 可以,定义多个卷来源和pvc
10.9.1创建pvc之后,一直绑定不上pv(pending)
1.pvc的空间申请大小大于pv的大小;
2.pvc的StorageClassName没有和pv的的一致;
3.pvc的accessModes和pv的一致。
10.9.2创建挂载了PVC的pod之后,一直处于pending状态:
- pvc没有被创建成功,或者被创建
- pvc和pod不在同一Namespace。
10.9.3 pvc的删除操作:
删除pvc后,k8s会创建一个用于回收的pod,根据pv的回收策略进行pv的回收,回收完以后pv的状态就会变成可被绑定的状态也就是空闲状态,其他的pending状态的pvc如果匹配到了这个pv,他就能和这个pv进行绑定。
10.10有状态应用部署:statefulset控制器
有状态与无状态:
Deployment控制器设计原则:管理所有pod一摸一样,提供同一服务,也不考虑在那台Node运行,可随意扩容和缩容。这种应用称为"无状态",例如web服务。
在实际的场景中,这并不能满足所有应用,尤其是分布式应用,会部署多个实例,这些实例之间往往有依赖关系,例如主从关系,主备关系,这种应用称为"有状态",例如:mysql,etcd集群
10.10.1statefulset控制器的特定:
- 部署有状态应用
- 解决Pod独立生命周期,保持Pod启动顺序和唯一性
(1) 稳定,唯一的网络标识符,持久存储
(2) 有序,优雅的部署和扩展、删除和终止
(3) 有序,滚动更新
应用场景:分布式应用,数据库集群
10.10.2StatefulSet控制器的网络ID和存储
- 稳定的网络ID
使用Headless Service(相比普通Service只是将spec.clusterIP定义为None)来维护Pod网络身份。并且添加serviceName:“nginx”字段指定StatefulSet控制器要使用这个Headless Service。
DNS解析名称:<statefulsetName-index>.<service-name> .<namespace-name>.svc.cluster.local
- 稳定的存储
StatefulSet的存储卷使用VolumeClaim Template创建,称为卷申请模板,当StatefulSet使用VolumeClaimTemplate创建一个PersistentVolume时,同样也会为每个Pod分配并创建一个编号的PVC
10.10.3 StatefulSet与Deployment区别:有身份的!
身份三要素:
- 域名
- 主机名
- 存储(PVC)
10.11应用程序数据存储
- ConfigMap:存储配置文件
- Secret:存储敏感数据
10.11.1 应用程序数据存储:ConfigMap
创建ConfigMap后,数据实际会存储在k8s中Etcd,然后通过创建pod时引用该数据。
应用场景:应用程序配置
Pod使用configmap数据有两种方式:
- 变量注入
- 数据卷挂载
10.11.2 应用程序数据存储:ConfigMap
apiVersion: v1 kind: Pod metadata: name: configmap-demo-pod spec: containers: - name: demo image: nginx env: - name: ABCD valueFrom: configMapKeyRef: name: configmap-demo key: abc - name: CDEF valueFrom: configMapKeyRef: name: configmap-demo key: cde volumeMounts: - name: config mountPath: "/config" readOnly: true volumes: - name: config configMap: name: configmap-demo items: - key: "redis.properties" path: "redis.properties" --- apiVersion: v1 kind: ConfigMap metadata: name: configmap-demo data: abc: "123" cde: "456" redis.properties: | port: 6379 host: 192.168.31.10
验证结果:
10.11.3 应用程序数据存储:Secret
与ConfigMap类似,区别在于Secret主要存储敏感数据,所有的数据要经过base64编码。。
应用场景:凭据
Kubectl create secret 支持三种数据类型:
- Docker-registry:存储镜像仓库认证信息
- Generic:从文件、目录或者字符串创建,例如存储用密码
- Tls:存储证书,例如Https证书
10.11.4 使用应用程序数据存储Secret

apiVersion: v1 kind: Secret metadata: name: db-user-pass type: Opaque data: username: YWRtaW4= password: MWYyZDFlMmU2N2Rm --- apiVersion: v1 kind: Pod metadata: name: secret-demo-pod spec: containers: - name: demo image: nginx env: - name: USER valueFrom: secretKeyRef: name: db-user-pass key: username - name: PASS valueFrom: secretKeyRef: name: db-user-pass key: password volumeMounts: - name: config mountPath: "/config" readOnly: true volumes: - name: config secret: secretName: db-user-pass items: - key: username path: my-username
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!