Kubernetes:Service剖析
一. 简介
Service 是 Kubernetes 里重要的服务对象,而 Kubernetes 之所以需要 Service,一方面是因为 Pod 的 IP 不是固定的,另一方面则是因为一组 Pod 实例之间总会有负载均衡的需求。
通过创建 Service 可以为一组相同功能的容器应用提供一个统一的入口,并将请求均衡负载发送到后端的各个容器应用上。
- 通过
label selector
来实现选中具体哪些容器 - 均衡负载算法默认是 RR (Round-Robin 轮询调度)
- 还可以通过设置
service.spec.sessionAffinity=ClientIp
来启用SessionAffinity
策略 - Service 只提供4层负载均衡能力(只能基于ip地址和端口进行转发),而没有7层功能(不能通过主机名及域名的方案去进行负载均衡)
关于本文的项目的代码,都放于链接:GitHub资源
基础架构图如下:
Service Architecture
二. 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 的转发流程如下:
Service-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 架构图
Iptables architecture3.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 架构图
IPVS architecture3.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
)。
欢迎收藏个人博客: Wyatt's Blog ,非常感谢~
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/