service和ingress

本文最后更新于:2023年12月5日 晚上

service

参考:https://www.cnblogs.com/fuyuteng/p/11598768.html
kubernetes service 原理解析 - 知乎 (zhihu.com)

为什么需要 service

在 kubernetes 中,当创建带有多个副本的 deployment 时,kubernetes 会创建出多个 pod,此时即一个服务后端有多个容器,那么在 kubernetes 中负载均衡怎么做,容器漂移后 ip 也会发生变化,如何做服务发现以及会话保持?这就是 service 的作用,service 是一组具有相同 label pod 集合的抽象,集群内外的各个服务可以通过 service 进行互相通信,当创建一个 service 对象时也会对应创建一个 endpoint 对象,endpoint 是用来做容器发现的,service 只是将多个 pod 进行关联,实际的路由转发都是由 kubernetes 中的 kube-proxy 组件来实现,因此,service 必须结合 kube-proxy 使用,kube-proxy 组件可以运行在 kubernetes 集群中的每一个节点上也可以只运行在单独的几个节点上,其会根据 service 和 endpoints 的变动来改变节点上 iptables 或者 ipvs 中保存的路由规则

service 的工作原理

service 资源基于标签选择器将一组 pod 定义成一个逻辑组合,并通过自己的 ip 地址和端口调度代理请求至组内 pod 的对象之上,它向客户隐藏了真实的、处理用户请求的 pod 资源,使得客户端的请求看上去像是由 service 直接处理并进行响应一样

Service 对象的 IP 地址也称为 Cluster IP,是一种 VIP(虚拟 IP),k8s 集群内部的 VIP

service 网段不能跟机房网络、docker 网段、容器网段冲突,否则可能会导致网络不通

Service 以负载均衡的方式进行流量调度,Service 和 Pod 之间松耦合,创建 Service 和 Pod 的任务可由不同的用户分别完成

Service 通过 API Server 实时监控(watch)标签选择器匹配到的后端 Pod,不过 Service 并不直接链接至 Pod,它们中间还有一个中间层 --Endpoints资源对象,默认情况下,创建 Service 对象时,其关联的 Endpoints 对象会自动创建

endpoints controller 是负责生成和维护所有 endpoints 对象的控制器,监听 service 和对应 pod 的变化,更新对应 service 的 endpoints 对象。当用户创建 service 后 endpoints controller 会监听 pod 的状态,当 pod 处于 running 且准备就绪时,endpoints controller 会将 pod ip 记录到 endpoints 对象中,因此,service 的容器发现是通过 endpoints 来实现的。而 kube-proxy 会监听 service 和 endpoints 的更新并调用其代理模块在主机上刷新路由转发规则

Endpoints 是一个由 IP 地址和端口组成的列表,这些 IP 和端口来自于其 Service 关联的 Pod

每个节点上的 kube-proxy 组件 watch 各 Service 及其关联的 Endpoints,如果有变动,就会实时更新当前节点上相应的 iptables 或 ipvs 规则,确保 Cluster IP 的流量能调度到 Endpoints

简单来讲,一个 Service 对象就是一个 Node 上的这些 iptables 和 ipvs 规则

service 的负载均衡

pod 之间通信,一般不会 pod 直接访问 pod,而是 pod 先访问 service,然后 service 再到 pod

关于将 Cluster IP 的流量能调度到 Endpoints,有三种方式:userspace 代理、iptables 代理、ipvs 代理

  1. userspace 代理

    1.1 版本之前的默认代理模型,效率低

  2. iptables 代理

    通过 iptables 进行目标地址转换和流量调度,缺点是不会在后端 Pod 资源无响应时自动进行重定向

  3. ipvs 代理

    和 iptables 代理模型的区别仅在于,流量的调度功能由 ipvs 实现,其他功能仍由 iptables 完成

service 的类型

service 支持的类型也就是 kubernetes 中服务暴露的方式,默认有四种:ClusterIP、NodePort、LoadBalancer、ExternelName

ClusterIP

kubernetes 集群默认的服务暴露方式,它只能用于集群内部通信,可以被各 pod 访问,其访问方式为:

pod ---> ClusterIP:ServicePort --> (iptables)DNAT --> PodIP:containePort

# 集群内pod直接访问service的ip即可

NodePort

如果想要在集群外访问集群内部的服务,可以使用 NodePort 类型的 service,在集群内部署了 kube-proxy 的节点打开一个指定的端口,将访问 node 此端口的流量直接发送到这个端口,然后会被转发到 service 后端真实的服务进行访问。Nodeport 构建在 ClusterIP 上,其访问链路如下所示:

client ---> NodeIP:NodePort ---> ClusterIP:ServicePort ---> (iptables)DNAT ---> PodIP:containePort

# 外部流量请求先到node,再到service
# 只要安装了kube-proxy的node,都可以处理外部流量,有实力的话可以单独拿出几个node,打上污点,然后负载均衡只会将外部流量转发到这几个node

LoadBalancer

主要在公有云如阿里云、AWS 上使用,LoadBalancer 构建在 nodePort 基础之上,通过公有云服务商提供的负载均衡器将 k8s 集群中的服务暴露到外网,云厂商的 LoadBalancer 会给用户分配一个 IP,之后通过该 IP 的流量会转发到你的 service 上

LoadBalancer service 类型的结构如下图所示:

ExternelName

通过 CNAME 将 service 与 externalName 的值(比如:http://foo.bar.example.com)映射起来,这种方式用的比较少。

service 的服务发现

Pod 与 Service 的 DNS:https://kubernetes.io/zh/docs/concepts/services-networking/dns-pod-service/

虽然 service 的 endpoints 解决了容器发现问题,但不提前知道 service 的 Cluster IP,怎么发现 service 服务呢?service 当前支持两种类型的服务发现机制,一种是通过环境变量,另一种是通过 DNS。在这两种方案中,建议使用后者:

在集群中部署 CoreDNS 服务, 来达到集群内部的 pod 通过 DNS 的方式进行集群内部各个服务之间的通讯

当前 kubernetes 集群默认使用 CoreDNS 作为默认的 DNS 服务,主要原因是 CoreDNS 是基于 Plugin 的方式进行扩展的,简单,灵活,并且不完全被 Kubernetes 所捆绑

service 的使用

ClusterIP 方式

apiVersion: v1
kind: Service
metadata:
  name: my-nginx #service的名称,此名称会被DNS解析
spec:
  clusterIP: 10.105.146.177
  ports:
    - port: 80 # Service的端口号
      protocol: TCP
      targetPort: 8080 # 后端目标进程的端口号或名称,名称需由Pod规范定义
  selector:
    app: my-nginx
  sessionAffinity: None
  type: ClusterIP

NodePort 方式

apiVersion: v1
kind: Service
metadata:
  name: my-nginx
spec:
  ports:
    - nodePort: 30090 # 节点端口,kube-proxy监听本机此端口,将访问此端口的流量转发到service
      port: 80 # service端口
      protocol: TCP
      targetPort: 8080 # 目标pod端口
  selector:
    app: my-nginx
  sessionAffinity: None
  type: NodePort

Headless service(就是没有 Cluster IP 的 service )

当不需要负载均衡以及单独的 ClusterIP 时,可以通过指定 spec.clusterIP 的值为 None 来创建 Headless service,它会给一个集群内部的每个成员提供一个唯一的 DNS 域名来作为每个成员的网络标识,集群内部成员之间使用域名通信。

deployment 中对应的服务是 service,而 statefulset 中与之对应的服务就是 headless service

apiVersion: v1
kind: Service
metadata:
  name: my-nginx
spec:
  clusterIP: None
  ports:
    - nodePort: 30090
      port: 80
      protocol: TCP
      targetPort: 8080
  selector:
    app: my-nginx

Ingress

https://kubernetes.io/zh/docs/concepts/services-networking/ingress/
https://kubernetes.io/zh/docs/concepts/services-networking/ingress-controllers/

上面介绍的 ClusterIP 、NodePort 、LoadBalancer 都是基于 ip 和 port 做负载均衡,属于四层负载均衡

Ingress 可以作用于多个 service,被称为 service 的 service,作为集群内部服务的入口,Ingress 其实就是七层的反向代理,可以根据不同的 url,将请求转发到不同的 service 上

Ingress 的结构如下图所示:

kube-proxy 监听 node 的指定端口,将此端口的流量转发到 ingress,然后 ingress 再转发到不同的 service

反正都是实现七层代理,也可以使用deployment创建一组pod,提供nginx服务,实现代替ingress的效果:

kube-proxy监听node的指定端口,将此端口的流量转发到nginx service,然后nginx在转发到不同的service,可以配置service ip,也可以使用服务发现

建议:域名多、流量大,使用nginx;反之使用ingress

其他服务类型

完全不使用 k8s 内置的 service,而是通过注册中心自动发现 pod 地址,如果开发有实力,推荐使用这种方式


service和ingress
http://blog.lujinkai.cn/运维/Kubernetes/service和ingress/
作者
像方便面一样的男子
发布于
2021年3月11日
更新于
2023年12月5日
许可协议