详解K8S网络模型(包含Service讲解)

1 缘起

无意中翻阅官方文档,看到了Kubernetes中的网络模型,
于是,开始学习,分享如下。
官方文档:
网络模型https://kubernetes.io/docs/concepts/services-networking/
Servicehttps://kubernetes.io/docs/concepts/services-networking/service/

2 Kubernetes网络模型

集群中的每个Pod会在集群范围内获取自己唯一的IP地址。
因此,Pod间不需要创建链接,同样不要处理容器端口与主机端口的映射关系。
这样即构建了简洁且向后兼容的模型。
可以将Pod视为虚拟机或者物理机,比如在为Pod进行端口分配、命名、服务发现、负载均衡、应用配置和迁移。
Kubernetes在对网络实现上提出如下基本要求(禁止任何故意进行网络分段):

  • Pod与其他节点的Pod进行通信无需NAT
  • Node的代理(如系统进程、kublet)可以与该节点的任何Pod通信

注意:对于支持Pod运行于主机网络的平台(如Linux),当Pod获取节点的主机网络时,可以不通过NAT与所有节点的Pod通信。

该网络模型不仅仅减少全局复杂度,最重要是这种设计方式与Kubernetes的期望一样,即应用程序从虚拟机到容器平滑迁移。如果之前的任务运行于虚拟机中,那么虚拟机只要有IP即可与项目中其他虚拟机同通信。两者的基本模型是一致的。

Kubernetes的IP地址存在于Pod作用域中,包括IP地址和MAC地址(Pod中的容器共享网络命名空间),意味着Pod中的容器可以通过localhost在各自端口上相互通信,并且Pod需要管理端口的使用,这与虚拟机中的处理是一致的,称为“IP-per-pod”模型。
将Node端口的请求转发到Pod(称为主机端口),但这是非常常规的操作,如何实现转发仍然是容器运行时的处理的,Pod自身是不关心主机端口的。

3 Service

Service是将一系列Pod应用暴露为网络服务的一种方法。
Kubernetes中不需要编辑应用来使用陌生的服务发现机制,Kubernetes为Pod分配了IP地址、独立的DNS名称,通过IP和DNS名称实现Pod的负载均衡。

3.1 动机

Kubernetes中Pod的创建和销毁是为了集群的正常的运行,Pod是非持久性的资源(可以按需要创建和销毁),如果使用Deployment运行应用可以动态地创建或销毁Pod(即水平扩缩容)。

虽然每个Pod可以获取各自的IP地址,但是在Deployment中,Pod的IP地址是随时间发生变化的,即时刻A Pod的IP可能和时刻B的IP不同。
这会导致的问题:集群中如果后端Pod为前端Pod提供服务,前端如何实时跟随变化的后端IP?
答案:使用Service。

3.2 Service资源

Kubernetes中,Service是一种顶层抽象,定义了进入Pod的逻辑和策略(有时,这种模式称为微服务)。Service通过定义selector定向到Pod群组。
比如,一个无状态的镜像处理后台有3个副本,这些副本是可替代的,前端无需关心他们使用的是哪个,虽然实际的后台Pod可能会发生变更,但是,前端客户端无需感知以及实时跟踪这些Pod的变化。

Service的这种设计方式实现不同服务的Pod解耦,结构如下图所示。
不同业务间的Pod通过Service转发请求与响应。
在这里插入图片描述

云原生服务发现

如果应用中可以使用Kubernetes的API进行服务发现,
那么,通过API即可查询服务变更情况。
对于非原生应用,Kubernetes在应用和后端Pod间提供了配置网络端口和负载均衡的入口。

3.3 定义Service

Kubernetes中的Service是REST对象,类似于Pod,与所有REST对象一样,可以通过POST请求API来创建实例,Service对象名称需是有效的RFC 1035标签名称。
比如,假定每个Pod监听TCP端口为9376,包含标签:app=MyApp,

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

上述配置创建的Service对象名称为my-service,将请求转发到标签为MyApp、TCP端口为9376的Pod上。
Kubernetes为my-service这个服务分配IP地址(有时称为clusterIP),供Service代理使用。
控制器为Service选择器持续扫描匹配的Pod,然后向my-service提交更新信息。
注意:Service可以将port映射到targetPort,为方便起见,默认将port和targetPort设为相同的值。

