Kubernetes 设计模式笔记 —— Service Discovery

Service Discovery 主要负责提供一类稳定的入口,利用这些入口服务的客户端能够访问到提供服务的后台实例。

部署到 Kubernetes 的应用很少是单独存在的,它们往往需要与集群内部的其他服务甚至集群外部的系统产生交互。
比如 DaemonSet 或者 ReplicaSet 中的 long-running Pod,通常需要处理来自外部的比如 HTTP 连接请求。这些时候,服务的消费者就需要某种机制发现 Pod 的位置,因为 Pod 会动态地由 scheduler 分配给节点,其位置在应用扩展和收缩时也会发生变化。

在 Kubernetes 之前,最常用的机制是 client-side discovery。当一个消费者需要访问另一个有可能扩展到多个实例上的服务时,消费者本身会有一个 agent 负责查询记录有实例信息的注册表,选择其中一个实例进行访问。这个 agent 有可能嵌入到消费者中(比如 Zookeeper client、Consul client 或 Ribbon),也有可能作为并置进程存在(比如 Prana)。


Client-side service discovery

在后 Kubernetes 时代,分布式系统的很多非功能性职责比如资源调度、健康检查、自恢复、资源隔离等都交由平台负责。服务发现和负载均衡也属于这部分职责。
在 Kubernetes 里,所有服务实例注册以及注册信息的访问这类工作,都在后台完成。服务的消费者访问一个固定的虚拟服务端点,这个端点能够动态地发现提供服务的 Pod。


Server-side service discovery

Internal Service Discovery

当我们创建一个包含多个副本的 Deployment,scheduler 会将 Pod 调度到合适的节点,每个 Pod 在启动之前都会获得一个集群 IP。
如果另一个客户端服务想要访问 Deployment 部署的服务,想要获悉集群 IP 的具体地址并没有简单直接的方式。
因而 Kubernetes 提供了 Service 组件。Service 可以为一组相同功能的 Pod 提供一个不变的稳定入口。

apiVersion: v1
kind: Service
metadata:
  name: random-generator
spec:
  selector:
    app: random-generator
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP

上述配置会创建一个名为 random-generator 的 Service,其类型为 ClusterIP(默认值),会在 80 端口上监听 TCP 连接,并转发到所有匹配的 Pod。
匹配条件 app: random-generator 由 selector 指定。
不管 Pod 是何时或者怎样创建的,任何带有 app: random-generator 标签的 Pod 都会作为转发的目标。

Internal service discovery

需要注意的一点是,当 Service 创建完成时,它获取到的 ClusterIP 只允许 Kubernetes 集群内部访问。只要 Service 定义一直存在,IP 就保持不变。
关于集群内部的其他应用如何知晓这个动态的 ClusterIP 具体是多少,有两种方式:

  • 环境变量:此方式的主要问题是依赖于 Service 创建的时间,即 Service 必须在环境变量注入之前创建。因为环境变量无法注入到已经在运行的 Pod 中
  • DNS lookup:Kubernetes 包含一个内置的 DNS 服务,被所有的 Pod 默认配置使用。当一个新的 Service 创建后,它会自动获得一条新的 DNS 记录,能够被所有 Pod 使用。比如 random-generator.default.svc.cluster.local,其中 random-generator 表示 Service 的名称,default 表示命名空间,svc 表示这是一个 Service,cluster.local 是集群前缀,可以省略
Service 的高级特性

Multiple ports
一个 Service 定义可以支持多个源端口和目标端口。

Session affinity
当新的请求出现时,Service 默认会随机挑选一个 Pod 作为转发的目标。可以配置 sessionAffinity: ClientIP,从而来自同一个客户端 IP 的请求都会转发给同一个 Pod。

Readiness Probes
如果 Pod 定义了 readiness 检查,当它失效时,即便标签匹配,该 Pod 也会从 Service 端点中移除。

Virtual IP
ClusterIP 类型的 Service 在创建时会获得一个稳定的虚拟 IP,这个 IP 与任何网络接口都不相关,在现实中并不真实存在。
是每个节点上都有的 kube-proxy 意识到 Service 的存在后,更新节点上的 iptables,设置规则捕获目标是这个虚拟 IP 的网络包,将目标地址替换为选定的 Pod IP 地址。
iptables 中添加的规则并不包含 ICMP 协议,因此 Service 的 IP 地址无法被 ping。

Choosing ClusterIP
在 Service 创建过程中,可以通过 .spec.clusterIP 指定其使用的 IP 地址。

Manual Service Discovery

当我们创建一个带有 selector 的 Service 时,Kubernetes 会负责在 endpoint 资源列表里记录所有可提供服务的 Pod。可以使用类似 kubectl get endpoints random-generator 的命令查看 endpoint 列表。
除了将请求转发给集群内部的 Pod,还可以将连接转发给外部的 IP 和端口,比如像下面这样创建一个不带 selector 的 Service 并手动创建 endpoint 资源:

apiVersion: v1
kind: Service
metadata:
  name: external-service
spec:
  type: ClusterIP
  ports:
  - protocol: TCP
    port: 80
apiVersion: v1
kind: Endpoints
metadata:
  name: external-service
subsets:
  - addresses:
    - ip: 1.1.1.1
    - ip: 2.2.2.2
    ports:
    - port: 8080

上面创建的 Service 和之前的一样,都只能在集群内部访问。区别在于其 endpoint 是手动维护的,并且指向了集群外部的 IP 地址。


Manual service discovery

上述机制主要应用在需要访问外部资源的时候。Endpoint 还可以绑定 Pod 的 IP 地址,但是不支持其他 Service 的虚拟 IP。

