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的规则 img.png 我们定义了一个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的具体实现