k8s

ipvs模式下externalIP问题

现象

mysql服务不是容器化的,是单独的几台服务器,然后使用一个VIP,将VIP作为externalIP注入到集群中,这时候如果在node上通过ssh连接vip,就可能会随机连接到某一台node上。

结论

当开启IPVS后,externalIP也会作为VIP被ipvs接管,因此如果在externalIp指定的是集群中具体Node节点的IP,在重启kube-proxy之前需要提前将externalIp替换成预先规划好的VIP,否则会出现VIP和Node节点IP冲突的问题。
使用ep+svc的方式代替externalIP。

原因

修改kube-proxy cm,将mode改为ipvs,删除kube-proxy pod。

file

查看kube-proxy 日志,确认使用的是ipvs模式。

file

部署mysql测试服务

apiVersion: v1
kind: Service
metadata:
  name: mysql-mha
spec:
  ports:
  - port: 3306
    targetPort: 3306
    protocol: TCP
  externalIPs:
    - 172.50.60.105

查看ipvs0网卡地址
file

file

查看ipvs规则

ipvsadm -Ln
ipset list | grep -A 20 KUBE-EXTERNAL-IP

file

ipvs0网卡

ipvs0这块网卡和其他网卡有什么不同之处,可以发现ipvs0网卡有一个NOARP的标志,表示的是禁用arp,并且不管是down状态和up状态都有这个标志,说明这块网卡无论如何都是无法响应arp请求的。

ipvs0处于up状态时,内核会认为ipvs0这块网卡是可用的,但是因为禁用了arp,所以对这块网卡的arp请求都会直接丢弃掉,所以外部直接ping这块网卡的ip无法ping通。

当ipvs0这块网卡处于down状态时,因为arp请求的目标地址已经配在了ipvs0网卡上,所以内核当然会认为这次请求确实就是发给它的,但由于ipvs0这块网卡是down状态,所以内核会认为这块网卡不可用,并将同属于当前局域网的另一块网卡,也就是真实连接在交换机上的物理网卡的mac地址返回回去。

可以看到172.50.60.105的mac地址为node1 eth0网卡的mac地址。

为什么ssh会连到node

ip冲突一般是在多个节点上配置了同样的ip,首先肯定也是先通过arp请求获取到mac地址,然后通过mac地址最终定位到具体的节点,但是问题是所有配置了这个ip的节点都会将mac地址返回,我们并不能确定第一次返回的mac地址是哪个节点的mac地址,所以ssh或者http请求可能就会发往错误的节点。

externalip访问方式

但是在使用ipvs0这块网卡的时候,首先,我们不会通过ssh去连它,在k8s里就不是干这件事的,因为已经有真实的物理网卡可以连,然后就是对外提供服务,这个只有在给服务设置了externalip的时候,会直接在ipvs0网卡上配置一个外部能访问的ip,外部直接通过这个ip访问对应的服务,除externalip之外,ipvs0上配的都是内部的ip,只会在集群内部访问。

先看从外部访问,也就是externalip,通过externalip+port对外暴露服务,每个节点都能提供一样的服务,所以不管那个节点先返回arp请求,结果是一样的,所以外部用户需要知道访问的是哪个节点吗?不需要!
然后看内部访问,内部访问ipvs0上的ip等于访问localhost,本地就会将ip截获到,然后ipvs再根据负载均衡策略将流量转发到对应的pod。

clusterip访问方式

当创建 ClusterIP type 的 service 时,IPVS proxier 会执行以下三个操作:

  1. 确保本机已创建 dummy 网卡,默认为 kube-ipvs0。为什么要创建 dummy 网卡?因为 ipvs netfilter 的 DNAT 钩子挂载在 INPUT 链上,当访问 ClusterIP 时,将 ClusterIP 绑定在 dummy 网卡上为了让内核识别该 IP 就是本机 IP,进而进入 INPUT 链,然后通过钩子函数 ip_vs_in 转发到 POSTROUTING 链;
  2. 将 ClusterIP 绑定到 dummy 网卡;
  3. 为每个 ClusterIP 创建 IPVS virtual servers 和 real server,分别对应 service 和 endpoints;

数据包流向

  1. PREROUTING --> KUBE-SERVICES --> KUBE-CLUSTER-IP --> INPUT --> KUBE-FIREWALL --> POSTROUTING
  2. 首先进入 PREROUTING 链
  3. 从 PREROUTING 链会转到 KUBE-SERVICES 链,10.244.0.0/16 为 ClusterIP 网段
  4. 在 KUBE-SERVICES 链打标记
  5. 从 KUBE-SERVICES 链再进入到 KUBE-CLUSTER-IP 链
  6. KUBE-CLUSTER-IP 为 ipset 集合,在此处会进行 DNAT
  7. 然后会进入 INPUT 链
  8. 从 INPUT 链会转到 KUBE-FIREWALL 链,在此处检查标记
  9. 在 INPUT 链处,ipvs 的 LOCAL_IN Hook 发现此包在 ipvs 规则中则直接转发到 POSTROUTING 链
-A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES

-A KUBE-SERVICES ! -s 10.244.0.0/16 -m comment --comment "Kubernetes service cluster ip + port for masquerade purpose" -m set --match-set KUBE-CLUSTER-IP dst,dst -j KUBE-MARK-MASQ

// 执行完 PREROUTING
 MARK --set-xmark 0x4000/0x4000

-A KUBE-SERVICES -m set --match-set KUBE-CLUSTER-IP dst,dst -j ACCEPT

// 进入 INPUT
-A INPUT -j KUBE-FIREWALL

-A KUBE-FIREWALL -m comment --comment "kubernetes firewall for dropping marked packets" -m mark --mark 0x8000/0x8000 -j DROP

// 如果进来的数据带有 0x8000/0x8000 标记则丢弃,若有 0x4000/0x4000 标记则正常执行
-A POSTROUTING -m comment --comment "kubernetes postrouting rules" -j KUBE-POSTROUTING
-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -m mark --mark 0x4000/0x4000 -j MASQUERADE

nodePort访问方式

数据包流向

PREROUTING --> KUBE-SERVICES --> KUBE-NODE-PORT --> INPUT --> KUBE-FIREWALL --> POSTROUTING

  1. 首先进入 PREROUTING 链
  2. 从 PREROUTING 链会转到 KUBE-SERVICES 链
  3. 在 KUBE-SERVICES 链打标记
  4. 从 KUBE-SERVICES 链再进入到 KUBE-NODE-PORT 链
  5. KUBE-NODE-PORT 为 ipset 集合,在此处会进行 DNAT
  6. 然后会进入 INPUT 链
  7. 从 INPUT 链会转到 KUBE-FIREWALL 链,在此处检查标记
  8. 在 INPUT 链处,ipvs 的 LOCAL_IN Hook 发现此包在 ipvs 规则中则直接转发到 POSTROUTING 链
分类: k8s
0 0 投票数
文章评分
订阅评论
提醒
guest

0 评论
最旧
最新 最多投票
内联反馈
查看所有评论

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部
0
希望看到您的想法,请您发表评论x