文章

k8s服务发现

k8s为什么需要服务发现,服务发现的流程是怎么样的

k8s服务发现

基础知识

了解服务发现之前,先明确k8s内部为什么需要服务发现,而在了解为什么需要服务发现之前,先看下pod、service他们的关系。

  1. 应用运行在k8s的容器之中,而容器运行在pod内,一般来说一个pod一个容器
  2. 每个pod都处于一个扁平的IP网段内,每个pod都有其唯一IP,在这个网段内pod之间可以直接进行通信

在这个网络内,会新增、删除pod,从而也会分配新的IP或者删除IP,对于我们在pod内的应用来说,就得手动维护一个应用的IP列表,以此知道我们要访问别的应用时,对应的是哪个IP,非常耦合以及痛苦。

好在k8s提供了service这个组件,从网络的层面上,一个service代表了一组pod。当需要访问pod内的应用时,访问service的ip即可,而service除非手动删除变更,否则他的ip是稳定的。因此外界从直接访问ip经常变化的pod,变成了访问ip稳定的service,再由service将流量负载均衡到这些pod上。

服务发现是什么

平时用浏览器上过网都知道,输入一个网址比如google.com就能访问内容,背后是DNS帮我们将google.com解析成IP地址,最终浏览器才能基于TCP协议,从本地连接到这个服务提供商的IP地址。所以DNS属于服务发现的其中一种方式。

对于k8s内部来说,如果一个service中的pod的应用,想访问处于另一个service中pod的应用,最简单的就是知道对方service的IP地址。但我们编写应用的时候往往更希望连接的是一个service的名字而不是service的IP,因为service的IP说到底也还是动态分配的,如果service经过销毁重建,IP变化了,应用代码也得跟着改。

因此k8s需要服务发现,将service的名字解析为service的IP供应用去正确访问。

服务发现实际上包含两个功能点:

  1. 服务注册
  2. 服务发现

service-registration.png

服务注册

k8s使用的是DNS服务发现,每个k8s集群都会在kube-system命名空间中运行DNS服务(这个服务本质上也是pod内的应用),称为集群DNS。每个service都会自动注册到集群DNS中,注册过程如下:

  1. 向API server提交一个新的service定义请求,请求经过认证、鉴权等准入策略后放行
  2. Service分配得到ClusterIP,保存到集群数据仓库
  3. 在集群范围内传播该Service配置
  4. 集群DNS感知到该Service创建,创建DNS A记录

可以看到最关键的是第4步,这一步创建了A记录指向ClusterIP,这个A记录名称就是service的metadata.name,并且之后会持续关注这个Service对象。

接着就是service管理的pods,k8s自动为每个service创建endpoints对象,其中保存的是匹配标签选择器的pod列表,service后续会将流量负载均衡到这些pod上。

服务发现

img

为了让应用使用上集群DNS提供的服务发现功能,每个pod中的每个容器的/etc/resolv.conf文件都被配置为使用集群DNS进行解析。

比如图中my-app中的应用想要访问your-app中的应用,就得拿着”your-app-svr”这个名称去查询DNS服务拿到IP 10.0.0.20,但显然容器的网关并不维护到这个IP的路由,最终会缺省地转发到pod所在节点的网卡上,随后转发到节点的缺省网关,经过节点内核。

在继续之前,有必要插播一下:k8s的每个节点上都会运行一个名为kube-proxy的服务,其会监控API Server上service的变化,这些变化包括endpoints的变化(对应pod的增删),并根据这些变化创建iptablesIPVS规则,目的是告知节点捕获目标为Service的网络报文并转发给pod。

因此my-app发送出来的这个报文,最终会到达节点的缺省网关,经过节点内核时根据kube-proxy写入的路由规则,报文的目标IP被改成目标pod的IP。

参考

浅谈 Kubernetes 中的服务发现

本文由作者按照 CC BY 4.0 进行授权