浅谈kubernetes之statefulset

阿凡达2018-06-28 08:51

1. Headless Service 简述

   什么是Service?

   Service是kubernetes最核心的概念,通过创建Service,可以为一组具有相同功能的容器应用提供一个统一的入口地址,并且将请求进行负载分发到后端的各个容器应用上。

  •  Service定义中的关键字段是ports和selector
  •  ports定义部分指定了Service所需的虚拟端口号,由于与Pod容器端口号不一样,所以需要在通过targetPort来指定后端Pod的端口。
        举例:
             apiVersion: v1
             kind: Service
             metadata:
                  name: k8s
             spec:
               ports:
                 - port: 8081
                 targetPort: 8080
              selector:
                  app: k8s
  • selector定义部分设置的是后端Pod所拥有的label,例如: app=k8s

  什么是Headless Service?

  需要Service不做负载均衡,并且不在意cluster ip。

  • 要定义一个服务(Service)为无头服务(Headless Service),需要把Service定义中的ClusterIP配置项设置为空: spec.clusterIP:None
  • 可以不配置Selector,
  • 普通Service相比,Headless Service没有ClusterIP(所以没有负载均衡),它会给一个集群内部的每个成员提供一个唯一的DNS域名来作为每个成员的网络标识,集群内部成员之间使用域名通信
  • 通过查询无头服务域名的A记录,就可以得到集群内成员的域名信息。
2. StatefulSets 


  什么是StatefuleSets?

  目前容器里跑的服务有两类,无状态和有状态。无状态服务异常后,可以直接删掉并新建一个,管理起来非常简单。但是对于有状态服务,它要求有更长的生命周期,并且要保持网络、存储的稳定性。在一个集群的情况下,集群成员之间如何能保持稳定的成员关系?这都对容器编排系统提出了新的挑战。

有状态pod特性:

  •  稳定的、唯一的网络标识。 
  • 稳定的、持久化的存储。 
  • 有序的、优雅的部署和扩展。
  • 有序的、优雅的删除和停止。

而StatefulSets的目的就是给为数众多的有状态服务提供正确的控制器支持,给有状态Pod提供唯一标志的控制器,可以保证有状态pod集群的部署和扩展

  使用StatefulSets的时机

  不一定所有的有存储应用都是适合移植到 Kubernetes 上的,在移植存储层和编排框架之前,需要考虑以下几个问题,然后再这几个问题的基础上考虑采用ReplicaSets还是Statefulsets:

    1) 应用是否可以使用持久化的存储?
    2) 应用是否有伸缩需求?
    3) 预期性能增长的重要性?
    4) 应用是否需要特定的硬件或者实例类型

3. kubernetes 1.6 版本 StatefulSets 特性

  创建副本

   StatefulSets按顺序创建副本。例如,StatefulSets创建一个3副本的有状态服务,statefulset描述文件中replicas为 3,如下所示:

     apiVersion: apps/v1beta1
     kind: StatefulSet
     metadata:
       name: web
     spec:
       serviceName: "nginx"
     replicas: 3
     template:
       metadata:
         labels:
           app: nginx
      spec:
        terminationGracePeriodSeconds: 10
        containers:
        - name: test
          image: gcr.io/google_containers/nginx-slim:0.8
          ports:
          - containerPort: 80
            name: test
          volumeMounts:
          - name: disk
            mountPath: /usr/share/nginx/html
    volumeClaimTemplates:
    - metadata:
        name: disk
      spec:
        accessModes: [ "ReadWriteOnce" ]
        storageClassName: my-storage-class
        resources:
          requests:
            storage: 1Gi

   创建步骤如下:

     1) 先创建pod-0,等pod-0状态为Running and Ready 后,再创建pod-1;

     2) 等pod-1 状态为Running and Ready 后,再创建pod-2;

     3) 若pod-0创建失败,则创建pod-1,等pod-1状态为Running and Ready后,再重试创建pod-0(而不是创建pod-2);

     4)等pod-0重新创建成功,再创建pod-2

  删除副本

   StatefulSets按倒序删除pod(或者缩容,replicas 变为 1)。例如,StatefulSets删除pod-0、pod-1、pod-2 3副本的有状态服务,步骤如下:

     1) 先删除pod-2,等pod-2 完全关机并彻底删除后,再开始删除pod-1;

     2) 如果删除pod-2后,发现pod-0状态不健康,则先不删除pod-1,等pod-0重新变成Running and Ready 后,再开始删除pod-1

总之,statefulsets总是按顺序部署服务。

4. kubernetes 1.7 版本 StatefulSets 新增特性

   在1.6版本中,StatefulSets只有当前序Pod都运行起来才能创建后面的Pod。虽然一个Pod挂掉不会死锁,但如果是Unhealthy状态会卡住这个过程。尤其是,如果pod-0和pod-4都挂了,当前的实现会试着重启pod-0,而pod-4只有pod-0起来后才能启动。这样的表现很安全,但是对于另外一些有状态应用,不需要考虑已经部署的Pod的顺序,那么上面的规则会带来一些约束,造成不必要的不可用现象。所以需要指定一个注解(annotation)来与StatefulSetController进行通信,让它可以同时处理多个Pod。

   在1.7版本中,StatefulSets可以通过定义spec.podManagementPolicy来实现并行控制pod的创建和删除,不用再等待前序pods已部署。(不设置则默认顺序部署)

  1.7版本中,还可以通过定义spec.updateStrategy来开启或者禁用自动更新statefulset集群中pod资源对象的配置项,例如 containers,labels,resoruce request/limits,annotations等。

  1.7版本中,增加了spec.updateStrategy字段,若定义该字段为OnDelete,则StatefulSet控制器将不会再去自动更新StatefulSet集群中的pod,用户需手动删除非健康状态的pod,如此StatefulSet控制器才能重新部署该pod。

  1.7版本中的RollingUpdate更新策略实现了StatefulSet中pods的自动滚动更新。 StatefulSet的.spec.updateStrategy.type设置为RollingUpdate时,StatefulSet控制器将在StatefulSet中删除并重新创建每个Pod。 它将按照Pod终止(从最大序数到最小)的顺序进行,每次更新一个Pod,并且要等待更新的Pod正在运行和就绪后,才能更新后续pod。也可以通过指定spec.updateStrategy.rollingUpdate.partition来对RollingUpdate更新策略进行分区。 如果指定了分区,则当StatefulSet的spec.template更新时,具有大于或等于分区的序数的所有Pod将被更新。具有小于分区的序数的所有Pods将不会更新,即使删除它们也将在以前的版本中重新创建,而不是在更新后的版本中。 如果StatefulSet的spec.updateStrategy.rollingUpdate.partition大于其spec.replicas,则spec.template的更新将不会影响到已经创建的Pods。

本文来自网易实践者社区,经作者张文娟授权发布。