Kubernetes 理解笔记之 StatefulSet


本文是对“有状态应用”的多副本控制器 StatefulSet 的理解笔记。StatefulSet 是 Kubernetes 中用来处理“有状态应用”的,所以理解过程从“什么是有状态应用”到“Kubernetes的解决方案”顺序展开,着重在记录 Kubernetes 在处理拥有拓扑状态和存储状态的“有状态应用”的控制管理上的的解决方案。大部分都是按照自己的理解记录,所以若有错误欢迎指正。



“使用”一句话:StatefulSet 是 Kubernetes 中用来处理“多实例的有状态应用”的。
“原理”一句话:StatefulSet 直接管理 Pod,并为每一个 Pod 编号命名。


一. 有状态应用

1.1 这个“有没有状态”到底代表什么?

这里首先要讲一个自己的理解:”应用“ 和 ”实例“
之前从容器到Pod理解的时候,往往说 Kubernetes 通过 Pod 来对“应用”进行描述,但准确地说应该是 Pod YAML 来描述,而通过这个 Pod YAML 建立运行起来的 Pod ,在这里就称为一个“实例”。
所以在 Kubernetes 中 Pod 描述的其实应该是”单实例应用“,但是应用的部署为了高可用和方便扩展伸缩,往往都是多实例部署,也就是”多实例应用“,之前理解的 Deployment 便是一种描述,多副本可伸缩控制器。
这里就会有一个困惑:每一个 Pod 都会有自己的网络地址、数据依赖的,要不然也不会有 Volume 这种设置了。为什么还会出现”无状态应用“呢 ?
其实,这里的”应用状态“是对于”多实例应用“而言的,并不是对于 Pod 这种”单实例应用“而言。如果一个”多实例应用“的各个实例承担不同的责任相互配合,并各自有着不同的外部数据依赖就是”有状态应用“,如果多个实例仅是单纯的副本扩展则是”无状态应用“了。

1.2 “有状态应用”的状态抽象

Kubernetes 将“多实例应用”的有状态抽象为了两种情况:

  1. 有拓扑状态:一个应用的多个实例之间有拓扑状态
    场景:应用多个实例必须按照某些顺序启动,比如应用的主节点 A 要先于从节点 B 启动。而如果你把 A 和 B 两个 Pod 删除掉,它们再次被创建出来时也必须严格按照这个顺序才行。并且,新创建出来的 Pod,必须和原来 Pod 的网络标识一样,这样原先的访问者才能使用同样的方法,访问到这个新 Pod。
  2. 有存储状态:一个应用的每个实例有各自不同的存储状态
    场景:一个数据库应用的多个存储实例,往往都会在本地磁盘上保存一份数据,这样每个实例关联的数据依赖就不同了,同时这些实例哪怕被重建过,每个实例与各自数据之间的对应关系也应该是不变的。

二. Kubernetes 如何处理有状态应用:StatefulSet

如何处理好有状态应用,就需要有方法维护好各个实例的状态,这也是 StatefulSet 的核心功能,其实就是:通过某些方法记录这些状态,然后在 Pod 被重新创建时能够为新的 Pod 恢复这些状态。

2.1 针对拓扑状态

拓扑状态如图,可以看到各个实例之间有拓扑状态的应用有2个点:(1)不同实例扮演不同角色承担不同任务。(2)每个实例需要知道其他实例的访问地址,实例之间需要互通。

首先解答第一个点:
【问】都是通过同一个 Pod 模板建立的 Pod,StatefulSet 如何让它们扮演不同角色承担不同任务?
【答】每个实例虽然通过相同的镜像创建,但启动 Pod 的命令和初始化流程可以完全不一样。StatefulSet 实现的一个基础点是为创建的每一个 Pod 提供了一个命名编号(假设 nginx 就是从nginx-1 到 nginx-N),这样之后就可以根据 Pod 的不同命名来设定不同命令和流程来让特定 Pod 承担特定任务(可以给 Pod1-name 和其他 Pod 设定不同配置从而区分 master 和 slave )。Pod 重建也会按照命名编号重建,这样假设 Pod-N 重建产生新的 Pod,名字不会变,还是叫 Pod-N,也还是会按照 Pod-N 原来的命令和配置重新设置, 在拓扑中扮演的角色也就不会变。

然后解答第二个点:
【问】Pod 重建后 IP 会发生改变,网络标识不稳定,StatefulSet 如何为每一个 Pod 提供一个稳定的网络访问点从而维持拓扑状态不变?
【答】固定的网络标识一般使用 IP 地址表示,也就是每个 Pod 的 IP地址,但因为 Pod 会出现删除重建等操作,IP 会变,无法固定,所以需要另一种方式。而 StatefulSet 创建或重建的一组 Pod 有一个东西是固定的,就是 Pod 的编号命名(pod-name),比如10个实例的应用,Pod 命名就是从 pod1 到 pod10,即使重建也会按顺序,而且不会出现 pod11。
另外,再借助另一对象 Service (Service 是 Kubernetes 项目中用来将一组 Pod 暴露给外界访问的一种机制)的 Headless Service 方式,它为所代理的每一个 Pod 绑定一个固定格式的 DNS 记录作为“可解析身份”,核心由 pod-name 和 service-name 标识:

<pod-name>.<svc-name>.<namespace>.svc.cluster.local

这样针对固定 pod-name 的 Pod 就有了唯一的网络访问地址了。所以想要用 StatefulSet 部署有状态应用,也要配合部署一个 Service(Headless Service) ,为每个 Pod 提供DNS记录及解析。

