深入剖析Kubernetes笔记:Kubernetes资源对象Service

本文深入探讨Kubernetes中Service的概念,包括其作用、类型及其在Pods间的负载均衡机制。介绍了ClusterIP、NodePort、LoadBalancer、ExternalName及Ingress等多种服务类型的特点与应用场景,以及Service与iptables、IPVS的关系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

service 介绍

Kubernetes 之所以需要 Service,一方面是因为 Pod 的 IP 不是固定的,另一方面则是因为一组 Pod 实例之间总会有负载均衡的需求。

service是对一组提供相同功能的 Pods 的抽象,并为它们提供一个统一的入口。借助 Service,应用可以方便的实现服务发现与负载均衡,并实现应用的零宕机升级。

被 selector 选中的 Pod,就称为 Service 的 Endpoints

service作为pods 的入口,通过labelselector找寻到满足label的pods,这样就能把service 与 pods 的对应关系确立。

apiVersion: v1

kind: Service

metadata:

  labels:

    run: nginx

  name: nginx

  namespace: default

spec:

  ports:

  - port: 80

    protocol: TCP

    targetPort: 80

  selector:

    run: nginx

根据上述的service yaml,当pod有着run:nginx的label的时候,就会被service 捕获到; service 的 targetPort 对应着捕获到pod的port

Kubernetes 里的 Service 究竟是如何工作的呢?

实际上,Service 是由 kube-proxy 组件,加上 iptables 来共同实现的。

访问 Service VIP 的 IP 包经过 iptables 处理之后,就已经变成了访问具体某一个后端 Pod 的 IP 包了。不难理解,这些 Endpoints 对应的 iptables 规则,正是 kube-proxy 通过监听 Pod 的变化事件,在宿主机上生成并维护的。

你可能已经听说过,Kubernetes 的 kube-proxy 还支持一种叫作 IPVS 的模式。这又是怎么一回事儿呢?

其实,通过上面的讲解,你可以看到,kube-proxy 通过 iptables 处理 Service 的过程,其实需要在宿主机上设置相当多的 iptables 规则。而且,kube-proxy 还需要在控制循环里不断地刷新这些规则来确保它们始终是正确的。

不难想到,当你的宿主机上有大量 Pod 的时候,成百上千条 iptables 规则不断地被刷新,会大量占用该宿主机的 CPU 资源,甚至会让宿主机“卡”在这个过程中。所以说,一直以来,基于 iptables 的 Service 实现,都是制约 Kubernetes 项目承载更多量级的 Pod 的主要障碍。

IPVS 模式的 Service,就是解决这个问题的一个行之有效的方法

IPVS 模式的工作原理,其实跟 iptables 模式类似。当我们创建了前面的 Service 之后,kube-proxy 首先会在宿主机上创建一个虚拟网卡(叫作:kube-ipvs0),并为它分配 Service VIP 作为 IP 地址。

相比于 iptables,IPVS 在内核中的实现其实也是基于 Netfilter 的 NAT 模式,所以在转发这一层上,理论上 IPVS 并没有显著的性能提升。但是,IPVS 并不需要在宿主机上为每个 Pod 设置 iptables 规则,而是把对这些“规则”的处理放到了内核态,从而极大地降低了维护这些规则的代价。这也正印证了我在前面提到过的,“将重要操作放入内核态”是提高性能的重要手段。

不过需要注意的是,IPVS 模块只负责上述的负载均衡和代理功能。而一个完整的 Service 流程正常工作所需要的包过滤、SNAT 等操作,还是要靠 iptables 来实现。只不过,这些辅助性的 iptables 规则数量有限,也不会随着 Pod 数量的增加而增加。

所以,在大规模集群里,我非常建议你为 kube-proxy 设置–proxy-mode=ipvs 来开启这个功能。它为 Kubernetes 集群规模带来的提升,还是非常巨大的。

ClusterIP 模式的 Service 为你提供的,就是一个 Pod 的稳定的 IP 地址,即 VIP。并且,这里 Pod 和 Service 的关系是可以通过 Label 确定的。

而 Headless Service 为你提供的,则是一个 Pod 的稳定的 DNS 名字,并且,这个名字是可以通过 Pod 名字和 Service 名字拼接出来的。

service 类型

service 有着种类型:NodePort,LoadBalancer,ClusterIP,ExternalNme

集群内部访问

  • ClusterIP:默认方式。根据是否生成ClusterIP又可分为普通Service和Headless Service两类:
     
    • 普通Service:通过为Kubernetes的Service分配一个集群内部可访问的固定虚拟IP(Cluster IP),实现集群内的访问。为最常见的方式。
    • Headless Service:该服务不会分配Cluster IP,也不通过kube-proxy做反向代理和负载均衡。而是通过DNS提供稳定的网络ID来访问,DNS会将headless service的后端直接解析为podIP列表。主要供StatefulSet使用。
       

