Kubernetes是分布式容器集群,如何在多个Pod之间或多 个Node之间进行数据存储和共享是非常重要的问题。
Kubernetes引入了网络存储卷,它支持为数众多的云提供商的产 品和网络存储方案。
网络存储卷还能够 满足持久化数据的要求,这些数据将永久保存。
网络存储卷是集成各种第三方的存储系统,不同的服务商提供的配置有一些不同,NFS只是其中一种。
注意:指定一台机器,我选择了master
安装NFS服务器应用:yum install -y nfs-utils rpcbind
创建NFS共享目录:mkdir -p /data/k8snfs
编辑NFS配置文件:vim /etc/exports
/data/k8snfs
*(rw,sync,insecure,no_subtree_check,no_root_squash)
第一个参数是NFS共享目录的路径;
第二个参数是允许共享目录的 网段,这里设置的是本书中的Kubernetes集群机器网段,也可以设置 为“*”以表示不限制。
最后小括号中的参数为权限设置,rw表示允 许读写访问,sync表示所有数据在请求时写入共享目录,insecure 表示NFS通过1024以上的端口进行发送,no_root_squash表示root 用户对根目录具有完全的管理访问权限,no_subtree_check表示 不检查父目录的权限。
重启服务:
service rpcbind restart
service nfs restart
检查服务器端是否正常加载 了/etc/exports的配置
注意:如果使用云服务器,需要开放一下端口,否则客户端连接不上。使用:rpcinfo -p 查看需要开放的端口。注意有tcp和udp
注意:每台需要使用NFS的Node都需要安装NFS
安装客户端:yum install -y nfs-utils
检查是否能访问远端的NFS服务器:sudo showmount -e {NFS服务器IP地址}
如果出现clnt_create: RPC: Port mapper failure - Timed out,使用云服务的话大概率是接口没开放。
安装完成后可以使用NFS作为存储卷。pod配置后,NFS中的数据可永久保存且可以被多个pod共享。
使用nfs
创建一个exampledeployfornfs.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: exampledeployfornfs
spec:
replicas: 2
selector:
matchLabels:
example: examplefornfs
template:
metadata:
labels:
example: examplefornfs
spec:
containers:
- name: containerfornfs
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c']
args: ['echo "The host is $(hostname)" >> /dir/data; sleep 3600']
volumeMounts:
- name: nfsdata
mountPath: /dir
volumes:
- name: nfsdata
nfs:
path: /data/k8snfs
server: xx.xx.236.113
解释:
args: [‘echo “The host is $(hostname)” >> /dir/data; sleep 3600’]
创建的名为containerfornfs的容器用于向存储卷写入数据,容器内的存储卷映射地址为/dir,**它引用的存储卷为nfsdata。**容 器启动后会以追加方式(使用echo …>>…命令)向/dir/data 文件写入文本,这段代码中使用$(hostname)环境变量获取主机名 称,对于Pod中的容器,获取到的是Pod名称。因为Deployment控制器 拥有多个Pod,所以通过这种方式,在同一个文件下会由多个Pod写入多行信息。
创建的存储卷名称为nfsdata,这个名称会被容器设置中的 volumeMounts所引用
server和path属性分别对应之前在安装时配置的NFS机器IP地址与共享目录
查看结果:
网络存储卷使用的是不同于Kubernetes的额外系统,因此从 使用角度来说,网络存储卷存在两个问题。
Kubernetes支持为数众多的 云提供商和网络存储方案,如 NFS/iSCSI/GlusterFS/RDB/azureDisk/flocker等。但因为网络存储卷 通常是集成各种第三方的存储系统,所以在配置上各有差别。
不同的存储的配置参数不太一样,这些参数应该是存储管理员关注的,而非开发人员,Kubernetes提供了3种基于存储的抽象对象—— PersistentVolume(PV)、StorageClass和 PersistentVolumeClaim(PVC),以支持基础设施和应用之间的分离。
存储管理人员设置 PV或StorageClass,并在里面配置存储系统和参数
开发人员只 需要创建PVC来申请指定空间的资源以存储与共享数据即可,无须再关 注存储的具体实现和操作。
当删除PVC时,它写入具体存储资源中的数据可以根据回收策略自动清理。
PV表示持久存储卷,定义了Kubernetes集群中可用的存储资源, 其中包含存储资源实现的细节,如包含如何使用 NFS/iSCSI/GlusterFS/RDB/azureDisk/flocker 等资源的具体设置。
PVC表示持久存储卷的申请,是由用户发起的对存储资源的请求。 申请中只包含请求资源的大小和读写访问模式,无须关注具体的资源 实现细节,Kubernetes会自动为其绑定符合条件的PV
创建examplefornfspv.yml kubectl apply -f examplefornfspv.yml
apiVersion: v1
kind: PersistentVolume # 资源类型
metadata:
name: examplefornfspv
spec: # 该资源对象的具体设置
capacity: # 表示PV的容量,通过storage子属性可以指定占用的具体存储资源(如NFS)的大小,在本例中设定为1Gi。
storage: 1Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Recycle
storageClassName: examplenfs
nfs:
path: /data/k8snfs
server: xx.xx.236.113
解析:
accessModes:定义 PV 对具体存储资源(如 NFS)的访问模式。一共有3种访问模式分别为
ReadWriteOnce(该卷可以被单个节点以读写模式挂载)
ReadOnlyMany(该卷可以被多个节点以只读模式挂载)
ReadWriteMany(该卷可以被多个节点以读写模式挂载)。在本例中使用ReadWriteMany。
persistentVolumeReclaimPolicy:表示当删除PVC时,PV资源的回收策略。
一共有3种策略,分别为Retain(保留)、Recycle(自动回收)、Delete(自动删除)。
当前只有NFS和hostPath支持Recycle策略,AWSEBS、GCE PD、Azure Disk和Cinder卷支持Delete策略。
storageClassName:表示PV资源的描述性分类名称。
例如,可以使用“ssd”“slowdisk”等具备分类的描述性名称。
后续在创建PVC时可以引用这个名称来绑定PV。
nfs:表示该PV使用NFS服务器作为具体的存储资源,
server和path属性为之前网络存储卷示例中配置的NFS服务
器及共享目录。
查看pv资源kubectl get pv
PV已成功创建,其STATUS属性为 Available,这表示资源空闲,尚未被PVC申请使用
查看PV详情:kubectl describe pv examplefornfspv
在Source处可以看到 具体的资源配置信息
PV定义完成后就可以创建PVC以申请使用存储卷资源
创建examplepvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: examplefornfspvc
spec:
accessModes:
- ReadWriteMany
storageClassName: "examplenfs"
resources:
requests:
storage: 500Mi
解释:
创建pvc : kubectl apply -f examplefornfspvc.yml
查看pvc:kubectl get pvc
STATUS属性为 Bound,表示已成功绑定到符合PVC资源申请条件的PV上.
VOLUME属 性显示了绑定的PV的名称,这正是我们之前创建的examplefornfspv
pv的状态也发生了变化
其STATUS属性由之前的Available变为Bound,CLAIM属性由 空值变为刚才创建的PVC
查看pvc详情:kubectl describe pvc {PVC名称}
PVC创建完成后,定义Pod并使用PVC引用的资源
创建exampledeployforpvc.yml文件。
apiVersion: apps/v1
kind: Deployment
metadata:
name: exampledeployforpvc
spec:
replicas: 2
selector:
matchLabels:
example: exampleforpvc
template:
metadata:
labels:
example: exampleforpvc
spec:
containers:
- name: containerforpvc
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c']
args: ['echo "The host is $(hostname)" >> /dir/dataforpvc;sleep 3600']
volumeMounts:
- name: pvcdata
mountPath: /dir
volumes:
- name: pvcdata
persistentVolumeClaim:
claimName: examplefornfspvc
解析:
创建的存储卷名称为pvcdata,这个名称会被容器设置中的 volumeMounts所引用。存储卷的类型是persistentVolumeClaim(即使 用PVC),claimName属性表示引用的PVC名称,本例中为 examplefornfspvc。
运行容器时,向/dir/dataforpvc写入信息
运行文件:kubectl apply -f exampledeployforpvc.yml
PVC所绑定的PV引用中NFS服务器的共享目录 为/data/k8snfs。在NFS服务器上执行 cat /data/k8snfs/dataforpvc,可输出NFS共享目录下的文件内容。
# nfs-client-provisioner-authority.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
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
# replace with namespace where provisioner is deployed
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
# replace with namespace where provisioner is deployed
namespace: default
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: default
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
roleRef:
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.io
下载NFS存储分配器:https://raw.githubusercontent.com/Kubernetes-incubator/external-storage/master/nfs-client/deploy/deployment.yaml
是一个yaml文件,可以浏览器打开直接复制
修改相关的卷信息 文件名:nfs-client-provisioner.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
labels:
app: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
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: quay.io/external_storage/nfs-client-provisioner:latest
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME # 存储分配器的默认名称
value: fuseim.pri/ifs
- name: NFS_SERVER # NFS服务器地址
value: xx.xx.236.113
- name: NFS_PATH # NFS共享目录地址
value: /data/k8snfs
volumes:
- name: nfs-client-root
nfs:
server: xx.xx.236.113 # NFS服务器地址
path: /data/k8snfs # NFS共享目录
kubectl apply -f nfs-client-provisioner.yaml
创建nfs-storage-class.yml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: managed-nfs-storage # 存储分配器名称
# 存储分配器的名称
# 对应“nfs-client-provisioner.yaml”文件中env.PROVISIONER_NAME.value
provisioner: fuseim.pri/ifs
# 允许pvc创建后扩容
allowVolumeExpansion: True
parameters:
# 资源删除策略,“true”表示删除PVC时,同时删除绑定的PV,false删除PVC时,对应的PV不会删除
archiveOnDelete: "false"
执行文件: kubectl apply -f nfs-storage-class.yml
查看状态: kubectl get storageclass
查看详情:kubectl describe storageclass nfs-storage-class.yml
StorageClass 创建完成后就可以创建 PVC 了。
StorageClass 创建完成后就可以创建 PVC 了。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: exampleforstorageclass
spec:
accessModes:
- ReadWriteMany
storageClassName: "managed-nfs-storage"
resources:
requests:
storage: 500Mi
问题:此时发现,pvc一直是pending状态,并没有默认分配PV。 分析,可能是存储分配器除了问题
查看pv详情
创建一个 exampledeployforstorageclass.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: exampledeployforstorageclass
spec:
replicas: 2
selector:
matchLabels:
example: exampleforstorageclass
template:
metadata:
labels:
example: exampleforstorageclass
spec:
containers:
- name: containerforstorageclass
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh', '-c']
args: ['echo "The host is $(hostname)" >>/dir/dataforstorageclass; sleep 3600']
volumeMounts:
- name: pvcdata
mountPath: /dir
volumes:
- name: pvcdata
persistentVolumeClaim:
claimName: exampleforstorageclass # 绑定的PVC
执行:kubectl apply -f exampledeployforstorageclass.yml
进入nfs服务器,查看输出结果:
容器中的信息:
kubectl delete pvc {pvcname}
pvc删除之后,对应的pv也会删除,需要注意的是,删除pvc之前需要先释放掉使用pvc的资源,否则会删除不成功
pvc删除之后,对应的NFS中的相应文件夹也会随之删除
如果只删除pod,不删除pvc,那么挂载的文件信息不会删除
创建PV:kubectl apply -f default-nfs-storage.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: default-nfs-storage # 存储分配器名称
# 存储分配器的名称
# 对应“nfs-client-provisioner.yaml”文件中env.PROVISIONER_NAME.value
provisioner: fuseim.pri/ifs
# 允许pvc创建后扩容
allowVolumeExpansion: True
parameters:
# 资源删除策略,“true”表示删除PVC时,同时删除绑定的PV,false删除PVC时,对应的PV不会删除
archiveOnDelete: "false"
将PV设置成默认PV:kubectl patch storageclass managed-nfs-storage -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
# 创建一个命名空间 Minio
apiVersion: v1
kind: Namespace
metadata:
name: minio
---
apiVersion: v1
kind: Service
metadata:
name: minio
namespace: minio
labels:
name: minio
spec:
type: NodePort
ports:
- port: 9000
nodePort: 30090
name: m90
- port: 9001
nodePort: 30091
name: m91
selector:
name: minio
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: minio
namespace: minio
spec:
selector:
matchLabels:
name: minio
replicas: 2
template:
metadata:
labels:
name: minio
spec:
containers:
- name: minio
env:
- name: MINIO_ROOT_USER
value: admin
- name: MINIO_ROOT_PASSWORD
value: train@2022
image: docker.io/minio/minio
imagePullPolicy: Never
args:
- server
- /data0
- /data1
- /data2
- /data3
- --console-address
- ":9001"
ports:
- containerPort: 9000
- containerPort: 9001
volumeMounts:
- name: data0
mountPath: /data0
- name: data1
mountPath: /data1
- name: data2
mountPath: /data2
- name: data3
mountPath: /data3
volumes:
- name: data0
persistentVolumeClaim:
claimName: minio-data0
- name: data1
persistentVolumeClaim:
claimName: minio-data1
- name: data2
persistentVolumeClaim:
claimName: minio-data2
- name: data3
persistentVolumeClaim:
claimName: minio-data3
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-data-d0
spec:
storageClassName: minio-data-00
mountOptions:
- nolock
capacity:
storage: 5Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: /data/k8snfs/d0
server: xx.xx.236.133
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-data-d1
spec:
storageClassName: minio-data-01
mountOptions:
- nolock
capacity:
storage: 5Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: /data/k8snfs/d1
server: xx.xx.236.133
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-data-d2
spec:
storageClassName: minio-data-02
mountOptions:
- nolock
capacity:
storage: 5Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: /data/k8snfs/d2
server: xx.xx.236.133
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-data-d3
spec:
storageClassName: minio-data-03
mountOptions:
- nolock
capacity:
storage: 5Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: /data/k8snfs/d3
server: xx.xx.236.133
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: minio-data0
namespace: minio
spec:
storageClassName: minio-data-00
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: minio-data1
namespace: minio
spec:
storageClassName: minio-data-01
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: minio-data2
namespace: minio
spec:
storageClassName: minio-data-02
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: minio-data3
namespace: minio
spec:
storageClassName: minio-data-03
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
参考文章:minio 高性能 Kubernetes 原生对象存储 - 掘金 (juejin.cn)