一、服务注册

服务注册过程指的是在服务注册表中登记一个服务,以便让其它服务发现。

下图是service注册与发现流程(service简称svc):

Kubernetes 使用 DNS 作为服务注册表。

为了满足这一需要,每个 Kubernetes 集群都会在 kube-system 命名空间中用 Pod 的形式运行一个 DNS 服务,通常称之为集群 DNS。

每个 Kubernetes 服务(Service资源对象)都会自动注册到集群 DNS 之中。

Service注册过程大致如下:

1、向 API Server 用 POST 方式提交一个新的 Service 定义;

2、Service 得到一个 ClusterIP(虚拟 IP 地址),并保存到集群数据仓库;

3、在集群范围内传播 Service 配置;

4、集群 DNS 服务得知该 Service 的创建,据此创建必要的 DNS A 记录。


上面过程中,最后一步是关键环节。

集群 DNS 使用的是 CoreDNS,以 Kubernetes 原生应用的形式运行。CoreDNS 实现了一个控制器,会对 API Server 进行监听,一旦发现有新建的 Service 对象,就创建一个从 Service 名称映射到 ClusterIP 的域名记录。这样 Service 就不必自行向 DNS 进行注册,CoreDNS 控制器会关注新创建的 Service 对象,并实现后续的 DNS 过程。

DNS 中注册的名称就是 metadata.name,而 ClusterIP 则由 Kubernetes 自行分配。

Service 对象注册到集群 DNS 之中后,就能够被运行在集群中的其它 Pod 发现了。

Endpoint 对象

Service 的前端创建成功并注册到服务注册表(DNS)之后,剩下的就是后端的工作了。后端包含一个 Pod 列表,Service 对象会把流量分发给这些 Pod。

毫无疑问,这个 Pod 列表需要是最新的。

Service 对象有一个 Label Selector 字段,这个字段是一个标签列表,符合列表条件的 Pod 就会被服务纳入到服务的负载均衡范围之中。参见下图:

Kubernetes 自动为每个 Service 创建 Endpoints 对象。Endpoints 对象的职责就是保存一个符合 Service 标签选择器标准的 Pod 列表,这些 Pod 将接收来自 Service 的流量。

上面的图中,Service 会选择两个 Pod,并且还展示了 Service 的 Endpoints 对象,这个对象里包含了两个符合 Service 选择标准的 Pod 的 IP。

在后面我们将解释网络如何把 ClusterIP 流量转发给 Pod IP 的过程,还会引用到 Endpoints 对象。

二、服务发现

集群中pod访问pod的场景举例:

1、场景一:prometheus server(是一个pod) 访问 exporter(另一个pod)抓取监控数据

2、场景二:prometheus server将告警消息打到alertmanager

假设我们在一个 Kubernetes 集群中有两个应用,my-app 和 your-app,my-app 的 Pod 的前端是一个 名为 my-app-svc 的 Service 对象;your-app Pod 之前的 Service 就是 your-app-svc。

这两个 Service 对象对应的 DNS 记录是:

my-app-svc:10.0.0.10
your-app-svc:10.0.0.20

要使用服务发现功能,每个 Pod 都需要知道集群 DNS 的位置才能使用它。因此每个 Pod 中的每个容器的 /etc/resolv.conf 文件都被配置为使用集群 DNS 进行解析。

如果 my-app 中的 Pod 想要连接到 your-app 中的 Pod,就得向 DNS 服务器发起对域名 your-app-svc 的查询。假设它们本地的 DNS 解析缓存中没有这个记录,则需要把查询提交到集群 DNS 服务器。会得到 you-app-svc 的 ClusterIP(VIP)。

这里有个前提就是 my-app 需要知道目标服务的名称。

至此,my-app 中的 Pod 得到了一个目标 IP 地址,然而这只是个虚拟 IP,在转入目标 Pod 之前,还有些网络工作要做。

网络

一个 Pod 得到了 Service 的 ClusterIP 之后,就尝试向这个 IP 发送流量。然而 ClusterIP 所在的网络被称为 Service Network,这个网络有点特别——没有路由指向它。

