StatefulSet
一、简介
在Kubernetes系统中,Pod的管理对象RC、Deployment、DaemonSet和Job都面向无状态的服务。但是有很多服务是有状态的,特别是一些复杂的中间件集群,例如MySQL集群、MongoDB集群、Kafka集群、zookeeper集群等,这些应用集群有4个共同点:
-
每个节点都有固定的身份ID,通过这个ID,集群中的成员可以相互发现并通信
-
集群的规模是比较固定的,集群规模不能随意变动
-
集群中的每个节点都是有状态的,通常会持久化数据到永久存储中
-
如果磁盘损坏,则集群里的某个节点无法正常运行,集群功能受损
StatefulSet旨在与有状态的应用及分布式系统一起使用,它有如下特性:
-
StatefulSet里每个Pod都有稳定、唯一的网络标识,可以用来发现集群内的其他成员。假设StatefulSet的名称为Kafka,那么第一个Pod叫Kafka-0,第二个叫Kafka-1,以此类推
-
StatefulSet控制的Pod副本启停顺序是受控的,操作第n个Pod时,前n-1个Pod已经是运行且准备好的状态
-
StatefulSet的Pod采用稳定的持久化存储卷,通过PV或者PVC来实现,删除Pod时默认不会删除与StatefulSet相关的存储卷
StatefulSet除了要与PV卷捆绑使用以存储Pod的状态数据,还要与Headless Service配合使用,即在每个StatefulSet定义中都要声明它属于哪个Headless Service。Headless Service于普通Service的关键区别在于,它没有Cluster IP,如果解析Headless Service的DNS域名,则返回的是该Service对应的全部Pod的Endpoint列表。StatefulSet在Headless Service的基础上又为StatefulSet控制的每个Pod实例创建了一个DNS域名,这个域名格式为: $(podname).$(headless service name)
二、使用StatefulSet搭建MongoDB集群
为了完成MongoDB集群的搭建,需要创建如下三个资源对象
-
一个StorageClass,用于StatefulSet自动为各个应用Pod申请PVC
-
一个Headless Service,用于维护MongoDB的集群状态
-
一个StatefulSet
2.1创建一个StorageClass对象
storageclass-fast.yaml
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: fast provisioner: kubernetes.io/glusterfs parameters: restful: http://192.168.10.106:18080
执行kubectl create创建该storage class
# kubectl create -f storageclass-fast.yaml
storageclass.storage.k8s.io/fast created
2.2 授权ServiceAccount
在2.4步骤需要使用mongo-sidecar的pod来配置管理mongo pod。
由于默认的service account仅仅只能获取当前Pod自身的相关属性,无法观察到其他名称空间Pod的相关属性信息。如果想要扩展Pod,或者一个Pod需要用于管理其他Pod或者是其他资源对象,是无法通过自身的名称空间的serviceaccount进行获取其他Pod的相关属性信息的,因此需要进行手动创建一个serviceaccount,并在创建Pod时进行定义。或者直接将默认的serviceaccount进行授权。
defaultaccount.yaml
kind: ClusterRoleBinding metadata: name: default-view subjects: - kind: ServiceAccount name: default namespace: default roleRef: kind: ClusterRole name: view apiGroup: rbac.authorization.k8s.io
2.3 创建Headless Service
mongodb-sidecar作为MongoDB集群的管理者,将使用此Headless Service来维护各个MongoDB实例之间的集群关系,以及集群规模变化时的自动更新
mongodb-headless-service.yaml
apiVersion: v1 kind: Service metadata: name: mongo labels: name: mongo spec: ports: - port: 27017 targetPort: 27017 clusterIP: None selector: role: mongo
执行kubectl create命令创建该Headless Service
# kubectl create -f mongodb-headless-service.yaml
service/mongo created
2.4 MongoDB StatefulSet
statefulset-mongo.yaml
apiVersion: apps/v1 kind: StatefulSet metadata: name: mongo spec: serviceName: mongo # 声明它属于哪个Headless Service replicas: 3 selector: # has to match .sepc.template.metadata.labels matchLabels: role: mongo environment: test template: metadata: labels: # has to match .spec.selector.matchLabels role: mongo environment: test spec: terminationGracePeriodSeconds: 10 containers: - name: mongo image: mongo:3.4 command: - mongod - "--replSet" - rs0 - "--bind_ip" - 0.0.0.0 - "--smallfiles" - "--noprealloc" ports: - containerPort: 27017 volumeMounts: - name: mongo-persistent-storage mountPath: /data/db - name: mongo-sidecar image: cvallance/mongo-k8s-sidecar env: - name: MONGO_SIDECAR_POD_LABELS value: "role=mongo,environment=test" - name: KUBERNETES_MONGO_SERVICE_NAME value: "mongo" volumeClaimTemplates: - metadata: name: mongo-persistent-storage annotations: volume.beta.kubernetes.io/storage-class: "fast" spec: accessModes: [ "ReadWriteOnce" ] resources: requests: storage: 2Gi
其中主要配置说明如下:
(1)在该statefulset定义中包含连个容器:mongo和mongo-sidecar。mongo是主服务程序,mongo-sidecar是将多个mongo实例进行集群设置的工具。mongo-sidecar中的环境变量如下:
-
MONGO_SIDECAR_POD_LABELS:设置为mongo容器的标签,用于sidecar查询它所要管理的MongoDB集群实例
-
KUBERNETES_MONGO_SERVICE_NAME: 它的值为mongo,表示sidecar将使用mongo这个服务名来完成MongoDB集群的设置
(2)replicas=3 表示MongoDB集群由3个mongo实例组成
(3)volumeClaimTemplates是 StatefulSet最重要的存储设置。在annotations段设置volume.beta.kubernetes.io/storage-class="fast" 表示使用名为fast的StorageClass自动为每个mongo Pod实例分配后端存储
(4)resources.requests.storage=2Gi 表示为每个mongo实例都分配2G的磁盘空间
2.5 验证MongoDB集群
最终可以看到StatefulSet依次创建并启动了3个mongo Pod实例,它们的名字依次为mongo-0、mongo-1、mongo-2:
# kubectl get pods -l role=mongo NAME READY STATUS RESTARTS AGE mongo-0 2/2 Running 0 72m mongo-1 2/2 Running 0 71m mongo-2 2/2 Running 0 64m
StatefulSet会用volumeClaimTemplates中定义为每个Pod副本都创建一个PVC实例,每个PVC的名称由StatefulSet定义中volumeClaimTemplates的名称和Pod副本的名称组合而成:
# kubectl get pvc -l role=mongo NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE mongo-persistent-storage-mongo-0 Bound pvc-63b576fc-1324-46db-b708-0c40f5896103 2Gi RWO fast 86m mongo-persistent-storage-mongo-1 Bound pvc-6b2dfbc3-f57e-4e05-9cd7-dfabb3c5b00e 2Gi RWO fast 75m mongo-persistent-storage-mongo-2 Bound pvc-24ed1a31-b9a8-4214-a189-137cf9eccc12 2Gi RWO fast 67m
下面是mongo-0这个Pod中volume设置,可以看到系统自动为其挂载了对应的PVC:
# kubectl get pod mongo-0 -o yaml | grep -A 3 volumes volumes: - name: mongo-persistent-storage persistentVolumeClaim: claimName: mongo-persistent-storage-mongo-0
登入任意一个mongo Pod,在mongo命令行界面使用rs.status()命令查看MongoDB集群状态,该mongodb集群已由sidecar完成了创建。在集群中包含3个节点的名称都是StatefulSet设置的DNS域名格式的网络标识名称:
mongo-0.mongo.default.svc.cluster.local mongo-1.mongo.default.svc.cluster.local mongo-2.mongo.default.svc.cluster.local
同时,可以查看每个mongodb实例的各自角色(PRIMARY或SECONDARY)
rs.status() ... ... ... "members" : [ { "_id" : 0, "name" : "mongo-0.mongo.default.svc.cluster.local:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 100, "optime" : { "ts" : Timestamp(1592286258, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2020-06-16T05:44:18Z"), "syncingTo" : "", "syncSourceHost" : "", "syncSourceId" : -1, "infoMessage" : "could not find member to sync from", "electionTime" : Timestamp(1592286196, 2), "electionDate" : ISODate("2020-06-16T05:43:16Z"), "configVersion" : 5, "self" : true, "lastHeartbeatMessage" : "" }, { "_id" : 1, "name" : "mongo-1.mongo.default.svc.cluster.local:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 45, "optime" : {
...
...