4 种优雅的在 Kubernetes 中调试网络流量的方式

Posted by Mike on 2021-06-17

前言

在当今世界, 分布式系统, 微服务/SOA架构遍地, 服务之间的许多交互和通信都不再是同一主机的不同线程或进程, 而是跨主机, 甚至跨网络区域. 那么一旦相关服务出现问题, 我们就会需要调试服务间的通讯, 主机间的网络…

复杂的网络架构

Kubernetes 中的应用出了问题, 往往需要进行网络抓包分析. 本文介绍了在 Kubernetes 中网络调试分析的4种方法.

  1. 使用 sidecar
  2. 使用 netshoot - 一个 Docker + Kubernetes网络故障排除的瑞士军刀容器
  3. 利用Network Namespace
  4. 使用 kubectl 插件 - ksniff

方法一 使用 Sidecar

在分布式计算、容器和微服务的世界中,服务之间的许多交互和通信都是通过 RESTful Api 完成的。在开发这些 Api 和服务之间的交互时,我经常需要调试服务之间的通信,特别是当事情看起来不像预期的那样工作时。

在容器出现之前,我只需将服务部署到本地机器上,启动 Wireshark,执行测试,并分析服务之间的HTTP通信。对我来说,这是一种快速分析软件中通信问题的简单而有效的方法。然而,这种调试方法在一个容器化的世界中并不适用。

首先,容器很可能在您的机器无法直接访问的内部容器平台网络上运行。第二个问题是,按照容器设计最佳实践,容器只包含执行其任务所需的最小应用程序和库集。这意味着像 Tcpdump 这样的工具通常在容器中不可用。这使得调试和分析容器之间的网络通信变得更加困难,从而使得调试微服务间的通信比在非容器环境中更加困难。本文展示了一种解决方案。

Sidecar 前来救援

Sidecar

在过去的几个月里,我尝试了各种方法来克服这个问题,最终形成了我将在本文中概述的方法。它是捕获Kubernetes/OpenShift Pods 之间的网络流量数据的简单方法,允许开发人员更好地分析和调试容器化应用程序中的通信问题,并更快、更有效地解决问题。

我们将使用 Tcpdump 捕获一个所谓的 PCAP(packet capture)文件,该文件将包含 Pod 的网络流量。然后可以将这个 PCAP 文件加载到 Wireshark 之类的工具中来分析流量,在本例中,分析在 Pod 中运行的服务的 RESTful 通信。在本文中,我将使用 Red Hat Process Automation Manager 产品的 KIE 服务器(执行服务器)作为示例,但是这种方法应该适用于任何类型的容器化应用程序。

要克服的第一个问题是 Kubernetes Pod 中 Tcpdump 命令的可用性。KIE 服务器容器映像没有安装Tcpdump。其次,容器不提供从 Red Hat 存储库安装 Tcpdump 的实用程序。为了克服这个问题,我们使用了 “Sidecar 容器” 的概念。

Sidecar 概念

Sidecar 容器是与实际服务/应用程序运行在相同 Pod 中的容器,能够为服务/应用程序提供附加功能。Sidecar 容器的一个例子是 Istio 的 Envoy sidecar,它使pod成为服务网格的一部分 。在本例中,我们将部署一个 Sidecar 容器,该容器提供 Tcpdump 实用程序。由于 pod中的多个容器共享相同的网络层 ,所以我们可以使用 Sidecar 来捕获进出 KIE 服务器的网络流量。

部署 Sidecar

在这个例子中,我部署了Red Hat Process Automation Manager 7 Mortgage Demo,它将在我的 OpenShift namespace 中创建两个 Pod。一个 Pod 运行 Business Central workbench,另一个 Pod 是执行服务器的 Pod。这两个组件之间的通信是通过 REST 完成的,这是我们将要捕获的流量。

OpenShift Namespace Overview

我们的目标是捕获 KIE 服务器 Pod 上的网络流量,以便分析 Business Central Workbench 发送给 KIE 服务器的 RESTful 命令。要做到这一点,我们首先需要附加 (attach)一个 Sidecar 到 KIE 服务器的 Pod.

  1. 在 Overview 页面中,单击要分析的 Pod 的名称。这将打开 部署配置(Deployment Config, 简称DC) 页面。

  2. 部署配置 屏幕的右上角,单击 Actions -> Edit YAML。这将打开 DC 的 YAML配置。

  1. 向下滚动,直到看到单词 containers。我们将添加一个额外的容器,安装了 Tcpdump 的Sidecar 到 Pod 中。直接在 containers 定义下添加以下 YAML 片段:
1
2
3
4
5
- name: tcpdump
image: corfr/tcpdump
command:
- /bin/sleep
- infinity

  1. 保存配置。这将部署一个新的 Pod,它现在由两个容器组成: 一个容器包含 KIE 服务器,另一个容器包含我们的 Tcpdump 工具,它将无限期地持续运行。

捕获和分析流量

随着 Sidecar 的部署和运行,我们现在可以开始捕获数据了。我尝试的方法之一是使用 oc rsh 命令远程执行 Sidecar 中的 tcpdump 命令,将网络数据流输出到 FIFO 文件,并将数据直接导入 Wireshark。由于各种原因,这种方法失败了。其中一个问题是,tcpdumpstderr 发送信息消息,但是这些消息与 stdout 在相同的流中, 并且是通过 SSH 接收,从而破坏了进入 Wireshark 的数据。

我最后使用的方法是登录到 Sidecar 容器,并在 Sidecar 中运行 tcpdump 命令来创建 PCAP 文件。当您捕获了足够的数据后,就可以停止捕获过程并将 PCAP 文件复制到您希望使用 Wireshark 进行网络流量分析的机器上。具体步骤如下:

  1. 在您的开发机器上,用 oc 客户端连接到 OpenShift 实例,并激活正确的项目( project, 即namespace),运行 oc get pods 命令来列出您的 Pods:

  1. 使用以下命令登录到我们的 KIE 服务器 Pod 的 tcpdump 容器中: oc rsh -c tcpdump rhpam7-mortgage-kieserver-2-zcpsn

  2. tcpdump 容器中,运行此命令以启动网络流量捕获过程: tcpdump -s 0 -n -w /tmp/kieserver.pcap

  3. 运行要分析的网络流量的测试。在本例中,我将从 Business Central workbench 中启动一个业务流程,它将向 KIE 服务器发送一个 RESTful 请求。

  4. 捕获足够的数据后,在 tcpdump 容器中使用 Ctrl+C 完成捕获过程。

  5. 回到本地机器。将 PCAP 文件从 Pod 复制到本地机器: oc cp -c tcpdump rhpam7-mortgage-kieserver-2-zcpsn:tmp/kieserver.pcap kieserver.pcap

  6. 用 Wireshark 打开 PCAP 文件并分析网络流量。在这个例子中,我正在分析我的 HTTP POST 方法,它创建了 Mortgage 进程的一个新实例:

Wireshark 分析

总结

在容器环境(如 Kubernetes 和/或 OpenShift )中分析 Pod 之间的网络通信可能比在非容器环境中更困难一些。然而,Sidecar 容器的概念为开发人员提供了一种简单的工具,可以将容器连同所需的开发工具和实用程序附加到微服务pod上。这避免了开发人员必须在应用程序容器映像本身中安装这些调试工具,从而保持容器的轻便和干净。 使用像 oc rshoc cp 这样的 OpenShift 工具,我展示了如何轻松地从 Pod 捕获网络流量数据并将数据带到开发机器进行分析。

方法二 使用 netshoot

Netshoot - Docker + Kubernetes网络故障排除的瑞士军刀容器

瑞士军刀

用途

Docker 和 Kubernetes 网络故障排除变得复杂。通过正确理解 Docker 和 Kubernetes 网络的工作方式和正确的工具集,您可以排除故障并解决这些网络问题。netshoot 容器有一组强大的网络troubleshoot 工具,可以用来排除 Docker 网络问题。与这些工具一起出现的还有一组用例,展示了如何在真实场景中使用这个容器。

Network Namespaces - 网络名称空间

在开始使用这个工具之前,有一点很重要:网络名称空间。网络名称空间提供与网络相关的系统资源的隔离。Docker使用网络和其他类型的名称空间(pidmountuser…)为每个容器创建一个隔离的环境。从接口、路由到 ip 的所有内容都完全隔离在容器的网络名称空间中。

Kubernetes 也使用网络名称空间。Kubelets为每个pod创建一个网络名称空间,其中该 Pod 中的所有容器共享相同的网络名称空间(eths、IP、tcp套接字……)。这是 Docker 容器和Kubernetes pod之间的关键区别。

