热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

记一个HarvesterSNAT案例

记,一个,harvest

作者简介
姚灿武,SUSE Rancher 研发工程师,拥有 6 年云计算领域经验,热衷开源技术,在云原生相关技术领域拥有丰富的开发和实践经验。

Harvester 通过 Multus 扩展了标准的 Kubernetes CNI 网络,可以让虚拟机拥有基于 Bridge Vlan 技术分配的虚拟网卡。本文源于一次问题排查实践,以解决复杂网络情况下产生的通信问题。

本文使用的 Harvester 版本为 v1.0.0

问题描述

Harvester 利用 Kubernetes service 为虚拟机中的服务提供负载均衡。在这个方案中,负载均衡后端地址是<虚拟机的 IP 地址:端口>,被记录在与 Kubernetes service 对应的 endpointslice 中。示意图表示如下:

下面是本文所使用的例子对应的 service 与 endpointslice。

harvester-host:/home/rancher # kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE default-nginx-lb-db9bdca5 LoadBalancer 10.43.113.238 80:32586/TCP 4h32m harvester-host:/home/rancher # kubectl get endpointslices NAME ADDRESSTYPE PORTS ENDPOINTS AGE default-nginx-lb-db9bdca5 IPv4 80 172.16.178.178 4h33m 

但是,我们发现,当 VM 使用 Harvester VLAN 网络,并且发起请求的客户端(如 curl)与 VM 同在一个 Harvester 主机时,通过负载均衡(本例中是访问 service clusterIP)的请求失败了。结果如下:

harvester-host:/home/rancher # curl 10.43.113.238 curl: (7) Failed to connect to 10.53.202.161 port 80: Connection timed out 

整个网络拓扑表示如下:

分析过程

明确流量路径

一般来说,对于网络问题,我们首先需要明确流量的转发路径。在 Kubernetes 中,当请求 service clusterIP 时,Kube-proxy 会将请求目的地址转为后端服务的地址。在我们的案例中,后端地址是 172.16.178.178:80。因为目的地址172.16.178.178 是在 VLAN 178 里,请求和响应都需要经过外部网关。因此,我们可以在网络拓扑中标记上流量转发路径。

抓包

不通过负载均衡,在各个 VLAN 网络中直接访问后端服务是通的,我们可以首先排除是外部交换机以及网关引发的问题。为了定位到是在哪个转发环节发生的问题,我们需要对 Harvester 主机流量路径上的各个网络接口进行抓包。我们将抓包结果整理后简化表示如下:

从抓包结果可以看出,VM 网卡正常接收到了 TCP SYN 网络包,并且响应发送了 SYN/ACK 报文。但是,当 SYN/ACK 报文被网桥从 veth2db2ad9c 转发到 eth1后,目的端口发生了改变。因为该目的端口与 SYN 报文的源端口不匹配,SYN/ACK 报文被丢弃导致 TCP 三次握手失败。由 conntrack 表中的记录可以看出,修改后的目的端口是负载均衡之前原方向请求的源端口。

harvester-host:/home/rancher # conntrack -L | grep 172.16.178.178 tcp 6 56 SYN_RECV src=172.16.0.57 dst=10.43.113.238 sport=38944 dport=80 src=172.16.178.178 dst=172.16.0.57 sport=80 dport=10598 mark=0 use=1 conntrack v1.4.5 (conntrack-tools): 262 flow entries have been shown. 

因此,我们合理猜测在网桥转发 SYN/ACK 报文的过程中发生了一次网络地址转换(NAT)。

根据 Kubernetes 官方文档描述,Kubernetes 默认开启了net.bridge.bridge-nf-call-iptables设置。

if the plugin connects containers to a Linux bridge, the plugin must set the net/bridge/bridge-nf-call-iptables sysctl to 1 to ensure that the iptables proxy functions correctly. 

