目录

 一、Service知识回顾

二、k8s service工作原理

三、k8s serice其它模式


前章《k8s容器网络概述》讲了从docker讲起,讲到k8s怎样网络互通的。

我们在讲Service服务的时候,讲了服务的使用,但是没有讲服务的工作原理,所以在这里简单讲一下。

 

 一、Service知识回顾

Service 这个 Kubernetes 里重要的服务对象。而 Kubernetes 之所以需要 Service,一方面是因为 Pod 的 IP 不是固定的,另一方面则是因为一组 Pod 实例之间总会有负载均衡的需求。

我们就用之前讲的《hualinux 进阶 1.14:Services服务及种类1.2 建立service服务的例子,只不能我在deployment把pod节点改为2,下面是service实现代码


#1.建立群集,这里设置2台
mkdir -pv /disk1/myk8s
cd /disk1/myk8s/
 
#建立deployment群集,我设置2个pod,因为我这里只有一个节点
cat>nginx-deployment.yaml<<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    web: nginx18
spec:
  replicas: 2
  selector:
    matchLabels:
      web: nginx18
  template:
    metadata:
      labels:
        web: nginx18
    spec:
      containers:
      - name: nginx
        image: nginx:1.18
        ports:
        - containerPort: 80
EOF
cat nginx-deployment.yaml
kubectl apply -f nginx-deployment.yaml 

#2.创建一个服务
cat>nginx-ser.yaml<<EOF
apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  selector:
   #查找匹配的标签的pod
    web: nginx18
  ports:
    - protocol: TCP
      #services对外端口
      port: 80
      #这个是容器端口
      targetPort: 80
EOF
kubectl apply -f nginx-ser.yaml

 selector 选中的 Pod,就称为 Service 的 Endpoints,你可以使用 kubectl get ep 命令看到它们,

[root@vm82 myk8s]# kubectl get ep -o wide
NAME         ENDPOINTS                   AGE
kubernetes   192.168.128.82:6443         15d
nginx        10.44.0.1:80,10.44.0.2:80   81m

需要注意的是,只有处于 Running 状态,且 readinessProbe 检查通过的 Pod,才会出现在 Service 的 Endpoints 列表里。并且,当某一个 Pod 出现问题时,Kubernetes 会自动把它从 Service 里摘除掉。

此时,通过该 Service 的 VIP 地址 10.102.199.132,你就可以访问到它所代理的 Pod 了

#svc是service的简写,如果不懂可以使用 kubectl api-resources -o wide 命令查看
[root@vm82 myk8s]# kubectl get svc nginx
NAME    TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
nginx   ClusterIP   10.102.199.132   <none>        80/TCP    87m

二、k8s service工作原理

实际上,Service 是由 kube-proxy 组件,加上 iptables 来共同实现的。

在用kubeadm安装k8s的时候 (可以见我的文章《hualinux 进阶 1.7:kubeadm1.18搭建k8s群集》),如发现没有iptables,默认自动安装上了iptables

[root@vm82 myk8s]# rpm -qa|grep iptables
iptables-libs-1.8.4-10.el8.x86_64
iptables-1.8.4-10.el8.x86_64
iptables-ebtables-1.8.4-10.el8.x86_64

我们可以 iptables-save  查看一下 上面nginx serice的iptables情况

#以nginx service IP地址形式查看iptables情况
[root@vm82 myk8s]# iptables-save |grep 10.102.199.132
-A KUBE-SERVICES ! -s 172.168.0.0/16 -d 10.102.199.132/32 -p tcp -m comment --comment "default/nginx: cluster IP" -m tcp --dport 80 -j KUBE-MARK-MASQ
-A KUBE-SERVICES -d 10.102.199.132/32 -p tcp -m comment --comment "default/nginx: cluster IP" -m tcp --dport 80 -j KUBE-SVC-4N57TFCL4MD7ZTDA

发现了上面2条,一进一出,我们知道路径是有去有往的,所以2条,172.168.0.0是之前我kubeadm init初始化指定的cidr地址

#我这里指定网络段,如果不指定中可以省略--pod,可以加 “--dry-run”试运行,不会有改动
kubeadm init --pod-network-cidr=172.168.6.0/16

2条我就随便拿 最后一条查看吧,

可以看到,这条 iptables 规则的含义是:凡是目的地址是 10.102.199.132、目的端口是 80 的 IP 包,都应该跳转到另外一条名叫 KUBE-SVC-4N57TFCL4MD7ZTDA 的 iptables 链进行处理。

随便说一下第1条,KUBE-MARK-MASQ就是非172.168.0.0/16网段的,进入10.102.199.132前要进行伪装,即NAT

而我们前面已经看到,10.102.199.132正是这个 Service 的 VIP。所以这一条规则,就为这个 Service 设置了一个固定的入口地址。并且,由于10.102.199.132 只是一条 iptables 规则上的配置,并没有真正的网络设备,所以你 ping 这个地址,是不会有任何响应的。

现在再看一下 KUBE-SVC-4N57TFCL4MD7ZTDA 链的情况

[root@vm82 myk8s]# iptables-save |grep ' KUBE-SVC-4N57TFCL4MD7ZTDA'
-A KUBE-SERVICES -d 10.102.199.132/32 -p tcp -m comment --comment "default/nginx: cluster IP" -m tcp --dport 80 -j KUBE-SVC-4N57TFCL4MD7ZTDA
-A KUBE-SVC-4N57TFCL4MD7ZTDA -m comment --comment "default/nginx:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-RNUA2WF32F4IZIFK
-A KUBE-SVC-4N57TFCL4MD7ZTDA -m comment --comment "default/nginx:" -j KUBE-SEP-ZZWMMMZWIOYPPLV7

