调试 Istio 网格中运行的 Envoy sidecar C++ 代码

本文来源于我的开源书籍 《Istio Insider》 。

介绍

调试在 Istio 网格中运行的 Envoy sidecar C++ 代码。 它有助于在代码级别深入研究 sidecar。 它使咱们在解决 Istio 问题或编写更好的 EnvoyFilter 或 eBPF 跟踪程序时更有信念。 本文介绍如何应用 VSCodelldb 调试 Envoy istio-proxy sidecar。

我的动机

  1. 深入研究 Envoy Proxy 在 Istio 管制面管制下的实在行为和代码逻辑

    多年前,我写过一篇文章:
    gdb debug istio-proxy(envoy)(中文)。 它只是在 Istio 网格之外调试 Envoy 过程。对我来说,深入研究 Istio 服务网格中 sidecar (istio-proxy) 的行为让我更有信念实现我的书:《Istio Insider》。 我想应用 (lldb/gdb) + VSCode 来调试在 Istio 服务网格上运行的 Envoy(C++ 代码)。

  1. 深入研究 Envoy Proxy 在 Istio 管制面管制下的实在行为和代码逻辑

    去年,我在写《逆向工程与云原生现场剖析 —— eBPF 跟踪 Istio/Envoy 系列》 时,感觉应用 BPF uprobe 动静注入 probe ,探测过程数据最难的是理解运行期的 C++ 对象内存构造。而 debug 过程无疑是最间接牢靠的内存构造察看办法。能够用 debug 中的察看后果,领导 BPF uprobe 程序的精确编写。

环境架构

图: 应用 lldb 近程调试 istio-proxy
用 Draw.io 关上

环境阐明

Istio 版本: 1.17.2

环境阐明:

  • k8s cluster

    • node network CIDR: 192.168.122.0/24
    • Istio 1.17.2 已装置
    • 测试指标 k8s namespace: mark
    • 测试指标 pod 运行于 node: 192.168.122.55
  • 带 Linux 桌面的开发者 node

    • IP addr: 192.168.122.1
    • hostname: labile-T30
    • OS: Ubuntu 22.04.2 LTS
    • user home: /home/labile
    • 连通 k8s cluster node 网络
    • 有 X11 GUI
    • VSCode
    • Docker 已装置
    • 有 Internet 连贯

环境搭建步骤

1. 构建带 debug 信息的 istio-proxy

1.1 Clone 源码

运行于 labile-T30

mkdir -p $HOME/istio-testing/cd $HOME/istio-testing/git clone https://github.com/istio/proxy.git workcd workgit checkout tags/1.17.2 -b 1.17.2
1.2 启动 istio-proxy-builder 容器

编译像 istio-proxy 这样的大我的项目是环境相干的工作。 对于我这样的老手,我更违心间接应用官网的 Istio CI 编译容器。 益处是:
1.环境与Istio官网版本统一,防止版本陷阱。 实践上生成的可执行文件是雷同的
2.内置工具,简略易用

留神:build-tools-proxy 容器镜像列表能够在 https://console.cloud.google.com/gcr/images/istio-testing/global/build-tools-proxy 获取。 请抉择你要编译的 istio-proxy 版本对应的镜像。 办法是利用网页中的 Filter 性能。 以下仅以 release-1.17 为例。
# optionaldocker network create --subnet=172.18.0.0/16 routerdocker stop istio-proxy-builderdocker rm istio-proxy-buildermkdir -p $HOME/istio-testing/home/.cache# run istio-proxy-builder containerdocker run --init  --privileged --name istio-proxy-builder --hostname istio-proxy-builder \    --network router \    -v /var/run/docker.sock:/var/run/docker.sock:rw \    -v $HOME/istio-testing/work:/work \    -v $HOME/istio-testing/home/.cache:/home/.cache \    -w /work \    -d gcr.io/istio-testing/build-tools-proxy:release-1.17-latest-amd64 bash -c '/bin/sleep 300d'
1.3 构建 istio-proxy
## goto istio-proxy-builder containerdocker exec -it istio-proxy-builder bash## build istio-proxy with debug info in output ELFcd /workmake build BAZEL_STARTUP_ARGS='' BAZEL_BUILD_ARGS='-s  --explain=explain.txt --config=debug' BAZEL_TARGETS=':envoy'

