如何检查Kubernetes网络
介绍
Kubernetes 是一个容器编排系统,可以跨服务器节点集群管理容器化应用程序。 维护集群中所有容器之间的网络连接需要一些先进的网络技术。 在本文中,我们将简要介绍一些用于检查此网络设置的工具和技术。
如果您正在调试连接问题、调查网络吞吐量问题或探索 Kubernetes 以了解其运行方式,这些工具可能会很有用。
如果您想全面了解 Kubernetes 的更多信息,我们的指南 An Introduction to Kubernetes 涵盖了基础知识。 有关 Kubernetes 网络特定的概述,请阅读 Kubernetes Networking Under the Hood。
入门
本教程将假设您有一个 Kubernetes 集群,在本地安装了 kubectl
并配置为连接到集群。
以下部分包含许多旨在在 Kubernetes 节点上运行的命令。 它们看起来像这样:
echo 'this is a node command'
应该在本地计算机上运行的命令将具有以下外观:
echo 'this is a local command'
注意: 本教程中的大部分命令都需要以 root 用户身份运行。 如果您改为在 Kubernetes 节点上使用启用 sudo 的用户,请在必要时添加 sudo
以运行命令。
查找 Pod 的集群 IP
要查找 Kubernetes pod 的集群 IP 地址,请在本地计算机上使用 kubectl get pod
命令,并带有选项 -o wide
。 此选项将列出更多信息,包括 pod 所在的节点以及 pod 的集群 IP。
kubectl get pod -o wide
OutputNAME READY STATUS RESTARTS AGE IP NODE hello-world-5b446dd74b-7c7pk 1/1 Running 0 22m 10.244.18.4 node-one hello-world-5b446dd74b-pxtzt 1/1 Running 0 22m 10.244.3.4 node-two
IP 列将包含每个 pod 的内部集群 IP 地址。
如果您没有看到要查找的 pod,请确保您位于正确的命名空间中。 您可以通过添加标记 --all-namespaces
列出所有命名空间中的所有 pod。
查找服务的 IP
我们也可以使用 kubectl
找到服务 IP。 在这种情况下,我们将列出所有命名空间中的所有服务:
kubectl get service --all-namespaces
OutputNAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE default kubernetes ClusterIP 10.32.0.1 <none> 443/TCP 6d kube-system csi-attacher-doplugin ClusterIP 10.32.159.128 <none> 12345/TCP 6d kube-system csi-provisioner-doplugin ClusterIP 10.32.61.61 <none> 12345/TCP 6d kube-system kube-dns ClusterIP 10.32.0.10 <none> 53/UDP,53/TCP 6d kube-system kubernetes-dashboard ClusterIP 10.32.226.209 <none> 443/TCP 6d
服务 IP 可以在 CLUSTER-IP 列中找到。
查找并输入 Pod 网络命名空间
每个 Kubernetes pod 都被分配了自己的网络命名空间。 网络命名空间(或 netns)是一种 Linux 网络原语,可在网络设备之间提供隔离。
从 pod 的 netns 中运行命令以检查 DNS 解析或一般网络连接可能很有用。 为此,我们首先需要查找 pod 中一个容器的进程 ID。 对于 Docker,我们可以通过一系列的两个命令来做到这一点。 首先,列出节点上运行的容器:
docker ps
OutputCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 173ee46a3926 gcr.io/google-samples/node-hello "/bin/sh -c 'node se…" 9 days ago Up 9 days k8s_hello-world_hello-world-5b446dd74b-pxtzt_default_386a9073-7e35-11e8-8a3d-bae97d2c1afd_0 11ad51cb72df k8s.gcr.io/pause-amd64:3.1 "/pause" 9 days ago Up 9 days k8s_POD_hello-world-5b446dd74b-pxtzt_default_386a9073-7e35-11e8-8a3d-bae97d2c1afd_0 . . .
在您感兴趣的 pod 中找到任何容器的 容器 ID 或 名称。 在上面的输出中,我们展示了两个容器:
- 第一个容器是在
hello-world
pod 中运行的hello-world
应用程序 - 第二个是在
hello-world
pod 中运行的 pause 容器。 此容器的存在仅用于保存 pod 的网络命名空间
要获取任一容器的进程 ID,请记下容器 ID 或名称,并在以下 docker
命令中使用它:
docker inspect --format '{{ .State.Pid }}' container-id-or-name
Output14552
将输出进程 ID(或 PID)。 现在我们可以使用 nsenter
程序在该进程的网络命名空间中运行命令:
nsenter -t your-container-pid -n ip addr
请务必使用您自己的 PID,并将 ip addr
替换为您希望在 pod 的网络命名空间中运行的命令。
注意: 使用 nsenter
在 pod 的命名空间中运行命令的一个优势——与使用 docker exec
之类的东西相比——是您可以访问所有可用的命令node,而不是安装在容器中的通常有限的命令集。
查找 Pod 的虚拟以太网接口
每个 pod 的网络命名空间通过虚拟以太网管道与节点的根 netns 通信。 在节点端,此管道显示为通常以 veth
开头并以唯一标识符结尾的设备,例如 veth77f2275
或 veth01
。 在 pod 内,此管道显示为 eth0
。
关联哪个 veth
设备与特定 pod 配对可能很有用。 为此,我们将列出节点上的所有网络设备,然后列出 pod 的网络命名空间中的设备。 然后,我们可以关联两个列表之间的设备编号以建立连接。
首先,使用 nsenter
在 pod 的网络命名空间中运行 ip addr
。 有关如何执行此操作的详细信息,请参阅上一节 查找和输入 Pod 网络命名空间 :
nsenter -t your-container-pid -n ip addr
Output1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 10: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default link/ether 02:42:0a:f4:03:04 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 10.244.3.4/24 brd 10.244.3.255 scope global eth0 valid_lft forever preferred_lft forever
该命令将输出 pod 的接口列表。 请注意示例输出中 eth0@
之后的 if11
编号。 这意味着这个 pod 的 eth0
链接到节点的第 11 个接口。 现在在节点的默认命名空间中运行 ip addr
以列出其接口:
ip addr
Output1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever . . . 7: veth77f2275@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master docker0 state UP group default link/ether 26:05:99:58:0d:b9 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet6 fe80::2405:99ff:fe58:db9/64 scope link valid_lft forever preferred_lft forever 9: vethd36cef3@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master docker0 state UP group default link/ether ae:05:21:a2:9a:2b brd ff:ff:ff:ff:ff:ff link-netnsid 1 inet6 fe80::ac05:21ff:fea2:9a2b/64 scope link valid_lft forever preferred_lft forever 11: veth4f7342d@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master docker0 state UP group default link/ether e6:4d:7b:6f:56:4c brd ff:ff:ff:ff:ff:ff link-netnsid 2 inet6 fe80::e44d:7bff:fe6f:564c/64 scope link valid_lft forever preferred_lft forever
在此示例输出中,第 11 个接口是 veth4f7342d
。 这是我们正在调查的 pod 的虚拟以太网管道。
检查 Conntrack 连接跟踪
在 1.11 版本之前,Kubernetes 使用 iptables NAT 和 conntrack 内核模块来跟踪连接。 要列出当前正在跟踪的所有连接,请使用 conntrack
命令:
conntrack -L
要连续观察新连接,请使用 -E
标志:
conntrack -E
要列出到特定目标地址的 conntrack 跟踪连接,请使用 -d
标志:
conntrack -L -d 10.32.0.1
如果您的节点在建立与服务的可靠连接时遇到问题,则可能是您的连接跟踪表已满,并且正在删除新连接。 如果是这种情况,您可能会在系统日志中看到如下消息:
/var/log/系统日志
Jul 12 15:32:11 worker-528 kernel: nf_conntrack: table full, dropping packet.
要跟踪的最大连接数有一个 sysctl 设置。 您可以使用以下命令列出当前值:
sysctl net.netfilter.nf_conntrack_max
Outputnet.netfilter.nf_conntrack_max = 131072
要设置新值,请使用 -w
标志:
sysctl -w net.netfilter.nf_conntrack_max=198000
要使此设置永久化,请将其添加到 sysctl.conf
文件中:
/etc/sysctl.conf
. . . net.ipv4.netfilter.ip_conntrack_max = 198000
检查 iptables 规则
在 1.11 版本之前,Kubernetes 使用 iptables NAT 来实现 Service IP 的虚拟 IP 转换和负载均衡。
要转储节点上的所有 iptables 规则,请使用 iptables-save
命令:
iptables-save
由于输出可能很长,您可能希望通过管道传输到文件 (iptables-save > output.txt
) 或寻呼机 (iptables-save | less
) 以更轻松地查看规则。
要仅列出 Kubernetes 服务 NAT 规则,请使用 iptables
命令和 -L
标志指定正确的链:
iptables -t nat -L KUBE-SERVICES
OutputChain KUBE-SERVICES (2 references) target prot opt source destination KUBE-SVC-TCOU7JCQXEZGVUNU udp -- anywhere 10.32.0.10 /* kube-system/kube-dns:dns cluster IP */ udp dpt:domain KUBE-SVC-ERIFXISQEP7F7OF4 tcp -- anywhere 10.32.0.10 /* kube-system/kube-dns:dns-tcp cluster IP */ tcp dpt:domain KUBE-SVC-XGLOHA7QRQ3V22RZ tcp -- anywhere 10.32.226.209 /* kube-system/kubernetes-dashboard: cluster IP */ tcp dpt:https . . .
查询集群DNS
调试集群 DNS 解析的一种方法是使用您需要的所有工具部署一个调试容器,然后使用 kubectl
在其上执行 nslookup
。 这在 Kubernetes 官方文档 中有描述。
查询集群 DNS 的另一种方法是使用节点中的 dig
和 nsenter
。 如果没有安装 dig
,可以在基于 Debian 的 Linux 发行版上使用 apt
安装:
apt install dnsutils
首先,找到kube-dns服务的集群IP:
kubectl get service -n kube-system kube-dns
OutputNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kube-dns ClusterIP 10.32.0.10 <none> 53/UDP,53/TCP 15d
集群 IP 在上面突出显示。 接下来我们将使用 nsenter
在容器命名空间中运行 dig
。 查看 查找和输入 Pod 网络命名空间 部分了解更多信息:
nsenter -t 14346 -n dig kubernetes.default.svc.cluster.local @10.32.0.10
这个 dig
命令查找服务的完整域名 service-name.namespace.svc.cluster.local 并指定集群 DNS 服务 IP 的 IP (@10.32.0.10
)。
查看 IPVS 详细信息
从 Kubernetes 1.11 开始,kube-proxy
可以配置 IPVS 来处理虚拟服务 IP 到 pod IP 的转换。 您可以使用 ipvsadm
列出 IP 的转换表:
ipvsadm -Ln
OutputIP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP 100.64.0.1:443 rr -> 178.128.226.86:443 Masq 1 0 0 TCP 100.64.0.10:53 rr -> 100.96.1.3:53 Masq 1 0 0 -> 100.96.1.4:53 Masq 1 0 0 UDP 100.64.0.10:53 rr -> 100.96.1.3:53 Masq 1 0 0 -> 100.96.1.4:53 Masq 1 0 0
要显示单个服务 IP,请使用 -t
选项并指定所需的 IP:
ipvsadm -Ln -t 100.64.0.10:53
OutputProt LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP 100.64.0.10:53 rr -> 100.96.1.3:53 Masq 1 0 0 -> 100.96.1.4:53 Masq 1 0 0
结论
在本文中,我们回顾了一些用于探索和检查 Kubernetes 集群网络细节的命令和技术。 有关 Kubernetes 的更多信息,请查看 我们的 Kubernetes 教程标签 和 Kubernetes 官方文档 。