总有小白不懂跟着网上文章设置externalIPs后集群崩了
由来 今天中午又有小白不懂在 ipvs 模式下,跟着网上文章设置 externalIPs 为 k8s 的机器 IP 后集群崩了,本文讲解下原因和自救手段。
externalIPs 下面是 官方文档 externalIPs 的描述:
如果有外部 IP 能够路由到一个或多个集群节点上,则 Kubernetes 服务可以暴露在这些 externalIPs 上。 当网络流量到达集群时,如果外部 IP(作为目的 IP 地址)和端口都与该 Service 匹配,Kubernetes 配置的规则和路由会确保流量被路由到该 Service 的端点之一。
如果你第一次看不懂没关系,后面看完本文就懂了这部分文字。
iptables 模式 先看看 iptables 的模式下原理,iptables 模式集群信息为:
1 2 3 4 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
部署下下面的服务,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 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 后再导出对比下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $ 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 的处理链
-m physdev ! --physdev-is-in
意思为不是从桥接接口进来,也就是外部的物理网卡进来的,-m addrtype ! --src-type LOCAL
意思是来源地址不是本机上的 IP,最后是到 svc 的 DNAT 到 podIP
第三个结尾的 -m addrtype --dst-type LOCAL
是目标 IP 在本机,也就是针对 externalIPs 设置成机器上的 网卡 IP 的,此刻拦截发到 svc。
因为前面都是针对 ip:80 的,所以 iptables 模式针对的都是纯四层,设置为集群机器 IP 这样没啥问题,问题是 ipvs 模式,也就是接下来讲的
ipvs 模式 清理掉上面的svc:
1 2 kubectl delete pod nginx kubectl delete svc nginx-service
集群 kube-proxy 切换为 ipvs 模式后。可以先阅读之前的文章 在非容器环境上实现散装的 IPVS SVC 。
非 k8s IP 切换模式后,先找个不是 k8s 节点但是和 k8s 节点机器是同一个二层局域网的没使用的 IP 作为 externalIPs
,然后部署上面的步骤。
ipvs 下,kube-proxy 会把 svcIP/32
配置在 kube-ipvs0
的 dummy
接口上,而 externalIPs
也会把它配置在 kube-ipvs0
上。然后我们做一个实验,假设此刻 externalIPs 设置为 10.xxx.xx.250
可以任何一台k8s机器:
1 2 3 4 5 6 $ 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。
k8s 机器 IP 你可能在想,我 nginx 设置监听指定的本机节点网卡 IP ,这样 externalIPs:80
不就无法访问了吗,确实,但是 externalIPs:其他端口
也会定向到本机上,由于没端口监听访问最终会超时。假设你把 externalIPs
设置为第一个master 节点:
第一个 master 节点路由到本机 IP 都会到 kube-ipvs0(因为掩码32最大),也就会和外界断联
其他 master 访问 master1:6443 和 etcd 的 2379 都被定向到本机的,信息就乱了,包含其他端口都会定向到本机上,有端口监听就信息乱,没端口监听就超时
然后非第一个 master 上的 kubelet 和 kubectl 只要流量最终负载到第一个 master 上就都会超时
假设已经发生:
每台机器从 tty 或者虚拟化 vnc 进去后停止 kube-proxy,ip link delete <externalIPs>/32 dev kube-ipvs0
如果操作后能使用 kubectl 就 edit 或者删除那个 svc
如果不能就 etcdctl 删除掉这个 svc
最后再启动停止的进程
ipvs 模式如何使用 externalIPs 可以设置外部没使用的 IP,然后网络设备添加路由把这个 IP 导向 k8s 的机器。或者有其他进程/轮子宣告 externalIPs 的 arp,让外部机器访问 externalIPs 能到 k8s 机器上
参考