该设置可以控制当报文经过网桥时,原来作用于三层网络的 iptables 规则是否在此二层转发过程中生效。在 Kubernetes 中,默认生效。也就是说,Kube-proxy 所设置的 iptables 规则包括一些 NAT 规则都会在网桥转发过程中起作用。

当我们设置 net.bridge.bridge-nf-call-iptables为 0 时,我们发现请求成功了。所以,毫无疑问,kube-proxy 的 iptables 规则导致了这个问题。但是,到目前为止,我们仍然无法定位具体是哪一条规则,或者说我们还没有定位 netfilter 中哪个环节导致了该问题。我们需要深入研究分析 netfilter 才能找到问题背后的根本原因。

深入分析 netfilter NAT

在网络分析工具 pwru 的帮助下,通过观察目的端口的变化以及打印出内核函数调用栈,我们可以确定 NAT 发生在 pre-routing 链上。

0xffff9e90d1c55200 [] skb_ensure_writable 16542012456363 netns=4026531992 mark=0x0 ifindex=10 proto=8 mtu=1500 len=60 172.16.178.178:80->172.16.0.57:10598(tcp) 0xffff9e90d1c55200 [] inet_proto_csum_replace4 16542012502740 netns=4026531992 mark=0x0 ifindex=10 proto=8 mtu=1500 len=60 172.16.178.178:80->172.16.0.57:38944(tcp) 
# stack of the function skb_ensure_writable 0xffff9e90c12a8d00 [ksoftirqd/5] skb_ensure_writable 16558140061379 netns=4026531992 mark=0x0 ifindex=10 proto=8 mtu=1500 len=60 172.16.178.178:80->172.16.0.57:10598(tcp) skb_ensure_writable l4proto_manip_pkt [nf_nat] nf_nat_ipv4_manip_pkt [nf_nat] nf_nat_manip_pkt [nf_nat] nf_nat_ipv4_pre_routing [nf_nat] nf_hook_slow br_nf_pre_routing [br_netfilter] br_handle_frame [bridge] __netif_receive_skb_core __netif_receive_skb_one_core process_backlog __napi_poll net_rx_action __softirqentry_text_start run_ksoftirqd smpboot_thread_fn kthread ret_from_fork # stack of the function inet_proto_csum_replace4 0xffff9e90c12a8d00 [ksoftirqd/5] inet_proto_csum_replace4 16558140095491 netns=4026531992 mark=0x0 ifindex=10 proto=8 mtu=1500 len=60 172.16.178.178:80->172.16.0.57:38944(tcp) inet_proto_csum_replace4 l4proto_manip_pkt [nf_nat] nf_nat_ipv4_manip_pkt [nf_nat] nf_nat_manip_pkt [nf_nat] nf_nat_ipv4_pre_routing [nf_nat] nf_hook_slow br_nf_pre_routing [br_netfilter] br_handle_frame [bridge] __netif_receive_skb_core __netif_receive_skb_one_core process_backlog __napi_poll net_rx_action __softirqentry_text_start run_ksoftirqd smpboot_thread_fn kthread ret_from_fork 

通过 Linux 内核中的 netfilter 源码以及netfilter在三层协议上的结构图,我们可以看到,NF_IP_PRI_NAT_DST参数表明,pre-routing 链上的 NAT 钩子只会改变报文的目的地址,即在 pre-routing 链上只会发生 DNAT 或者 de-SNAT。

static const struct nf_hook_ops nf_nat_ipv4_ops[] = { { .hook = ipt_do_table, .pf = NFPROTO_IPV4, .hooknum = NF_INET_PRE_ROUTING, .priority = NF_IP_PRI_NAT_DST, }, ... } 

在我们的案例中,SYN/ACK 是回包,所以应当是发生了 de-SNAT。Kube-proxy 在 post-routing 链上添加了 SNAT iptables 规则。请求 service 的报文经过时会在 output 链上打上标记,打上标记的报文在 post-chain 上会发生 SNAT。回包时,报文会在 pre-routing 链上发生 de-SNAT。

