k8s服务代理和发现

今天主要学习一下k8s的Service,之前的文章中有介绍过k8s内服务访问的方式,但是自己也没有特别的去了解具体的实现方式,今天就专门再来学习下Service这个非常重要的资源。今天主要是学习官方的文档,文档地址:Service。另外我还是觉得中文文档翻译之后不太好理解,所以最好我觉得还是看英文文档。
k8s中的Service是一个抽象的概念,它定义了一组逻辑的Pod以及访问这些Pod的策略(有时这种模式被称作微服务)。直白点讲Service本身并不提供所谓的服务,它只是告诉你怎么访问你想要访问的服务。通常情况下Service是通过Selector来定位到目标Pod的,当然你也可以通过创建相应的Endpoints定位到具体的服务。

1、虚拟IP和服务代理

在k8s集群中的每个节点上都会运行着一个kube-proxy,由kube-proxy负责为非ExternalName类型的Service实现一种虚拟IP。
之所以k8s选择代理模式将入站流量转发到后端,而不是通过配置多个A值(ipv6为AAAA)0的DNS记录,然后依赖轮询名称解析。主要有几点原因:
1、一直以来DNS都不遵守数据的TTL(Time to live),即使在数据过期后依然会进行缓存;
2、对于某些应用只进行一次DNS查找,但是结果却会永久缓存;
3、即使应用和库进行了适当的重新解析,DNS 记录上的较低的TTL值低或零值可能会给DNS带来高负载,从而使管理变得困难。

1、用户空间代理模式

这种模式下kube-proxy会观察k8s控制节点对Service 对象和 Endpoints对象的添加和删除操作。对于每一个Servicekube-proxy都会在本地的节点上打开一个随机端口,任何连接到这个“代理端口”的请求,都会被转发到该Service对应的后端Pods中的某一个上面。具体使用哪一个Pod,是kube-proxy基于该Service的设置项SessionAffinity来确定的。
最后,kube-proxy会配置iptables规则,捕获到达该ServiceclusterIP(是虚拟 IP)和 port的请求,并重定向到代理端口,然后由代理端口再代理请求到后端Pod
用户空间模式下的kube-proxy默认通过轮转算法选择后端。
用户空间模式见下图:

图-1.png

2、iptables代理模式

和用户空间代理模式,这种模式下kube-proxy也会观察k8s控制节点对Service对象和Endpoints对象的添加和删除操作。 不同的是对每个Servicekube-proxy会通过配置iptables规则,从而捕获到达该ServiceclusterIPport的请求,然后将请求重定向到该Service后端集合中的某个上面。对于每个Endpoints对象,kube-proxy也会配置 iptables规则,这个规则会选择一个后端Pod
iptables模式下kube-proxy默认会随机选择一个后端。
使用iptables处理流量具有较低的系统开销,因为流量由Linux netfilter处理,而无需在用户空间和内核空间之间切换,而且这种方法也可能更可靠。
如果kube-proxyiptables模式下运行,kube-proxy随机选择的Pod没有响应,那么此次连接失败。这点与用户空间模式不同。在用户空间模式情况下,kube-proxy如果检测到与第一个Pod的连接失败,那么它会自动使用其他后端Pod进行重试。
我们可以使用Pod readiness probes 验证后端Pod是否就绪以便iptables模式下的kube-proxy仅看到状态正常且就绪的后端。readiness probes在滚动发布的时候也会用到。
iptables代理模式如下图:

图-2.png

3、IPVS代理模式