Pod中定义端口同时可以配置名称,根据名字可以配置Service中targetPort属性,如下配置:
在Pod中通过port名称:http-web-svc,将Servcie中的targetPort绑定到Pod中的containerPort:80。

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    app.kubernetes.io/name: proxy
spec:
  containers:
  - name: nginx
    image: nginx:stable
    ports:
      - containerPort: 80
        name: http-web-svc
        
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app.kubernetes.io/name: proxy
  ports:
  - name: name-of-service-port
    protocol: TCP
    port: 80
    targetPort: http-web-svc

即使Service中混合多个Pod使用同一个配置名称同样是可以正常工作的,相同的网络协议使用不同的端口号,这为部署和迭代Service提供了诸多方便。比如更新Pod的可以修改暴露的端口,而无需终止客户端。默认的Service协议为TCP,也可以使用其他支持的协议,如:UDP和SCTP。
鉴于Service需要暴露多个端口,Kubernetes支持在一个Service对象中定义多个端口,每个端口的协议可以相同,也可以不同。

3.3.1 Service没有选择器

Service可以通过选择器访问Kubernetes的Pod,当使用了没有选择器的终端,Service可以抽象出其他类型的后端服务,包括集群外的服务,如:

  • 生产环境使用外部数据库集群,但测试环境使用自己的数据库;
  • 将自己的Service指向有不同Namespace或者其他集群的服务;
  • 迁移工作负载到Kubernetes。

如上情况可以定义没有Pod选择器的Servcie,如:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

由于这个Service没有选择器,所以对应的终端对象不会自动创建。可以手动将Service映射到对应的地址和端口,手动添加如下:

apiVersion: v1
kind: Endpoints
metadata:
  # the name here should match the name of the Service
  name: my-service
subsets:
  - addresses:
      - ip: 192.0.2.42
    ports:
      - port: 9376

终端对象的名称必须是有效的DNS子域名。当为Service创建终端对象时,对象名称必须与Service名称一致,即metadata.name一致。
注意:终端的IP不应该是:环回地址(127.0.0.0/8 for IPv4, ::1/128 for IPv6)或者链路本地地址(169.254.0.0/16 and 224.0.0.0/24 for IPv4, fe80::/64 for IPv6)。
终端IP地址不能是其他Kubernetes集群Service的IP,因为,kube-proxy不支持虚拟IP作为终点。

访问Service无需区分有无选择器。上面的例子中,流量会路由到定义的终端对象:192.0.2.42:9376(TCP协议)。
Endpoint是Service的映射对象。

注意:Kubernetes API服务禁止代理没有映射的Pod。比如执行kubectl proxy ,由于这个限制,该Service没有选择器会代理失败。这会阻止Kubernetes API服务被用于代理未授权的终端接入。

ExternalName类型的Service是一类没有选择器并且使用DNS名称的特殊Service。

3.3.2 终端过载

如果终端资源超过1000个,Kubernetes集群(版本v1.22或更高),Endpoints会使用标识:endpoints.kubernetes.io/over-capacity: truncated。该注解说明Endpoints对象过载,控制器会删除Endpoints,保持1000个。

3.3.3 Endpoint分片

Endpoint分片是API资源提供可伸缩的Endpoint。虽然概念上与Endpoint非常相似,但是,Endpoint分片允许跨资源分配网络。默认情况下,Endpoint分片达到100个即认为是满载,多余的Endpoint分片会在其他的终端中创建并存储。

3.3.4 应用协议

appProtoco属性为每个Service的端口提供了访问协议,该属性由对应的Endpooints和EndpointSlice对象进行映射。
该属性遵循Kubernetes标准标签语法,值应为IANA标准服务名称或者域名前缀名称,如mycompany.com/my-custom-protocol

3.4 虚拟IP和服务代理

Kubernetes集群的每个节点(Node)都会运行kube-proxykube-proxy是Service虚拟IP的实现方式(除了ExternalName)。

3.4.1 为什么不使用round-robin域名系统