在我的 2 core CPU 和 64GB RAM 机器上花了 3 个小时构建它。 更多的 core 会更快。

构建实现后能够查看输入的 ELF:

## goto istio-proxy-builder containerdocker exec -it istio-proxy-builder bashbuild-tools: # ls -lh /work/bazel-out/k8-dbg/bin/src/envoy/envoy-r-xr-xr-x 1 root root 1.2G Feb 18 21:46 /work/bazel-out/k8-dbg/bin/src/envoy/envoy

2. 装置测试指标 pod

2.1 构建 istio-proxy docker image

labile-T30 上运行:

# start local private plain http docker image registrydocker run -d -p 5000:5000 --restart=always --name image-registry --hostname image-registry registry:2cd mkdir -p image/gdb-istio-proxycd image/gdb-istio-proxy# NOTICE: replae 1e0bb3bee2d09d2e4ad3523530d3b40c with the real path in your environmentsudo ln $HOME/istio-testing/home/.cache/bazel/_bazel_root/1e0bb3bee2d09d2e4ad3523530d3b40c/execroot/io_istio_proxy/bazel-out/k8-dbg/bin/envoy ./envoycat > proxyv2:1.17.2-debug.Dockerfile <<"EOF"FROM docker.io/istio/proxyv2:1.17.2COPY envoy /usr/local/bin/envoyRUN apt-get -y update \  && sudo apt -y install lldbEOF# build docker imagedocker build . -f ./proxyv2:1.17.2-debug.Dockerfile -t proxyv2:1.17.2-debugdocker tag proxyv2:1.17.2-debug localhost:5000/proxyv2:1.17.2-debug# push image to local image registrydocker push localhost:5000/proxyv2:1.17.2-debug

docker image 的大小:

  • Envoy elf: 1.4G
  • lldb package: 700Mb
  • others
2.2 运行指标 pod
kubectl -n mark apply -f - <<"EOF"apiVersion: apps/v1kind: StatefulSetmetadata:  name: fortio-server  labels:    app: fortio-serverspec:  serviceName: fortio-server  replicas: 1  selector:    matchLabels:      app: fortio-server  template:    metadata:      annotations:        sidecar.istio.io/proxyImage: 192.168.122.1:5000/proxyv2:1.17.2-debug        sidecar.istio.io/inject: "true"        sidecar.istio.io/proxyMemoryLimit: "4Gi"        sidecar.istio.io/proxyMemory: "512Mi"      labels:        app.kubernetes.io/name: fortio-server        app: fortio-server    spec:      restartPolicy: Always      containers:      - name: main-app        image: docker.io/fortio/fortio        imagePullPolicy: IfNotPresent        command: ["/usr/bin/fortio"]        args: ["server", "-M", "8070 http://fortio-server-l2:8080"]        ports:        - containerPort: 8080          protocol: TCP          name: http              - containerPort: 8070          protocol: TCP          name: http-m      - name: istio-proxy        image: auto        imagePullPolicy: IfNotPresentEOF
2.3 启动 lldb server
ssh 192.168.122.55sudo su# get PID of envoyexport POD="fortio-server-0"ENVOY_PIDS=$(pgrep envoy)while IFS= read -r ENVOY_PID; do    HN=$(sudo nsenter -u -t $ENVOY_PID hostname)    if [[ "$HN" = "$POD" ]]; then # space between = is important        sudo nsenter -u -t $ENVOY_PID hostname        export POD_PID=$ENVOY_PID    fidone <<< "$ENVOY_PIDS"echo $POD_PIDexport PID=$POD_PIDsudo nsenter -t $PID -u -p -m bash #NO -nsudo lldb-server platform --server --listen *:2159
测试 lldb-server(可选,可跳过)

labile-T30 上运行:

sudo lldb# commands run in lldb:platform select remote-linuxplatform connect connect://192.168.122.55:2159# list process of istio-proxy containerplatform process listfile /home/labile/istio-testing/home/.cache/bazel/_bazel_root/1e0bb3bee2d09d2e4ad3523530d3b40c/execroot/io_istio_proxy/bazel-out/k8-dbg/bin/envoy# Assuming pid of envoy is 15attach --pid 15# wait, please the big evnoy ELFexit