从外部访问 Service 的三种方式

  • NodePort在 ClusterIP 基础上为 Service 在每台机器上绑定一个端口,这样就可以通过 <NodeIP>:NodePort 来访问该服务。如果 kube-proxy 设置了 --nodeport-addresses=10.240.0.0/16(v1.10 支持),那么仅该 NodePort 仅对设置在范围内的 IP 有效。
  • LoadBalancer和nodePort类似,不过除了使用一个Cluster IP和nodePort之外,还会向所使用的公有云申请一个负载均衡器(负载均衡器后端映射到各节点的nodePort),并将请求转发到 <NodeIP>:NodePort ,实现从集群外通过LB访问服务。
  • ExternalName是 Service 的特例,将服务通过 DNS CNAME 记录方式转发到指定的域名(通过 spec.externlName 设定)。此模式主要面向运行在集群外部的服务,通过它可以将外部服务映射进k8s集群,且具备k8s内服务的一些特征(如具备namespace等属性),来为集群内部提供服务。此模式要求kube-dns的版本为1.7或以上这种模式和前三种模式(除headless service)最大的不同是重定向依赖的是dns层次,而不是通过kube-proxy。

备注:

  • 前3种模式,定义服务的时候通过selector指定服务对应的pods,根据pods的地址创建出endpoints作为服务后端;Endpoints Controller会watch Service以及pod的变化,维护对应的Endpoint信息。kube-proxy根据Service和Endpoint来维护本地的路由规则。当Endpoint发生变化,即Service以及关联的pod发生变化,kube-proxy都会在每个节点上更新iptables,实现一层负载均衡。
  • ExternalName模式则不指定selector,相应的也就没有port和endpoints。
  • ExternalName和ClusterIP中的Headles Service同属于Headless Service的两种情况。Headless Service主要是指不分配Service IP,且不通过kube-proxy做反向代理和负载均衡的服务。
     

 ClusterIP

ClusterIP 服务是 Kubernetes 的默认服务。

它给你一个集群内的服务,集群内的其它应用都可以访问该服务。

集群外部无法访问它。

ClusterIP 服务的 YAML 文件类似如下:

apiVersion: v1
kind: Service
metadata:  
  name: my-internal-service
selector:    
  app: my-app
spec:
  type: ClusterIP
  ports:  
    - name: http
      port: 80
      targetPort: 80
      protocol: TCP

如果 从Internet 没法访问 ClusterIP 服务,那么我们为什么要讨论它呢?那是因为我们可以通过 Kubernetes 的 proxy 模式来访问该服务!

启动 Kubernetes proxy 模式:

$ kubectl proxy --port=8080

这样你可以通过Kubernetes API,使用如下模式来访问这个服务:

http://localhost:8080/api/v1/proxy/namespaces/<NAMESPACE>/services/<SERVICE-NAME>:<PORT-NAME>/

要访问我们上面定义的服务,你可以使用如下地址:

http://localhost:8080/api/v1/proxy/namespaces/default/services/my-internal-service:http/

何时使用ClusterIP ?

有一些场景下,你得使用 Kubernetes 的 proxy 模式来访问你的服务:

  1. 由于某些原因,你需要调试你的服务,或者需要直接通过笔记本电脑去访问它们。
  2. 容许内部通信,展示内部仪表盘等。

这种方式要求我们运行 kubectl 作为一个未认证的用户,因此我们不能用这种方式把服务暴露到 internet 或者在生产环境使用。

NodePort

NodePort 服务是引导外部流量到你的服务的最原始方式。

NodePort,正如这个名字所示,在所有节点(虚拟机)上开放一个特定端口,任何发送到该端口的流量都被转发到对应服务。

NodePort 服务的 YAML 文件类似如下:

apiVersion: v1
kind: Service
metadata:  
  name: my-nodeport-service
  selector:    
    app: my-app
spec:
  type: NodePort
  ports:  
    - name: http
    port: 80
    targetPort: 80
    nodePort: 30036
    protocol: TCP


NodePort 服务是引导外部流量到你的服务的最原始方式。NodePort,正如这个名字所示,在所有节点(虚拟机)上开放一个特定端口,任何发送到该端口的流量都被转发到对应服务。
NodePort 服务主要有两点区别于普通的“ClusterIP”服务。

第一,它的类型是“NodePort”。有一个额外的端口,称为 nodePort,它指定节点上开放的端口值 。如果你不指定这个端口,系统将选择一个随机端口。大多数时候我们应该让 Kubernetes 来选择端口,用户自己来选择可用端口代价太大。

何时使用NodePort

这种方法有许多缺点:

  1. 每个端口只能是一种服务
  2. 端口范围只能是 30000-32767
  3. 如果节点/VM 的 IP 地址发生变化,你需要能处理这种情况。

