Kubernetes 网络排错利器之kubectl-debug

举报
kaliarch 发表于 2021/11/20 20:38:22 2021/11/20
【摘要】 一 背景容器技术的一个最佳实践是构建尽可能精简的容器镜像。但这一实践却会给排查问题带来麻烦:精简后的容器中普遍缺失常用的排障工具,部分容器里甚至没有 shell (比如 FROM scratch )。 在这种状况下,我们只能通过日志或者到宿主机上通过 docker-cli 或 nsenter 来排查问题,效率很低。需要一款k8s排障工具帮助诊断目标容器,kubectl-debug就是一款(...

一 背景

容器技术的一个最佳实践是构建尽可能精简的容器镜像。但这一实践却会给排查问题带来麻烦:精简后的容器中普遍缺失常用的排障工具,部分容器里甚至没有 shell (比如 FROM scratch )。 在这种状况下,我们只能通过日志或者到宿主机上通过 docker-cli 或 nsenter 来排查问题,效率很低。需要一款k8s排障工具帮助诊断目标容器,kubectl-debug就是一款(https://github.com/aylei/kubectl-debug): **通过启动一个安装了各种排障工具的容器,来帮助诊断目标容器的利器 。

二 概述

2.1 工作原理

容器本质上是带有 cgroup 资源限制和 namespace 隔离的一组进程。因此,我们只要启动一个进程,并且让这个进程加入到目标容器的各种 namespace 中,这个进程就能 “进入容器内部”(注意引号),与容器中的进程”看到”相同的根文件系统、虚拟网卡、进程空间了——这也正是 docker execkubectl exec 等命令的运行方式。

现在的状况是,我们不仅要 “进入容器内部”,还希望带一套工具集进去帮忙排查问题。那么,想要高效管理一套工具集,又要可以跨平台,最好的办法就是把工具本身都打包在一个容器镜像当中。 接下来,我们只需要通过这个”工具镜像”启动容器,再指定这个容器加入目标容器的的各种 namespace,自然就实现了 “携带一套工具集进入容器内部”。事实上,使用 docker-cli 就可以实现这个操作:

export TARGET_ID=666666666
# 加入目标容器的 network, pid 以及 ipc namespace
docker run -it --network=container:$TARGET_ID --pid=container:$TARGET_ID --ipc=container:$TARGET_ID busybox

2.2 流程图详解

2.2.1 流程图

这就是 kubectl-debug 的出发点: 用工具容器来诊断业务容器 。背后的设计思路和 sidecar 等模式是一致的:每个容器只做一件事情。

具体到实现上,一条 kubectl debug <target-pod> 命令背后是这样的:

2.2.2 流程详解

  1. 插件查询 ApiServer:demo-pod 是否存在,所在节点是什么
  2. ApiServer 返回 demo-pod 所在所在节点
  3. 插件请求在目标节点上创建 Debug Agent Pod
  4. Kubelet 创建 Debug Agent Pod
  5. 插件发现 Debug Agent 已经 Ready,发起 debug 请求(长连接)
  6. Debug Agent 收到 debug 请求,创建 Debug 容器并加入目标容器的各个 Namespace 中,创建完成后,与 Debug 容器的 tty 建立连接

接下来,客户端就可以开始通过 5,6 这两个连接开始 debug 操作。操作结束后,Debug Agent 清理 Debug 容器,插件清理 Debug Agent,一次 Debug 完成。效果如下图:

三 安装部署

3.1 kubectl-debug安装

3.1.1 mac安装

# brew安装
brew install aylei/tap/kubectl-debug
# 二进制安装
export PLUGIN_VERSION=0.1.1
curl -Lo kubectl-debug.tar.gz https://github.com/aylei/kubectl-debug/releases/download/v${PLUGIN_VERSION}/kubectl-debug_${PLUGIN_VERSION}_darwin_amd64.tar.gz

tar -zxvf kubectl-debug.tar.gz kubectl-debug
sudo mv kubectl-debug /usr/local/bin/

3.1.2 Linux安装

export PLUGIN_VERSION=0.1.1
# linux x86_64
curl -Lo kubectl-debug.tar.gz https://github.com/aylei/kubectl-debug/releases/download/v${PLUGIN_VERSION}/kubectl-debug_${PLUGIN_VERSION}_linux_amd64.tar.gz

tar -zxvf kubectl-debug.tar.gz kubectl-debug
sudo mv kubectl-debug /usr/local/bin/

3.2 debug-agent DaemonSet安装

kubectl-debug 包含两部分, 一部分是用户侧的 kubectl 插件, 另一部分是部署在所有 k8s 节点上的 agent(用于启动"新容器", 同时也作为 SPDY 连接的中继). 在 agentless 中, kubectl-debug 会在 debug 开始时创建 debug-agent Pod, 并在结束后自动清理.(默认开启agentless模式)

agentless 虽然方便, 但会让 debug 的启动速度显著下降, 你可以通过预先安装 debug-agent 的 DaemonSet 并配合 --agentless=false 参数来使用 agent 模式, 加快启动速度:

# 如果你的kubernetes版本为v1.16或更高
kubectl apply -f https://raw.githubusercontent.com/aylei/kubectl-debug/master/scripts/agent_daemonset.yml
# 如果你使用的是旧版本的kubernetes(<v1.16), 你需要先将apiVersion修改为extensions/v1beta1, 可以如下操作
wget https://raw.githubusercontent.com/aylei/kubectl-debug/master/scripts/agent_daemonset.yml


sed -i '' '1s/apps\/v1/extensions\/v1beta1/g' agent_daemonset.yml
kubectl apply -f agent_daemonset.yml
# 或者使用helm安装
helm install kubectl-debug -n=debug-agent ./contrib/helm/kubectl-debug
# 使用daemonset agent模式(关闭agentless模式)
kubectl debug --agentless=false POD_NAME

四 使用

kubectl debug -h

老版本的 kubectl 无法自动发现插件, 需要直接调用 binary
kubect-debug POD_NAME

假如安装了 debug-agent 的 daemonset, 可以略去 --agentless 来加快启动速度
之后的命令里会略去 --agentless
kubectl debug POD_NAME --agentless

假如 Pod 处于 CrashLookBackoff 状态无法连接, 可以复制一个完全相同的 Pod 来进行诊断
kubectl debug POD_NAME --fork


假如 Node 没有公网 IP 或无法直接访问(防火墙等原因), 请使用 port-forward 模式
kubectl debug POD_NAME --port-forward --daemonset-ns=kube-system --daemonset-name=debug-agent

4.1 使用agentless模式

使用agentless模式,无需在node节点安装pod,但是每次都需要拉取镜像,排错较慢,但是比较干净,不在集群内部安装debug-agent

[root@master01 kubectl-debug]# kubectl debug --agentless=true centos-778d877549-7r5h8 -n anchnet-devops-dev
Agent Pod info: [Name:debug-agent-pod-44e763d0-edad-11ea-bbaf-5254f5694cb5, Namespace:default, Image:aylei/debug-agent:latest, HostPort:10027, ContainerPort:10027]
Waiting for pod debug-agent-pod-44e763d0-edad-11ea-bbaf-5254f5694cb5 to run...
pulling image nicolaka/netshoot:latest... 
latest: Pulling from nicolaka/netshoot
Digest: sha256:04786602e5a9463f40da65aea06fe5a825425c7df53b307daa21f828cfe40bf8
Status: Image is up to date for nicolaka/netshoot:latest
starting debug container...
container created, open tty...
bash-5.0# netstat -lntup

4.2 使用其他方法排错

运行在k8s中的应用遇到问题的时候,除了利用kubectl-debug进入容器,也可以使用利用nsenter进入容器网络名称空间内,利用tcpdump进行抓包分析。

  • 确定需要查看的pod运行在那台宿主机节点,之后登录该宿主机节点排查网络问题
  • 通过pod找到容器,通过容器找到运行在宿主机上pid,之后利用nsenter -n —target $pid进入到容器的网络名称空间中,就可以运用宿主机的工具来查看pod内的网络情况。

可以使用以下脚本,在运行pod的宿主机进行注册函数,然后进行排查pod网络情况。

function netcheck() {
 set -eu
 ns=${2-"default"}
 pod=`kubectl -n $ns describe pod $1 | grep -A10 "^Containers:" | grep -Eo 'docker://.*$' | head -n 1 | sed 's/docker:\/\/\(.*\)$/\1/'`
 pid=`docker inspect -f {{.State.Pid}} $pod`
 echo "entering pod netns for $ns/$1"
 cmd="nsenter -n --target $pid"
 echo $cmd
 $cmd
}

使用脚本测试

[root@node01 ~]# kubectl  get po
NAME                    READY   STATUS    RESTARTS   AGE
nginx-98f85fbb8-ql2gv   1/1     Running   2          29d
# 注册函数
[root@node01 ~]# function netcheck() {
>  set -eu
>  ns=${2-"default"}
>  pod=`kubectl -n $ns describe pod $1 | grep -A10 "^Containers:" | grep -Eo 'docker://.*$' | head -n 1 | sed 's/docker:\/\/\(.*\)$/\1/'`
>  pid=`docker inspect -f {{.State.Pid}} $pod`
>  echo "entering pod netns for $ns/$1"
>  cmd="nsenter -n --target $pid"
>  echo $cmd
>  $cmd
> }
# 进入容器网络名称空间
[root@node01 ~]# netcheck nginx-98f85fbb8-ql2gv 
entering pod netns for default/nginx-98f85fbb8-ql2gv
nsenter -n --target 4536

# 通过工具查看网络情况。
[root@node01 ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 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
3: eth0@if12: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP 
    link/ether ba:0e:65:c4:bb:1c brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.233.82.89/32 scope global eth0
       valid_lft forever preferred_lft forever
4: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN qlen 1
    link/ipip 0.0.0.0 brd 0.0.0.0
[root@node01 ~]# ss -tnl
State       Recv-Q Send-Q                                     Local Address:Port                                                    Peer Address:Port              
LISTEN      0      128                                                    *:80                                                                 *:*                  
LISTEN      0      128                                                   :::80                                                                :::*                  

五 注意事项

  • 如果使用agentless模式,需要修改debug-agent安装中配置

  • 运行kubectl-debug 如果出现找不到pod,需要制定-n 目标pod的名称空间

六 参考链接

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。