• kubernetes StatefulSet控制器


    想学习更多相关知识请看博主的个人博客地址:https://blog.huli.com/

    https://blog.huli.com/

    在Kubernetes系统中,Pod的管理对象RC、Deployment、DaemonSet 和Job都面向无状态的服务。但现实中有很多服务是有状态的,特别是 一些复杂的中间件集群,例如MySQL集群、MongoDB集群、Akka集 群、ZooKeeper集群等,这些应用集群有4个共同点。
    (1)每个节点都有固定的身份ID,通过这个ID,集群中的成员可 以相互发现并通信。
    (2)集群的规模是比较固定的,集群规模不能随意变动。
    (3)集群中的每个节点都是有状态的,通常会持久化数据到永久 存储中。
    (4)如果磁盘损坏,则集群里的某个节点无法正常运行,集群功 能受损。

    如果通过RC或Deployment控制Pod副本数量来实现上述有状态的集 群,就会发现第1点是无法满足的,因为Pod的名称是随机产生的,Pod 的IP地址也是在运行期才确定且可能有变动的,我们事先无法为每个 Pod都确定唯一不变的ID。另外,为了能够在其他节点上恢复某个失败 的节点,这种集群中的Pod需要挂接某种共享存储,为了解决这个问 题,Kubernetes从1.4版本开始引入了PetSet这个新的资源对象,并且在 1.5版本时更名为StatefulSet,StatefulSet从本质上来说,可以看作 Deployment/RC的一个特殊变种,它有如下特性。

    • StatefulSet里的每个Pod都有稳定、唯一的网络标识,可以用来 发现集群内的其他成员。假设StatefulSet的名称为kafka,那么第1个Pod 叫kafka-0,第2个叫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)
    

    比如一个3节点的Kafka的StatefulSet集群对应的Headless Service的名 称为kafka,StatefulSet的名称为kafka,则StatefulSet里的3个Pod的DNS 名称分别为kafka-0.kafka、kafka-1.kafka、kafka-3.kafka,这些DNS名称 可以直接在集群的配置文件中固定下来

    1 简单示例

    以一个简单的 nginx 服务 web.yaml为例:

    apiVersion: v1
    kind: Service
    metadata:
      name: nginx
      labels:
        app: nginx
    spec:
      ports:
      - port: 80
        name: web
      clusterIP: None
      selector:
        app: nginx
    ---
    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-slim:0.8
            ports:
            - containerPort: 80
              name: web
            volumeMounts:
            - name: www
              mountPath: /usr/share/nginx/html
      volumeClaimTemplates:
      - metadata:
          name: www
        spec:
          accessModes: ["ReadWriteOnce"]
          resources:
            requests:
              storage: 1Gi
    
    (base) [root@liuxinyuan-master yaml]$ kubectl create -f web.yaml
    service "nginx" created
    statefulset "web" created
    
    # 查看创建的 headless service 和 statefulset
    (base) [root@liuxinyuan-master yaml]$kubectl get service nginx
    NAME      CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
    nginx     None         <none>        80/TCP    1m
    (base) [root@liuxinyuan-master yaml]$ kubectl get statefulset web
    NAME      DESIRED   CURRENT   AGE
    web       2         2         2m
    
    # 根据 volumeClaimTemplates 自动创建 PVC(在 GCE 中会自动创建 kubernetes.io/gce-pd 类型的 volume)
    (base) [root@liuxinyuan-master yaml]$kubectl get pvc
    NAME        STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
    www-web-0   Bound     pvc-d064a004-d8d4-11e6-b521-42010a800002   1Gi        RWO           16s
    www-web-1   Bound     pvc-d06a3946-d8d4-11e6-b521-42010a800002   1Gi        RWO           16s
    
    # 查看创建的 Pod,他们都是有序的
    (base) [root@liuxinyuan-master yaml]$ kubectl get pods -l app=nginx
    NAME      READY     STATUS    RESTARTS   AGE
    web-0     1/1       Running   0          5m
    web-1     1/1       Running   0          4m
    
    # 使用 nslookup 查看这些 Pod 的 DNS
    (base) [root@liuxinyuan-master yaml]$ kubectl run -i --tty --image busybox dns-test --restart=Never --rm /bin/sh
    / # nslookup web-0.nginx
    Server:    10.0.0.10
    Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
    
    Name:      web-0.nginx
    Address 1: 10.244.2.10
    / # nslookup web-1.nginx
    Server:    10.0.0.10
    Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
    
    Name:      web-1.nginx
    Address 1: 10.244.3.12
    / # nslookup web-0.nginx.default.svc.cluster.local
    Server:    10.0.0.10
    Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
    
    Name:      web-0.nginx.default.svc.cluster.local
    Address 1: 10.244.2.10
    

    还可以进行其他的操作

    # 扩容
    (base) [root@liuxinyuan-master yaml]$ kubectl scale statefulset web --replicas=5
    
    # 缩容
    (base) [root@liuxinyuan-master yaml]$ kubectl patch statefulset web -p '{"spec":{"replicas":3}}'
    
    # 镜像更新(目前还不支持直接更新 image,需要 patch 来间接实现)
    (base) [root@liuxinyuan-master yaml]$ kubectl patch statefulset web --type='json' -p='[{"op":"replace","path":"/spec/template/spec/containers/0/image","value":"gcr.io/google_containers/nginx-slim:0.7"}]'
    
    # 删除 StatefulSet 和 Headless Service
    (base) [root@liuxinyuan-master yaml]$ kubectl delete statefulset web
    (base) [root@liuxinyuan-master yaml]$ kubectl delete service nginx
    
    # StatefulSet 删除后 PVC 还会保留着,数据不再使用的话也需要删除
    (base) [root@liuxinyuan-master yaml]$ kubectl delete pvc www-web-0 www-web-1
    

    2 更新 StatefulSet

    v1.7 + 支持 StatefulSet 的自动更新,通过 spec.updateStrategy 设置更新策略。目前支持两种策略

    • OnDelete:当 .spec.template 更新时,并不立即删除旧的 Pod,而是等待用户手动删除这些旧 Pod 后自动创建新 Pod。这是默认的更新策略,兼容 v1.6 版本的行为
    • RollingUpdate:当 .spec.template 更新时,自动删除旧的 Pod 并创建新 Pod 替换。在更新时,这些 Pod 是按逆序的方式进行,依次删除、创建并等待 Pod 变成 Ready 状态才进行下一个 Pod 的更新。

    2.1 Partitions

    RollingUpdate 还支持 Partitions,通过 .spec.updateStrategy.rollingUpdate.partition 来设置。当 partition 设置后,只有序号大于或等于 partition 的 Pod 会在 .spec.template 更新的时候滚动更新,而其余的 Pod 则保持不变(即便是删除后也是用以前的版本重新创建)。

    # 设置 partition 为 3
    (base) [root@liuxinyuan-master yaml]$ kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":3}}}}'
    statefulset "web" patched
    
    # 更新 StatefulSet
    (base) [root@liuxinyuan-master yaml]$ kubectl patch statefulset web --type='json' -p='[{"op":"replace","path":"/spec/template/spec/containers/0/image","value":"gcr.io/google_containers/nginx-slim:0.7"}]'
    statefulset "web" patched
    
    # 验证更新
    (base) [root@liuxinyuan-master yaml]$ kubectl delete po web-2
    pod "web-2" deleted
    (base) [root@liuxinyuan-master yaml]$ kubectl get po -lapp=nginx -w
    NAME      READY     STATUS              RESTARTS   AGE
    web-0     1/1       Running             0          4m
    web-1     1/1       Running             0          4m
    web-2     0/1       ContainerCreating   0          11s
    web-2     1/1       Running             0          18s
    

    3 Pod 管理策略

    v1.7 + 可以通过 .spec.podManagementPolicy 设置 Pod 管理策略,支持两种方式

    • OrderedReady:默认的策略,按照 Pod 的次序依次创建每个 Pod 并等待 Ready 之后才创建后面的 Pod
    • Parallel:并行创建或删除 Pod(不等待前面的 Pod Ready 就开始创建所有的 Pod)

    3.1 Parallel 示例

    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: nginx
      labels:
        app: nginx
    spec:
      ports:
      - port: 80
        name: web
      clusterIP: None
      selector:
        app: nginx
    ---
    apiVersion: apps/v1beta1
    kind: StatefulSet
    metadata:
      name: web
    spec:
      serviceName: "nginx"
      podManagementPolicy: "Parallel"
      replicas: 2
      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
          - name: nginx
            image: nginx-slim:0.8
            ports:
            - containerPort: 80
              name: web
            volumeMounts:
            - name: www
              mountPath: /usr/share/nginx/html
      volumeClaimTemplates:
      - metadata:
          name: www
        spec:
          accessModes: ["ReadWriteOnce"]
          resources:
            requests:
              storage: 1Gi
    

    可以看到,所有 Pod 是并行创建的

    (base) [root@liuxinyuan-master yaml]$ kubectl create -f webp.yaml
    service "nginx" created
    statefulset "web" created
    
    (base) [root@liuxinyuan-master yaml]$ kubectl get po -lapp=nginx -w
    NAME      READY     STATUS              RESTARTS  AGE
    web-0     0/1       Pending             0         0s
    web-0     0/1       Pending             0         0s
    web-1     0/1       Pending             0         0s
    web-1     0/1       Pending             0         0s
    web-0     0/1       ContainerCreating   0         0s
    web-1     0/1       ContainerCreating   0         0s
    web-0     1/1       Running             0         10s
    web-1     1/1       Running             0         10s
    

    4 部署zookeeper

    另外一个更能说明 StatefulSet 强大功能的示例为 zookeeper.yaml。

    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: zk-headless
      labels:
        app: zk-headless
    spec:
      ports:
      - port: 2888
        name: server
      - port: 3888
        name: leader-election
      clusterIP: None
      selector:
        app: zk
    ---
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: zk-config
    data:
      ensemble: "zk-0;zk-1;zk-2"
      jvm.heap: "2G"
      tick: "2000"
      init: "10"
      sync: "5"
      client.cnxns: "60"
      snap.retain: "3"
      purge.interval: "1"
    ---
    apiVersion: policy/v1beta1
    kind: PodDisruptionBudget
    metadata:
      name: zk-budget
    spec:
      selector:
        matchLabels:
          app: zk
      minAvailable: 2
    ---
    apiVersion: apps/v1beta1
    kind: StatefulSet
    metadata:
      name: zk
    spec:
      serviceName: zk-headless
      replicas: 3
      template:
        metadata:
          labels:
            app: zk
          annotations:
            pod.alpha.kubernetes.io/initialized: "true"
            scheduler.alpha.kubernetes.io/affinity: >
                {
                  "podAntiAffinity": {
                    "requiredDuringSchedulingRequiredDuringExecution": [{
                      "labelSelector": {
                        "matchExpressions": [{
                          "key": "app",
                          "operator": "In",
                          "values": ["zk-headless"]
                        }]
                      },
                      "topologyKey": "kubernetes.io/hostname"
                    }]
                  }
                }
        spec:
          containers:
          - name: k8szk
            imagePullPolicy: Always
            image: gcr.io/google_samples/k8szk:v1
            resources:
              requests:
                memory: "4Gi"
                cpu: "1"
            ports:
            - containerPort: 2181
              name: client
            - containerPort: 2888
              name: server
            - containerPort: 3888
              name: leader-election
            env:
            - name : ZK_ENSEMBLE
              valueFrom:
                configMapKeyRef:
                  name: zk-config
                  key: ensemble
            - name : ZK_HEAP_SIZE
              valueFrom:
                configMapKeyRef:
                    name: zk-config
                    key: jvm.heap
            - name : ZK_TICK_TIME
              valueFrom:
                configMapKeyRef:
                    name: zk-config
                    key: tick
            - name : ZK_INIT_LIMIT
              valueFrom:
                configMapKeyRef:
                    name: zk-config
                    key: init
            - name : ZK_SYNC_LIMIT
              valueFrom:
                configMapKeyRef:
                    name: zk-config
                    key: tick
            - name : ZK_MAX_CLIENT_CNXNS
              valueFrom:
                configMapKeyRef:
                    name: zk-config
                    key: client.cnxns
            - name: ZK_SNAP_RETAIN_COUNT
              valueFrom:
                configMapKeyRef:
                    name: zk-config
                    key: snap.retain
            - name: ZK_PURGE_INTERVAL
              valueFrom:
                configMapKeyRef:
                    name: zk-config
                    key: purge.interval
            - name: ZK_CLIENT_PORT
              value: "2181"
            - name: ZK_SERVER_PORT
              value: "2888"
            - name: ZK_ELECTION_PORT
              value: "3888"
            command:
            - sh
            - -c
            - zkGenConfig.sh && zkServer.sh start-foreground
            readinessProbe:
              exec:
                command:
                - "zkOk.sh"
              initialDelaySeconds: 15
              timeoutSeconds: 5
            livenessProbe:
              exec:
                command:
                - "zkOk.sh"
              initialDelaySeconds: 15
              timeoutSeconds: 5
            volumeMounts:
            - name: datadir
              mountPath: /var/lib/zookeeper
          securityContext:
            runAsUser: 1000
            fsGroup: 1000
      volumeClaimTemplates:
      - metadata:
          name: datadir
          annotations:
            volume.alpha.kubernetes.io/storage-class: anything
        spec:
          accessModes: ["ReadWriteOnce"]
          resources:
            requests:
              storage: 20Gi
    
    (base) [root@liuxinyuan-master yaml]$ kubectl create -f zookeeper.yaml
    

    详细的使用说明见 zookeeper stateful application

    StatefulSet 注意事项

    1. 推荐在 Kubernetes v1.9 或以后的版本中使用
    2. 所有 Pod 的 Volume 必须使用 PersistentVolume 或者是管理员事先创建好
    3. 为了保证数据安全,删除 StatefulSet 时不会删除 Volume
    4. StatefulSet 需要一个 Headless Service 来定义 DNS domain,需要在 StatefulSet 之前创建好
  • 相关阅读:
    apache-kylin 权威指南—读书笔记
    数据仓库之数据仓库环境——读书笔记
    R 语言—基本绘图
    MapReduce 过程分析
    HDFS 的运行机制
    R 语言贷款月供数据分析
    UML 简介笔记
    为什么要学习 UML?
    scrum 项目的基本模式
    elasticsearch 集群配置
  • 原文地址:https://www.cnblogs.com/precipitation/p/15985390.html
Copyright © 2020-2023  润新知