Service 的一个优势在于它允许添加或者删除 selector,随意指向外部或内部的服务提供者,而不需要删除自身的资源定义。从而 Service 的 IP 地址保持不变。因此 Service 的客户端可以继续使用原来的访问地址,而 Service 本身指向的服务提供者可能已经从 on-premise 转到了 Kubernetes,客户端不受任何影响。

apiVersion: v1
kind: Service
metadata:
  name: database-service
spec:
  type: ExternalName
  externalName: my.database.example.com
  ports:
  - port: 80

ExternalName 是另外一种方式,通过 DNS CNAME 为外部的 endpoint 创建别名,而不是借助 IP 地址和代理。

面向集群外部的服务发现

前面提到的服务发现机制都使用了虚拟 IP,而这个虚拟 IP 本身只支持从集群内部访问。但是 Kubernetes 集群并不是与外部世界完全隔离的,除了 Pod 有时候需要访问外部资源以外,相反方向的访问也是经常发生的,即外部应用需要访问 Pod 提供的 endpoint。

NodePort

第一种创建 Service 并将其暴露给外部世界的方式是 NodePort

apiVersion: v1
kind: Service
metadata:
  name: random-generator
spec:
  type: NodePort
  selector:
    app: random-generator
  ports:
  - port: 80
    targetPort: 8080
    nodePort: 30036
    protocol: TCP

上述配置会创建一个 Service,在虚拟 IP 的 80 端口接收客户端连接,并有选择地转发给匹配 selector app: random-generator 的 后端 Pod 上的 8080 端口。
除此之外,上面的配置还会保留所有节点上的 30036 端口,并将所有访问此端口的连接转发给 Service。从而不仅可以在集群内部通过虚拟 IP 连接 Service,还可以借助每个节点上的保留端口从集群外部访问 Service。

Node port Service Discovery

NodePort 的特点和问题:

  • Port number:除了可以指定端口号以外(nodePort: 30036),还可以让 Kubernetes 自己选择可用的端口
  • Firewall rules:NodePort 会使用每一个节点上的指定端口,因此可能需要配置防火墙允许外部客户端连接指定端口
  • Node selection:客户端可以连接集群中的任意一个节点,潜在的问题是,当该节点不可用时,客户端应用负责实现访问另一个健康节点的功能。因此,在节点前部署一个负载均衡器会是一个好的措施
  • Pod selection:当客户端尝试通过节点端口连接服务时,其请求会被路由到随机选择的 Pod,该 Pod 可能位于同一个节点上,也可能位于另外的节点上。可以在 Service 的定义中使用 externalTrafficPolicy: Local 选项强制将请求只转发给当前节点上的 Pod。这同时会引发另一个问题,即必须确保每个节点上都有 Pod 部署(比如 daemon service),或者客户端知道哪个节点上有健康的 Pod 在运行
  • Source address:当 Service 类型为 NodePort 时,网络包中的源 IP 地址(即客户端 IP)会被替换成节点的内部 IP。比如客户端发送网络包给 node1,假如说 Pod 位于 node2 上,网络包从 node1 转发到 node2,网络包中的源 IP 地址会被替换成 node1 的 IP,目标地址替换成 Pod 的地址。当 Pod 最终接收到请求,源 IP 地址已经被替换成 node1 的地址。这类行为同样可以通过 externalTrafficPolicy: Local 避免
LoadBalancer

另一种针对外部客户端的服务发现方式是通过负载均衡器。NodePort 类型的 Service 构建在默认的 Service(type: ClusterIP)之上,额外在每个节点上开放了一个端口。
该方式的缺点就是,我们仍需要一个负载均衡器来为客户端应用选择健康的节点。而 LoadBalancer 类型的 Service 则解决了这个问题。

除了 NodePort 类型所做的操作以外,LoadBalancer 类型的 Service 还会借助云服务提供商的负载均衡器将服务暴露给外部使用。

Load balancer service discovery

apiVersion: v1
kind: Service
metadata:
  name: random-generator
spec:
  type: LoadBalancer
  clusterIP: 10.0.171.239
  loadBalancerIP: 78.11.24.19
  selector:
    app: random-generator
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP
status:
  loadBalancer:
    ingress:
    - ip: 146.148.47.155

应用层服务发现(Ingress)

不同于之前的服务发现机制,Ingress 并不是一种 Service 类型,而是一个独立的 Kubernetes 资源,部署在 Service 前端作为 smart router 和集群的入口。
Ingress 通常会提供基于 HTTP 协议的对 Service 的访问, 通过外部可见的 URL、负载均衡、SSL termination、基于名称的 virtual hosting 等。
为了 Ingress 能生效,集群本身必须要有一个或者多个 Ingress controller 在运行。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: random-generator
spec:
  backend:
    serviceName: random-generator
    servicePort: 8080

上述配置会分配一个可供外部访问的 IP 地址并在 80 端口对外暴露 Service。看上去与 type: LoadBalancer 并没有什么区别。实际上 Ingress 能够重复使用同一个外部负载均衡器和 IP 指向多个 Service。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: random-generator
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
    paths:
    - path: /
      backend:
        serviceName: random-generator
        servicePort: 8080
    - path: /cluster-status
      backend:
        serviceName: cluster-status
        servicePort: 80
Application layer service discovery

Ingress 是 Kubernetes 上最强大同时也最复杂的服务发现机制,其最常用的场景是当多个服务需要在同一个 IP 地址下,同时这些服务又都使用同样的第七层协议(通常是 HTTP)。

总结

Service Discovery mechanisms

参考资料

Kubernetes Patterns

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容