简介
Kubernetes 是一个容器编排系统,可以管理集群中的容器化应用程序。在集群中保持所有容器之间的网络连接需要一些高级网络技术。在本文中,我们将简要介绍一些工具和技术,用于检查这种网络设置。
如果您正在调试连接问题,调查网络吞吐量问题,或者探索 Kubernetes 的运行方式,这些工具可能会很有用。
如果您想了解更多关于 Kubernetes 的一般信息,我们的指南《Kubernetes 简介》涵盖了基础知识。对于 Kubernetes 的网络概述,请阅读《深入了解 Kubernetes 网络》。
如果您正在寻找托管的 Kubernetes 服务,可以查看我们为增长而构建的简单、托管的 Kubernetes 服务。
入门指南
本教程将假定您已经有一个 Kubernetes 集群,并在本地安装并配置了 kubectl
以连接到该集群。
以下各节包含许多命令,这些命令旨在在 Kubernetes 节点上运行。它们看起来像这样:
echo '这是一个节点命令'
应在本地机器上运行的命令将具有以下外观:
[本地环境] echo '这是一个本地命令'
查找 Pod 的集群 IP
要查找 Kubernetes Pod 的集群 IP 地址,请在本地机器上使用 kubectl get pod
命令,并带有 -o wide
选项。此选项将列出更多信息,包括 Pod 所在的节点以及 Pod 的集群 IP。
[本地环境] kubectl get pod -o wide
[本地环境] NAME 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
[本地环境] NAMESPACE 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
CONTAINER 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
14552
将输出一个进程 ID(或 PID)。现在我们可以使用 nsenter
程序在该进程的网络命名空间中运行命令:
nsenter -t your-container-pid -n ip addr
请确保使用您自己的 PID,并将 ip addr
替换为您想在 Pod 的网络命名空间中运行的命令。
查找 Pod 的虚拟以太网接口
每个 Pod 的网络命名空间通过虚拟以太网管道与节点的根 netns 进行通信。在节点端,这个管道会显示为一个设备,通常以 veth
开头并以唯一标识符结尾,比如 veth77f2275
或 veth01
。在 Pod 内部,这个管道会显示为 eth0
。
将特定的 veth
设备与相应的 Pod 相关联可能会很有用。为了做到这一点,我们将列出节点上的所有网络设备,然后列出 Pod 的网络命名空间中的设备。然后我们可以对比这两个列表中的设备编号,以建立它们之间的关联。
首先,在 Pod 的网络命名空间中使用 nsenter
运行 ip addr
。有关如何执行此操作,请参考前面的章节 查找并进入 Pod 网络命名空间:
nsenter -t your-container-pid -n ip addr
1: 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
1: 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
如果您的节点在与服务建立可靠连接时出现问题,可能是您的连接跟踪表已满,新连接正在被丢弃。如果是这种情况,您可能会在系统日志中看到以下消息:
Jul 12 15:32:11 worker-528 kernel: nf_conntrack: table full, dropping packet.
有一个 sysctl 设置用于跟踪的最大连接数。您可以使用以下命令列出当前值:
sysctl net.netfilter.nf_conntrack_max
net.netfilter.nf_conntrack_max = 131072
要设置新值,使用 -w
标志:
sysctl -w net.netfilter.nf_conntrack_max=198000
要使此设置永久生效,将其添加到 sysctl.conf
文件中:
. . . net.ipv4.netfilter.ip_conntrack_max = 198000
检查 Iptables 规则
在版本 1.11 之前,Kubernetes 使用 iptables NAT 来实现服务 IP 的虚拟 IP 转换和负载均衡。
要在节点上转储所有 iptables 规则,请使用 iptables-save
命令:
iptables-save
由于输出可能很长,您可能希望将其重定向到文件(iptables-save > output.txt
)或使用分页器(iptables-save | less
)以便更轻松地查看规则。
要仅列出 Kubernetes 服务 NAT 规则,请使用 iptables
命令和 -L
标志来指定正确的链:
iptables -t nat -L KUBE-SERVICES
Chain 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
。如果在基于 Debian 的 Linux 发行版上未安装 dig
,可以使用 apt
进行安装:
apt install dnsutils
首先,找到 kube-dns 服务的集群 IP:
[environment local] kubectl get service -n kube-system kube-dns
[environment local] NAME 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
IP 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
Prot 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 文档。