一. 简介

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

通过创建 Service 可以为一组相同功能的容器应用提供一个统一的入口,并将请求均衡负载发送到后端的各个容器应用上。

  • 通过label selector来实现选中具体哪些容器
  • 均衡负载算法默认是 RR (Round-Robin 轮询调度)
  • 还可以通过设置 service.spec.sessionAffinity=ClientIp 来启用SessionAffinity策略
  • Service 只提供4层负载均衡能力(只能基于ip地址和端口进行转发),而没有7层功能(不能通过主机名及域名的方案去进行负载均衡)

关于本文的项目的代码,都放于链接:GitHub资源

基础架构图如下:

二. Service类型

2.1 ClusterIP

在集群的内部ip上公开服务,这种类型使得只能从集群内访问服务。

2.1.1 定义

ClusterIP 是默认的方式。
对于 ClusterIP 模式的 Service 来说,它的 A 记录的格式是:..svc.cluster.local。当访问这条 A 记录的时候,它解析到的就是该 Service 的 VIP 地址。

ClusterIP 主要在每个node节点使用 Iptables 或者IPVS,将发向ClusterIP 对应端口的数据,转发到kube-proxy 中。然后kube-proxy自己内部实现有负载均衡的方法,并可以查询到这个service下对应pod的地址和端口,进而把数据转发给对应的pod的地址和端口。

2.1.2 转发流程

关于 ClusterIP 的转发流程如下:

为了实现图上的功能,需要以下几个组件协调工作:

  • api-server
    用户通过kubectl命令向apiserver发送创建 Service的命令,apiserver接收到请求后将数据存储到etcd中。

  • kube-proxy
    Kubernetes的每个节点中都有一个叫做kube-porxy的进程,这个进程负责感知Servicepod的变化,并将变化的信息写入本地的iptables规则中。

  • iptables
    使用NAT等技术将virtuallP的流量转至endpoint中。

2.1.3 案例

demo-svc-clusterip.yaml 文件参考案例如下:

apiVersion: v1
kind: Service
metadata:
  name: demo-svc-clusterip
spec:
  type: ClusterIP
  selector:
    app: demo-svc-clusterip
  ports:
  - name: http
    port: 80
    targetPort: 80

我们通过spec.type: ClusterIP字段来定义即可。

2.2 NodePort

2.2.1 定义

通过将 Service 的 port 映射到集群内每个节点的相同一个端口,实现通过 nodeIP:nodePort从集群外访问服务,这属于ClusterIP的超集。

2.2.2 Port

Service中主要涉及三种Port(这里的port表示service暴露在clusterIP上的端口):

  • ClusterIP
    Port 是提供给集群内部访问 Kubernetes 服务的入口。
  • targetPort
    containerPort,targetPort 是 pod 上的端口,从 port 和 nodePort 上到来的数据最终经过kube-proxy 流入到后端 pod 的 targetPort 上进入容器。
  • nodePort
    nodeIP:nodePort 是提供给从集群外部访问 Kubernetes 服务的入口。

总的来说,port 和 nodePort 都是 Service 的端口,前者暴露给从集群内访问服务,后者暴露给从集群外访问服务。从这两个端口到来的数据都需要经过反向代理 kube-proxy 流入后端具体 pod 的 targetPort ,从而进入到 pod 上的容器内。

2.2.3 案例

demo-svc-nodeport.yaml 案例代码如下:

apiVersion: v1 
kind: Service 
metadata:
  name: demo-svc-nodeport
spec:
  type: NodePort 
  selector:
    app: demo-svc-nodeport
  ports:
  - name: http 
    port: 80 
    targetPort: 80
    protocol: TCP
  - nodePort: 443
    protocol: TCP
    name: https

在这个 Service 的定义里,我们声明它的类型是,type=NodePort。然后,我在 ports 字段里声明了 Service 的 80 端口代理 Pod 的 80 端口,Service 的 443 端口代理 Pod 的 443 端口。

我们也可以不显式地声明 nodePort 字段,Kubernetes 就会分配随机的可用端口来设置代理。这个端口的范围默认是 30000-32767,可以通过 kube-apiserver 的–service-node-port-range 参数来修改它。

当我们创建完毕后,可以通过如下访问格式:

<任何一台宿主机的IP地址>:80

2.3 LoadBalancer

2.3.1 定义

在公有云提供的 Kubernetes 服务里,都使用了一个叫作CloudProvider的转接层,来跟公有云本身的 API 进行对接。所以,在 LoadBalancer类型的 Service 被提交后,Kubernetes 就会调用CloudProvider在公有云上创建一个负载均衡服务,并且把被代理的 Pod 的 IP 地址配置给负载均衡服务做后端。

2.3.2 案例

demo-svc-loadbalancer.yaml 案例如下:

kind: Service
apiVersion: v1
metadata:
  name: demo-svc-loadbalancer
spec:
  ports:
  - port: 8080
    targetPort: 8080
  selector:
    app: demo-svc-loadbalancer
  type: LoadBalancer

2.4 ExternalName

2.4.1 定义

通过返回具有该名称的 CNAME 记录,使用任意名称(在规范中指定)公开服务,并且不使用代理。

2.4.2 案例
kind: Service
apiVersion: v1
metadata:
  name: demo-svc-externalname