-A KUBE-SVC-2CMXP7HKUVJN7L6M ! -s 10.42.0.0/16 -d 10.43.220.155/32 -p tcp -m comment --comment "default/nginx cluster IP" -m tcp --dport 80 -j KUBE-MARK-MASQ -A KUBE-MARK-MASQ -j MARK --set-xmark 0x4000/0x4000 
-A KUBE-POSTROUTING -m mark ! --mark 0x4000/0x4000 -j RETURN -A KUBE-POSTROUTING -j MARK --set-xmark 0x4000/0x0 -A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE --random-fully 

在 Kubernetes 中,通常客户端请求与后端服务实例在同一个主机节点上,请求不会经过 KUBE-MART-MASQ 链,也就不会发生 SNAT 和 de-SNAT。但是不知道为什么在这个案例中发生了。通过查阅 Kubernetes 官方文档,确定 kube-proxy 的配置项 cluster-cidr 是罪魁祸首。

--cluster-cidr string The CIDR range of pods in the cluster. When configured, traffic sent to a Service cluster IP from outside this range will be masqueraded and traffic sent from pods to an external LoadBalancer IP will be directed to the respective cluster IP instead 

将 cluster-cidr 设置为空,请求成功。

解决方案

从上面的分析中可以知道,解决问题的关键是避免发生不必要的 SNAT。有两个可选的解决方案。

  1. 因为 Harvester 所使用的 canal CNI 不依赖 bridge netfilter,我们可以直接关闭 net.bridge.bridge-nf-call-iptables
  2. 将 kube-proxy cluster-cidr 配置为空。

参考文献

  • [kube-proxy]
    https://kubernetes.io/docs/reference/command-line-tools-reference/kube-proxy/

  • [network-plugin-requirements]
    https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/network-plugins/#network-plugin-requirements

  • [Net.bridge.bridge-nf-call and sysctl.conf]
    https://wiki.libvirt.org/page/Net.bridge.bridge-nf-call_and_sysctl.conf

  • [IPTABLES的连接跟踪与NAT分析]
    https://segmentfault.com/a/1190000041259845

  • [Deep Dive kube-proxy with iptables mode]
    https://serenafeng.github.io/2020/03/26/kube-proxy-in-iptables-mode/

  • [Service cluster ip How to disable snat]
    https://github.com/projectcalico/calico/issues/2999


