文章目录
  1. 简介
  2. 整体架构
  3. 底层实现
  4. 不足之处
  5. 实验
    1. 实验一:监听简单的 HTTP 服务
    2. 实验二:部署官方的 sock-shop

K8S 抓包工具 Kubeshark 简介

简介

随着云原生技术的大规模应用,可观察性也越来越重要。对于运维来说,一个比较麻烦的事情就是在有需要的时候,如何快速对某个 K8S 服务或者容器进行抓包。Kubeshark 由 2021 年 UP9 公司开源的 K8S API 流量查看器 Mizu 发展而来,试图成为一款 K8S 全过程流量监控工具,解决 K8S 上 API 流程可观察性的问题。Kubeshark 通过 libpcap、AF_PACKET、PF_RING 和 eBPF 等抓取 K8S 内部网络数据包,保存成 PCAP 格式,然后按照对应的应用协议进行解析,直观呈现给用户,相当于 K8S 版本的 TCPDump 和 Wireshark。目前支持的协议有:

  • HTTP/1.0
  • HTTP/1.1
  • HTTP/2
  • AMQP
  • Apache Kafka
  • Redis
  • DNS

此外,Kubeshark 也会解析 gRPC 和 GraphQL 协议的数据。

整体架构

Kueshark 架构

Kubeshark 由 cli、font-end、hub、worker 四部分组成:

  • cli:命令行客户端,Go 语言编写的,通过和 K8S 集群交互部署 font-end、hub、worker。
  • font-end:一个 React 前端应用,通过 WebSocket 和 hub 通讯,以流的方式展示抓取到的数据包。按照应用协议,而不是单个单个网络包的方式展示的
  • hub:作为 worker 的一个聚合网关,通过 HTTP 对外提供以下功能
    • 接收 WebSocket 请求和用户设置的过滤器
    • 和 worker 建立 WebSocket 连接
    • 从 worker 获取解析之后的流量数据
    • 将流量数据发给请求者
    • 通过 HTTP 配置 worker
  • worker:Kubeshark 以 DaemonSet 方式部署 worker,确保覆盖到集群每个节点。worker 会抓取机器上所有网卡的网络包,组装成 TCP 流,并且尝试解析这些流量,解析成功就保存成 PCAP 文件。worker 保存的是原始流量数据,当用户请求的时候才按照应用层协议进行解析。woker 具有分布式特点,网络包的保存和解析都是分散到集群各个节点上的。并且为了降低额外的网络影响,只有在需要的时候,worker 才会发送相关数据。

底层实现

Kubeshark 使用 Linux 内核中的各种内置方法和 API,抓取集群中加密和未加密的流量。对网络数据的收集主要有直接抓包法和基于 eBPF 的数据包获取。直接抓包法涉及 libpcap、AF_PACKET 和 PF_RING 获取集群 TCP 流量。eBPF 主要用于抓取加密流量 (TLS),通过给 OpenSSL 库和 Go 的 crypto/tls 库的某些函数的入口点和出口点添加 hook 方式实现,而不是进行数据的加解密,抓取 TLS 加密流量需要加上 --tls 选项(kubeshark tap --tls -n sock-shop)。

不足之处

  1. 从实验来看,只有可以被解析的流量才会被抓取,也就是无法抓取连接失败和非 Kubeshark 支持的协议流量,无法帮助分析一些连接上的问题。
  2. 目前在添加和清理过滤器的时候,经常会报 Hub is down。 Kuebeshark 过滤器 UI 问题

实验

