• 详解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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    上述配置创建的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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    即使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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    由于这个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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    终端对象的名称必须是有效的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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    注意:与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终止后,则会丢弃流量。

  • 相关阅读:
    保姆级教程:个人深度学习工作站配置指南
    8年开发经验,帮你总结这帮双面HR们
    七分钟学会 HTML 网页制作
    【大规模 MIMO 检测】基于ADMM的大型MU-MIMO无穷大范数检测研究(Matlab代码实现)
    CentOS停服背景下“浪潮信息KeyarchOS(KOS)系统迁移(无缝切换)解决方案”
    【前端小程序】关于小程序中.env 文件夹
    Spring Cloud Netflix 之 Eureka
    图论16-拓扑排序
    【C++11】std::function 包装器(又叫适配器),std::bind 绑定
    2023/09/19 qt day3
  • 原文地址:https://blog.csdn.net/Xin_101/article/details/125457526