推荐阅读
  • 在Linux系统中,网络配置是至关重要的任务之一。本文详细解析了Firewalld和Netfilter机制,并探讨了iptables的应用。通过使用`ip addr show`命令来查看网卡IP地址(需要安装`iproute`包),当网卡未分配IP地址或处于关闭状态时,可以通过`ip link set`命令进行配置和激活。此外,文章还介绍了如何利用Firewalld和iptables实现网络流量控制和安全策略管理,为系统管理员提供了实用的操作指南。 ... [详细]
  • CentOS 7 中 iptables 过滤表实例与 NAT 表应用详解
    在 CentOS 7 系统中,iptables 的过滤表和 NAT 表具有重要的应用价值。本文通过具体实例详细介绍了如何配置 iptables 的过滤表,包括编写脚本文件 `/usr/local/sbin/iptables.sh`,并使用 `iptables -F` 清空现有规则。此外,还深入探讨了 NAT 表的配置方法,帮助读者更好地理解和应用这些网络防火墙技术。 ... [详细]
  • 在CentOS 7环境中安装配置Redis及使用Redis Desktop Manager连接时的注意事项与技巧
    在 CentOS 7 环境中安装和配置 Redis 时,需要注意一些关键步骤和最佳实践。本文详细介绍了从安装 Redis 到配置其基本参数的全过程,并提供了使用 Redis Desktop Manager 连接 Redis 服务器的技巧和注意事项。此外,还探讨了如何优化性能和确保数据安全,帮助用户在生产环境中高效地管理和使用 Redis。 ... [详细]
  • 在Linux系统中避免安装MySQL的简易指南
    在Linux系统中避免安装MySQL的简易指南 ... [详细]
  • SecureCRT是一款功能强大的终端仿真软件,支持SSH1和SSH2协议,适用于在Windows环境下高效连接和管理Linux服务器。该工具不仅提供了稳定的连接性能,还具备丰富的配置选项,能够满足不同用户的需求。通过SecureCRT,用户可以轻松实现对远程Linux系统的安全访问和操作。 ... [详细]
  • 在前文探讨了Spring如何为特定的bean选择合适的通知器后,本文将进一步深入分析Spring AOP框架中代理对象的生成机制。具体而言,我们将详细解析如何通过代理技术将通知器(Advisor)中包含的通知(Advice)应用到目标bean上,以实现切面编程的核心功能。 ... [详细]
  • 一、Tomcat安装后本身提供了一个server,端口配置默认是8080,对应目录为:..\Tomcat8.0\webapps二、Tomcat8.0配置多个端口,其实也就是给T ... [详细]
  • 为什么多数程序员难以成为架构师?
    探讨80%的程序员为何难以晋升为架构师,涉及技术深度、经验积累和综合能力等方面。本文将详细解析Tomcat的配置和服务组件,帮助读者理解其内部机制。 ... [详细]
  • 本文详细介绍了如何在 Linux 系统上安装 JDK 1.8、MySQL 和 Redis,并提供了相应的环境配置和验证步骤。 ... [详细]
  • 本文详细介绍了如何在Linux系统(以CentOS为例)上彻底卸载Zimbra邮件系统,包括停止服务、删除文件和用户等步骤。 ... [详细]
  • 解决Only fullscreen opaque activities can request orientation错误的方法
    本文介绍了在使用PictureSelectorLight第三方框架时遇到的Only fullscreen opaque activities can request orientation错误,并提供了一种有效的解决方案。 ... [详细]
  • 如何在Linux服务器上配置MySQL和Tomcat的开机自动启动
    在Linux服务器上部署Web项目时,通常需要确保MySQL和Tomcat服务能够随系统启动而自动运行。本文将详细介绍如何在Linux环境中配置MySQL和Tomcat的开机自启动,以确保服务的稳定性和可靠性。通过合理的配置,可以有效避免因服务未启动而导致的项目故障。 ... [详细]
  • 本文介绍了如何利用Shell脚本高效地部署MHA(MySQL High Availability)高可用集群。通过详细的脚本编写和配置示例,展示了自动化部署过程中的关键步骤和注意事项。该方法不仅简化了集群的部署流程,还提高了系统的稳定性和可用性。 ... [详细]
  • 在重新安装Ubuntu并配置Django和PyCharm后,忘记测试MySQL连接,导致在后续配置过程中遇到错误:ERROR 2003 (HY000) - 无法连接到本地服务器 ‘127.0.0.1’ (111)。本文将详细介绍该错误的原因及解决步骤,帮助用户快速恢复MySQL服务的正常运行。我们将从检查网络配置、验证MySQL服务状态、配置防火墙规则等方面入手,提供全面的故障排除指南。 ... [详细]
  • 在 CentOS 6.5 系统上部署 VNC 服务器的详细步骤与配置指南
    在 CentOS 6.5 系统上部署 VNC 服务器时,首先需要确认 VNC 服务是否已安装。通常情况下,VNC 服务默认未安装。可以通过运行特定的查询命令来检查其安装状态。如果查询结果为空,则表明 VNC 服务尚未安装,需进行手动安装。此外,建议在安装前确保系统的软件包管理器已更新至最新版本,以避免兼容性问题。 ... [详细]
author-avatar
NE丰胸茶urghx
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有