实验一:监听简单的 HTTP 服务

  1. 安装 Kubeshark
    1
    2
    3
    4
    5
    6
    # 方法一:自动根据系统和 CPU 架构安装对应的版本
    sh <(curl -Ls https://kubeshark.co/install)

    # 方法二:在 Mac 系统上可以使用 brew 工具直接安装
    brew tap kubeshark/kubeshark
    brew install kubeshark
  2. 使用 kind 创建 demo 使用的 K8S 集群
    1
    kind create cluster --name demo --config kind-cluster.yaml
    1
    2
    3
    4
    5
    6
    7
    8
    # kind-cluster.yaml
    apiVersion: kind.x-k8s.io/v1alpha4
    kind: Cluster
    nodes:
    - role: control-plane
    - role: worker
    - role: worker
    - role: worker
  3. 部署 nginx 和 netshoot 作为测试服务(nginx 作为服务端,在 netshoot 上进行调用测试)
    1
    2
    3
    4
    5
    6
    7
    8
    # 设置使用 demo 集群
    kubectl cluster-info --context kind-demo
    # 部署 nginx deplyment 和 service
    kubectl apply -f nginx.yml
    # 部署 netshoot deployment
    kubectl apply -f netshoot.yml
    # 检查启动成功
    kubectl get pods -o wide
    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
    # nginx.yml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: nginx
    spec:
    selector:
    matchLabels:
    app: nginx
    replicas: 2
    template:
    metadata:
    labels:
    app: nginx
    spec:
    containers:
    - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80
    ---
    apiVersion: v1
    kind: Service
    metadata:
    name: nginx-service
    spec:
    selector:
    app: nginx
    ports:
    - protocol: TCP
    port: 80
    targetPort: 80
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    # netshoot.yml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: netshoot
    spec:
    selector:
    matchLabels:
    app: netshoot
    replicas: 1
    template:
    metadata:
    labels:
    app: netshoot
    spec:
    containers:
    - name: netshoot
    image: nicolaka/netshoot:latest
    command: ["sleep"]
    args: ["1000000"]
  4. 运行 Kubeshark,通过 http://localhost:8898 可以直接打开 UI 界面。默认情况下只抓 default 命名空间的 pod
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    ⋊> ~/2/c/k/demo kubeshark tap                                                                                                                                                                                                                         10:10:18
    2023-02-02T10:22:26+08:00 INF tapRunner.go:45 > Using Docker: registry=docker.io/kubeshark/ tag=latest
    2023-02-02T10:22:26+08:00 INF versionCheck.go:23 > Checking for a newer version...
    2023-02-02T10:22:26+08:00 INF tapRunner.go:53 > Kubeshark will store the traffic up to a limit (per node). Oldest TCP streams will be removed once the limit is reached. limit=200MB
    2023-02-02T10:22:26+08:00 INF common.go:69 > Using kubeconfig: path=/Users/longhua/.kube/config
    2023-02-02T10:22:26+08:00 INF tapRunner.go:74 > Targeting pods in: namespaces=["default"]
    2023-02-02T10:22:26+08:00 INF tapRunner.go:129 > Targeted pod: netshoot-694f4cdf-s86lg
    2023-02-02T10:22:26+08:00 INF tapRunner.go:129 > Targeted pod: nginx-7fb96c846b-6mcbv
    2023-02-02T10:22:26+08:00 INF tapRunner.go:129 > Targeted pod: nginx-7fb96c846b-f9df9
    2023-02-02T10:22:26+08:00 INF tapRunner.go:84 > Waiting for the creation of Kubeshark resources...
    2023-02-02T10:22:26+08:00 INF createResources.go:110 > Successfully created a pod. pod=kubeshark-hub
    2023-02-02T10:22:26+08:00 INF createResources.go:122 > Successfully created a pod. pod=kubeshark-front
    2023-02-02T10:22:26+08:00 ERR versionCheck.go:28 > Failed to get the latest release.
    2023-02-02T10:22:26+08:00 INF createResources.go:71 > Successfully created a service. service=kubeshark-hub
    2023-02-02T10:22:26+08:00 INF createResources.go:78 > Successfully created a service. service=kubeshark-front
    2023-02-02T10:22:26+08:00 INF tapRunner.go:160 > Added: pod=kubeshark-hub
    2023-02-02T10:22:26+08:00 INF tapRunner.go:240 > Added: pod=kubeshark-front
    2023-02-02T10:22:40+08:00 INF proxy.go:29 > Starting proxy... namespace=kubeshark service=kubeshark-hub src-port=8898
    2023-02-02T10:22:40+08:00 INF workers.go:32 > Creating the worker DaemonSet...
    2023-02-02T10:22:40+08:00 INF workers.go:51 > Successfully created the worker DaemonSet.
    2023-02-02T10:22:41+08:00 INF tapRunner.go:402 > Hub is available at: url=http://localhost:8898
    2023-02-02T10:22:42+08:00 INF proxy.go:29 > Starting proxy... namespace=kubeshark service=kubeshark-front src-port=8899
    2023-02-02T10:22:44+08:00 INF tapRunner.go:418 > Kubeshark is available at: url=http://localhost:8899
  5. 在 netshoot 容器上多次请求 nginx 服务
    1
    2
    3
    ⋊> ~/2/c/k/demo kubectl exec -it netshoot-694f4cdf-s86lg bash                                                                                                                                                                                         10:26:20
    kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
    bash-5.2# curl nginx-service
  6. 从 Kubeshark 界面可以查看到每个请求详细信息,并可以重放请求或者下载具体的包做更加详细的分析 Kubeshark 界面 通过 Wireshark 查看下载的网络包
  7. 使用过滤条件:http and request.headers["user"] == "longhua” Kubeshark 过滤器
  8. 通过 Service Map 查看服务之间依赖及调用情况 Untitled

实验二:部署官方的 sock-shop

官方的 sock-shop 例子做得比较完整,包括了多种支持的协议。

  1. 下载 sock-shop-demo 代码
    1
    git clone https://github.com/kubeshark/sock-shop-demo.git
  2. 部署 sock-shop 服务,并且等待服务启动完成
    1
    kubectl apply -f sock-shop-demo/deploy/kubernetes/complete-demo.yaml
  3. 抓取 sock-shop 命名空间下的流量
    1
    kubeshark tap -n sock-shop
  4. 通过 Kubeshark UI 查看各个协议的流量 sock-shop 流量图 sock-shop service map

Kubeshark - API Traffic viewer for Kubernetes