如图,Pod 的唯一标识过程:
Pod 的唯一标识过程
接下来任凭 Pod 重建 IP 如何变化,StatefulSet 会维护特定角色的 Pod 的 pod-name 不变,而 Service 会更新 DNS 对 Pod 的解析,这样由 pod-name 和 svc-name 组成的“特定格式地址”是一直不需要变化的,什么时候通过这一 DNS 记录中的“特定格式地址”都可以访问到担任特定角色的 Pod,就实现了 Pod 网络标识的稳定。换句话说,DNS 记录中的“特定格式地址”是一直不变的,但地址到 Pod IP 的解析会随着 Pod 重建而更新,不过我们使用的仅仅就是这一“特定格式地址”就够了。

如图,Pod2重建过程:
Pod2重建过程
图中过程解释:一共3个 Pod,当 Pod2 重建时,因为 Pod 创建是有序的,所以 Pod3 也会删除,然后重新按顺序创建 Pod2、Pod3。这时候原有 DNS 记录中的“特定格式地址”(图中蓝色部分)是不需要变化的,更新的是 DNS 对新 Pod IP 的解析,这样访问 Pod 的入口地址(图中蓝色部分)一直就是稳定的了。

一句话总结:
StatefulSet 为管理的每个 Pod 实例提供编号命名;Headless Service 根据 Pod 命名绑定固定格式的 DNS 记录,然后不断自动更新 DNS 记录到 Pod IP 解析;从而达到由 pod-name 和 service-name 组成的“固定格式地址”来作为每一个 Pod 稳定的网络标识的效果。

2.2 针对存储状态

1、首先,补充一下 StatefulSet 使用到的 PVC 和 PV 机制。

Pod 中直接配置 Volume,对开发人员会有字段使用复杂以及过度暴露存储系统细节的风险,如下:

apiVersion: v1
kind: Pod
metadata:
  name: rbd
spec:
  containers:
    - image: kubernetes/pause
      name: rbd-rw
      volumeMounts:
      - name: rbdpd
        mountPath: /mnt/rbd
  volumes:
    - name: rbdpd
      rbd:
        monitors:
        - '10.16.154.78:6789'
        - '10.16.154.82:6789'
        - '10.16.154.83:6789'
        pool: kube
        image: foo
        fsType: ext4
        readOnly: true
        user: admin
        keyring: /etc/ceph/keyring
        imageformat: "2"
        imagefeatures: "layering"

所以 Kubernetes 项目引入了一组叫作 Persistent Volume Claim(PVC)和 Persistent Volume(PV)的 API 对象。Kubernetes 中 PVC 和 PV 的设计,实际上类似于“接口”和“实现”的思想。开发者只要知道并会使用“接口”,即:PVC;而运维人员则负责给“接口”绑定具体的实现,即:PV。
运维人员维护一组 PV 对象,开发人员使用时只需要定义 PVC 声明想要的 Volume 属性,然后创建 Pod 时指明使用这个 PVC 就可以了。

2、然后,看看 StatefulSet 实现每个 Pod 实例配置各自 Volume 的方式

(1) 运维人员已经在系统里创建好了符合条件的 PV,这也是一个前提。例如:

kind: PersistentVolume
apiVersion: v1
metadata:
  name: pv-volume
  labels:
    type: local
spec:
  capacity:
    storage: 10Gi
  rbd:
    monitors:
    - '10.16.154.78:6789'
    - '10.16.154.82:6789'
    - '10.16.154.83:6789'
    pool: kube
    image: foo
    fsType: ext4
    readOnly: true
    user: admin
    keyring: /etc/ceph/keyring
    imageformat: "2"
    imagefeatures: "layering"

(2) 利用 StatefulSet 的 volumeClaimTemplates 字段,为创建的每一个 Pod 声明一个对应的 PVC(这个 PVC 的名字,会被分配一个与这个 Pod 完全一致的编号)。

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:1.9.1
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes:
      - ReadWriteOnce
      resources:
        requests:
          storage: 1Gi

(3) Kubernetes 自动通过 Persistent Volume 机制为每个 PVC 绑定上对应的 PV。

所以,即使 Pod 删除,原来声明的 PVC 及其后面绑定的 PV是没影响的,重建之后因为 Pod 编号命名没变,按照名字依然可以直接找到同名 PVC 实现绑定。这样新的 Pod 就可以挂载到旧 Pod 对应的那个 Volume,并且获取到保存在 Volume 里的数据。

总结

  1. StatefulSet 是 Kubernetes 中用来处理“有状态应用”的。这个应用是指“多实例应用”。多实例应用的”状态“可以抽象为”实例之间的拓扑状态“和”实例各自的存储状态“ 。
  2. 为了区分“有状态应用”的各个 Pod 实例:StatefulSet 会为创建的 Pod 在名字里加上事先约定好的顺序编号。
  3. 为了创建稳定网络标识维护各个实例拓扑网络的“有状态”:Kubernetes 通过 Headless Service,为这些有编号的 Pod,在 DNS 服务器中生成带有同样编号的 DNS 记录。只要 StatefulSet 能够保证这些 Pod 名字里的编号不变,那么 Service 里类似于 web-0.nginx.default.svc.cluster.local 这样的 DNS 记录也就不会变,而这条记录解析出来的 Pod 的 IP 地址,则会随着后端 Pod 的删除和再创建而自动更新。这当然是 Service 机制本身的能力,不需要 StatefulSet 操心。
  4. 为了从而维护各个实例数据存储的“有状态”:StatefulSet 为每一个 Pod 分配并创建一个同样编号的 PVC。这样,Kubernetes 就可以通过 Persistent Volume 机制为这个 PVC 绑定上对应的 PV,从而保证了每一个 Pod 都拥有一个独立的 Volume。

参考:
《深入剖析Kubernetes》张磊,极客时间,容器编排和Kubernetes作业管理 18 19 20

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值