最上面第1条讲了。

第2条:--mode random 是随便模式的,--probability 0.50000000000 即为1/2机率被选中,因为2个pod。可以看出使用的是rr算法,而随机模式发向的是 KUBE-SEP-RNUA2WF32F4IZIFK

第3条:就是转到KUBE-SEP-ZZWMMMZWIOYPPLV7链处理,指向的最终目的地,其实就是这个 Service 代理的2个 Pod

所以这一组规则,就是 Service 实现负载均衡的功能。

再分别查看 KUBE-SEP-RNUA2WF32F4IZIFK 和 KUBE-SEP-ZZWMMMZWIOYPPLV7 这2个链表,如下所示:

[root@vm82 myk8s]# iptables-save |egrep ' KUBE-SEP-RNUA2WF32F4IZIFK'
-A KUBE-SVC-4N57TFCL4MD7ZTDA -m comment --comment "default/nginx:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-RNUA2WF32F4IZIFK
-A KUBE-SEP-RNUA2WF32F4IZIFK -s 10.44.0.1/32 -m comment --comment "default/nginx:" -j KUBE-MARK-MASQ
-A KUBE-SEP-RNUA2WF32F4IZIFK -p tcp -m comment --comment "default/nginx:" -m tcp -j DNAT --to-destination 10.44.0.1:80
[root@vm82 myk8s]# 
[root@vm82 myk8s]# iptables-save |egrep ' KUBE-SEP-ZZWMMMZWIOYPPLV7'
-A KUBE-SVC-4N57TFCL4MD7ZTDA -m comment --comment "default/nginx:" -j KUBE-SEP-ZZWMMMZWIOYPPLV7
-A KUBE-SEP-ZZWMMMZWIOYPPLV7 -s 10.44.0.2/32 -m comment --comment "default/nginx:" -j KUBE-MARK-MASQ
-A KUBE-SEP-ZZWMMMZWIOYPPLV7 -p tcp -m comment --comment "default/nginx:" -m tcp -j DNAT --to-destination 10.44.0.2:80

这个很清晰了吧,就是2个pod的IP地址

DNAT 规则的作用,就是在 PREROUTING 检查点之前,也就是在路由之前,将流入 IP 包的目的地址和端口,改成–to-destination 所指定的新的目的地址和端口。可以看到,这个目的地址和端口,正是被代理 Pod 的 IP 地址和端口。

访问 Service VIP 的 IP 包经过上述 iptables 处理之后,就已经变成了访问具体某一个后端 Pod 的 IP 包了。不难理解,这些 Endpoints 对应的 iptables 规则,正是 kube-proxy 通过监听 Pod 的变化事件,在宿主机上生成并维护的。

以上,就是 Service 最基本的工作原理。

上面使用的是 iptables 代理模式 

 

三、k8s serice其它模式

k8s service除了iptables 代理模式 ​​​​​​​,还有 IPVS 代理模式,还有一种比较旧的 userspace 代理模式,我这里就不讲了,主要讲一下IPVS 代理模式

 

其实,通过上面的讲解,你可以看到,kube-proxy 通过 iptables 处理 Service 的过程,其实需要在宿主机上设置相当多的 iptables 规则。而且,kube-proxy 还需要在控制循环里不断地刷新这些规则来确保它们始终是正确的。

不难想到,当你的宿主机上有大量 Pod 的时候,成百上千条 iptables 规则不断地被刷新,会大量占用该宿主机的 CPU 资源,甚至会让宿主机“卡”在这个过程中。所以说,一直以来,基于 iptables 的 Service 实现,都是制约 Kubernetes 项目承载更多量级的 Pod 的主要障碍。

IPVS 模式的 Service,就是解决这个问题的一个行之有效的方法。

IPVS 模式的工作原理,其实跟 iptables 模式类似。当我们创建了前面的 Service 之后,kube-proxy 首先会在宿主机上创建一个虚拟网卡(叫作:kube-ipvs0),并为它分配 Service VIP 作为 IP 地址。

而接下来,kube-proxy 就会通过 Linux 的 IPVS 模块,为这个 IP 地址设置三个 IPVS 虚拟主机,并设置这三个虚拟主机之间使用轮询模式 (rr) 来作为负载均衡策略。我们可以通过 ipvsadm 查看到这个设置

比于 iptables,IPVS 在内核中的实现其实也是基于 Netfilter 的 NAT 模式,所以在转发这一层上,理论上 IPVS 并没有显著的性能提升。但是,IPVS 并不需要在宿主机上为每个 Pod 设置 iptables 规则,而是把对这些“规则”的处理放到了内核态,从而极大地降低了维护这些规则的代价。“将重要操作放入内核态”是提高性能的重要手段。

不过需要注意的是,IPVS 模块只负责上述的负载均衡和代理功能。而一个完整的 Service 流程正常工作所需要的包过滤、SNAT 等操作,还是要靠 iptables 来实现。只不过,这些辅助性的 iptables 规则数量有限,也不会随着 Pod 数量的增加而增加。

所以,在大规模集群里,我非常建议你为 kube-proxy 设置–proxy-mode=ipvs 来开启这个功能。它为 Kubernetes 集群规模带来的提升,还是非常巨大的。

 

PS:

kubeadm 配置中有关 kube-proxy 的说明请查看:

使用 kubeadm 启用 IPVS 模式的说明请查看:

 

 

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