名称空间很酷的一点是您可以在它们之间进行切换。您可以输入不同容器的网络名称空间,使用甚至没有安装在该容器上的工具在其网络堆栈上执行一些故障排除。此外,netshoot 可以通过使用主机的网络名称空间来对主机本身进行故障排除。这允许您在不直接在主机或应用程序包上安装任何新包的情况下执行任何故障排除。

针对容器的用法

  • 容器的网络名称空间 :如果您的应用程序的容器存在网络问题,您可以像这样使用容器的网络名称空间启动 netshoot: $ docker run -it --net container:<container_name> nicolaka/netshoot
  • 主机的网络名称空间 :如果您认为网络问题在于主机本身,那么可以使用该主机的网络名称空间启动 netshoot。命令: $ docker run -it --net host nicolaka/netshoot
  • 网络的网络名称空间 :如果要对Docker网络进行故障排除,可以使用 nsenter 输入网络的名称空间。这将在下面的 nsenter 部分进行解释。

针对 Kubernetes 的用法

Kubernetes: 如果你想打开一个临时的容器来调试。

1
$ kubectl run --generator=run-pod/v1 tmp-shell --rm -i --tty --image nicolaka/netshoot -- /bin/bash

如果您想在主机的网络名称空间上 spin up 一个容器。

1
$ kubectl run tmp-shell --generator=run-pod/v1 --rm -i --tty --overrides='{"spec": {"hostNetwork": true}}' --image nicolaka/netshoot -- /bin/bash

同样的原理, netshoot 也可以通过 Sidecar 的方式进行使用.

网络问题

许多网络问题可能导致应用程序性能下降。其中一些问题可能与底层网络基础设施有关。其他问题可能与主机或 Docker 级别的配置错误有关。让我们来看看常见的网络问题

  • 延迟(latency)
  • 路由(routing)
  • DNS解析(DNS resolution)
  • 防火墙(firewall)
  • 不完整的 ARP(incomplete ARPs)

为了解决这些问题,netshoot 包含了一组强大的工具,如图所示。

netshoot 工具集

被包含的包

以下包被包含在 netshoot 中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
apache2-utils
bash
bind-tools
bird
bridge-utils
busybox-extras
calicoctl
conntrack-tools
ctop
curl
dhcping
drill
ethtool
file
fping
iftop
iperf
iproute2
ipset
iptables
iptraf-ng
iputils
ipvsadm
libc6-compat
liboping
mtr
net-snmp-tools
netcat-openbsd
netgen
nftables
ngrep
nmap
nmap-nping
openssl
py-crypto
py2-virtualenv
python2
scapy
socat
strace
tcpdump
tcptraceroute
util-linux
vim

方法三 利用Network Namespace

正如方法二中提到的 Network Namespace 概念, 实际上, 不同的容器, 只是在宿主机上不同 namespace 运行的进程而已 . 因此要在不同的容器抓包可以简单地使用命令切换 Network Namespace 即可,可以使用在宿主机上的 tcpdump 等应用进行抓包。

前提条件: 宿主机上已安装tcpdump

参考链接: 在 k8s 中对指定 Pod 进行抓包

具体操作步骤如下:

  1. 查看指定 Pod 运行在哪个宿主机上:
1
kubctl describe pod <pod> -n mservice
  1. 获得容器的 Pid:
1
docker inspect -f {{.State.Pid}} <container>
  1. 进入该容器的 network namespace:
1
nsenter --target <PID> -n
  1. 使用宿主机的 tcpdump 抓包, 指定 eth0 网卡:
1
tcpdump -i eth0 tcp and port 80 -vvv
  1. 或者直接抓包并导出到文件:
1
tcpdump -i eth0 -w /tmp/out.cap
  1. 从远程 scp 到本地:
1
scp ipaddr:/tmp/out.cap ./
  1. 之后在 Wireshark 中可以打开文件非常直观得查看过滤抓到的数据。

方法四 使用 kubectl 插件 ksniff

ksniff 项目地址:https://github.com/eldadru/ksniff

题外话: krew - kubectl 插件包管理器

前提条件: kubectl v1.12 或更高.

项目地址:https://github.com/kubernetes-sigs/krew/

Krew 是 kubectl 插件的包管理器。

