一. 简介
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
的进程,这个进程负责感知Service
,pod
的变化,并将变化的信息写入本地的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-proxy
,kube-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
就会通过Linux
的IPVS
模块,为这个 IP 地址设置三个 IPVS 虚拟主机,并设置这三个虚拟主机之间使用轮询模式(rr)
来作为负载均衡策略。
3.2.2 架构图
3.2.3 负载均衡
IPVS 使用哈希表作为底层数据结构并在内核空间中工作,这意味着 IPVS 可以更快地重定向流量,并且在同步代理规则时具有更好的性能。
此外,IPVS 为负载均衡算法提供了更多选项,例如
- rr:轮询调度
- 1c:最小连接数
- dh:目标哈希
- sh:源哈希
- sed:最短期望延迟
- nq:不排队调度
3.2.4 优缺点
而相比于 Iptables,IPVS 在内核中的实现其实也是基于Netfilter
的NAT
模式,所以在转发这一层上,理论上 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
的方式,可以解决hostname
和portname
的变化问题,也就是通过它去进行绑定。例如,我们之前提到的 StatefulSet
这种有状态应用。
五. 总结
Service,其实就是 Kubernetes 为 Pod 分配的、固定的、基于Iptables(或者 IPVS)
的访问入口。而这些访问入口代理的 Pod 信息,则来自于Etcd
,由kube-proxy
通过控制循环来维护。
当然,我们发现 Service 和DNS
机制 不具备强多租户能力。比如,在多租户情况下,每个租户应该拥有一套独立的 Service 规则(Service 只应该看到和代理同一个租户下的 Pod)。再比如 DNS,在多租户情况下,每个租户应该拥有自己的 kube-dns
(kube-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/