我们确保了StatefulSet如何保证应用实例的拓补状态

在Pod删除和再创建的过程中保持稳定

我们继续看StatefulSet对存储状态的管理,使用了一个叫做Persistent Volume Claim的功能

我们介绍Pod的时候,要在一个Pod里面声明Volume,只需要在Pod里面加上spec.volumes字段就可以了,接下来就可以定义一个具体类型的Volume了

可是,很多持久化的Volume的类型,是不容易记住和得知的

作为一个应用开发者,我也不知道我的容器可能部署在上面持久化存储项目中,或者说,我根本不知道公司的K8S使用的持久化存储是什么?

而且,对于运维来说,全权将Volume相关的信息暴露给了开发者,可能会有暴露公司基础设置的风险

比如下面的Ceph RBD类型的Volume

apiVersion: v1

kind: Pod

metadata:

name: rbd

spec:

containers:

– image: kubernetes/pause

name: rbd-rw

volumeMounts:

– name: rbdpd

mountPath: /mnt/rbd

volumes:

– name: rbdpd

rbd:

monitors:

– ‘10.16.154.78:6789’

– ‘10.16.154.82:6789’

– ‘10.16.154.83:6789’

pool: kube

image: foo

fsType: ext4

readOnly: true

user: admin

keyring: /etc/ceph/keyring

imageformat: “2”

imagefeatures: “layering”

在这个Pod声明的volumes中,存储了Ceph RBD对应的存储服务的地址,用户名,授权文件的地址,这样都暴露给了全公司的开发人员,这是属于过度暴露的例子

这也是为何,再后来的过程中,Kubernetes项目引入了一组叫做Persistent Volume Clain (PVC)和Persistent Volume (PV)的API对象,大大降低了用户使用的门槛

使用的方式不难

我们首先声明自己想要的PVC长什么样

kind: PersistentVolumeClaim

apiVersion: v1

metadata:

name: pv-claim

spec:

accessModes:

– ReadWriteOnce

resources:

requests:

storage: 1Gi

我们声明了name:pv-claim

和spec中的读写方式ReadWriteOnce 只能挂在一个节点上而不是多个节点共享 ,以及大小storage为1GB

接下来是

apiVersion: v1

kind: Pod

metadata:

name: pv-pod

spec:

containers:

– name: pv-container

image: nginx

ports:

– containerPort: 80

name: “http-server”

volumeMounts:

– mountPath: “/usr/share/nginx/html”

name: pv-storage

volumes:

– name: pv-storage

persistentVolumeClaim:

claimName: pv-claim

我队友我们直接声明volumes的类型是persisentVolumeClaim,然后指定PVC的名字,不关心Volume本身的定义

只要我们创建PVC的对象,Kubernetes就会开始绑定对应的Volumes

当然,这个Volumes不会凭空出现

自然是,由一个运维人员维护的PV对象,我们看一下常见的PV对象的YAML文件

kind: PersistentVolume

apiVersion: v1

metadata:

name: pv-volume

labels:

type: local

spec:

capacity:

storage: 10Gi

accessModes:

– ReadWriteOnce

rbd:

monitors:

# 使用 kubectl get pods -n rook-ceph 查看 rook-ceph-mon- 开头的 POD IP 即可得下面的列表

– ‘10.16.154.78:6789’

– ‘10.16.154.82:6789’

– ‘10.16.154.83:6789’

pool: kube

image: foo

fsType: ext4

readOnly: true

user: admin

keyring: /etc/ceph/keyring

这个PV的spec.rbd字段,正是之前说的Ceph RBD Volume的详细定义,还生命了这个PV的容量是10GB,这样Kuberenetes就会进行自动的绑定

Kuberentes中PVC和PV的设计,类似接口和实现的思想,开发者只要知道接口就可以既PVC,运维人员给接口绑定具体的实现PV

这种解耦,避免了向开发者暴露过多的存储细节

避免出现事故时候的扯皮

PVC和PV的设计,使得StatefulSet对存储的状态的管理成为了可能

apiVersion: apps/v1

kind: StatefulSet

metadata:

name: web

spec:

serviceName: “nginx”

replicas: 2

selector:

matchLabels:

app: nginx

template:

metadata:

labels:

app: nginx

spec:

containers:

– name: nginx

image: nginx:1.9.1

