集群信息为:
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME 10.xxx.xx.211 Ready master,node 14d v1.20.11 10.xxx.xx.211 <none> Kylin Linux Advanced Server V10 (Sword) 4.19.90-24.4.v2101.ky10.aarch64 docker://20.10.22 10.xxx.xx.213 Ready master,node 14d v1.20.11 10.xxx.xx.213 <none> Kylin Linux Advanced Server V10 (Sword) 4.19.90-24.4.v2101.ky10.aarch64 docker://20.10.22 10.xxx.xx.214 Ready master,node 14d v1.20.11 10.xxx.xx.214 <none> Kylin Linux Advanced Server V10 (Sword) 4.19.90-24.4.v2101.ky10.aarch64 docker://20.10.22 |
部署下下面的服务
apiVersion: v1 kind: Pod metadata: name: nginx labels: app: zgz 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: zgz ports: - name: name-of-service-port protocol: TCP port: 80 targetPort: http-web-svc |
部署后,选个节点导出下 iptables nat 表规则,然后增加 externalIPs 设置为第一个节点的 IP 后再导出对比下
$ iptables -t nat -S > before-nat-pod.txt $ kubectl patch svc nginx-service -p '{"spec":{"externalIPs":["10.xxx.xx.211"]}}' $ iptables -t nat -S > after-nat-pod.txt # 对比 $ diff <(sort before-nat-pod.txt) <(sort after-nat-pod.txt) 149a150,152 > -A KUBE-SERVICES -d 10.xxx.xx.211/32 -p tcp -m comment --comment "default/nginx-service:name-of-service-port external IP" -m tcp --dport 80 -j KUBE-MARK-MASQ > -A KUBE-SERVICES -d 10.xxx.xx.211/32 -p tcp -m comment --comment "default/nginx-service:name-of-service-port external IP" -m tcp --dport 80 -m addrtype --dst-type LOCAL -j KUBE-SVC-IQGXNJVVP26VHMIN > -A KUBE-SERVICES -d 10.xxx.xx.211/32 -p tcp -m comment --comment "default/nginx-service:name-of-service-port external IP" -m tcp --dport 80 -m physdev ! --physdev-is-in -m addrtype ! --src-type LOCAL -j KUBE-SVC-IQGXNJVVP26VHMIN # 不 sort 对比就多了因为顺序乱了的重复内容,sort 对比又会导致上面的规则顺序乱了,所以直接正则匹配 $ grep -w 'name-of-service-port external IP' after-nat-pod.txt -A KUBE-SERVICES -d 10.xxx.xx.211/32 -p tcp -m comment --comment "default/nginx-service:name-of-service-port external IP" -m tcp --dport 80 -j KUBE-MARK-MASQ -A KUBE-SERVICES -d 10.xxx.xx.211/32 -p tcp -m comment --comment "default/nginx-service:name-of-service-port external IP" -m tcp --dport 80 -m physdev ! --physdev-is-in -m addrtype ! --src-type LOCAL -j KUBE-SVC-IQGXNJVVP26VHMIN -A KUBE-SERVICES -d 10.xxx.xx.211/32 -p tcp -m comment --comment "default/nginx-service:name-of-service-port external IP" -m tcp --dport 80 -m addrtype --dst-type LOCAL -j KUBE-SVC-IQGXNJVVP26VHMIN # KUBE-SVC-IQGXNJVVP26VHMIN 则是 svc 的 iptables 负载入口链 |
diff 发现多了三条 iptables 规则,三条规则的前面属性都是 目标 IP 和端口为 10.xxx.xx.211:80 的:
因为前面都是针对 ip:80 的,所以 iptables 模式针对的都是纯四层,设置为集群机器 IP 这样没啥问题,问题是 ipvs 模式
清理掉上面的svc:
kubectl delete pod nginx kubectl delete svc nginx-service |
集群 kube-proxy 切换为 ipvs 模式后,先找个不是 k8s 节点但是和 k8s 节点机器是同一个二层局域网的没使用的 IP 作为 externalIPs,然后部署上面的步骤。
ipvs 下,kube-proxy 会把 svcIP/32 配置在 kube-ipvs0 的 dummy 接口上,而 externalIPs 也会把它配置在 kube-ipvs0 上。然后我们做一个实验,假设此刻 externalIPs 设置为 10.xxx.xx.250 可以任何一台k8s机器:
$ docker run --rm -d --net host --name test nginx:alpine $ curl -I localhost HTTP/1.1 200 OK $ curl -I 10.xxx.xx.250 HTTP/1.1 200 OK |
你会发现也能通,这是因为 kube-proxy 把 externalIPs/32 配置在 kube-ipvs0 的 dummy 上了,因为 IP 在本机上,而且是32位掩码,又因为 nginx 进程 bind 的 0.0.0.0 ,所以访问 externalIPs:80 也能到 nginx。
你可能在想,我 nginx 设置监听指定的本机节点网卡 IP ,这样 externalIPs:80 不就无法访问了吗,确实,但是 externalIPs:其他端口 也会定向到本机上,由于没端口监听访问最终会超时。假设你把 externalIPs 设置为第一个master 节点:
假设已经发生:
可以设置外部没使用的 IP,然后网络设备添加路由把这个 IP 导向 k8s 的机器。或者有其他进程/工具宣告 externalIPs 的 arp,让外部机器访问 externalIPs 能到 k8s 机器上