iptables 模式下

集群信息为:

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 的处理链
  • -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:

kubectl delete pod nginx
kubectl delete svc nginx-service

非 k8s IP

集群 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。

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 机器上

参考

  • 无标签

0 评论

你还没有登录。你所做的任何更改会将作者标记为匿名用户。 如果你已经拥有帐户,请登录