1、有无状态服务

前面我们了解了 Deployment 和 ReplicaSet 两种资源对象得使用,在实际使用的过程中,Deployment 并不能编排所有类型的应用,对无状态服务编排是非常容易的,但是对于有状态服务就无能为力了。我们需要先明白一个概念:什么是有状态服务,什么是无状态服务。

  • 无状态服务(Stateless Service):该服务运行的实例不会在本地存储需要持久化的数据,并且多个实例对于同一个请求响应的结果是完全一致的,比如前面我们讲解的 WordPress 实例,我们是不是可以同时启动多个实例,但是我们访问任意一个实例得到的结果都是一样的吧?因为他唯一需要持久化的数据是存储在MySQL数据库中的,所以我们可以说 WordPress 这个应用是无状态服务,但是 MySQL 数据库就不是了,因为他需要把数据持久化到本地。
  • 有状态服务(Stateful Service):就和上面的概念是对立的了,该服务运行的实例需要在本地存储持久化数据,比如上面的 MySQL 数据库,你现在运行在节点 A,那么他的数据就存储在节点 A 上面的,如果这个时候你把该服务迁移到节点 B 去的话,那么就没有之前的数据了,因为他需要去对应的数据目录里面恢复数据,而此时没有任何数据。

现在对有状态和无状态有一定的认识了吧,比如我们常见的 WEB 应用,是通过 Session 来保持用户的登录状态的,如果我们将 Session 持久化到节点上,那么该应用就是一个有状态的服务了,因为我现在登录进来你把我的 Session 持久化到节点 A 上了,下次我登录的时候可能会将请求路由到节点 B 上去了,但是节点 B 上根本就没有我当前的 Session 数据,就会被认为是未登录状态了,这样就导致我前后两次请求得到的结果不一致了。所以一般为了横向扩展,我们都会把这类 WEB 应用改成无状态的服务,怎么改?将 Session 数据存入一个公共的地方,比如 Redis 里面,是不是就可以了,对于一些客户端请求 API 的情况,我们就不使用 Session 来保持用户状态,改成用 Token 也是可以的。

无状态服务利用我们前面的 Deployment 可以很好的进行编排,对应有状态服务,需要考虑的细节就要多很多了,容器化应用程序最困难的任务之一,就是设计有状态分布式组件的部署体系结构。由于无状态组件没有预定义的启动顺序、集群要求、点对点 TCP 连接、唯一的网络标识符、正常的启动和终止要求等,因此可以很容易地进行容器化。诸如数据库,大数据分析系统,分布式 key/value 存储、消息中间件需要有复杂的分布式体系结构,都可能会用到上述功能。为此,Kubernetes 引入了StatefulSet这种资源对象来支持这种复杂的需求。StatefulSet类似于ReplicaSet,但是它可以处理 Pod 的启动顺序,为保留每个 Pod 的状态设置唯一标识,具有以下几个功能特性:

  • 稳定的、唯一的网络标识符
  • 稳定的、持久化的存储
  • 有序的、优雅的部署和缩放
  • 有序的、优雅的删除和终止
  • 有序的、自动滚动更新

2、初识Service

Service其实在之前我们提到过,Service 是应用服务的抽象,通过 Labels 为应用提供负载均衡和服务发现,每个 Service 都会自动分配一个 cluster IP 和 DNS 名,在集群内部我们可以通过该地址或者通过 FDQN 的形式来访问服务。比如,一个 Deployment 有 3 个 Pod,那么我就可以定义一个 Service,有如下两种方式来访问这个 Service:

  • cluster IP 的方式,比如:当我访问 10.109.169.155 这个 Service 的 IP 地址时,10.109.169.155 其实就是一个 VIP,它会把请求转发到该 Service 所代理的 Endpoints 列表中的某一个 Pod 上。具体原理我们会在后面的 Service 章节中和大家深入了解。
  • Service 的 DNS 方式,比如我们访问“mysvc.mynamespace.svc.cluster.local”这条 DNS 记录,就可以访问到 mynamespace 这个命名空间下面名为 mysvc 的 Service 所代理的某一个 Pod。