一个常见的问题是:为什么Kubernetes依赖于代理将流量转发给后端。有没有其他方案呢?
比如,是否可以配置有多个A值(或IPv6的AAAA) DNS以及依赖round-robin名称的解析?
Service使用代理的几点原因:

  • DNS的实现在很长一段时间中不遵守记录TTL(存活时间)并且缓存应该过期的查询结果;
  • 一些应用使用DNS搜索一次,会永久缓存该结果;
  • 虽然应用和相关工具库重新解析,但是,DNS记录的低存活时间或者零存活时间会导致DNS负载增加,导致DNS最终很难管理。

3.4.2 配置

kube-proxy可以通过配置使用不同的启动模式。

  • kube-proxy通过ConfigMap进行配置,ConfigMap可以有效清除无需使用的kube-proxy功能;
  • CoinfigMap不支持实时重载配置;
  • kube-proxy的ConfigMap参数并不是总是有效的,因为,有硬件要求。比如,你的操作系统不允许使用iptables命令,kube-proxy内核的标准实现将无法正常工作。

3.4.3 用户空间代理模式

该模式下,kube-proxy监视Kubernetes控制平面中Service和Endpoint对象的添加和移除。
kube-proxy在本地节点(Node)上为每个Service开放一个随机选择的端口。
任何连接到这个“代理端口”的服务都会代理到Service的后端Pod上(通过Endpoint)。
kube-proxy Sevice根据SessionAffinity配置决定使用哪个后端Pod。
最后,用户空间代理安装iptables规则来获取流量,
流量分配被到Service的clusterIP(虚拟IP)和端口上。
kube-proxy将流量重定向到后端Pod的代理端口上,
默认情况下,kube-proxy在用户空间模式下通过round-robin算法选后端Pod,
过程如下图所示,由图可知,流量分发:traffic->clusterIP->kube-proxy->pod
图片地址:https://d33wubrfki0l68.cloudfront.net/e351b830334b8622a700a8da6568cb081c464a9b/13020/images/docs/services-userspace-overview.svg

在这里插入图片描述

3.4.4 iptables代理模式

iptable代理模式中,kube-proxy通过Kubernetes控制平面监控Service和Endpoint对象的添加和移除。kube-proxy为每个Servcie安装iptable规则,将流量分配到Service的clusterIP和端口上,然后重定向到Service的后端Pod上。kube-proxy为每个Endpoint对象安装iptable规则来选择后端Pod。默认情况下,iptable模式下的kube-proxy是纯随机选择后端Pod。
该模式的架构示意图如下图所示,由图可知,流量分发:traffic->clusterIP->pod或者traffic->kube-proxy->pod
原图地址:https://d33wubrfki0l68.cloudfront.net/27b2978647a8d7bdc2a96b213f0c0d3242ef9ce0/e8c9b/images/docs/services-iptables-overview.svg

在这里插入图片描述

使用iptable处理流量系统开销很小,因为Linux网络筛选器处理流量时无需进行用户空间和内核空间的切换。这种处理方式貌似更加可靠。
如果kube-proxy运行在iptable模式化下,选中的第一个Pod无响应,连接会失败。与用户空间模式不同的是:用户空间模式下,kube-proxy在第一个Pod中无法获取响应时,会自动向其他Pod发起重试请求。

可以使用就绪探针(readiness)验证后端Pod是否正常工作,因此iptable模式下kube-proxy只能观测到测试正常的后端Pod。这样意味着无需将流量通过kube-proxy分发到Pod就可以知道响应异常。

3.4.5 IPVS代理模式

ipvs模式下,kube-proxy观测kubernetes的Service和Endpoint,调用netlink接口创建对应的IPVS规则,并定期向Kubernetes的Sevice和Endpoint同步IPVS规则。该控制回环保证IPVS与期望的状态是一致的。当访问Servcie时,IPVS将流量直接定向到后端Pod。
IPVS模式是基于网络筛选器回调功能,与iptable模式是非常相似的,不同的是,IPVS使用hashtable作为数据结构并在内核空间中进行工作。这意味着IPVS模式下,kube-proxy流量重定向的时延比iptable模式中更低,同步代理规则的性能会更好。与其他模式相比,IPVS也支持更高吞吐量。
该模式的架构示意图如下图所示,由图可知,流量分发:traffic->clusterIP->pod或者traffic->kube-proxy->pod
原图地址:https://d33wubrfki0l68.cloudfront.net/2d3d2b521cf7f9ff83238218dac1c019c270b1ed/9ac5c/images/docs/services-ipvs-overview.svg
在这里插入图片描述

