一、在k8s中为什么要做持久化存储?
在k8s中部署的应用都是以pod容器的形式运行的,假如我们部署MySQL、Redis等数据库,需要对这些数据库产生的数据做备份。因为Pod是有生命周期的,如果pod不挂载数据卷,那pod被删除或重启后这些数据会随之消失,如果想要长久的保留这些数据就要用到pod数据持久化存储。
二、k8s支持哪些存储?
2.1、k8s支持哪些存储?
通过帮助命令查看k8s支持哪些存储:
帮助命令:kubectl explain pods.spec.volumes
root@k8s-master:~# kubectl explain pod.spec.volumes |grep '<*>'
FIELD: volumes <[]Volume>
awsElasticBlockStore <AWSElasticBlockStoreVolumeSource>
azureDisk <AzureDiskVolumeSource>
azureFile <AzureFileVolumeSource>
cephfs <CephFSVolumeSource>
cinder <CinderVolumeSource>
configMap <ConfigMapVolumeSource>
csi <CSIVolumeSource>
downwardAPI <DownwardAPIVolumeSource>
emptyDir <EmptyDirVolumeSource>
ephemeral <EphemeralVolumeSource>
fc <FCVolumeSource>
flexVolume <FlexVolumeSource>
flocker <FlockerVolumeSource>
gcePersistentDisk <GCEPersistentDiskVolumeSource>
gitRepo <GitRepoVolumeSource>
glusterfs <GlusterfsVolumeSource>
hostPath <HostPathVolumeSource>
iscsi <ISCSIVolumeSource>
name <string> -required-
nfs <NFSVolumeSource>
persistentVolumeClaim <PersistentVolumeClaimVolumeSource>
photonPersistentDisk <PhotonPersistentDiskVolumeSource>
portworxVolume <PortworxVolumeSource>
projected <ProjectedVolumeSource>
quobyte <QuobyteVolumeSource>
rbd <RBDVolumeSource>
scaleIO <ScaleIOVolumeSource>
secret <SecretVolumeSource>
storageos <StorageOSVolumeSource>
vsphereVolume <VsphereVirtualDiskVolumeSource>
从上述帮助命令的结果看,k8s支持多种存储方式。
2.2、常用的存储方式
常用的存储方式:
- emptyDir
- hostPath
- nfs
- persistentVolumeClaim
- glusterfs
- cephfs
- configMap
- secret
如何使用存储卷?
需要经历如下步骤:
1、定义pod的volume,这个volume指明它要关联到哪个存储上的
2、在容器中要使用volumemounts挂载对应的存储
经过以上两步才能正确的使用存储卷。
三、常用存储方式介绍及举例
3.1、k8s持久化存储:emptyDir
图片中有些地方字母c不知为何打不出来,用空格代替的,需注意。
emptyDir类型的Volume是在Pod分配到Node上时被创建,Kubernetes会在Node上自动分配一个目录,因此无需指定宿主机Node上对应的目录文件。 这个目录的初始内容为空,当Pod从Node上移除时,emptyDir中的数据会被永久删除。emptyDir Volume主要用于某些应用程序无需永久保存的临时目录,多个容器的共享目录等。
创建一个pod,挂载临时目录emptyDir:
这里用这个挂载临时目录emptyDir的pod举例,介绍一下我学到的书写yaml文件的技巧,不需要死记硬背。以创建Pod为例,其他资源技巧一样。
首先这里我们明确是要创建一个pod资源,然后需要挂载一个临时目录。
第一步:
通过帮助命令查看pod资源下有哪些字段:kubectl explain pod
上面的帮助命令显示的是全部的帮助文档,主要是为了获得KIND、VERSION、GROUP等这几个值,一般在帮助命令的前几行,主要是为了确定字段apiVersion和kind这两个字段的值,这里我们可以确定apiVersion的值为v1;kind的值为Pod,必须是和帮助命令中的值是一样的有大小写的区分。
熟练之后我们不需要显示所有的帮助命令信息,只需要我们需要的信息就可以了。我们可以用以下命令简化帮助命令:
kubectl explain pod |grep '<*>'
, 过滤帮助命令输出的信息中包含<*>的行,因为这样的行基本都是资源的字段
通过上述帮助命令,我们可以开始写Pod的yaml文件,创建一个Eg-Pod.yaml
其中apiVersion的值为v1,kind的值为Pod。status的值不可修改,目前还用不到这个字段;另外只剩下两个字段spec和metadata。
第二步:
通过帮助命令查看Pod资源下metadata字段下有什么字段:
根据帮助命令,我们可以看到metadata字段下有多个子字段,不知道注意到没有?每个字段后面有个<>,这个我理解为字段的数据类型。比如:
name <string> # 我理解为字符串类型
finalizers <[]string> # 我理解为字符串列表类型
labels <map[string]string> # 我理解为键值对类型
generation <integer> # 我理解为整数类型
其他带[]的数据类型,我都理解为一种列表类型
不同的数据类型,在书写yaml的时候都有其书写技巧,不同的资源类型,对应的字段根据数据类型书写方式是一样的,一种弄懂了,其他的也是一样的。
字符串类型:字段名称: 字段值 # 字段名称直接用": "+字段的值
举例如下:
name: pod-emptydir # name字段是字符串类型,用冒号空格+字段值
字符串列表类型:字段名称:换行用"- "连接子字段,只有第一个子字段需要用"- "连接,多个子字段则与第一个子字段左对齐即可
举例如下:
ports: # ports字段是字符串列表类型
- name: http # 第一个字段name用"- "和父字段ports连接
containerPort: 80 # 第二个字段和第一个子字段name左对齐
整数类型:字段名称: 整数值 # 字段名称直接用": "+字段的整数值
举例如下:
replicas: 3 # replicas字段为整数类型,用冒号空格+字段整数值
键值对类型:字段名称:换行用缩进2个字符连接键值对,多个键值对与第一个键值对对齐
举例如下:
metadata:
labels: # labels字段是键值对类型
app: myapp # 第一个键值对和父字段缩进2个字符连接
name: nginx # 多个键值对和第一个键值对对齐
这里我们继续写yaml文件,继续写metadata字段的子字段,这里选取name和namespace,以及labels字段。
第三步:
通过帮助命令查看Pod资源下spec字段下有什么字段:
root@k8s-master:~/K8sStudy/Chapter2-10# kubectl explain pod.spec |grep '<*>'
FIELD: spec <PodSpec>
activeDeadlineSeconds <integer>
affinity <Affinity>
automountServiceAccountToken <boolean>
containers <[]Container> -required-
dnsConfig <PodDNSConfig>
dnsPolicy <string>
enableServiceLinks <boolean>
ephemeralContainers <[]EphemeralContainer>
hostAliases <[]HostAlias>
hostIPC <boolean>
hostNetwork <boolean>
hostPID <boolean>
hostUsers <boolean>
hostname <string>
imagePullSecrets <[]LocalObjectReference>
initContainers <[]Container>
nodeName <string>
nodeSelector <map[string]string>
os <PodOS>
overhead <map[string]Quantity>
preemptionPolicy <string>
priority <integer>
priorityClassName <string>
readinessGates <[]PodReadinessGate>
resourceClaims <[]PodResourceClaim>
restartPolicy <string>
runtimeClassName <string>
schedulerName <string>
schedulingGates <[]PodSchedulingGate>
securityContext <PodSecurityContext>
serviceAccount <string>
serviceAccountName <string>
setHostnameAsFQDN <boolean>
shareProcessNamespace <boolean>
subdomain <string>
"<hostname>.<subdomain>.<pod namespace>.svc.<cluster domain>". If not
terminationGracePeriodSeconds <integer>
tolerations <[]Toleration>
topologySpreadConstraints <[]TopologySpreadConstraint>
volumes <[]Volume>
通过帮助命令,我们可以看到spec字段下有很多个子字段,也是有字段值和数据类型,但是唯一值得注意的是containers <[]Container> -required-字段,这个字段后面有个required,表示这个字段是必须要写的,同时这个字段的数据类型中有个[],表示这个字段的数据类型是列表。这里我选去了spec的两个字段:containers <[]Container> -required-和volumes <[]Volume>,两个字段都是列表类型。
继续写yaml文件,spec字段下选取了两个字段containers和volumes。
继续查看containers字段下还有哪些子字段?
这里选取了4个子字段:
image: 字符串类型,配置镜像名称,直接冒号空格+镜像名称
imagePullPolicy: 字符串类型,镜像拉取策略,直接冒号空格+拉取策略。IfNotPresent,Nerver,Always等可选其一。
name: 不许字段必须写,字符串类型,直接冒号空格+名称
volumeMounts: 列表类型,子字段用"- "连接
继续查看volumeMounts字段下还有哪些字段?
继续写yaml文件,containers字段下选取了4个字段,image、imagePullPolicy、name、volumeMounts;其中volumeMounts字段下还有两个字段mountPath和name。
继续查看spec下volumes字段下还有哪些字段?
可以看到spec.volumes字段下有很多子字段,这里选取两个字段name和emptyDir。
name字段是指存储卷的名称,这个名称和spec.containers.volumeMounts.name的名称需要一致。
继续写yaml文件,spec.volumes字段下选取两个字段name和emptyDir。
至此,一个完整的Pod的资源清单文件就写完了。
查看一下完整的Pod 的yaml文件。
root@k8s-master:~/K8sStudy/Chapter2-10# cat Eg-Pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-empty
namespace: default
labels:
app: myapp
name: nginx
spec:
containers:
- name: nginx
image: docker.io/library/nginx:latest
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /usr/share/nginx/html
name: nginx
volumes:
- name: nginx
emptyDir: {}
应用/更新清单文件Eg-Pod.yaml:
root@k8s-master:~/K8sStudy/Chapter2-10# kubectl apply -f Eg-Pod.yaml
pod/pod-empty created
root@k8s-master:~/K8sStudy/Chapter2-10#
可以看到pod正常被创建并正常运行,所以yaml文件没有问题。
查看本机临时目录存在的位置,可用如下方法:
root@k8s-master:~/K8sStudy/Chapter2-10# kubectl get pods pod-empty -o yaml | grep uid
uid: a732f3a6-9942-4708-8913-9a73dcd5a815
root@k8s-master:~/K8sStudy/Chapter2-10#
在node节点的/var/lib/kubelet/pods/a732f3a6-9942-4708-8913-9a73dcd5a815即为临时目录的路径。
root@k8s-node:/var/lib/kubelet/pods/a732f3a6-9942-4708-8913-9a73dcd5a815# tree ./
./
├── containers
│ └── nginx
│ └── 169e73d7
├── etc-hosts
├── plugins
│ └── kubernetes.io~empty-dir
│ ├── nginx
│ │ └── ready
│ └── wrapped_kube-api-access-zdv5g
│ └── ready
└── volumes
├── kubernetes.io~empty-dir
│ └── nginx
└── kubernetes.io~projected
└── kube-api-access-zdv5g
├── ca.crt -> ..data/ca.crt
├── namespace -> ..data/namespace
└── token -> ..data/token
11 directories, 7 files
root@k8s-node:/var/lib/kubelet/pods/a732f3a6-9942-4708-8913-9a73dcd5a815# cd volumes/kubernetes.io~empty-dir/nginx/
root@k8s-node:/var/lib/kubelet/pods/a732f3a6-9942-4708-8913-9a73dcd5a815/volumes/kubernetes.io~empty-dir/nginx# echo aaa > index.html
root@k8s-node:/var/lib/kubelet/pods/a732f3a6-9942-4708-8913-9a73dcd5a815/volumes/kubernetes.io~empty-dir/nginx#
在node节点上找到了临时目录的路径/var/lib/kubelet/pods/a732f3a6-9942-4708-8913-9a73dcd5a815/volumes/kubernetes.io~empty-dir/nginx。用echo命令创建了一个内容为aaa的index.html文件,也就是Nginx默认网页。
再次访问pod的80端口,可以看到刚才在node节点上创建的内容为aaa的网页内容了。
3.2、k8s持久化存储:hostPath
hostPath Volume是指Pod挂载宿主机上的目录或文件。 hostPath Volume使得容器可以使用宿主机的文件系统进行存储,hostpath(宿主机路径):节点级别的存储卷,在pod被删除,这个存储卷还是存在的,不会被删除,所以只要同一个pod被调度到同一个节点上来,在pod被删除重新被调度到这个节点之后,对应的数据依然是存在的。
1、查看hostPath存储卷的用法:kubectl explain pod.spec.volumes.hostPaht
#把tomcat.tar.gz上传到xianchaonode2和xianchaonode1上,手动解压:
[root@xianchaonode2 ~]# ctr -n=k8s.io images import tomcat.tar.gz
[root@xianchaonode1 ~]# ctr -n=k8s.io images import tomcat.tar.gz
2、创建一个pod,挂载hostPath存储卷
查看yaml清单文件Eg-Pod-hostPath.yaml
root@k8s-master:~/K8sStudy/Chapter2-10# cat Eg-Pod-hostPath.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-volumes-hostpath
namespace: default
labels:
app: mynginx
name: volumes-hostpath
spec:
containers:
- name: nginx
image: docker.io/library/nginx:latest
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /usr/share/nginx/html # 将存储卷挂载在pod中的挂载点
name: nginx # 挂载的卷为nginx,和下方定义的存储卷的名称一致,表示挂载下方的定义的卷
volumes:
- name: nginx # 创建的卷的名称为nginx
hostPath: # 存储方式为hostPath
path: /mnt/nginx # hostPath在宿主机的路径,绝对路径
type: DirectoryOrCreate # 存储路径为目录,调度的节点上有则直接用,不存在则创建
注意:DirectoryOrCreate表示本地有/data1目录,就用本地的,本地没有就会在pod调度到的节点自动创建一个。
应用/更新清单文件Eg-Pod-hostPath.yaml
root@k8s-master:~/K8sStudy/Chapter2-10# kubectl apply -f Eg-Pod-hostPath.yaml
pod/pod-volumes-hostpath created
root@k8s-master:~/K8sStudy/Chapter2-10#
查看创建的pod的状态:
请求pod中的web站点
发现站点报403错误,原因是我们挂载的 /mnt/nginx是个空目录,挂载到pod中的/usr/share/nginx/html,将默认的网站页面index.html给覆盖掉了,nginx找不到网页页面则报403错误,我们可以再node节点的/mnt/nginx目录中创建一个index.html文件,内容为pod-volumes-hostPath。然后再请求pod中的web站点,看看是否能得到网页结果内容是pod-volumes-hostPath。
通过上述结果,可确认,node节点的/mnt/nginx被创建出来了,默认/mnt下是没有nginx这个目录的,而且该路径确实是挂载到了pod中的/usr/share/nginx/html。
模拟删除pod,看看新生成的pod的web站点的请求结果是否还是pod-volumes-hostPath。如果是的话,就能证明hostPath的存储方式在pod被删除情况下,数据是不会删除的。
结果证明hostPath的存储方式在pod被删除情况下,数据是不会删除的。
hostpath存储卷缺点:
- 单节点
- pod删除之后重新创建必须调度到同一个node节点,数据才不会丢失
3.3、k8s持久化存储:nfs
NFS:网络文件系统,英文Network File System(NFS),是由SUN公司研制的UNIX表示层协议(presentation layer protocol),能使使用者访问网络上别处的文件就像在使用自己的计算机一样。
1、搭建nfs服务
以k8s的控制节点作为NFS服务端,我这里是Ubuntu系统
# 安装nfs服务
root@k8s-master:~/K8sStudy/Chapter2-10# apt-get -y install nfs-kernel-server
# 创建nfs数据目录
root@k8s-master:~/K8sStudy/Chapter2-10# mkdir -pv /data/volumes-nfs
mkdir: created directory '/data'
mkdir: created directory '/data/volumes-nfs'
# 配置创建的数据目录为nfs的共享目录
root@k8s-master:~/K8sStudy/Chapter2-10# cat /etc/exports
# /etc/exports: the access control list for filesystems which may be exported
# to NFS clients. See exports(5).
#
# Example for NFSv2 and NFSv3:
# /srv/homes hostname1(rw,sync,no_subtree_check) hostname2(ro,sync,no_subtree_check)
#
# Example for NFSv4:
# /srv/nfs4 gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check)
# /srv/nfs4/homes gss/krb5i(rw,sync,no_subtree_check)
#
/data/volumes-nfs *(rw,no_root_squash)
# 重启nfs服务,并设置开机启动
root@k8s-master:~/K8sStudy/Chapter2-10# systemctl enable nfs-server.service --now
# 查看nfs服务状态
root@k8s-master:~/K8sStudy/Chapter2-10#
root@k8s-master:~/K8sStudy/Chapter2-10# systemctl status nfs-server.service
● nfs-server.service - NFS server and services
Loaded: loaded (/lib/systemd/system/nfs-server.service; enabled; vendor preset: enabled)
Drop-In: /run/systemd/generator/nfs-server.service.d
└─order-with-mounts.conf
Active: active (exited) since Mon 2024-05-20 11:31:03 CST; 4s ago
Process: 180437 ExecStartPre=/usr/sbin/exportfs -r (code=exited, status=0/SUCCESS)
Process: 180438 ExecStart=/usr/sbin/rpc.nfsd (code=exited, status=0/SUCCESS)
Main PID: 180438 (code=exited, status=0/SUCCESS)
CPU: 7ms
May 20 11:31:03 k8s-master systemd[1]: Starting NFS server and services...
May 20 11:31:03 k8s-master exportfs[180437]: exportfs: /etc/exports [1]: Neither 'subtree_check' or 'no_subtree_check' specified for export "*:/data/volu>
May 20 11:31:03 k8s-master exportfs[180437]: Assuming default behaviour ('no_subtree_check').
May 20 11:31:03 k8s-master exportfs[180437]: NOTE: this default has changed since nfs-utils version 1.0.x
May 20 11:31:03 k8s-master systemd[1]: Finished NFS server and services.
root@k8s-master:~/K8sStudy/Chapter2-10#
nfs服务配置文件注解:
rw 该主机对该共享目录有读写权限;no_root_squash 登入 NFS 主机使用分享目录的使用者,如果是 root 的话,那么对于这个分享的目录来说,他就具有 root 的权限。
查看nfs配置是否生效:showmount -e 192.168.60.140
, 192.168.60.140是nfs服务的服务器地址,这里是master的物理ip地址。另外有一条命令是nfs的配置生效 exportfs -arv
root@k8s-master:~/K8sStudy/Chapter2-10# showmount -e 192.168.60.140
Export list for 192.168.60.140:
/data/volumes-nfs * # 可以看到配置的共享目录
root@k8s-master:~/K8sStudy/Chapter2-10#
在node节点上尝试挂载nfs共享目录,验证nfs服务是否正常可用。
# 安装nfs客户端
root@k8s-node:~# apt-get -y install nfs-kernel-server
# 挂载nfs共享目录
root@k8s-node:~# mount 192.168.60.140:/data/volumes-nfs /mnt
# 确认是否挂载
root@k8s-node:~# df -h |grep 'volumes-nfs'
192.168.60.140:/data/volumes-nfs 14G 9.3G 3.8G 72% /mnt # 表示挂载成功
# 取消挂载
root@k8s-node:~# umount /mnt
root@k8s-node:~# df -h |grep 'volumes-nfs'
root@k8s-node:~# # 取消挂载成功
挂载成功。nfs服务可用。
2、创建Pod,挂载NFS共享出来的目录
查看Pod使用nfs存储卷的用法:kubectl explain pod.spec.volumes.nfs
查看yaml资源清单文件:Eg-Pod-nfs.yaml
root@k8s-master:~/K8sStudy/Chapter2-10# cat Eg-Pod-nfs.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-volumes-nfs
namespace: default
labels:
app: mynginx
name: volumes-nfs
spec:
containers:
- name: nginx
image: docker.io/library/nginx:latest
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /usr/share/nginx/html
name: nginx
volumes:
- name: nginx
nfs: # 存储卷方式为nfs
path: /data/volumes-nfs # nfs共享目录
server: 192.168.60.140 # nfs服务器ip地址
注:path: /data/volumes-nfs #nfs的共享目录
server:192.168.60.140是master机器的ip,这个是安装nfs服务的地址
更新资源清单文件Eg-Pod-nfs.yaml
root@k8s-master:~/K8sStudy/Chapter2-10# kubectl apply -f Eg-Pod-nfs.yaml
pod/pod-volumes-nfs created
root@k8s-master:~/K8sStudy/Chapter2-10#
查看pod状态
请求pod中的web站点
发现站点报403错误,原因是我们挂载的192.168.60.140:/data/volumes-nfs是个空目录,挂载到pod中的/usr/share/nginx/html,将默认的网站页面index.html给覆盖掉了,nginx找不到网页页面则报403错误,我们可以在nfs(这里也就是master)的/data/volumes-nfs目录中创建一个index.html文件,内容为pod-volumes-nfs。然后再请求pod中的web站点,看看是否能得到网页结果内容是pod-volumes-nfs。
通过上述结果,可确认,nfs共享的数据目录确实是挂载到了pod中的/usr/share/nginx/html。
上面说明挂载nfs存储卷成功了,nfs支持多个客户端挂载,可以创建多个pod,挂载同一个nfs服务器共享出来的目录;但是nfs如果宕机了,数据也就丢失了,所以需要使用分布式存储,常见的分布式存储有glusterfs和cephfs
3.4、k8s持久化存储:PVC
3.4.1、k8s PV是什么?
PersistentVolume(PV)是群集中的一块存储,由管理员配置或使用存储类动态配置。 它是集群中的资源,就像pod是k8s集群资源一样。 PV是容量插件,如Volumes,其生命周期独立于使用PV的任何单个pod。
3.4.2、k8s PVC是什么?
PersistentVolumeClaim(PVC)是一个持久化存储卷,我们在创建pod时可以定义这个类型的存储卷。 它类似于一个pod。 Pod消耗节点资源,PVC消耗PV资源。 Pod可以请求特定级别的资源(CPU和内存)。 pvc在申请pv的时候也可以请求特定的大小和访问模式(例如,可以一次读写或多次只读)。
3.4.3、k8s PVC和PV工作原理
PV是群集中的资源。 PVC是对这些资源的请求。
PV和PVC之间的相互作用遵循以下生命周期:
- pv的供应方式
可以通过两种方式配置PV:静态或动态。
- 静态的:集群管理员创建了许多PV。它们包含可供群集用户使用的实际存储的详细信息。它们存在于Kubernetes API中,可供使用。
- 动态的:当管理员创建的静态PV都不匹配用户的PersistentVolumeClaim时,群集可能会尝试为PVC专门动态配置卷。此配置基于StorageClasses,PVC必须请求存储类,管理员必须创建并配置该类,以便进行动态配置。
- 绑定
用户创建pvc并指定需要的资源和访问模式。在找到可用pv之前,pvc会保持未绑定状态
- 使用
- 需要找一个存储服务器,把它划分成多个存储空间;
- k8s管理员可以把这些存储空间定义成多个pv;
- 在pod中使用pvc类型的存储卷之前需要先创建pvc,通过定义需要使用的pv的大小和对应的访问模式,找到合适的pv;
- pvc被创建之后,就可以当成存储卷来使用了,我们在定义pod时就可以使用这个pvc的存储卷
- pvc和pv它们是一一对应的关系,pv如果被pvc绑定了,就不能被其他pvc使用了;
- 我们在创建pvc的时候,应该确保和底下的pv能绑定,如果没有合适的pv,那么pvc就会处于pending状态。
- 回收策略
当我们创建pod时如果使用pvc做为存储卷,那么它会和pv绑定,当删除pod,pvc和pv绑定就会解除,解除之后和pvc绑定的pv卷里的数据需要怎么处理,目前,卷可以保留,回收或删除:Retain、Recycle (不推荐使用,1.15可能被废弃了)、Delete
- Retain:当删除pvc的时候,pv仍然存在,处于released状态,但是它不能被其他pvc绑定使用,里面的数据还是存在的,当我们下次再使用的时候,数据还是存在的,这个是默认的回收策略
- Delete:删除pvc时即会从Kubernetes中移除PV,也会从相关的外部设施中删除存储资产
3.4.4、创建pod,使用pvc作为持久化存储卷
1、创建nfs共享目录
# 在宿主机master创建NFS需要的共享目录/data/volumes/v{1..3}
root@k8s-master:~# mkdir -pv /data/volumes/v{1..3}
mkdir: created directory '/data/volumes/v1'
mkdir: created directory '/data/volumes/v2'
mkdir: created directory '/data/volumes/v3'
# 将创建的共享目录通过nfs共享出去。修改配置文件如下
root@k8s-master:~# cat /etc/exports
# /etc/exports: the access control list for filesystems which may be exported
# to NFS clients. See exports(5).
#
# Example for NFSv2 and NFSv3:
# /srv/homes hostname1(rw,sync,no_subtree_check) hostname2(ro,sync,no_subtree_check)
#
# Example for NFSv4:
# /srv/nfs4 gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check)
# /srv/nfs4/homes gss/krb5i(rw,sync,no_subtree_check)
#
/data/volumes-nfs *(rw,no_root_squash)
/data/volumes/v1 *(rw,no_root_squash)
/data/volumes/v2 *(rw,no_root_squash)
/data/volumes/v3 *(rw,no_root_squash)
# 使配置文件生效
root@k8s-master:~# exportfs -arv
exportfs: /etc/exports [1]: Neither 'subtree_check' or 'no_subtree_check' specified for export "*:/data/volumes-nfs".
Assuming default behaviour ('no_subtree_check').
NOTE: this default has changed since nfs-utils version 1.0.x
exportfs: /etc/exports [2]: Neither 'subtree_check' or 'no_subtree_check' specified for export "*:/data/volumes/v1".
Assuming default behaviour ('no_subtree_check').
NOTE: this default has changed since nfs-utils version 1.0.x
exportfs: /etc/exports [3]: Neither 'subtree_check' or 'no_subtree_check' specified for export "*:/data/volumes/v2".
Assuming default behaviour ('no_subtree_check').
NOTE: this default has changed since nfs-utils version 1.0.x
exportfs: /etc/exports [4]: Neither 'subtree_check' or 'no_subtree_check' specified for export "*:/data/volumes/v3".
Assuming default behaviour ('no_subtree_check').
NOTE: this default has changed since nfs-utils version 1.0.x
exporting *:/data/volumes/v3
exporting *:/data/volumes/v2
exporting *:/data/volumes/v1
exporting *:/data/volumes-nfs
# 查看nfs服务所有的共享目录
root@k8s-master:~# showmount -e 192.168.60.140
Export list for 192.168.60.140:
/data/volumes/v3 *
/data/volumes/v2 *
/data/volumes/v1 *
/data/volumes-nfs *
root@k8s-master:~#
2、如何编写pv的资源清单文件
查看定义pv需要的字段:kubectl explain pv
root@k8s-master:~# kubectl explain pv |grep '<*>'
apiVersion <string>
kind <string>
metadata <ObjectMeta>
spec <PersistentVolumeSpec>
status <PersistentVolumeStatus>
查看定义pv的spec需要哪些字段:kubectl explain pv.spec
root@k8s-master:~/K8sStudy/Chapter2-10# kubectl explain pv.spec |grep '<*>'
FIELD: spec <PersistentVolumeSpec>
accessModes <[]string>
awsElasticBlockStore <AWSElasticBlockStoreVolumeSource>
azureDisk <AzureDiskVolumeSource>
azureFile <AzureFilePersistentVolumeSource>
capacity <map[string]Quantity>
cephfs <CephFSPersistentVolumeSource>
cinder <CinderPersistentVolumeSource>
claimRef <ObjectReference>
csi <CSIPersistentVolumeSource>
fc <FCVolumeSource>
flexVolume <FlexPersistentVolumeSource>
flocker <FlockerVolumeSource>
gcePersistentDisk <GCEPersistentDiskVolumeSource>
glusterfs <GlusterfsPersistentVolumeSource>
hostPath <HostPathVolumeSource>
iscsi <ISCSIPersistentVolumeSource>
local <LocalVolumeSource>
mountOptions <[]string>
nfs <NFSVolumeSource>
nodeAffinity <VolumeNodeAffinity>
persistentVolumeReclaimPolicy <string>
photonPersistentDisk <PhotonPersistentDiskVolumeSource>
portworxVolume <PortworxVolumeSource>
quobyte <QuobyteVolumeSource>
rbd <RBDPersistentVolumeSource>
scaleIO <ScaleIOPersistentVolumeSource>
storageClassName <string>
storageos <StorageOSPersistentVolumeSource>
volumeMode <string>
vsphereVolume <VsphereVirtualDiskVolumeSource>
查看定义nfs类型的pv需要的字段:kubectl explain pv.spec.nfs
root@k8s-master:~# kubectl explain pv.spec.nfs |grep '<*>'
FIELD: nfs <NFSVolumeSource>
path <string> -required-
readOnly <boolean>
server <string> -required-
3、创建pv,查看yaml清单文件
root@k8s-master:~/K8sStudy/Chapter2-10# cat Eg-pv-nfs.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-v1
namespace: default
labels:
app: v1
spec:
accessModes: ["ReadWriteOnce"]
nfs:
path: /data/volumes/v1
server: 192.168.60.140
capacity:
storage: 20M
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-v2
namespace: default
labels:
app: v2
spec:
accessModes: ["ReadOnlyMany"]
nfs:
path: /data/volumes/v2
server: 192.168.60.140
capacity:
storage: 20M
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-v3
namespace: default
labels:
app: v3
spec:
accessModes: ["ReadWriteMany"]
nfs:
path: /data/volumes/v3
server: 192.168.60.140
capacity:
storage: 20M
应用/更新yaml资源清单文件:Eg-pv-nfs.yaml
root@k8s-master:~/K8sStudy/Chapter2-10# kubectl apply -f Eg-pv-nfs.yaml
persistentvolume/pv-v1 created
persistentvolume/pv-v2 created
persistentvolume/pv-v3 created
查看创建的pv:kubectl get pv
注解:RWO:readwariteonce: 单路读写,允许同一个node节点上的pod访问
ROX: readonlymany: 多路只读,允许不通node节点的pod以只读方式访问
RWX:readwritemany: 多路读写,允许不同的node节点的pod以读写方式访问
4、创建pvc,和符合条件的pv绑定
查看定义pvc需要的字段:kubectl explain pvc
root@k8s-master:~/K8sStudy/Chapter2-10# kubectl explain pvc |grep '<*>'
apiVersion <string>
kind <string>
metadata <ObjectMeta>
spec <PersistentVolumeClaimSpec>
status <PersistentVolumeClaimStatus>
查看定义pvc.spec需要的字段:kubectl explain pvc.spec
root@k8s-master:~/K8sStudy/Chapter2-10# kubectl explain pvc.spec |grep '<*>'
FIELD: spec <PersistentVolumeClaimSpec>
accessModes <[]string>
dataSource <TypedLocalObjectReference>
dataSourceRef <TypedObjectReference>
resources <ResourceRequirements>
selector <LabelSelector>
storageClassName <string>
volumeMode <string>
volumeName <string>
创建pv,查看yaml清单文件Eg-pvc.yaml
root@k8s-master:~/K8sStudy/Chapter2-10# cat Eg-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-v1
namespace: default
spec:
accessModes: ["ReadWriteOnce"]
selector:
matchLabels:
app: v1
resources:
requests:
storage: 20M
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-v2
namespace: default
spec:
accessModes: ["ReadOnlyMany"]
selector:
matchLabels:
app: v2
resources:
requests:
storage: 20M
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-v3
namespace: default
spec:
accessModes: ["ReadWriteMany"]
selector:
matchLabels:
app: v3
resources:
requests:
storage: 20M
应用/更新资源清单文件:kubectl apply -f Eg-pvc.yaml
root@k8s-master:~/K8sStudy/Chapter2-10# kubectl apply -f Eg-pvc.yaml
persistentvolumeclaim/pvc-v1 created
persistentvolumeclaim/pvc-v2 created
persistentvolumeclaim/pvc-v3 created
查看创建的pvc:kubectl get pvc
看到pvc的status都是bound状态,就说明pvc跟pv已经绑定了
5、创建pod,挂载pvc
查看pod使用pvc作为存储卷需要的字段:kubectl explain pod.spec.volumes.persistentVolumeClaim
root@k8s-master:~/K8sStudy/Chapter2-10# kubectl explain pod.spec.volumes.persistentVolumeClaim |grep '<*>'
FIELD: persistentVolumeClaim <PersistentVolumeClaimVolumeSource>
claimName <string> -required-
readOnly <boolean>
查看yaml资源清单文件:Eg-Pod-pvc.yaml
root@k8s-master:~/K8sStudy/Chapter2-10# cat Eg-Pod-pvc.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pod-pvc-test
spec:
replicas: 2
selector:
matchLabels:
storage: pvc
template:
metadata:
labels:
storage: pvc
spec:
containers:
- name: nginx
image: docker.io/library/nginx:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
protocol: TCP
volumeMounts:
- name: nginx-html
mountPath: /usr/share/nginx/html
volumes:
- persistentVolumeClaim:
claimName: pvc-v1
name: nginx-html
应用/更新yaml资源清单文件:Eg-Pod-pvc.yaml
root@k8s-master:~/K8sStudy/Chapter2-10# kubectl apply -f Eg-Pod-pvc.yaml
deployment.apps/pod-pvc-test created
查看创建的Deployment和pod
验证pvc:请求pod中的web服务,也就是Deployment中容器镜像为nginx
发现站点报403错误,原因是我们挂载的pvc绑定的pvc是个空目录,挂载到pod中的/usr/share/nginx/html,将默认的网站页面index.html给覆盖掉了,nginx找不到网页页面则报403错误,我们可以在pvc绑定的pv-1(这里是master主机上/data/volumes/v1)目录中创建一个index.html文件,内容为pod-volumes-pvc-v1。然后再请求pod中的web站点,看看是否能得到网页结果内容是pod-volumes-pvc-v1。
上述结果证明,pod通过挂载pvc可以将数据存储到pv中。
注:使用pvc和pv的注意事项
1、我们每次创建pvc的时候,需要事先有划分好的pv,这样可能不方便,那么可以在创建pvc的时候直接动态创建一个pv这个存储类,pv事先是不存在的。
2、pvc和pv绑定,如果使用默认的回收策略retain,那么删除pvc之后,pv会处于released状态,我们想要继续使用这个pv,需要手动删除pv,kubectl delete pv pv_name,删除pv,不会删除pv里的数据,当我们重新创建pvc时还会和这个最匹配的pv绑定,数据还是原来数据,不会丢失。
经过测试,如果回收策略是Delete,删除pv,pv后端存储的数据也不会被删除
回收策略:persistentVolumeReclaimPolicy字段
3.4.5、删除pvc的步骤
需要先删除使用pvc的pod,再删除pvc。
演示删除pvc:
直接删除在使用中的pvc,会卡住删不掉。
需要先删除使用pvc的pod或者控制器。
上述结果证明,删除pvc前需要删除正在使用pvc资源的其他资源如Pod或者其他控制器。