Kubernetes 存储卷
根据应用本身是否需要持久存储数据,以及每一次的请求跟此前的请求之间是否有关联性,可以分为四类应用:
- 有状态、要存储
- 有状态、无存储
- 无状态、有存储
- 无状态、无存储
持久化数据的方式
节点级:节点提供存储卷。此方式在k8s上,不太适用。这是因为一旦Pod被删除了,Pod是要被Scheduler组件重新调度的,调度之后的Pod并不一定还能运行在原来的节点上。
脱离节点而存在的网络共享存储设备。k8s使用这种方式。
Pod的特点:
同一个Pod内的多个容器,可共享访问同一个存储卷。因为:对k8s来讲,存储卷不属于容器,而属于Pod,可以在容器中挂在存储卷,所以,如果两个容器挂在了同一个存储卷,相当于两个容器共享数据了。
为什么Pod能有存储卷Volume,也能有网络命名空间Namespace呢?Pod其实底层有个基础容器pause。所有的网络名称空间,存储卷的挂载,都是分配给基础容器pause的。在Pod中运行的容器是共享基础容器pause的命名空间、存储卷的。所以这就是为什么同一个Pod内的所有容器都能使用同一个IP地址、共享同一个TCP/IP协议栈、使用同一个主机名的原因(因为ipc、net、uts名称空间是共享的)。
如果在Pod上使用存储卷:Pod的基础架构容器pause使用存储卷——>容器的存储卷就是容器的目录与宿主机目录建立关系——>如果宿主机的目录就是宿主机本地的,那么就随着宿主机的终结而终结,所以为了真正实现持久性,宿主机的目录也不是宿主机本地的,而是宿主机挂载的外部存储设备上的存储卷。
k8s提供的存储卷类型
empryDir
emptyDir类型的volume在pod分配到node上时被创建,kubernetes会在node上自动分配 一个目录,因此无需指定宿主机node上对应的目录文件。这个目录的初始内容为空,当Pod从node上移除时,emptyDir中的数据会被永久删除。不具备持久存储性。
hostPath
hostPath Volume为pod挂载宿主机上的目录或文件,使得容器可以使用宿主机的高速文件系统进行存储。缺点是,在k8s中,pod都是动态在各node节点上调度。当一个pod在当前node节点上启动并通过hostPath存储了文件到本地以后,下次调度到另一个节点上启动时,就无法使用在之前节点上存储的文件(如果调度到同一个节点,还是可以继续使用之前节点上存储的文件的)。不具备持久存储性。
使用 subPath
引用卷的时候,默认会将卷的根目录挂载到指定路径中。
可以通过 subPath 挂载卷的子目录,而不是根目录。
示例
apiVersion: v1
kind: Pod
metadata:
name: my-lamp-site
spec:
containers:
- name: mysql
image: mysql
volumeMounts:
- mountPath: /var/lib/mysql
name: site-data
subPath: mysql
volumes:
- name: site-data
persistentVolumeClaim:
claimName: my-lamp-site-data
将挂载 site-data 卷的子目录 mysql 到容器目录 /var/lib/mysql 中
gitRepo
gitRepo—通过检查git仓库的内容来初始化卷(实质也是一个emptyDir,只不过预置了git仓库内容)
每次pod重新创建时,会自动拉取最新代码
网络存储设备
无论Pod重启后是否调度到原来的节点,存储的文件可以继续使用。
传统意义上的网络存储设备:SAN(存储区域网络的简称,常见的有:iSCSI...)、NAS(网络附属存储:常见的有:nfs、cifs)...
nfs 卷能将 NFS (网络文件系统) 挂载到您的 Pod 中。
不像 emptyDir 那样会在删除 Pod 的同时也会被删除,nfs 卷的内容在删除 Pod 时会被保存,卷只是被卸载掉了。
这意味着 nfs 卷可以被预先填充数据,并且这些数据可以在 Pod 之间"传递"。分布式网络存储设备:glusterfs(文件系统级别存储)、cephfs(文件系统级别存储)、ceph rbd(块级别存储)...
云网络存储设备:EBS(亚马逊的)、Azure Disk(微软的)、Azure File(微软的)...
pvc和pv
两种特殊类型的存储卷:目的不是给Pod提供存储空间,而是给管理员或用户提供了,从集群外部向Pod内部的应用,注入配置信息的configmap
ConfigMap 资源提供了向 Pod 注入配置数据的方法。
ConfigMap 对象中存储的数据可以被 configMap 类型的卷引用,然后被应用到 Pod 中运行的容器。
可以在 volumes 字段中引用 configMap 名称来生成一个卷。
可以自定义 ConfigMap 中特定条目所要使用的路径。
如果容器以 subPath 卷挂载方式使用 ConfigMap 时,将无法接收 ConfigMap 的更新。
示例
apiVersion: v1
kind: Pod
metadata:
name: configmap-pod
spec:
containers:
- name: test
image: busybox
volumeMounts:
- name: config-vol
mountPath: /etc/config
volumes:
- name: config-vol
configMap:
name: log-config
items:
- key: log_level
path: log_level
ConfigMap 中 key log_level 中的内容将被挂载到 Pod 路径 /etc/config/log_level 中
路径由 spec.containers 中参数 volumeMounts.mountpath 和 volumes 中参数 items.path 拼接而成
- secret(方式)
secret 卷用来给 Pod 传递敏感信息,例如密码。
可以将 secret 存储在 Kubernetes API 服务器上,然后以文件的形式挂在到 Pod 中,实现与 Pod 解耦。
secret 卷由 tmpfs(基于内存的文件系统)提供存储,因此它们永远不会被写入持久化的存储器。
容器以 subPath 卷的方式挂载 Secret 时,它将无法实时获取 Secret 的更新
pvc的相关知识
pvc——>与当前命名空间中的pvc资源建立关系——>与pv资源(真正提供存储的资源)建立关系——>与存储系统建立关系
- 在pvc不被调用时,pvc和pv之间是没有绑定的。
- pv和pvc是一一对应的,一旦某个pv被某个pvc绑定了,这个pv就不能被其他pvc绑定。
- pvc究竟绑定哪个pv?取决于pvc的定义中所指定的dataSource的大小。比如pvc指定存储空间的大小为5G,那么存储空间为2G的pv就不符合条件。
-
还可以指定pvc的访问模型(取决于pvc的定义中做指定的accessModes).
首先我们看下PV和PVC的生命周期过程:
资源供应 (Provisioning)
资源供应 就是集群管理员(k8s集群)将volume虚拟化为很多个PV,目的就是创建合适的PV,有静态模式和动态模式。
静态模式:集群管理员手工创建许多PV,在定义PV时需要将后端存储的特性进行设置
动态模式:集群管理员无须手工创建PV,而是通过StorageClass的设置对后端存储进行描述,标记为某种 “类型(Class)”。此时要求PVC对存储的类型进行声明,系统将自动完成PV的创建及PVC的绑定。PVC可以声明Class为””,说明该PVC禁止使用动态模式
资源绑定 (Binding)
用户定义好PVC后,系统会根据PVC对存储的请求选择一个满足PVC要求的PV(PV的存在可能比PVC请求的大),找到就绑定,PV就被该PVC独占,不能和其他PVC绑定,如果没找到就一直处于pending中。如果资源供应使用的是动态模式,则系统在PVC找到合适的StorageClass后,将会自动创建PV并完成PVC的绑定。
资源使用 (Using)
POD创建可以绑定PVC,也可以多个POD挂载同一个PVC即共享多个实例共同访问一块存储空间
资源释放 (Releasing):
当用户对存储资源使用哪个完毕后,用户可以删除PVC,与该PVC绑定的PV将会被标记为已释放,但还不能立刻与其他PVC进行绑定。通过之前PVC写入的数据可能还留在存储设备上,只有在清除之后该PV才能继续使用
资源回收 (Reclaiming)
对于PV,管理员可以设定回收策略(Reclaim Policy)用于设置与之绑定的PVC释放资源之后,对于遗留数据如何处理。只有PV的存储空间完成回收,才能供新的PVC绑定和使用。
对于上述生命周期过程,对于PV的不同状态:
- Available (可用): 表示可用状态,还未被任何PVC绑定 (资源供应、资源释放<数据清除完>)
- Bound (已绑定):已经绑定到某个PVC (资源绑定、资源使用)
- Released (已释放):对应的PVC已经删除,但资源还没有被集群收回 (资源释放<数据未清除>)
- Failed:PV自动回收失败
下面说下怎么创建PV:
首先要创建存储服务,以NFS存储为例:https://www.csdn.net/gather_26/MtjaggzsOTk0NTItYmxvZwO0O0OO0O0O.html
定义好PV模型及pv的yaml文件:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv1 #pv名称
spec:
capacity: #存储能力,一个pv对象都要指定一个存储能力,目前仅支持存储空间的设置
storage: 1Gi #存储空间
accessModes:
- ReadWriteOnce #访问模式
persistentVolumeReclaimPolicy: Recycle #回收策略
nfs: #服务模式 (nfs、ceph、hostpath等)
path: /data/k8s-volume #共享数据目录挂载点
server: 192.168.1.4044 #nfs服务器地址
其中访问模式有三种:
- ReadWriteOnce (RWO) 读写权限,但是只能被单个节点挂载
- ReadOnlyMany (ROX) 只读权限,可能被多个节点挂载
- ReadWriteMany (RWX) 读写权限,可以被多个节点挂载
回收策略也有三种:
- Retain (保留) 保留数据,需要管理员手动清理
- Recycle (回收) 清除PV中的数据,效果相当于执行删除命令
- Delete (删除) 与PV相连的后端存储完成volume的删除操作,常见于云服务商的存储服务
创建PV:
[root]# kubectl apply -f pv.yaml
persistentvolume/pv1 created
查看PV:
[root]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv1 1Gi RWO Recycle Available 5s
如果PV被PVC绑定,会有CLAIM信息:
创建PVC:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-nfs
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
[root@harbor ~]# kubectl apply -f pv.yaml
persistentvolume/pv1 created
[root@harbor ~]# kubectl get pvc -n default
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pvc-nfs Bound pv1 1Gi RWO 71s
[root@harbor ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv1 1Gi RWO Recycle Bound default/pvc-nfs 4m35s
这里我们可以看到,当我们创建pvc之后,pv的状态变成了Bound绑定状态,并且和pvc的状态相同。并且可以看到pvc已经绑定到名称为pv1的volume上,同时在pv上可以看到绑定到名称为pvc-nfs的pvc中
其中查询PVC信息要带namespaces,因为PVC被POD使用必须归属某个命名空间
但是我们怎么通过创建PVC绑定我们需要的PV呢,这边就涉及label的使用了,在k8s中pod绑定node等都是通过label来的:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv2
labels: #这里将pv设置一个labels
app: nfs
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
nfs:
path: /data/k8s-volume
server: 192.168.1.144
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc2-nfs
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
selector: ##pvc匹配标签为app=nfs的pv
matchLabels:
app: nfs
下面我们创建deployment使用PVC:
apiVersion: apps/v1
kind: Deployment
metadata:
name: pv-nfs-nginx
spec:
replicas: 3
selector:
matchLabels:
app: pv-nfs-nginx
template:
metadata:
labels:
app: pv-nfs-nginx
spec:
containers:
- name: pv-nfs-nginx
image: nginx
ports:
- containerPort: 801
volumeMounts: #挂载,首先添加需要挂载的目录
- name: pv-nginx #挂载点的名称
mountPath: /usr/share/nginx/html #挂载点的路径
volumes: #绑定
- name: pv-nginx
persistentVolumeClaim: #将镜像中的nginx目录挂载到下面名称的pvc中
claimName: pvc2-nfs #pvc名称
---
apiVersion: v1
kind: Service
metadata:
name: nfs-pvc
labels:
app: pv-nfs-nginx
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
nodePort: 324
selector:
app: pv-nfs-nginx
deploy是replicaset上一层的控制器,而service为deployment对外提供访问服务的能力的,而本项目是没有使用deployment而只使用了replicaset,且也没有使用service
StorageClass
理论
在动态资源供应模式下,通过StorageClass和PVC完成资源动态绑定(系统自动生成PV),并供Pod使用的存储管理机制。
volumeClaimTemplates实现了pvc的自动化,StorageClass实现了pv的自动化
什么是StorageClass
Kubernetes提供了一套可以自动创建PV的机制,即:Dynamic Provisioning。而这个机制的核心在于StorageClass这个API对象。
StorageClass对象会定义下面两部分内容:
- PV的属性。比如,存储类型,Volume的大小等。
- 创建这种PV需要用到的存储插件,即存储制备器。
有了这两个信息之后,Kubernetes就能够根据用户提交的PVC,找到一个对应的StorageClass,之后Kubernetes就会调用该StorageClass声明的存储插件,进而创建出需要的PV。
但是其实使用起来是一件很简单的事情,你只需要根据自己的需求,编写YAML文件即可,然后使用kubectl create命令执行即可。
StorageClass 为管理员提供了描述存储 “类” 的方法。 不同的类型可能会映射到不同的服务质量等级或备份策略,或是由集群管理员制定的任意策略。 Kubernetes 本身并不清楚各种类代表的什么。这个类的概念在其他存储系统中有时被称为 “配置文件”。
为什么需要StorageClass
在一个大规模的Kubernetes集群里,可能有成千上万个PVC,这就意味着运维人员必须实现创建出这个多个PV,此外,随着项目的需要,会有新的PVC不断被提交,那么运维人员就需要不断的添加新的,满足要求的PV,否则新的Pod就会因为PVC绑定不到PV而导致创建失败。而且通过 PVC 请求到一定的存储空间也很有可能不足以满足应用对于存储设备的各种需求。
而且不同的应用程序对于存储性能的要求可能也不尽相同,比如读写速度、并发性能等,为了解决这一问题,Kubernetes 又为我们引入了一个新的资源对象:StorageClass,通过 StorageClass 的定义,管理员可以将存储资源定义为某种类型的资源,比如快速存储、慢速存储等,用户根据 StorageClass 的描述就可以非常直观的知道各种存储资源的具体特性了,这样就可以根据应用的特性去申请合适的存储资源了。
运行原理
要使用 StorageClass,我们就得安装对应的自动配置程序,比如我们这里存储后端使用的是 nfs,那么我们就需要使用到一个 nfs-client 的自动配置程序,我们也叫它 Provisioner(制备器),这个程序使用我们已经配置好的 nfs 服务器,来自动创建持久卷,也就是自动帮我们创建 PV。
- 自动创建的 PV 以{pvcName}-${pvName}这样的命名格式创建在 NFS 服务器上的共享数据目录中
- 而当这个 PV 被回收后会以archieved-{pvcName}-${pvName}这样的命名格式存在 NFS 服务器上。
StorageClass 资源
每个 StorageClass 都包含 provisioner、parameters 和 reclaimPolicy 字段, 这些字段会在 StorageClass 需要动态分配 PersistentVolume 时会使用到。
StorageClass 对象的命名很重要,用户使用这个命名来请求生成一个特定的类。 当创建 StorageClass 对象时,管理员设置 StorageClass 对象的命名和其他参数,一旦创建了对象就不能再对其更新。
管理员可以为没有申请绑定到特定 StorageClass 的 PVC 指定一个默认的存储类。
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: standard
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp2
reclaimPolicy: Retain
allowVolumeExpansion: true
mountOptions:
- debug
volumeBindingMode: Immediate
存储制备器
每个 StorageClass 都有一个制备器(Provisioner),用来决定使用哪个卷插件制备 PV。 该字段必须指定
表卷插件 | 内置制备器 | 配置例子 |
---|---|---|
AWSElasticBlockStore | ✓ | AWS EBS |
AzureFile | ✓ | Azure File |
AzureDisk | ✓ | Azure Disk |
CephFS | - | - |
Cinder | ✓ | OpenStack Cinder |
FC | - | - |
FlexVolume | - | - |
Flocker | ✓ | - |
GCEPersistentDisk | ✓ | GCE PD |
Glusterfs | ✓ | Glusterfs |
iSCSI | - | - |
Quobyte | ✓ | Quobyte |
NFS | - | - |
RBD | ✓ | Ceph RBD |
VsphereVolume | ✓ | vSphere |
PortworxVolume | ✓ | Portworx Volume |
ScaleIO ✓ ScaleIO | ||
StorageOS | ✓ | StorageOS |
Local | - | Local |
你不限于指定此处列出的 “内置” 制备器(其名称前缀为 “kubernetes.io” 并打包在 Kubernetes 中)。 你还可以运行和指定外部制备器,这些独立的程序遵循由 Kubernetes 定义的 规范。 外部供应商的作者完全可以自由决定他们的代码保存于何处、打包方式、运行方式、使用的插件(包括 Flex)等。 代码仓库 kubernetes-sigs/sig-storage-lib-external-provisioner 包含一个用于为外部制备器编写功能实现的类库。你可以访问代码仓库 kubernetes-sigs/sig-storage-lib-external-provisioner 了解外部驱动列表。
例如,NFS 没有内部制备器,但可以使用外部制备器。 也有第三方存储供应商提供自己的外部制备器。
后面会演示NFS
回收策略
由 StorageClass 动态创建的 PersistentVolume 会在类的 reclaimPolicy 字段中指定回收策略,可以是 Delete 或者 Retain。如果 StorageClass 对象被创建时没有指定 reclaimPolicy,它将默认为 Delete。
通过 StorageClass 手动创建并管理的 PersistentVolume 会使用它们被创建时指定的回收政策。
允许卷扩展
PersistentVolume 可以配置为可扩展。将此功能设置为 true 时,允许用户通过编辑相应的 PVC 对象来调整卷大小。
当下层 StorageClass 的 allowVolumeExpansion 字段设置为 true 时,以下类型的卷支持卷扩展。
卷类型 | Kubernetes 版本要求 |
---|---|
gcePersistentDisk | 1.11 |
awsElasticBlockStore | 1.11 |
Cinder | 1.11 |
glusterfs | 1.11 |
rbd | 1.11 |
Azure File | 1.11 |
Azure Disk | 1.11 |
Portworx | 1.11 |
FlexVolume | 1.13 |
CSI | 1.14 (alpha), 1.16 (beta) |
说明: 此功能仅可用于扩容卷,不能用于缩小卷。
挂载选项
由 StorageClass 动态创建的 PersistentVolume 将使用类中 mountOptions 字段指定的挂载选项。
如果卷插件不支持挂载选项,却指定了该选项,则制备操作会失败。 挂载选项在 StorageClass 和 PV 上都不会做验证,所以如果挂载选项无效,那么这个 PV 就会失败。
卷绑定模式
volumeBindingMode 字段控制了卷绑定和动态制备 应该发生在什么时候。
默认情况下,Immediate 模式表示一旦创建了 PersistentVolumeClaim 也就完成了卷绑定和动态制备。 对于由于拓扑限制而非集群所有节点可达的存储后端,PersistentVolume 会在不知道 Pod 调度要求的情况下绑定或者制备。
集群管理员可以通过指定 WaitForFirstConsumer 模式来解决此问题。 该模式将延迟 PersistentVolume 的绑定和制备,直到使用该 PersistentVolumeClaim 的 Pod 被创建。 PersistentVolume 会根据 Pod 调度约束指定的拓扑来选择或制备。这些包括但不限于 资源需求、 节点筛选器、 pod 亲和性和互斥性、 以及污点和容忍度。
以下插件支持动态供应的 WaitForFirstConsumer 模式:
- AWSElasticBlockStore
- GCEPersistentDisk
- AzureDisk
以下插件支持预创建绑定 PersistentVolume 的 WaitForFirstConsumer 模式: - 上述全部
- Local
动态配置和预先创建的 PV 也支持 CSI卷, 但是你需要查看特定 CSI 驱动程序的文档以查看其支持的拓扑键名和例子。
允许的拓扑结构
当集群操作人员使用了 WaitForFirstConsumer 的卷绑定模式, 在大部分情况下就没有必要将制备限制为特定的拓扑结构。 然而,如果还有需要的话,可以使用 allowedTopologies。
这个例子描述了如何将供应卷的拓扑限制在特定的区域,在使用时应该根据插件 支持情况替换 zone 和 zones 参数。
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: standard
provisioner: kubernetes.io/gce-pd
parameters:
type: pd-standard
volumeBindingMode: WaitForFirstConsumer
allowedTopologies:
- matchLabelExpressions:
- key: failure-domain.beta.kubernetes.io/zone
values:
- us-central1-a
- us-central1-b
实战演示
部署流程
搭建StorageClass + NFS,大致有以下几个步骤:
创建一个可用的NFS Server
创建Service Account,这是用来管控NFS Provisioner 在k8s集群中运行的权限
NFS provisioner是一个provisioner相关的插件,需要从网络上下载,我们已经下载下来放到镜像库中了
下载以后需要以pod方式运行通过deployment部署导入到本地环境中
创建StorageClass的时候需要指定provisioner
创建StorageClass,负责建立PVC并调用NFS provisioner进行预定的工作,并让PV与PVC建立关联
创建NFS provisioner,有两个功能,一个是在NFS共享目录下创建挂载点(volume),另一个则是建了PV并将PV与NFS的挂载点建立关联
创建Service Account
NFS Server在前面的章节中已经安装过了。
鉴权概述 官方文档
使用 RBAC 鉴权 官方文档
# rbac.yaml:#唯一需要修改的地方只有namespace,根据实际情况定义
apiVersion: v1
kind: ServiceAccount # 创建一个账户,主要用来管理NFS provisioner在k8s集群中运行的权限
metadata:
name: nfs-client-provisioner
namespace: default
---
kind: ClusterRole # 创建集群角色
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-client-provisioner-runner # 角色名
rules: # 角色权限
- 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"]
---
kind: ClusterRoleBinding # 集群角色绑定
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-client-provisioner
subjects: # 角色绑定对象
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: default
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 # 角色名
namespace: default # Role需要指定名称空间,ClusterRole 不需要
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
subjects: # 角色绑定对象
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: default
roleRef: # 绑定哪个角色
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.io
创建StorageClass,指定provisioner
#创建NFS资源的StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass # 创建StorageClass
metadata:
name: managed-nfs-storage
provisioner: qgg-nfs-storage #这里的名称要和provisioner配置文件中的环境变量PROVISIONER_NAME保持一致
parameters:
archiveOnDelete: "false"
# 创建NFS provisioner
apiVersion: apps/v1
kind: Deployment # 部署nfs-client-provisioner
metadata:
name: nfs-client-provisioner
labels:
app: nfs-client-provisioner
namespace: default #与RBAC文件中的namespace保持一致
spec:
replicas: 1
selector:
matchLabels:
app: nfs-client-provisioner
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner # 指定serviceAccount!
containers:
- name: nfs-client-provisioner
image: hub.kaikeba.com/java12/nfs-client-provisioner:v1 #镜像地址
volumeMounts: # 挂载数据卷到容器指定目录
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME # 配置provisioner的Name
value: qgg-nfs-storage # 确保该名称与 StorageClass 资源中的provisioner名称保持一致
- name: NFS_SERVER #绑定的nfs服务器
value: 192.168.66.13
- name: NFS_PATH #绑定的nfs服务器目录
value: /opt/k8s
volumes: # 申明nfs数据卷
- name: nfs-client-root
nfs:
server: 192.168.66.13
path: /opt/k8s
创建pod测试
创建pod,申明PVC进行测试
# 申明一个PVC,指定StorageClass
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: test-claim
annotations:
# 通过annotations注解,和storage-class进行关联,为什么不使用storageClassName,因为版本比较低
# 这里指定的名字就是上面创建的StorageClass的名字,让它帮我们创建PV
volume.beta.kubernetes.io/storage-class: "managed-nfs-storage"
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Mi
---
# 创建测试pod,查看是否可以正常挂载
kind: Pod
apiVersion: v1
metadata:
name: test-pod
spec:
containers:
- name: test-pod
image: busybox:1.24
command:
- "/bin/sh"
args:
- "-c"
- "touch /mnt/SUCCESS && exit 0 || exit 1" #创建一个SUCCESS文件后退出
volumeMounts:
- name: nfs-pvc # 挂载数据卷
mountPath: "/mnt"
restartPolicy: "Never"
volumes:
- name: nfs-pvc
persistentVolumeClaim: # 数据卷挂载的是pvc
claimName: test-claim #与PVC名称保持一致
测试是否会帮我自动创建pv:
看到自动创建了一个PV与PVC进行绑定。
清除环境
集合StatefulSet测试
StatefulSet+volumeClaimTemplates自动创建PV:
# StateFulDet+volumeClaimTemplates自动创建PV
---
apiVersion: v1
kind: Service
metadata:
name: nginx-headless
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: "nginx"
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: ikubernetes/myapp:v1
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates: # 通过模板化方式绑定
- metadata:
name: www # 指定pvc的名字
annotations:
volume.beta.kubernetes.io/storage-class: "managed-nfs-storage" #只指定了storageClass
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
看到是有序依次创建:
看到pvc是自动创建的,pv也是: