kubernetes1.6管中窥豹-StatefulSets概念、约束、原理及实例

阿凡达2018-07-06 14:16

前言

默认读者有kubernetes基础概念的背景知识,因此基础概念例如有状态、pod、Replica Sets、Deployments等不在此文详细阐述。

可以看我之前的一些关于kubernetes的文章:

kubernetes pod&container生命周期浅析

本文主要介绍1.5版本以后新出现的概念StatefulSets的定义以及应用。

StatefulSets的概念

StatefulSets在kubernetes 1.5中作为beta特性出现,这个特性是用来取代1.4之前的PetSets的概念。

StatefulSets简而言之是一个用来给自己的pods提供特定唯一标识的控制器。它提供了有状态容器的顺序扩容以及部署的能力。

下面这张表给出一个直观的对1.3之前网易云基础服务中有状态容器与社区1.6有状态容器的对比。

1.3 有状态(网易云基础服务适配) 1.6 有状态(社区)
实现方式 pod StatefulSet
副本数 1 n(n为大于等于1的整数)
是否可迁移 是(经适配产品后,系统盘保留数据) 是,系统盘默认不保留数据
是否支持扩缩容
是否支持顺序部署 否(本身就1个pod,没有顺序的概念) 是(自动顺序启动和销毁StatefuleSet中的pod)
是否支持回收资源 否(需要适配产品做额外的回收逻辑) 是(将副本数设成0,就自动回收pod资源)

在1.3以前的版本中有状态的容器是不支持扩容以及动态部署的。有状态容器跟无状态最大的区别就是有状态可以持久化数据,无状态容器经过重新部署、扩缩容等操作,数据是不会保留持久化的。但是有状态容器通过挂载数据盘的方式可以做到持久化。需要注意的一点是StatefulSets的扩容以及动态部署不支持系统盘的数据保留。因此应用StatefulSets部署的程序需要确保尽量使用数据盘做数据的持久化。

如何做到数据盘的数据持久化呢?如果应用StatefulSets的话,当应用所在的node节点挂掉或者断链以后,node上面所在的pod可以被自动调度到其他node上,并且使用网络访问数据盘,以确保重新调度以后应用还是可以访问到它原有数据盘上的数据。

在1.3之前如果直接应用pod的话,需要自定义实现pod迁移以后,将原始的数据盘从老node上卸载后再挂载到新调度的node上的步骤。而使用StatefulSets后可以自动完成,因为不是以卸载挂载的方式来实现,而是直接走网络远程访问数据盘。

StatefulSets的约束

说了那么多StatefulSets的概念以及1.3跟现在社区1.6的实现对比以后,来看下StatefulSets本身的一些约束限制。

1.在社区1.5之前无法应用StatefulSets。

2.可以通过apiserver的启动参数--runtime-config来禁用StatefulSets特性。

3.pod上的存储需要以PersistentVolume的方式提供(基于Kubernetes StorageClass文档实现)。

4.对StatefulSets进行删除以及扩缩容操作不会删除StatefulSets上关联的卷。这样会确保数据持久化。

5.StatefulSets需要Headless Service来为Pods分配一个可靠的网络唯一标识符。

6.升级已有的StatefulSets需要手工运维。

约束里提到的两个概念:

1.PersistentVolume:简称PV,是一种可以由管理员分配提供的网络存储资源,跟Volumes类似,但是它的生命周期独立的所使用PV的Pod。kubenetes目前支持多种PV插件,例如AWSElasticBlockStore、NFS、iScsi、Ceph等。详情可以看github链接

2.Headless Service:是service的一种,用于不需要负载均衡以及单个serviceIP等场景。这时创建Headless Service时将spec.clusterIP标记为None即可。这样的service,由于clusterIP没有被分配,因此kube-proxy无法管理这些service。DNS如何自动分配依赖于service是否定义了selectors。

定义了selector的service:endpoints控制器可以创建endpoints记录,修改DNS配置返回一个指向service内pod的地址信息。 没有定义selector的service:endpoints不能被创建,然而可以通过ExternalName方式记录CNAME(ExternalName类型的service),例如foo.bar.example.com,用来作为服务发现的途径。

以下例子给出了StatefulSets的一些组成部分

1.一个headless service,命名为nginx,用来控制网络域。

2.一个StatefulSet,命名为web,描述了nginx容器的3个副本,会分配到特定的pod上。

3.volumeClainTemplates使用PV提供了稳定的存储资源。

service 的json描述如下,需要关注的是clusterIP是置为null的,是headless service的最重要的标志:

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx

StatefuleSet 的json描述如下,关注副本数以及volumeClaimTemplates的配置。

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

Pod的唯一标识

上面的例子中名为web的StatefulSet定义为三副本以后,kubenetes就会自动创建3个pod,用来调度及启动容器。StatefulSet pods有一个包含序号、稳定网络标识、稳定存储的唯一标识。不管pod调度到哪个node,这个唯一标识是不变的。

web包含的三副本pod命名规则为:(statefulsetname)(ordinal),即顺序命名为web-0,web-1,web-2,headless service管理pod的所在域,service域格式为:(servicename).(namespace).svc.cluster.local。pod生成后会得到一个DNS的子域,格式为:(podname).(governing service domain)。下面的图展示了pod的域跟cluster域、service命名之间的关系。

部署以及扩缩容保障

StatefulSet最大的特色就是提供了有状态容器的顺序扩容以及部署的能力,下面来看下机制上如何确保部署和扩缩容成功执行。

1.N个副本的StatefulSet,包含的Pods会从0~N-1顺序创建并部署。

2.Pods被删除回收时,终止的时候反序终止,从N-1~0。

3.在扩容操作执行之前,所有的Pod必须处于Running和Ready态。

4.在一个Pod终止之前,它的继承者们需要完全关闭。

不推荐将StatefulSet的参数pod.Spec.TerminationGracePeriodSeconds配置为0,这样代表强制删除里面的pod,不安全也不建议这样做。

当上面的例子中nginx service被创建以后,web StatefuleSet创建成功,三个Pods依次顺序部署(web-0,web-1,web-2)。web-1只有当web-0处于Running和Ready态以后才会进行部署。当web-0在web-1部署成功后再变成fail的话,除非web-0自己恢复重新拉起,否则web-2不会进行部署动作。

假如想把web这个StatefuleSet缩容成1副本,则将replicas=1。这样的话,web-2则首先会被终止,然后web-1再会终止。如果web-0在web-2终止以后先fail了,则web-1不会终止,直到web-0恢复成Running和Ready。

总结

StatefulSet使得有状态的应用扩缩容以及部署更加轻量级,更加便利,当然也会有它的一些限制,例如使用远端网络存储盘作为数据保持。总的来说社区建议使用这种资源去进行有状态的应用的部署和管理。在网易云基础服务中也会尝试使用1.6版本中的StatefulSets来进行有状态容器的管理。

网易云容器服务为用户提供了无服务器容器,让企业能够快速部署业务,轻松运维服务。容器服务支持弹性伸缩、垂直扩容、灰度升级、服务发现、服务编排、错误恢复及性能监测等功能。

本文来自网易实践者社区,经作者崔晓晴授权发布。