因为没有路由,所有容器把发现这种地址的流量都发送到了缺省网关(名为 CBR0 的网桥)。这些流量会被转发给 Pod 所在节点的网卡上。节点的网络栈也同样没有路由能到达 Service Network,所以只能发送到自己的缺省网关。路由到节点缺省网关的数据包会通过 Node 内核——这里有了变化。

回顾一下前面的内容。首先 Service 对象的配置是全集群范围有效的,另外还会再次说到 Endpoints 对象。我们要在回顾中发现他们各自在这一过程中的职责。

每个 Kubernetes 节点上都会运行一个叫做 kube-proxy 的系统服务。这是一个基于 Pod 运行的 Kubernetes 原生应用,它所实现的控制器会监控 API Server 上 Service 的变化,并据此创建 iptables 或者 IPVS 规则,这些规则告知节点,捕获目标为 Service 网络的报文,并转发给 Pod IP。

有趣的是,kube-proxy 并不是一个普遍意义上的代理。它的工作不过是创建和管理 iptables/IPVS 规则。这个命名的原因是它过去使用 unserspace 模式的代理。

每个新 Service 对象的配置,其中包含它的 ClusterIP 以及 Endpoints 对象(其中包含健康 Pod 的列表),都会被发送给 每个节点上的 kube-proxy 进程。kube-proxy 会创建 iptables 或者 IPVS 规则,告知节点捕获目标为 Service ClusterIP 的流量,并根据 Endpoints 对象的内容转发给对应的 Pod。

也就是说每次节点内核处理到目标为 Service 网络的数据包时,都会对数据包的 Header 进行改写,把目标 IP 改为 Service Endpoints 对象中的健康 Pod 的 IP。

原本使用的 iptables 正在被 IPVS 取代(Kubernetes 1.11 进入稳定期)。长话短说,iptables 是一个包过滤器,并非为负载均衡设计的。IPVS 是一个 4 层的负载均衡器,其性能和实现方式都比 iptables 更适合这种使用场景。

三、总结


需要消化的内容很多,简单回顾一下。

创建新的 Service 对象时,会得到一个虚拟 IP,被称为 ClusterIP。服务名及其 ClusterIP 被自动注册到集群 DNS 中,并且会创建相关的 Endpoints 对象用于保存符合标签条件的健康 Pod 的列表,Service 对象会向列表中的 Pod 转发流量。
与此同时集群中所有节点都会配置相应的 iptables/IPVS 规则,监听目标为 ClusterIP 的流量并转发给真实的 Pod IP。这个过程如下图所示:

一个 Pod 需要用 Service 连接其它 Pod。首先向集群 DNS 发出查询,把 Service 名称解析为 ClusterIP,然后把流量发送给位于 Service 网络的 ClusterIP 上。然而没有到 Service 网络的路由,所以 Pod 把流量发送给它的缺省网关。这一行为导致流量被转发给 Pod 所在节点的网卡,然后是节点的缺省网关。这个操作中,节点的内核修改了数据包 Header 中的目标 IP,使其转向健康的 Pod。

最终所有 Pod 都是在同一个可路由的扁平的叠加网络上,剩下的内容就很简单了。

四、服务发现配置

直接上案例:如下配置:alertmanager.prom-alert.svc:9093

那么prometheus是如何找到alertmanager并将数据精确转发给alertmanager的?

流程:

1、服务注册:alertmanager组件会创建一个Service(rancher点击服务发现能看到,alertmanager就是service的名称),Service会以my-svc.my-namespace.svc.cluster.local 的形式指派一个 DNS A 记录,并分配一个Clusterip给Service

2、服务发现:prometheus服务实例有告警事件产生的时候,就将消息投递给alertmanager.prom-alert.svc:9093,其实就是去DNS上精确的找到prom-alert命名空间下,开放端口为9093的Service,然后发送数据

参考:

一文了解 Kubernetes 中的服务发现

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