spec:
  type: ExternalName
  externalName: demo-svc-externalname.wyatt.plus

在上述 Service 的 YAML 文件中,我指定了一个 externalName=demo-svc-externalname.wyatt.plus 的字段。
当通过 Service 的 DNS 名字访问它的时候,比如访问:demo-svc-externalname.default.svc.cluster.local。那么,Kubernetes 返回的就是demo-svc-externalname.wyatt.plus

2.4.3 CNAME

所以说,ExternalName 类型的 Service 其实是在 kube-dns 里添加了一条 CNAME 记录。当访问 demo-svc-externalname.default.svc.cluster.local 就和访问 demo-svc-externalname.wyatt.plus 这个域名效果一样。

2.4.4 externalIPs

ExternalName 模式下,Kubernetes 的Service 还允许为Service分配公有 IP 地址。
demo-svc-externalips.yaml 案例如下:

kind: Service
apiVersion: v1
metadata:
  name: demo-svc-externalips
spec:
  selector:
    app: demo-svc-externalips
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 80
  externalIPs:
  - 192.11.11.11

在上述 Service 中,为它指定的 externalIPs=192.11.11.11,就可以通过访问 192.11.11.11:80访问到被代理的 Pod 了。

三. Service代理

在Kubernetes集群中,为每个节点运行了一个kube-proxykube-proxy 负责为 Service 实现一种virtual ip 的形式,而这个过程称之为Service代理模式。
不同的 Kubernetes 版本,代理模式的实现方式也不尽相同,前后共有三种模式:

  • userspace(已过期):Kubernetes v1.0 版本使用的是这种代理模式
  • Iptables:从 Kubernetes v1.2 开始使用 Iptables
  • IPVS:Kubernetes v1.14 开始默认使用 IPVS 代理

3.1 Iptables

3.1.1 原理

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

3.1.2 架构图

3.1.3 优缺点

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

3.2 IPVS

3.2.1 原理

IPVS 模式的工作原理,其实跟Iptables模式类似。当我们创建了前面的 Service之后,kube-proxy 首先会在宿主机上创建一个虚拟网卡(叫作:kube-ipvs0),并为它分配Service VIP作为IP地址。
而接下来,kube-proxy 就会通过LinuxIPVS 模块,为这个 IP 地址设置三个 IPVS 虚拟主机,并设置这三个虚拟主机之间使用轮询模式(rr)来作为负载均衡策略。

3.2.2 架构图

3.2.3 负载均衡

IPVS 使用哈希表作为底层数据结构并在内核空间中工作,这意味着 IPVS 可以更快地重定向流量,并且在同步代理规则时具有更好的性能。
此外,IPVS 为负载均衡算法提供了更多选项,例如

  • rr:轮询调度
  • 1c:最小连接数
  • dh:目标哈希
  • sh:源哈希
  • sed:最短期望延迟
  • nq:不排队调度
3.2.4 优缺点

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

注意: IPVS 需要节点上的IPVS内核模块支持,如果未安装,则kube-proxy 将回退到Iptables 代理模式。

四. 拓展

4.1 Endpoints

在 Kubernetes 中,selector选中的 Pod,就称为 Service 的Endpoints,可以使用kubectl get ep命令看到它们。
需要注意的是,只有处于Running状态,且readinessProbe检查通过的 Pod,才会出现在 Service 的Endpoints列表里。并且,当某一个 Pod 出现问题时,Kubernetes 会自动把它从 Service 里摘除掉。

4.2 Headless Service

Headless Service 也是一种ClusterIP,只不过是一种特殊的情况。
有时不需要或不想要负载均衡,以及单独的Service IP。遇到这种情况,可以通过指定ClusterIP(spec.clusterIP) 的值为 None 来创建 Headless Service
这类 Service 具有如下的特点:

  • 不会分配 Cluster IP
  • kube-proxy 不会处理它们
  • 平台也不会为它们进行负载均衡和路由

通过Headless Service的方式,可以解决hostnameportname的变化问题,也就是通过它去进行绑定。例如,我们之前提到的 StatefulSet这种有状态应用。

五. 总结

Service,其实就是 Kubernetes 为 Pod 分配的、固定的、基于Iptables(或者 IPVS)的访问入口。而这些访问入口代理的 Pod 信息,则来自于Etcd,由kube-proxy通过控制循环来维护。

当然,我们发现 Service 和DNS机制 不具备强多租户能力。比如,在多租户情况下,每个租户应该拥有一套独立的 Service 规则(Service 只应该看到和代理同一个租户下的 Pod)。再比如 DNS,在多租户情况下,每个租户应该拥有自己的 kube-dnskube-dns 只应该为同一个租户下的 Service 和 Pod 创建DNS Entry)。

Reference

https://kubernetes.io/zh/docs/concepts/services-networking/service/
https://www.cnblogs.com/binghe001/p/13166641.html
https://draveness.me/kubernetes-service/
https://www.cnblogs.com/baoshu/p/13233014.html
https://time.geekbang.org/column/article/68964?utm_campaign=guanwang&utm_source=baidu-ad&utm_medium=ppzq-pc&utm_content=title&utm_term=baidu-ad-ppzq-title
https://draveness.me/kubernetes-service/

转自:https://blog.wyatt.plus/archives/157