什么是 krew

krew 是一个使 kubectl插件 易于使用的工具。krew 帮助您发现插件,并在您的机器上安装和管理它们。它类似于 apt、dnf 或 brew 等工具。

  • 对于 kubectl 用户 : krew 帮助您以一致的方式查找、安装和管理 kubectl插件。

krew 易于使用:

1
2
3
4
5
kubectl krew search                 # show all plugins
kubectl krew install view-secret # install a plugin named "view-secret"
kubectl view-secret # use the plugin
kubectl krew upgrade # upgrade installed plugins
kubectl krew uninstall view-secret # uninstall a plugin

详细文档请参阅用户指南

查看在 krew 上可用的 kubectl 插件列表,或者运行 kubectl krew search 来发现可用的插件。

安装 krew

Bash 和 ZSH:

  1. 确保 git 已安装;
  2. 运行如下命令, 下载并安装 krew
1
2
3
4
5
6
7
(
set -x; cd "$(mktemp -d)" &&
curl -fsSLO "https://github.com/kubernetes-sigs/krew/releases/download/v0.3.1/krew.{tar.gz,yaml}" &&
tar zxvf krew.tar.gz &&
./krew-"$(uname | tr '[:upper:]' '[:lower:]')_amd64" install \
--manifest=krew.yaml --archive=krew.tar.gz
)
  1. 添加 $HOME/.krew/bin 目录到 PATH 环境变量. 如下: export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH" 并重启下 Shell 生效.

安装 ksniff

通过 krew: kubectl krew install sniff

使用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# kubectl < 1.12:
$ kubectl plugin sniff <POD_NAME> [-n <NAMESPACE_NAME>] [-c <CONTAINER_NAME>] [-i <INTERFACE_NAME>] [-f <CAPTURE_FILTER>] [-o OUTPUT_FILE] [-l LOCAL_TCPDUMP_FILE] [-r REMOTE_TCPDUMP_FILE]

# kubectl >= 1.12:
$ kubectl sniff <POD_NAME> [-n <NAMESPACE_NAME>] [-c <CONTAINER_NAME>] [-i <INTERFACE_NAME>] [-f <CAPTURE_FILTER>] [-o OUTPUT_FILE] [-l LOCAL_TCPDUMP_FILE] [-r REMOTE_TCPDUMP_FILE]

POD_NAME: Required. the name of the kubernetes pod to start capture it's traffic.
NAMESPACE_NAME: Optional. Namespace name. used to specify the target namespace to operate on.
CONTAINER_NAME: Optional. If omitted, the first container in the pod will be chosen.
INTERFACE_NAME: Optional. Pod Interface to capture from. If omited, all Pod interfaces will be captured.
CAPTURE_FILTER: Optional. specify a specific tcpdump capture filter. If omitted no filter will be used.
OUTPUT_FILE: Optional. if specified, ksniff will redirect tcpdump output to local file instead of wireshark.
LOCAL_TCPDUMP_FILE: Optional. if specified, ksniff will use this path as the local path of the static tcpdump binary.
REMOTE_TCPDUMP_FILE: Optional. if specified, ksniff will use the specified path as the remote path to upload static tcpdump to.

举例:

1
$ kubectl sniff mypod -n myproject -o /tmp/mypod.pcap

总结

为了在容器或 K8S 中进行网络调试和分析, 本文列举了 4 种方法, 现在进行总结归纳:

  1. 使用 Sidecar - Sidecar 容器所在的 Pod 中的多个容器共享相同的网络层, 且Sidecar 容器可以包含 tcpdump 等工具;

  2. 利用 Network Namespace - 不同的容器, 只是在宿主机上不同 namespace 运行的进程而已. 容器的网络也是如此.

  3. 使用 netshoot - netshoot 其实是包含一系列的常用网络分析调试工具集的容器, 真正的使用方法其实还是以上 2 种:

    1. 通过 sidecar 挂载
    2. 利用 Network Namespace 分析调试
  4. 使用 kubectl 插件 - ksniff.

以上这些方法, 有不同的前提条件和使用场景, 希望本文读完会让你的 K8S 调试技能有所提升.

本文转载自:「 个人技术分享 」,原文:http://t.cn/AimWk1Wl,版权归原作者所有。欢迎投稿,投稿邮箱: editor@hi-linux.com