有状态的节点控制器 -- StatefulSet 及其网络状态

2022-06-27 17:05:23 浏览数 (1)

1. 引言

上一篇文章中,我们详细介绍了 Kubernetes 中的作业副本控制器 Deployment:

详解 K8s 作业副本控制器 Deployment

对于 Deployment 来说,每一种 Pod 都是完全一样的,因此,Deployment 可以按照配置任意增删任何一个 Pod,但在我们的实际场景中,情况并非总是如此,多个实例之间往往会有着复杂的依赖关系,比如主从、主备关系等等。这些情况下,实例之间的地位是不对等的,这样的应用就被称为“有状态应用”。

容器的解决方案是针对无状态应用场景的最佳实践,但对于有状态应用来说,就并非如此了。Kubernetes 用 StatefulSet 解决了有状态应用编排的问题,本文我们就来初步认识一下 StatefulSet。

2. 应用组织的两种状态

StatefulSet 将应用设计抽象为了两种状态:

2.1 拓扑状态

应用存在多个实例,但多个实例地位并不完全对等。

应用的多个实例必须按照某种顺序启动,并且必须成组存在,例如一个应用中必须存在一个 A Pod 和两个 B Pod,且 A Pod 必须先于 B Pod 启动的场景。

2.2 存储状态

应用存在多个实例,但每个实例绑定的存储数据不同,那么对于一个 Pod 来说,无论它是否被重新创建,它读到的数据状态应该是一致的。

3. 实战拓扑状态下的 StatefulSet

一个最简单的场景,我们用一个 nginx Headless Service 反向代理 Kubernetes 中的两个 Pod,并且这两个 Pod 具有不完全对等的网络身份,这个情况下,就是典型的拓扑状态下的 StatefulSet 的使用场景。

3.1 初识 Kubernetes Service

1. 两种访问方式

Kubernetes 的 Service 就是对外提供的可访问服务,它有两种访问方式:

  1. VIP 方式:它是 Virtual IP 的缩写,通过将服务绑定到 Kubernetes 虚拟的 IP 地址,提供给外部调用,通过虚拟 IP 地址隐藏了服务的具体实现与地址。
  2. DNS 方式:与虚拟 IP 地址类似,外部通过访问 DNS 记录的方式实现对具体 Service 的转发。

2. DNS 的两种处理方式

  1. Normal Service:将 DNS 地址绑定到虚拟 IP 地址,从而复用虚拟 IP 地址的设计和逻辑;
  2. Headless Service:将 DNS 地址直接代理到 Pod。

3.2 配置一个 Headless nginx Service

下面的配置文件配置了一个 Headless 方式启动的 nginx Service:

代码语言:javascript复制
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx

这个配置中,clusterIP 设置为了 None,表示不为这个 Service 分配 VIP,而是通过 Headless DNS 的方式来处理该 Service 的调用。

具体的 DNS 地址是这样的:

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

这个 DNS 就是 Kubernetes 为 Pod 分配的唯一可解析身份,这样一来,只要有了 Pod 的名字和 Service 的名字,我们就能唯一确定一个能够访问这个 Pod 的 DNS 地址了。

3.3 配置 StatefulSet

下面,我们就来配置一个使用上述 nginx Service 的 StatefulSet:

代码语言:javascript复制
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

这个 yaml 与 Deployment 的配置的唯一区别就是多了一个 ServiceName=nginx 的配置,它意味着 StatefulSet 控制器必须在执行控制循环时使用 nginx 这个 Service 来保证每一个 Pod 可解析。

通过 kubectl get 命令就可以查看 pod 的启动:

$ kubectl get statefulset web

通过命令 kubectl get pods -w -l app=nginx 可以看到这些 pod 的启动顺序,每一个 pod 都被命名为了 web-<编号>,比如 web-0 与 web-1,他们是严格按照编号顺次启动的。

如果我们通过 kubectl delete pod -l app=nginx,再通过 kubectl get 观察,就可以看到两个 Pod 删除后,Kubernetes 会按照原先的编号和顺序再次启动一组新的 pod,并且他们各自的网络身份与原 Pod 是一一对应的。

4. 结论

通过上述实践,我们看到,只要我们使用 DNS 记录来访问 StatefulSet 控制器控制下的 Pod,即使 Pod 发生了宕机和重启,DNS 记录对应的 nginx 记录本身是不会发生变化的,同一个“名字-编号”组合的 Pod 在 StatefulSet 中总是稳定地对外提供服务的,进而实现了整个“网络状态”的稳定。

0 人点赞