services详解
Kubernetes中Service是将运行在一个或一组Pod上的网络应用程序公开为网络服务的方法。通过与kube-proxy配置iptable/ipvs规则使流量分发到不同的pod
上,而这些pod被存储在endpoint/endpoint slice里,简单来说就是k8s对外访问的负载均衡,下面我们来看看services的类型以及具体的实现
services的类型
- ClusterIp(Headless): 配置一个虚拟ip仅供内部访问,一般配合ingress或api gateway使用
- NodePort:配置虚拟ip+暴露端口供内外部访问
- LoadBalancer:一般为云厂商提供负载均衡接口,且同时存在NodePort+ClusterIp(本文暂不讲,后续专门出一篇详细讲解)
- ExternalName:域名的A记录提供转发
services的原理
在endpoint详解里我们了解了services相关后端pod信息的存储,然后本文我们了解下services负载均衡的原理
kubectl创建kind为services后, apiserver会自动帮services分配一个虚ip(ClusterIp),ip段在启动的时候配置项--service-cluster-ip-range=10.245.0.0/12配置
如果是NodePort类型,则会分配端口,默认30000到32000,可通过启动项--services-node-port-range修改范围
现在ip分配好了,那我们怎么根据这个ip访问到endpoint里存储的pod呢,k8s里通过iptable/ipvs来实现负载均衡,下面我们结合实例看下iptable具体规则
iptables的4表5链如下,这里不深入讲解,我们来创建一个services看下具体iptable/ipvs的规则
我们定义了一个services,以及一个deploy,deploy定义了两个pod,下面我们看下怎么通过services访问到pod
apiVersion: v1
kind: Service
metadata:
name: pg-services
spec:
type: NodePort
ports:
- port: 80
protocol: TCP
name: http
selector:
run: pg-services
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
run: pg-services
name: pg-services
namespace: default
spec:
replicas: 2
selector:
matchLabels:
run: pg-services
template:
metadata:
labels:
run: pg-services
spec:
containers:
- image: nginx
name: pg-services-pod
查看services以及pod的ip,我们看到services ip为10.253.177.238,pod ip分别为10.244.166.140,10.244.166.184,需要注意的是
可以基于tcp访问通,ping由于是3层协议,ping services ip是不通的,下面我们来看下对应的iptables规则
➜ work k get svc pg-services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
pg-services NodePort 10.253.177.238 <none> 80:31300/TCP 27m
➜ work k get po -o wide | grep pg-services
pg-services-769476fc67-m4fpm 1/1 Running 0 27m 10.244.166.140 node1 <none> <none>
pg-services-769476fc67-mx7cf 1/1 Running 0 27m 10.244.166.184 node1 <none> <none>
iptabls模式
iptables具体访问规则如下,不是深入搞网络大概理解意思即可
10.253.177.238:80->
10.244.166.140
10.244.166.184
# 目标地址为10.253.177.238的走KUBE-SVC-GZRUDV7MBLRIN7L3这个Chain
-A KUBE-SERVICES -d 10.253.177.238/32 -p tcp -m comment --comment "default/pg-services:http cluster IP" -m tcp --dport 80 -j KUBE-SVC-GZRUDV7MBLRIN7L3
# 规则是50%走KUBE-SEP-IKQDZUH6CUQQNRIQ,剩下的走KUBE-SEP-SUNXK5D6H4DYANEZ
Chain KUBE-SVC-GZRUDV7MBLRIN7L3 (2 references)
KUBE-MARK-MASQ tcp -- !10.244.0.0/16 10.253.177.238 /* default/pg-services:http cluster IP */ tcp dpt:http
KUBE-SEP-IKQDZUH6CUQQNRIQ all -- anywhere anywhere /* default/pg-services:http -> 10.244.166.140:80 */ statistic mode random probability 0.50000000000
KUBE-SEP-SUNXK5D6H4DYANEZ all -- anywhere anywhere /* default/pg-services:http -> 10.244.166.184:80 */
-A KUBE-SVC-GZRUDV7MBLRIN7L3 ! -s 10.244.0.0/16 -d 10.253.177.238/32 -p tcp -m comment --comment "default/pg-services:http cluster IP" -m tcp --dport 80 -j KUBE-MARK-MASQ
ipvs模式
默认走的iptables,我们需要手动修改配置文件走ipvs模式,首先查看配置文件,查看kube-proxy配置文件是通过configmap挂载的,我们修改configmap里
mode为ipvs,ipvs是增量,iptables是全量更新,跟endpoint一样,数量多了后全量会有问题,ipvs感兴趣可以自行查阅资料
➜ ~ alias ks
ks='kubectl -n kube-system'
ks get po
# 查看配置文件对应的configmap
ks get po kube-proxy-fhdhn -o yaml
ks edit configmaps kube-proxy
#mode: "" -> mode: "ipvs"
ks delete po kube-proxy-fhdhn
# 等待自动重启使用新配置ipvs
# ipvs对应的规则如下
TCP node1:31300 rr
-> 10.244.166.140:http Masq 1 0 0
-> 10.244.166.184:http Masq 1 0 0
总结
总结下,services通过endpoint/endpoint slice存储后端的pod信息,apiserver分配ip,port,kube-proxy通过iptables配置负载均衡的规则。也可配置ipvs规则
ipvs支持以下负载均衡类型
- rr:轮询
- lc:最少连接(打开连接数最少)
- dh:目标地址哈希
- sh:源地址哈希
- sed:最短预期延迟
- nq:最少队列 原理与iptable类似,感兴趣可以自行查看,下文我们将详细了解kube-proxy的具体实现