service和ingress

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地址,如果开发有实力,推荐使用这种方式