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" : {
...
...

 

 
posted @ 2020-08-13 09:37  Bigberg  阅读(454)  评论(0编辑  收藏  举报