我们知道,平时kubernetes在部署无状态服务的时候,并不需要太多考虑持久化存储的事情,直接通过volume挂载网络存储,比如常见的nfs就能实现文件共享存储。
但是如果在有状态服务中,你就会需要很多的问题,比如:当你部署一些集群服务的时候:
1. 不可以用共享存储,因为每个服务的数据都不能相同。
2. 不可以直接用node的本地路径进行挂载,因为pod的随机性,一个node可能会跑多个pod副本,则会造成冲突。
3. 数据量可能会很大,可能需要使用外部存储,或者独立的磁盘,云盘等
4. 就算用pv,pvc也很不方便,需要你提前手动创建,不方便扩展。
然后我们细数一下k8s里的持久化存储,总的分为两种,静态卷和动态卷。静态卷就是刚才我们说的,volume挂载,或者通过手动创建pv,pvc进行挂载。都属于静态卷。而动态卷,则是将一个网络存储作为一个StorageClass类,通过自己的配置,来动态的创建pv,pvc并进行绑定,这样就可以实现动态的存储生成与持久化保存。
具体详细的k8s的存储系统知识我这里就不想洗说明,有兴趣的自行百度,谷歌就可以了,先了解一下k8s的存储,再来看如何实现
下面我们就说一下,怎么通过nfs动态卷来实现有状态服务的储存。
环境:
kubernetes v1.12.2
centos7
这里以搭建neo4j 因果集群为例,展示nfs动态卷的使用,namespace为neo4j。
nfs动态卷的整体结构大致如下图,用户通过yaml创建StatefulSet,StatefulSet找到StorageClass,StorageClass指定到nfs-provisioner为nfs的pv提供者,这是一个pod的服务,用来自动生成pv的,此pod来绑定到对应的nfs服务。以此来通过nfs服务进行动态的pv生成,然后通过StatefulSet的pvc,与pod进行绑定,实现数据的持久化存储。
1. 搭建好nfs服务
nfs服务的搭建,请看:《centos7.2 配置NFS文件服务器,远程共享文件》
我这里搭建好nfs后,创建文件夹 /opt/nfs 作为主目录进行,配置如下:
# 172.18.0.0/16是我的内网网段,自己根据情况自定义
/opt/nfs 172.18.0.0/16(rw,sync,no_root_squash)
nfs服务的ip为:172.18.196.238
2. 配置rbac权限
由于我这里以neo4j为namespace,所以先创建namespace.yaml 文件,内容如下:
---
apiVersion: v1
kind: Namespace
metadata:
name: neo4j
labels:
name: neo4j
执行此yaml文件,创建neo4j的命名空间,以保证下面的使用
kubectl create -f namespace.yaml
然后开始创建rbac.yaml,进行账号的授权,内容如下:
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-provisioner
namespace: neo4j
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-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: ["list", "watch", "create", "update", "patch"]
- apiGroups: [""]
resources: ["services", "endpoints"]
verbs: ["get","create","list", "watch","update"]
- apiGroups: ["extensions"]
resources: ["podsecuritypolicies"]
resourceNames: ["nfs-provisioner"]
verbs: ["use"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-provisioner
subjects:
- kind: ServiceAccount
name: nfs-provisioner
namespace: neo4j
roleRef:
kind: ClusterRole
name: nfs-provisioner-runner
apiGroup: rbac.authorization.k8s.io
执行此yaml文件,执行账号创建与授权,必须进行账号的授权,否则无法自动创建pv和pvc
kubectl create -f rbac.yaml
3. 创建nfs-provisioner
创建deployment.yaml 文件,内容如下:
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: nfs-client-provisioner
namespace: neo4j
labels:
app: nfs-client-provisioner
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccount: nfs-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: nfs-provisioner
- name: NFS_SERVER
value: 172.18.196.238
- name: NFS_PATH
value: /opt/nfs
volumes:
- name: nfs-client-root
nfs:
server: 172.18.196.238
path: /opt/nfs
执行此yaml文件,创建nfs-provisioner,此nfs-provisioner通过授权的账号实现自动创建pv,pvc
kubectl create -f deployment.yaml
3. 创建storageclass
创建storageclass.yaml ,内容如下:
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: managed-nfs-storage
provisioner: nfs-provisioner
parameters:
archiveOnDelete: "true" # 这里为true,表示pv删除后,会自动归档,不会删除文件
执行此yaml文件,创建storageclass,指定使用的provisioner为nfs-provisioner
kubectl create -f storageclass.yaml
4. 测试使用
在StatefulSet中,通过配置volumeClaimTemplates来配置使用动态卷,这里以搭建neo4j的因果集群为例子,进行测试,创建demo.yaml 文件,内容如下:
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: neo4j-core
namespace: neo4j
labels:
app: neo4j-core
spec:
replicas: 2
selector:
matchLabels:
app: neo4j-core
serviceName: neo4j-core
template:
metadata:
labels:
app: neo4j-core
spec:
nodeSelector:
neo4j-core: "yes"
containers:
- name: neo4j-core
image: neo4j:3.5.5-enterprise
imagePullPolicy: IfNotPresent
env:
- name: NEO4J_ACCEPT_LICENSE_AGREEMENT
value: "yes"
- name: NEO4J_dbms_connectors_default__advertised__address
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: NEO4J_dbms_mode
value: "CORE"
- name: NEO4J_causal__clustering_minimum__core__cluster__size__at__formation
value: "2"
- name: NEO4J_causal__clustering_minimum__core__cluster__size__at__runtime
value: "2"
- name: NEO4J_causal__clustering_discovery__type
value: "LIST"
- name: NEO4J_causal__clustering_initial__discovery__members
value: "neo4j-core-0.neo4j-core.neo4j.svc.cluster.local:5000,neo4j-core-1.neo4j-core.neo4j.svc.cluster.local:5000"
- name: NEO4J_causal__clustering_discovery__advertised__address
value: $(NEO4J_dbms_connectors_default__advertised__address):5000
- name: NEO4J_causalClustering_transactionAdvertisedAddress
value: $(NEO4J_dbms_connectors_default__advertised__address):6000
- name: NEO4J_causalClustering_raftAdvertisedAddress
value: $(NEO4J_dbms_connectors_default__advertised__address):7000
volumeMounts:
- name: neo4j-core # 这里的name要和 下面volumeClaimTemplates的metdata中name一致
mountPath: /data
volumeClaimTemplates:
- metadata:
name: neo4j-core
annotations:
volume.beta.kubernetes.io/storage-class: "managed-nfs-storage" # 这里managed-nfs-storage就是我们创建的storageclass的name
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 2Gi
---
apiVersion: v1
kind: Service
metadata:
name: neo4j-core
namespace: neo4j
spec:
selector:
app: neo4j-core
type: NodePort
ports:
- protocol: TCP
name: http
port: 7474
nodePort: 27474
targetPort: 7474
- protocol: TCP
name: blot
port: 7687
nodePort: 27687
targetPort: 7687
执行此demo.yaml
kubectl create -f demo.yaml
执行过后,等待片刻,查看pvc已经生成,并与pv进行了绑定。
查看pv
这里pv的回收策略是delete,就是说pod删除后,pv,pvc与自动删除,但是数据文件并不会删除,会进行归档,因为在storageclass中设置了对删除PV的文件的归档。
我们查看一下nfs服务的目录,会发现生成了两个文件夹,名字为 pvc的name加上pv的name:
进入文件夹,查看数据存在。
然后现在我们删掉此neo4j的集群,查看数据是否会被删除
kubectl delete -f demo.yaml
删除后,查看pv,pvc,会发现,pv,pvc都被删除掉了,然后到nfs目录中查看:
会发现,文件夹还在,且其名称前面多一个archived,这是归档的意思,表示此文件夹的pv已经被删除了,进去查看文件是否存在
ok,到此测试通过,就可以使用volumeClaimTemplates来配置动态卷的使用,非常方便简单。当然动态卷不止支持nfs,还支持很多的储存插件来提供PV,详细可查看Storage Classes 官方文档 provisioner部分。
我这里并没有讲很多的k8s的存储系统相关的东西,只是主要实现了nfs动态卷的配置和使用。使用则主要是demo.yaml文件文件中的volumeClaimTemplates来进行配置。有任何不明白的,欢迎留言