背景&问题
当K8s的无状态应用Deployment有多个Pod的时候,我们访问这个应用,面临以下两个问题:
- 需要负载均衡能力,转发到后端Pod
- 负载均衡器需动态更新后端Pod(因为Pod重启后,IP会变化的问题)
解决方案
kubernetes提供了Service资源,作用如下:
- 主要用于定义服务类型,服务如何找到后端Pod,服务端口和Pod端口映射关系等
Service支持的访问需求:
- 集群内访问:
- 服务类型:ClusterIP(默认值)
- 含义:会创建一个集群内的服务IP,client只能在集群内,通过该IP负载到后端的Pod
- 集群外访问:
- 通过Node节点暴露:
- 服务类型:NodePort
- 含义:会在Node节点上创建一个端口,通过NodeIP:NodePort负载到后端Pod
- 通过集群外LoadBalance(Node节点外层再顶一个LoadBalance)
- 服务类型:LoadBalance
- 含义:请求先到ELB -> NodeIP:NodePort -> PodIP:PodPort(有很多种实现方式,这是其中一种)
常见问题
Service如何找到Pod?
使用selector通过标签,找到同标签的Pod(注意,service和deployment没什么关系)
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app.kubernetes.io/name: proxy
spec:
containers:
- name: nginx
image: nginx:stable
ports:
- containerPort: 80
name: http-web-svc
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app.kubernetes.io/name: proxy
ports:
- name: name-of-service-port
protocol: TCP
port: 80
targetPort: http-web-svc
例子:
- 通过标签找Pod
- service找标签:app.kubernetes.io/name: proxy
- Pod定义标签:app.kubernetes.io/name: proxy
- 指定端口转发
- service定义入口端口:port: 80,并转向pod的端口名,targetPort: http-web-svc
- port定义端口名和端口号: - containerPort: 80 name: http-web-svc
Service类型有哪些?
- 集群内访问:
- 服务类型:ClusterIP(默认值)
- 含义:会创建一个集群内的服务IP,client只能在集群内,通过该IP负载到后端的Pod
- 集群外访问:
- 通过Node节点暴露:
- 服务类型:NodePort
- 含义:会在Node节点上创建一个端口,通过NodeIP:NodePort负载到后端Pod
- 通过集群外LoadBalance(Node节点外层再顶一个LoadBalance)
- 服务类型:LoadBalance
- 含义:请求先到ELB -> NodeIP:NodePort -> PodIP:PodPort(有很多种实现方式,这是其中一种)
Endpoint是什么?
可以简单理解为已经ready的pod,具备被访问条件的pod。
当pod的readiness检测失败,pod不会加入到某个服务的Endpoints里面
如上图:10.1.3.8还没ready,就没有被加入my-service的Endpoints里
Service和Pod都会被分配域名吗?
Service 和 Pod 都会被分配对应的 DNS A 记录(从域名解析 IP 的记录)。
- service
- 对于 ClusterIP 模式的 Service 来说,它的 A 记录的格式是:..svc.cluster.local。
- 当你访问这条 A 记录的时候,它解析到的就是该 Service 的cluster的 VIP 地址。
- pod
- 对于 ClusterIP 模式的 Service 来说,它代理的 Pod 被自动分配的 A 记录的格式是:..pod.cluster.local。
- 这条记录指向 Pod 的 IP 地址
ClusterIP类型Service如何定义?
- 集群内访问:
- 服务类型:ClusterIP(默认值)
- 含义:会创建一个集群内的服务IP,通过该IP会负载到后端的Pod
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app.kubernetes.io/name: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
ClusterIP类型Service实现原理?
k8s的每个Node节点上都运行着一个kube-proxy服务进程。
- 控制面:
- 当创建Service的时候,会通过api-server向etcd写入创建的service的信息,而kube-proxy会基于监听的机制发现这种Service的变动,然后它会将最新的Service信息转换成对应的访问规则(有3种工作模式)。
- 数据面:
- client访问服务IP(ClusterIp),会被Iptables拦截,转给ipvs做负载均衡转发
ClusterIp的实现原理?
- 是什么?
- 当创建一个带有ClusterIP类型的Service时,Kubernetes会为该Service分配一个唯一的ClusterIP地址。
- 这个ClusterIp其实是一个VIP,只做负载用
- 这个IP地址是在集群的内部网络中使用的,并且只能由同一集群内的其他Pod访问
- 如何规划?
- Service地址段的管理由集群的网络插件负责。不同的网络插件可能有不同的管理方式。
- Kubernetes在集群创建时会预先定义一个内部IP地址段,这个IP地址段是专门用于集群内部通信的
- 参考云厂商的规划,是不能和Pod的CIDR地址段重叠
- Flannel网络模式Service地址段。Service类型为ClusterIP(Type=ClusterIP)时,每个Service有自己的地址。配置网段时,请注意:
- Service地址只在Kubernetes集群内使用,不能在集群外使用。
- Service地址段不能和虚拟交换机地址段重叠。
- Service地址段不能和Pod网络CIDR地址段重叠。
- 如何分配某个IP?
- Kubernetes会自动为该Service分配一个ClusterIP地址。
- 参考Kubernetes 1.24: 避免为 Services 分配 IP 地址时发生冲突
kube-proxy3种工作模式原理?
1. userspace模式(淘汰)
- 图示:
- 工作方式:
- 控制面:
- kube-proxy创建一个clusterIP
- kube-proxy配置Iptables,将访问clusterIp请求转给kube-proxy
- kube-proxy配置自身做为proxy,为每个Service创建一个监听端口,配置LB算法将请求转发到Pod上。
- 数据面:
- client发向Cluster IP的请求
- 被iptables规则重定向到kube-proxy监听的端口上,
- kube-proxy根据LB算法选择一个提供服务的Pod并和其建立链接,以将请求转发到Pod上。
- 缺点:
- kube-proxy充当了一个四层负责均衡器的角色。由于kube-proxy运行在userspace中,性能较差
2. iptables模式(淘汰)
- 图示:
- 工作方式:
- 控制面:
- kube-proxy创建一个clusterIP
- 自动从Service地址段分配一个ip:10.0.1.175
- kube-proxy为service后端的每个Pod创建对应的iptables规则
- -A KUBE-SERVICES -d 10.0.1.175/32 -p tcp -m comment --comment "default/hostnames: cluster IP" -m tcp --dport 80 -j KUBE-SVC-NWV5X2332I4OT4T3
- 表示:凡是目的地址是 10.0.1.175、目的端口是 80 的 IP 包,都应该跳转到另外一条名叫 KUBE-SVC-NWV5X2332I4OT4T3 的 iptables 链进行处理
- -A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -m statistic --mode random --probability 0.33332999982 -j KUBE-SEP-WNBA2IHDGP2BOBGZ
- -A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-X3P2623AGDH6CDF3
- -A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -j KUBE-SEP-57KPRZ3JQVENLNBR
- 表示:这三条链指向的最终目的地,其实就是这个 Service 代理的三个 Pod。所以这一组规则,就是 Service 实现负载均衡的位置。
- 需要注意的是,iptables 规则的匹配是从上到下逐条进行的,所以为了保证上述三条规则每条被选中的概率都相同,我们应该将它们的 probability 字段的值分别设置为 1/3(0.333…)、1/2 和 1。
- -A KUBE-SEP-57KPRZ3JQVENLNBR -s 10.244.3.6/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
- -A KUBE-SEP-57KPRZ3JQVENLNBR -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.3.6:9376
- -A KUBE-SEP-WNBA2IHDGP2BOBGZ -s 10.244.1.7/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
- -A KUBE-SEP-WNBA2IHDGP2BOBGZ -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.1.7:9376
- -A KUBE-SEP-X3P2623AGDH6CDF3 -s 10.244.2.3/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000
- -A KUBE-SEP-X3P2623AGDH6CDF3 -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.2.3:9376
- 数据面:
- client发向Cluster IP的请求
- 被iptables规则直接重定向到一个Pod IP
- 优点:
- 该模式下kube-proxy不承担四层负责均衡器的角色,只负责创建iptables规则,较userspace模式效率更高
- 缺点
- 当你的宿主机上有大量 Pod 的时候,成百上千条 iptables 规则不断地被刷新,会大量占用该宿主机的 CPU 资源,甚至会让宿主机“卡”在这个过程中。
3. ipvs模式(推荐)
- 图示:
- 工作方式:
- 控制面:
- kube-proxy监控Pod的变化并创建相应的ipvs规则
- kube-proxy 首先会在宿主机上创建一个虚拟网卡(叫作:kube-ipvs0),并为它分配 Service VIP 作为 IP 地址
- # ip addr
- ...
- 73:kube-ipvs0:<BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN qlen 1000
- link/ether 1a:ce:f5:5f:c1:4d brd ff:ff:ff:ff:ff:ff
- inet 10.0.1.175/32 scope global kube-ipvs0
- valid_lft forever preferred_lft forever
- kube-proxy 就会通过 Linux 的 IPVS 模块,为这个 IP 地址设置n个 IPVS 虚拟主机,并设置这n个虚拟主机之间使用轮询模式 (rr) 来作为负载均衡策略
- # ipvsadm -ln
- IP Virtual Server version 1.2.1 (size=4096)
- Prot LocalAddress:Port Scheduler Flags
- -> RemoteAddress:Port Forward Weight ActiveConn InActConn
- TCP 10.0.1.175:80 rr
- -> 10.244.3.6:9376 Masq 1 0 0
- -> 10.244.1.7:9376 Masq 1 0 0
- -> 10.244.2.3:9376 Masq 1 0 0
- 数据面:
- client发向Cluster IP的请求
- 被iptables规则直接重定向到ipvs(IPVS 模块只负责上述的负载均衡和代理功能。而一个完整的 Service 流程正常工作所需要的包过滤、SNAT 等操作,还是要靠 iptables 来实现。)
- IPVS 模块根据负载均衡算法,转发到某一个后端 Pod 上了
- 优点:
- 性能高一点,维护性也好点(在大规模集群里,为 kube-proxy 设置–proxy-mode=ipvs 来开启这个功能。)
- IPVS 并不需要在宿主机上为每个 Pod 设置 iptables 规则,而是把对这些“规则”的处理放到了内核态,从而极大地降低了维护这些规则的代价