基于以上原因,我不建议在生产环境上用这种方式暴露服务

如果你运行的服务不要求一直可用,或者对成本比较敏感,你可以使用这种方法。

这样的应用的最佳例子是 demo 应用,或者某些临时应用。

LoadBalancer

LoadBalancer 服务是暴露服务到 internet 的标准方式。在 GKE 上,这种方式会启动一个 Network Load Balancer,它将给你一个单独的 IP 地址,转发所有流量到你的服务。

何时使用LoadBalancer?

如果你想要直接暴露服务,这就是默认方式。所有通往你指定的端口的流量都会被转发到对应的服务。它没有过滤条件,没有路由等。这意味着你几乎可以发送任何种类的流量到该服务,像 HTTP,TCP,UDP,Websocket,gRPC 或其它任意种类。

这个方式的最大缺点是每一个用 LoadBalancer 暴露的服务都会有它自己的 IP 地址,每个用到的 LoadBalancer 都需要付费,这将是非常昂贵的。

Ingress

有别于以上所有例子,Ingress 事实上不是一种服务类型。相反,它处于多个服务的前端,扮演着“智能路由”或者集群入口的角色。

这种全局的、为了代理不同后端 Service 而设置的负载均衡服务,就是 Kubernetes 里的 Ingress 服务。

所以,Ingress 的功能其实很容易理解:所谓 Ingress,就是 Service 的“Service”。

你可以用 Ingress 来做许多不同的事情,各种不同类型的 Ingress 控制器也有不同的能力。默认 ingress 控制器是启动一个 HTTP(S) Load Balancer。它允许你基于路径或者子域名来路由流量到后端服务

例如,你可以将任何发往域名 foo.yourdomain.com 的流量转到 foo 服务,将路径 yourdomain.com/bar/path 的流量转到 bar 服务。

Ingress 对象的 YAML 文件类似如下:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: my-ingress
spec:
  backend:
    serviceName: other
    servicePort: 8080
  rules:
  - host: foo.mydomain.com
  http:
    paths:
    - backend:
        serviceName: foo
        servicePort: 8080
  - host: mydomain.com
  http:
    paths:
    - path: /bar/*
      backend:
        serviceName: bar
        servicePort: 8080

上面这个名叫 yaml 文件中,最值得我们关注的,是 rules 字段。在 Kubernetes 里,这个字段叫作:IngressRule

IngressRule 的 Key,就叫做:host。它必须是一个标准的域名格式(Fully Qualified Domain Name)的字符串,而不能是 IP 地址。

所谓 Ingress 对象,其实就是 Kubernetes 项目对“反向代理”的一种抽象。

一个 Ingress 对象的主要内容,实际上就是一个“反向代理”服务(比如:Nginx)的配置文件的描述。而这个代理服务对应的转发规则,就是 IngressRule。

何时使用Ingress?

Ingress 可能是暴露服务的最强大方式,但同时也是最复杂的。Ingress 控制器有各种类型,包括 Google Cloud Load Balancer, NginxContourIstio,等等。它还有各种插件,比如 cert-manager,它可以为你的服务自动提供 SSL 证书。

如果你想要使用同一个 IP 暴露多个服务,这些服务都是使用相同的七层协议(典型如 HTTP),那么Ingress 就是最有用的。

如果你使用本地的 GCP 集成,你只需要为一个负载均衡器付费,且由于 Ingress是“智能”的,你还可以获取各种开箱即用的特性(比如 SSL,认证,路由,等等)。

不指定selector的service

不指定selector,则不会自动创建endpoint,关于这个转发,则有两种方式

  •  自定义endpoint:手动创建endpoint ,各组件去寻找endpoint,寻找方式是去找同一个namespace且与service name 一致的endpoint资源
  •  在service 配置externalName
    kind: Service

     apiVersion: v1

     metadata:

       name: my-service

       namespace: default

     spec:

       type: ExternalName

       externalName: my.database.example.com

     该service不会赋予有clusterip,服务访问该service,域名解析返回externalName 参数对应的my.database.example.com

headless service

即不需要 Cluster IP 的服务,即在创建service的时候,配置 spec.clusterIPNone, kube-apiserver则不会分配ip给该service

headless service包括两种类型

  • 不指定 Selectors,但设置 externalName,通过 CNAME 记录处理
  • 指定 Selectors,通过 kube-dns 返回endpoint 列表,也就是kube-dns进行负载均衡

ClusterIP 模式的 Service 为你提供的,就是一个 Pod 的稳定的 IP 地址,即 VIP。并且,这里 Pod 和 Service 的关系是可以通过 Label 确定的。

而 Headless Service 为你提供的,则是一个 Pod 的稳定的 DNS 名字,并且,这个名字是可以通过 Pod 名字和 Service 名字拼接出来的。

参考链接:

https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0

http://dockone.io/article/4884

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

萤火AI百宝箱

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值