ports:

– containerPort: 80

name: web

volumeMounts:

– name: www

mountPath: /usr/share/nginx/html

volumeClaimTemplates:

– metadata:

name: www

spec:

accessModes:

– ReadWriteOnce

resources:

requests:

storage: 1Gi

比如,我们创建的StatefulSet,绑定了对应的volumeClaimTempaltes字段,从名字看出来,和Deployment中的Pod模板作用类似,凡事和StatefulSet管理的Pod,都会声明一个对应的PVC,这个PVC的定义,来自volumeClaimTemplates

这个PVC的名字也会有一个和Pod一致的编号

创建的PVC,也会自动和PV进行绑定

不过PVC和PV的绑定的前提在于,运维人员已经提前创建好了符合条件的PV,或者Kubermetes集群运行在公有云中,这样Kubernetes可以用过Dynamic Provisioning的方式,自动创建匹配的PV

然后我们看一下对应的StatefulSet绑定的PVC

kubectl get pvc -l app=nginx

NAME        STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE

www-web-0   Bound     pvc-15c268c7-b507-11e6-932f-42010a800002   1Gi        RWO           48s

www-web-1   Bound     pvc-15c79307-b507-11e6-932f-42010a800002   1Gi        RWO           48s

这些PVC都以,<PVC名字>-<StatefulSet名字>-<编号> 方式进行命名,并且处于Bound状态

而且创建出的Pod,会使用对应的volumes

web-0的Pod,会使用名为www-web-0的PVC,挂载到这个PVC的PV

然后,我们尝试绑定如下的Volume分配

$ for i in 0 1; do kubectl exec web-$i — sh -c ‘echo hello $(hostname) > /usr/share/nginx/html/index.html’; done

给每一个pod的volume写入一个对应的index文件

web-0写入的就是hello web-0

如果是在Pod中访问对应的nginx的文件,就会返回/usr/share/nginx/html/index.html

如果利用kubectl delete删除这两个Pod,这些Volume中的文件会不会丢失呢?

kubectl delete pod -l app=nginx

我们前面介绍的,在被删除之后,这些Pod会按照编号的顺序被重新创建出来,这是,如果在进行容器访问nginx服务

得到的还是对应的hell信息

说明,新创建的Pod和之前创建的pod仍然绑定在了一起,对于Pod-web-1来说也是一样的

这个原理相当的简单,先把一个Pod删除,但是这个Pod对应的PVC和PV,并不会被删除,但是Volume中已经写入了数据,也会被保存在远程存储服务里

而且,在一个web-0的Pod消失了,控制会发现并重新创建一个web-0出来

纠正不一致

而且,还会绑定对应的PVC,www-web-0,这个PVC的定义,来自于PVC模板,这是StatefulSet创建Pod的标准流程

这样,挂载上了对应的Volume之后,还能获取到之前的volume中的数据

这样,就实现了存储状态的管理

总结一下

StatefulSet中管理的是Pod,而不是和Deployment那样管理RS,每一个Pod的hostname,名字是不一样的,其中带了编号的,而StatefulSet利用这些编号进行了区分

其次,Kubernetes通过Headless Service,为这些有编号的POD,在DNS服务器中生成带有同样编号的DNS记录,只要Pod中编号不变,那么DNS记录也不会变,那么这条记录解析出的Pod的IP地址,会随着Pod的删除和再次创建而更新

最后,StatefulSet还为每一个Pod分配了一个同样编号的PVC,这样,Kubernetes就可以利用PV机制,来给这个PVC绑定上PV,保证每一个Pod有一个唯一的独立的Volume

这样看来,即使Pod已经被删除,对应的PV和PVC也会被保留,方便重新创建的Pod进行绑定,从而找到之前保存在Volume中的数据

在本章文章中,讲解了StatefulSet处理存储状态的方法,然后以此为基础,梳理了StatefuleSet控制器的原理

StatefulSet其实本质上就是一种特殊的Deployement,特殊在于,其给每一个Pod都进行了编号,这个编号会体现在Pod的名字和hostname上,这代表了Pod的唯一身份

有了这个编号,StatefulSet就使用Kubernetes中两个标准的功能,Headless Service和PV/PVC实现了对拓补状态和存储状态的维护

发表评论

邮箱地址不会被公开。 必填项已用*标注