ipvs模式下kube-proxy观察k8s的ServicesEndpoints,调用netlink接口相应地创建IPVS规则,并定期将IPVS规则与k8s的ServicesEndpoints同步。该控制循环可确保IPVS状态与所需状态匹配。访问Service时,IPVS将流量定向到后端Pod之一。
IPVS代理模式基于类似于iptables模式的netfilter钩子函数, 但是使用哈希表作为基础数据结构,并且在内核空间中工作。这意味着与iptables模式下的kube-proxy相比,IPVS模式下的kube-proxy重定向通信的延迟要短,并且在同步代理规则时具有更好的性能。 与其他代理模式相比IPVS模式还支持更高的网络流量吞吐量。
IPVS提供了更多选项来平衡到达后端Pod的流量。 这些是:
rr:轮替(Round-Robin)
lc:最少链接(Least Connection),即打开链接数量最少者优先
dh:目标地址哈希(Destination Hashing)
sh:源地址哈希(Source Hashing)
sed:最短预期延迟(Shortest Expected Delay)
nq:从不排队(Never Queue)

说明:
要想kube-proxy使用IPVS模式,必须在启动kube-proxy之前保证IPVS在节点上可用。因为当kube-proxyIPVS代理模式启动时,它将验证IPVS内核模块是否可用, 如果未检测到IPVS内核模块,则kube-proxy将会使用iptables代理模式运行。

IPVS代理模式如下图:

图-3.png


在这些代理模式中,在客户端不了解k8s或ServicePod的任何信息的情况下,请求到Serviceip:port的流量最终被代理到具体的后端。
如果要确保每次都将来自特定客户端的连接传递到同一Pod, 则可以通过将service.spec.sessionAffinity设置为ClientIP(默认值是None),来基于客户端的IP地址选择会话关联。 你还可以通过适当设置 service.spec.sessionAffinityConfig.clientIP.timeoutSeconds来设置最大会话停留时间(默认值为 10800 秒,即 3 小时)。

2、服务发现

k8s支持两种基本的服务发现模式——环境变量和DNS

1、环境变量

Pod运行在Node上,kubelet会为每个存活的Service添加一组环境变量。 它同时支持Docker links compatible变量(见makeLinkVariables)和简单的 {SVCNAME}_SERVICE_HOST{SVCNAME}_SERVICE_PORT 变量。 这里Service的名称需大写,横线被转换成下划线。

举个例子,一个名称为 redis-masterService暴露了TCP端口6379, 同时给它分配了ClusterIP地址 10.0.0.11,这个Service生成了如下环境变量:

REDIS_MASTER_SERVICE_HOST=10.0.0.11
REDIS_MASTER_SERVICE_PORT=6379
REDIS_MASTER_PORT=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
REDIS_MASTER_PORT_6379_TCP_PORT=6379
REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11

但是这个环境变量只针对同Namespace下的Service,比如我在namespace A下的Pod的环境变量中是查看到namespace B下的Service的相关信息的。

2、DNS

通常来讲我们的k8s集群都会安装相关的网络插件,比如Calico、Flannel等来设置DNS服务。
支持集群的DNS服务器(例如CoreDNS)会观察k8s API中的新Service,并为每个服务创建一组DNS记录。 如果在整个集群中都启用了DNS,则所有Pod都应该能够通过其DNS名称自动解析服务。
例如,如果你在命名空间 my-ns中有一个名为my-service的服务,则k8s控制面板和DNS服务共同为my-service.my-ns创建DNS记录。my-ns命名空间中的Pod能够通过名称my-service来找到服务(这里可以直接通过环境变量获取my-serviceclusterIpport等),当然也可以通过my-service.my-ns来定位到具体的cluserIp
其他命名空间中的Pod必须将名称限定为my-service.my-ns。这个名称将解析为服务的clusterIP
k8s还支持命名端口的DNS SRV记录。 如果my-service.my-ns服务具有名为http的端口,且协议设置为 TCP,则可以对_http._tcp.my-service.my-ns执行DNS SRV查询,以发现该服务http的端口号以及IP地址。
k8s DNS服务器是唯一的一种能够访问ExternalName类型的Service的方式。 更多关于ExternalName信息可以查看DNS Pod 和 Service。

你可能感兴趣的:(k8s服务代理和发现)