容器内的目录和宿主机的目录进行挂载。
容器在系统上的生命周期是短暂的,k8s用控制创建的pod,delete相当于重启,容器的状态也会回复到初始状态。一旦回到初始状态,所有的后来编辑的文件都会消失。
所以需要在容器和节点之间创建一个可以持久化保存容器内文件的存储卷。即使容器被销毁,删除,重启,节点上的存储卷的数据依然存在,后续也I可以继续使用。可以继续将容器内目录和宿主机挂载,继续使用保存的数据。
emptyDir存储卷
当Pod被分配给节点时,首先创建emptyDir卷,并且只要该Pod在该节点上运行,该卷就会存在。正如卷的名字所述,它最初是空的。Pod 中的容器可以读取和写入emptyDir卷中的相同文件,尽管该卷可以挂载到每个容器中的相同或不同路径上。当出于任何原因从节点中删除 Pod 时,emptyDir中的数据将被永久删除。
emptyDir可实现Pod中的容器之间共享目录数据,但是emptyDir卷不能持久化数据,会随着Pod生命周期结束而一起删除。
mkdir /opt/volumes
cd /opt/volumes
vim pod-emptydir.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-emptydir
namespace: default
labels:
app: myapp
tier: frontend
spec:
containers:
- name: myapp
image: nginx:1.14
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
#定义容器挂载内容
volumeMounts:
#使用的存储卷名称,如果跟下面volume字段name值相同,则表示使用volume的这个存储卷
- name: html
#挂载至容器中哪个目录
mountPath: /usr/share/nginx/html/
- name: busybox
image: busybox:latest
imagePullPolicy: IfNotPresent
volumeMounts:
- name: html
#在容器内定义挂载存储名称和挂载路径
mountPath: /data/
command: ['/bin/sh','-c','while true;do echo $(date) >> /data/index.html;sleep 2;done']
#定义存储卷
volumes:
#定义存储卷名称
- name: html
#定义存储卷类型
emptyDir: {}
kubectl apply -f pod-emptydir.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-emptydir
namespace: default
labels:
app: myapp
tier: frontend
spec:
containers:
- name: myapp
image: nginx:1.14
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html/
- name: busybox
image: busybox:latest
imagePullPolicy: IfNotPresent
volumeMounts:
- name: html
mountPath: /data/
command: ['/bin/sh','-c','while true;do echo $(date) >> /data/index.html;sleep 2;done']
volumes:
- name: html
emptyDir: {}

kubectl exec -it pod-emptydir -c myapp bash
cd /usr/share/nginx/html
/usr/share/nginx/html# cat index.html




apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx:1.22
name: nginx1
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
- image: nginx:1.22
name: nginx2
volumeMounts:
- name: html
mountPath: /data
command: ["/bin/bash", "-c", "while true; do echo $(date) >> /data/index.html; sleep 2; done"]
volumes:
- name: html
emptyDir: {}
hostPath存储卷
hostPath卷将 node 节点的文件系统中的文件或目录挂载到集群中。
hostPath可以实现持久存储,但是在node节点故障时,也会导致数据的丢失
把Node节点上的目录/文件挂载到容器中,可实现持久化数据存储。但是存储空间会受到Node节点的单机限制,Node节点故障数据就会丢失,且Pod不能实现跨节点共享数据
污点设置为NoExecute时,节点上的pod会被驱逐,文件数据在不在?pod被驱逐,并不是node节点被销毁。所有数据还保留在节点上。pod被驱逐(基于控制器创建的)会在其他节点重新部署,又会在其他节点生成一个新的存储卷。数据依然可以持久化。
emptyDir的共享数据,会丢失。
//在 node01 节点上创建挂载目录
mkdir -p /data/pod/volume1
echo 'this is node01' > /data/pod/volume1/index.html
//在 node02 节点上创建挂载目录
mkdir -p /data/pod/volume1
echo 'this is node02' > /data/pod/volume1/index.html
//创建 Pod 资源
vim pod-hostpath.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-hostpath
namespace: default
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
#定义容器挂载内容
volumeMounts:
#使用的存储卷名称,如果跟下面volume字段name值相同,则表示使用volume的这个存储卷
- name: html
#挂载至容器中哪个目录
mountPath: /usr/share/nginx/html
#读写挂载方式,默认为读写模式false
readOnly: false
#volumes字段定义了paues容器关联的宿主机或分布式文件系统存储卷
volumes:
#存储卷名称
- name: html
#路径,为宿主机存储路径
hostPath:
#在宿主机上目录的路径
path: /data/pod/volume1
#定义类型,这表示如果宿主机没有此目录则会自动创建
type: DirectoryOrCreate
apiVersion: v1
kind: Pod
metadata:
name: pod-hostpath
namespace: default
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
readOnly: false
volumes:
- name: html
hostPath:
path: /data/pod/volume1
type: DirectoryOrCreate
kubectl apply -f pod-hostpath.yaml
kubectl get pods -o wide


apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx:1.22
name: nginx1
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
- image: nginx:1.22
name: nginx2
volumeMounts:
- name: html
mountPath: /data
command: ["/bin/bash", "-c", "while true; do echo $(date) >> /data/index.html; sleep 2; done"]
volumes:
- name: html
hostPath:
path: /opt/test
type: DirectoryOrCreate
mkdir /opt/volumes
cd /opt/volumes
root@master volumes\]# vim hostPath-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: hostpath-pod-demo
namespace: default
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
hostPath:
path: /data/pod/volume1
type: DirectoryOrCreate
\[root@node01 \~\]# mkdir -p /data/pod/volume1
\[root@node01 \~\]# vim /data/pod/volume1/index.html
node1
\[root@node02 \~\]# mkdir -p /data/pod/volume1
\[root@node02 \~\]# vim /data/pod/volume1/index.html
node2
\[root@master volumes\]# kubectl apply -f hostPath-demo.yaml
pod/hostpath-pod-demo created
\[root@master volumes\]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
hostpath-pod-demo 1/1 Running 0 38s 10.101.231.178 node02 \
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: ["list", "watch", "create", "update", "patch"]
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]
#集群角色绑定
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: nfs-client-provisioner-clusterrolebinding
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: default
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-clusterrole
apiGroup: rbac.authorization.k8s.io
kubectl apply -f nfs-client-rbac.yaml

使用 Deployment 来创建 NFS Provisioner
vim /etc/kubernetes/manifests/kube-apiserver.yaml
spec:
containers:
-
command:
-
kube-apiserver
-
--feature-gates=RemoveSelfLink=false #添加这一行
-
--advertise-address=192.168.73.105
......
kubectl apply -f /etc/kubernetes/manifests/kube-apiserver.yaml
kubectl delete pods kube-apiserver -n kube-system
kubectl get pods -n kube-system | grep apiserver


#创建 NFS Provisioner
vim nfs-client-provisioner.yaml
kind: Deployment
apiVersion: apps/v1
metadata:
name: nfs-client-provisioner
spec:
replicas: 1
selector:
matchLabels:
app: nfs-client-provisioner
strategy:
type: Recreate
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner #指定Service Account账户
containers:
- name: nfs-client-provisioner
image: quay.io/external_storage/nfs-client-provisioner:latest
imagePullPolicy: IfNotPresent
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: nfs-storage #配置provisioner的Name,确保该名称与StorageClass资源中的provisioner名称保持一致
- name: NFS_SERVER
value: stor01 #配置绑定的nfs服务器
- name: NFS_PATH
value: /opt/k8s #配置绑定的nfs服务器目录
volumes: #申明nfs数据卷
- name: nfs-client-root
nfs:
server: stor01
path: /opt/k8s
kubectl apply -f nfs-client-provisioner.yaml


创建 StorageClass,负责建立 PVC 并调用 NFS provisioner 进行预定的工作,并让 PV 与 PVC 建立关联
vim nfs-client-storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-client-storageclass
provisioner: nfs-storage #这里的名称要和provisioner配置文件中的环境变量PROVISIONER_NAME保持一致
parameters:
archiveOnDelete: "false" #false表示在删除PVC时不会对数据目录进行打包存档,即删除数据;为ture时就会自动对数据目录进行打包存档,存档文件以archived开头
kubectl apply -f nfs-client-storageclass.yaml


创建 PVC 和 Pod 测试
vim test-pvc-pod.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-nfs-pvc
#annotations: volume.beta.kubernetes.io/storage-class: "nfs-client-storageclass" #另一种SC配置方式
spec:
accessModes:
- ReadWriteMany
storageClassName: nfs-client-storageclass #关联StorageClass对象
resources:
requests:
storage: 1Gi
apiVersion: v1
kind: Pod
metadata:
name: test-storageclass-pod
spec:
containers:
- name: busybox
image: busybox:latest
imagePullPolicy: IfNotPresent
command:
-
"/bin/sh"
-
"-c"
args:
- "sleep 3600"
volumeMounts:
- name: nfs-pvc
mountPath: /mnt
restartPolicy: Never
volumes:
- name: nfs-pvc
persistentVolumeClaim:
claimName: test-nfs-pvc #与PVC名称保持一致
kubectl apply -f test-pvc-pod.yaml




总结
静态PV的使用:
1、准备好存储设备和共享目录
2、创建PV资源,配置存储卷类型、访问模式(RWX,RWO,ROX)、存储能力大小
3、创建PVC资源,配置请求PV资源的访问模式和存储大小,绑定PV(一个PV只能绑定一个PVC)
4、创建Pod资源挂载PVC,配置存储卷类型为persistentVolumeClaim,在容器配置中定义存储卷挂载内容
StorageClass 简称 SC 资源 实现动态创建 PV 资源
StorageClass :可以调用指定的存储卷插件(provisioner),根据PVC的请求内容动态创建PV资源,使PV与PVC绑定
provisioner :在存储设备上创建PV资源的卷所使用的目录,将PV与卷目录建立关联
StorageClass + nfs-client-provisioner 动态创建PV:
1、准备好NFS服务器和共享目录
2、创建service account服务账户和RBAC授权,使得sa服务账户具有对PV,PVC,SC等资源的操作权限
3、创建nfs-client-provisioner存储卷插件Pod,配置中使用之前授权过的sa服务账户,设置环境变量定义provisioner的名称及NFS服务器地址和共享目录路径
4、创建StorageClass资源,配置中使用之前定义provisioner的名称
------------- 以上过程是一劳永逸的,以后只需要创建PVC就可以动态生成相关PV资源 -------------
5、创建PVC资源,配置中关联StorageClass资源。创建PVC资源后会在NFS服务器上自动生成相关的PV使用的目录,目录名以<pvc的namespace_name>-<pvc_name>-<pv_name>格式命名
6、创建Pod资源,配置存储卷类型为persistentVolumeClaim,在容器配置中定义存储卷挂载内容