3. attach debuger 到 istio-proxy

3.1 启动 lldb-vscode-server container

labile-T30 运行:

  1. 启动 lldb-vscode-server container
docker stop lldb-vscode-serverdocker rm lldb-vscode-serverdocker run \--entrypoint /bin/bash \--init  --privileged --name lldb-vscode-server --hostname lldb-vscode-server \    --network router \    -v /var/run/docker.sock:/var/run/docker.sock:rw \    -v $HOME/istio-testing/work:/work \    -v $HOME/istio-testing/home/.cache:/home/.cache \    -w /work \    -d localhost:5000/proxyv2:1.17.2-debug \    -c '/bin/sleep 300d'
3.2 VSCode attach lldb-vscode-server container
  1. labile-T30 桌面 启动 VSCode GUI.
  2. 在 vscode 执行命令(Ctrl+Shift+p): Remote Containers: Attach to Running Container, 抉择 lldb-vscode-server container.
  3. 在 attached 到 container 后, open folder: /work.
  4. 装置 VSCode extensions:

    • CodeLLDB
    • clangd (Optional)
3.3 lldb 近程 attach Envoy 过程
3.3.1 创立 launch.json 文件

/work 下创立 .vscode/launch.json

{    "version": "0.2.0",    "configurations": [        {            "name": "AttachLLDBRemote",            "type": "lldb",            "request": "attach",            "program": "/work/bazel-out/k8-dbg/bin/envoy",            "pid": "15", //pid of envoy in istio-proxy container            "sourceMap": {                "/proc/self/cwd": "/work/bazel-work",                "/home/.cache/bazel/_bazel_root/1e0bb3bee2d09d2e4ad3523530d3b40c/sandbox/linux-sandbox/263/execroot/io_istio_proxy": "/work/bazel-work"            },            "initCommands": [                "platform select remote-linux", // Execute `platform list` for a list of available remote platform plugins.                "platform connect connect://192.168.122.55:2159"            ],                                      }                             ]}
3.3.2 Attach 近程 Envoy 过程

在 VSCode 中 Run and debug: AttachLLDBRemote.

加载 1GB 的 ELF 可能须要大概 1 分钟。 请急躁期待。

4. 开始调试

FAQ

containerd allow pull image from plain http docker image registry

Update /etc/containerd/config.toml of the node in k8s cluster:

sudo vi /etc/containerd/config.tomlversion = 2[plugins]  [plugins."io.containerd.grpc.v1.cri".registry]    [plugins."io.containerd.grpc.v1.cri".registry.mirrors]      [plugins."io.containerd.grpc.v1.cri".registry.mirrors."192.168.122.1:5000"]        endpoint = ["http://192.168.122.1:5000"]

动静 path

Please update /home/.cache/bazel/_bazel_root/1e0bb3bee2d09d2e4ad3523530d3b40c path according to your environment.

为何用 lldb 而不是 gdb

我在应用 gdb 时遇到了很多问题。

更 Cloud Native 的近程调试的办法

多年前,我写过一篇文章:
从新思考云原生时代的开发环境——从Dev-to-Cloud到Dev@Cloud。 介绍如何在k8s集群中装置一个运行 X11 桌面环境的 Pod,并通过浏览器间接连贯到桌面。

纯云原生风味是指标。 为了让调试 istio-proxy 更有云原生的滋味。 您能够将下图中的一些组件替换为 k8s 组件。 这同时也能够升高开发者进入调试环境的门槛。 例如:

  • labile-T30 上运行的 docker 容器之间的共享文件夹能够用 k8s RWX(ReadWriteMany) PV 替换。 例如 NFS/CephFS。
  • istio-proxy-builderlldb-vscode-server 容器能够作为 k8s Pod 运行并挂载下面的 RWX PVC。
  • Remote Containers: Attach to Running Container 能够替换为 VSCode-server k8s 服务,它的益处是,能够通过任何网络浏览器轻松拜访。 不再须要具备 X11 桌面/VSCode GUI 应用程序和 docker 或 ssh 连贯的节点。 只需将 VSCode-server 公布为 k8s 服务并在开发者电脑的网络浏览器上拜访它。

图: 应用 lldb 近程调试 istio-proxy
用 Draw.io 关上