IPVS为后端Pod的流量分发提供多种均衡策略:

  • rr:轮询
  • lc:最小连接(开启连接数量最少)
  • dh:目标地址Hash
  • sh:源Hash
  • seq:期待延迟最少
  • nq:非队列

注意:IPVS模式下运行kube-proxy,需保证启动kube-proxy前,在节点(Node)中IPVS是可用的。
kube-proxy以IPVS代理模式启动时,会确认IPVS内核模块是否可用,如果没有检测到可用的IPVS内核模块,kube-proxy会回退使用iptable代理模式。

这些代理模型,流量会绑定到Service的IP和Port(代理了后端Pod的Service),客户端无需感知任何关于Kubernetes、Servcie或者Pod。
如果想将某个特定的客户端的流量发送到同一个Pod,可以基于客户端IP地址设置会话,通过属性service.spec.sessionAffinity,配置为ClientIP,默认为None,同时支持配置会话最大存活时间,通过属性service.spec.sessionAffinityConfig.clientIP.timeoutSeconds配置,默认为10800秒,即3小时。

注意:Windows系统中,Service不支持最大会话存活时间。

3.5 多个端口Service

对于某些Service需要暴露多个端口。Kubernetes允许在Service对象中配置多个端口。一个Service配置多个端口,必须给端口配置名称,明确目的。多个端口配置样例如下:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 9376
    - name: https
      protocol: TCP
      port: 443
      targetPort: 9377

注意:与Kubernetes命名一致,端口的名称只能包含小写字母和中划线。端口名称的开始和结束必须是字母或数字。如合法的:123-abc,web;不合法:123_abc,-web。

3.6 选择IP地址

通过配置.spec.clusterIP属性,在Service创建请求中指定集群的IP地址。如,想重用已经有DNS或者原有系统已配置IP地址并且很难重新配置。
选择的IP地址须满足:在API server配置的service-cluster-ip-range CIDR范围内,有效的IPv4或者IPv6地址。如果创建的Service包含无效的clusterIP地址,API server会返回422状态码,表明错误。

3.7 流量策略

3.7.1 外部流量策略

通过属性spec.externalTrafficPolicy控制流量在外部资源的分发。可用的类型有Cluster和Local。Cluster:流量分发到已就绪的Endpoint,Local:只分发到已就绪的本地节点Endpoint。如果流量策略为Local,但是没有本地节点Endpoint,kube-proxy不会将流量转发到相关的Service。

注意:如果为kube-proxy开启ProxyTerminatingEndpoints属性,kube-proxy会检测节点是否有本地Endpoint以及所有本地Endpoint是否标记为terminating。如果有本地Endpoint并且所有本地Endpoint标记为terminating,kube-proxy会忽略Local策略的外部流量。如果外部流量策略为Cluster,本地节点Endpoint为terminating,kube-proxy会将流量分发到其他正常的Endpoint。即使健康检查节点启动失败时,这种转发方式对于终止的Endpoint,仍允许外部负载均衡器优雅地分离出NodePort Service的连接。否则,流量会在Pod终止期间丢失。

3.7.2 内部流量策略

通过属性spec.internalTrafficPolicy控制流量在内部资源的分发。与externalTrafficPolicy一样,有两种值:Cluster和Local。Cluster:内部流量只分配到就绪的Endpoint;Local:流量值分发到本地节点的Endpoint。如果流量策略为Local,如果没有本地Endpoint,kube-proxy会丢弃该流量。

3.8 Service部署

https://blog.csdn.net/Xin_101/article/details/124519232

4 小结

核心:
(1)集群中的每个Pod会在集群范围内获取自己唯一的IP地址,Pod间通信无需建立连接,无需考虑端口映射;
(2)Service是将一系列Pod应用暴露为网络服务的一种方法,即通过Service访问Pod;
(3)代理模式有3种:用户空间代理模式、iptables代理模式和IPVs代理模式;
(4)Kubernetes允许在Service对象中配置多个端口;
(5)流量分配策略:Cluster和Local两种方式,Cluster:流量分发到就绪的Endpoint;Local流量只分发到本机Endpoint,Endpoint终止后,则会丢弃流量。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天然玩家

坚持才能做到极致

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值