对于 DNS 这种方式实际上也有两种情况:

  • 第一种就是普通的 Service,我们访问“mysvc.mynamespace.svc.cluster.local”的时候是通过集群中的 DNS 服务解析到的 mysvc 这个 Service 的 cluster IP 的
  • 第二种情况就是Headless Service,对于这种情况,我们访问“mysvc.mynamespace.svc.cluster.local”的时候是直接解析到的 mysvc 代理的某一个具体的 Pod 的 IP 地址,中间少了 cluster IP 的转发,这就是二者的最大区别,Headless Service 不需要分配一个 VIP,而是可以直接以 DNS 的记录方式解析到后面的 Pod 的 IP 地址。

3、初识HeadlessService

Service我们了解了今天我们来了解一下Headless Service。

普通的service,每个 Service 都会自动分配一个 cluster IP 和 DNS 名,在集群内部我们可以通过该地址或者通过 FDQN 的形式来访问服务,实际上Headless Service在定义上和普通的 Service 几乎一致, 只是他的clusterIP=None,所以,这个 Service 被创建后并不会被分配一个 cluster IP,而是会以 DNS 记录的方式暴露出它所代理的 Pod,而且还有一个非常重要的特性,对于Headless Service所代理的所有 Pod 的 IP 地址都会绑定一个如下所示的 DNS 记录:

<pod-name>.<svc-name>.<namespace>.svc.cluster.local
# 作用:这是一个Headless Service
# 实际上Headless Service在定义上和普通的Service几乎一致 只不过它的clusterIP:None 这样就不会给这个service分配集群内ip通过dns的方式访问该service服务
apiVersion: v1
kind: Service
metadata:
  name: nginx                            # 所定义的service的名称
  namespace: default                     # 这个service所在的命名空间
  labels:                                # 这个标签 随便定义 不重要
    app: nginx
spec:
  ports:
    - name: http                           # 所定义的service里面ports端口的名称叫做http
      port: 80                             # 所定义的service的端口号80
  clusterIP: None                        # clusterIP:None 看到这个 那么就一定是Headless Service 不会为该service分配集群内ip而是通过dns的形式访问
  selector:                              # 这个就很重要了  这个指的是所定义的service关联的要管理的pod的标签 所有pod的标签被定义为了app:mynginx的都将会通过该service被访问到 相当重要
    app: mynginx 

# StatefulSet 资源对象是要结合 Headless Service 提供服务的!

这个 DNS 记录正是 Kubernetes 集群为 Pod 分配的一个唯一标识,只要我们知道 Pod 的名字,以及它对应的 Service 名字,就可以组装出这样一条 DNS 记录访问到 Pod 的 IP 地址,这个能力是非常重要的,接下来我们就来看下 StatefulSet 资源对象是如何结合 Headless Service 提供服务的。

HeadlessService.yaml

# 作用:这是一个Headless Service
# 实际上Headless Service在定义上和普通的Service几乎一致 只不过它的clusterIP:None 这样就不会给这个service分配集群内ip通过dns的方式访问该service服务
apiVersion: v1
kind: Service
metadata:
  name: nginx                            # 所定义的service的名称
  namespace: default                     # 这个service所在的命名空间
  labels:                                # 这个标签 随便定义 不重要
    app: nginx
spec:
  ports:
    - name: http                           # 所定义的service里面ports端口的名称叫做http
      port: 80                             # 所定义的service的端口号80
  clusterIP: None                        # clusterIP:None 看到这个 那么就一定是Headless Service 不会为该service分配集群内ip而是通过dns的形式访问
  selector:                              # 这个就很重要了  这个指的是所定义的service关联的要管理的pod的标签 所有pod的标签被定义为了app:nginx的都将会通过该service被访问到 相当重要
    app: mynginx

# StatefulSet 资源对象是要结合 Headless Service 提供服务的!
Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