Please enable Javascript to view the contents

容器调试工具-cdebug

 ·  ☕ 4 分钟

背景

在云原生时代,容器化技术正逐渐成为软件开发和部署的主流。然而,随着容器化应用的广泛采用,传统镜像可能包含许多不必要的软件包和工具,这些可能成为安全漏洞的来源,占用大量存储空间,并导致容器启动和部署时间过长。此外,不同操作系统版本或发行版之间的差异也可能引发兼容性问题。在这样的背景下,distroless 镜像应运而生,它们是精简的容器镜像,移除了不必要的操作系统组件,仅包含运行应用所需的最小文件集,从而减少了潜在的攻击面并提高了安全性。

distroless 镜像虽然在安全性和性能上具有优势,但它们通常不包含传统的调试工具,这给开发者调试带来了挑战。在容器设计为轻量级且无Shell环境时,传统调试方法可能不适用。为了应对这些挑战,cdebug 工具被开发出来,它为容器环境提供了一种强大而灵活的调试解决方案。

问题

  • 如何调试一个没有shell的容器
  • 如何访问容器未export的端口
  • 如何将容器内的文件导出

cdebug的关键特性

Github 地址: https://github.com/iximiuz/cdebug

关键功能

  • 调试 无 Shell 容器: 即使容器内没有 Shell 或调试工具,也能轻松进入并调试。
  • 端口转发: 将未公开的端口或 localhost 端口转发到主机系统。
  • 反向通道: 将主机系统的端点暴露给容器和 Kubernetes 网络。
  • 文件系统导出专家: 轻松将镜像或容器的文件系统导出到本地文件夹。

安装cdebug

1
2
3
4
5
6
GOOS=linux
GOARCH=amd64

curl -Ls https://github.com/iximiuz/cdebug/releases/latest/download/cdebug_${GOOS}_${GOARCH}.tar.gz | tar xvz

mv cdebug /usr/local/bin/

exec: 调试容器

cdebug exec = docker exec + kubectl debug 。允许在目标容器中启动一个调试用的 Sidecar 容器,但功能更强:

  • 调试容器的根文件系统==目标容器的根文件系统;
  • 目标容器不会被重新创建或重启;
  • 无需额外的卷或复制调试工具;
  • 调试工具在目标容器中随时可用;

参数说明:

  • -it 交互(STDIN\TTY)
  • -n,--namespace 命名空间,会对应到相应的runtime。比如,调试K8S容器时,为K8S集群命名空间;调试containerd容器时,是containerd的命名空间。
  • --image 进行debug的工具镜像,默认为docker.io/library/busybox:musl

1. 调试 Dockerd 容器

1
2
3
4
5
6
7
# 启动测试容器
docker run -d --rm \
  --name my-distroless gcr.io/distroless/nodejs \
  -e 'setTimeout(() => console.log("Done"), 99999999)'

# cdebug启动调试容器
cdebug exec -it my-distroless

2. 调试K8S集群中的普通容器

命令:cdebug exec -it <-n NAMESPACE> pod/{POD_NAME}/{CONTAINER_NAME}

  • NAMESPACE : 选填,对应运行时的命名空间,此处为K8S集群中的命名空间
  • POD_NAME : 必填,Pod名称
  • CONTAINER_NAME:选填,容器名称,可指定特定的容器
1
2
# cdebug启动调试容器
cdebug exec -it -n kube-system --image jenkins/busybox:musl pod/rke2-coredns-rke2-coredns-84b9cb946c-47n5s

3. 调试 Containerd 容器

3.1 获取容器ID

ctr 参数说明

  • -n 命名空间,默认k8s集群镜像在k8s.io命名空间,docker镜像在moby命名空间,ctr命名空间为default
1
etcd_container_id=$(ctr -n k8s.io container ls |grep etcd |awk '{print $1}')

3.2 运行调试容器

-n 此处调试containerd容器,指定的命名空间是containerd的命名空间
–image: 进行debug的工具镜像

1
cdebug exec -n k8s.io --image jenkins/busybox:musl -it --rm containerd://${etcd_container_id}

4. 调试K8S集群中的静态容器

4.1 调试方式选择

cdebug调试K8S集群容器,是通过K8S debug实现的,但由于无法对StaticPod创建临时容器,因此,需要采取宿主机调试containerd容器的方式。下图为报错举例。

1
2
3
4
root@devops-k8s-master1:~# cdebug exec --namespace kube-system -it pod/etcd-devops-k8s-master1
Debugger container name: cdebug-e59ec365
Starting debugger container...
cdebug: Error adding debugger container: Pod "etcd-devops-k8s-master1" is invalid: []: Forbidden: static pods do not support ephemeral containers.

4.2 环境信息

环境为rke2搭建K8S集群,待调试容器为etcd-01(在节点master-1),登录待调试容器所在宿主机,执行以下命令进行ctr客户端设置:

1
2
3
4
5
# 将工具复制到PATH目录下
cp /var/lib/rancher/rke2/bin/ctr /usr/local/bin/

# 向containerd的默认sock位置,创建一个软链接
ln -s  /run/k3s/containerd/containerd.sock /run/containerd/containerd.sock

4.3 获取容器ID

1
etcd_container_id=$(ctr -n k8s.io container ls |grep etcd |awk '{print $1}')

4.4 运行调试容器

-n,–namespace: 命名空间,会对应到相应的runtime,比如此处是containerd的命名空间
–image: 进行debug的工具镜像,默认为docker.io/library/busybox:musl

1
cdebug exec -n k8s.io --image jenkins/busybox:musl -it --rm containerd://${etcd_container_id}

port-forward:端口转发

1. 未发布容器端口:将容器中的端口映射到宿主机本地端口

1
2
3
4
5
# 使用 cdebug 将本地 8080 端口转发到容器的 80 端口
cdebug port-forward {容器名称: nginx} -L 8080:80

# 可以通过宿主机8000端口,访问nginx容器的80端口
curl localhost:8080

2. 容器监听127.0.0.1:将宿主机本地端口映射到容器内的端口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10

docker run -d --name local-c python:3-alpine python3 -m 'http.server' -b 127.0.0.1 8888

# 使用 cdebug 将本地 8080 端口转发到容器的 80 端口
cdebug port-forward {容器名称: local-c} -L 127.0.0.1:8888
# 返回信息
# ...
# Forwarding 127.0.0.1:49176 to 127.0.0.1:8888 through 172.17.0.4:34128
# 从上面信息可知,通过宿主机49176端口可访问该容器内服务
curl localhost:49176

结论

cdebug 作为一款专为容器环境设计的调试工具,它通过提供一系列高级功能,极大地简化了容器化应用的调试流程。无论是面对无 Shell 容器、需要端口转发的场景,还是需要导出文件系统,cdebug 都能提供有效的解决方案,是云原生开发者的得力助手。

参考

cdebug github

分享

Hex
作者
Hex
CloudNative Developer

目录