/容器调试与排障/
团队简介
我们是光大科技有限公司智能云计算部云计算团队容器云项目组,致力于容器技术在金融行业的落地与实践,以容器云助力金融行业科技转型。我们的团队拥有经验丰富的容器与云原生领域研发工程师和专家,将不定期分享容器和云原生行业的原创技术文章和实践经验,共同探索与见证云原生时代金融领域的前沿技术和发展趋势。
1
Sketch
概述
■ 对于容器而言,我们通常希望构建的容器镜像尽可能精简以节省资源,提高容器部署效率,同时尽可能保证安全性,事实上公共容器镜像仓库中发布的base镜像大多都是精简版。
■ 无论是自构建的精简镜像还是基于精简镜像构建的其他镜像,都意味着大量常用工具的缺失,都为容器的测试调试和故障排除带来很大的不便。仅仅依靠nsenter,docker-cli及容器日志往往是不够的,而为了调试和故障诊断去构建工具齐全的业务容器又得不偿失。
■ 对于Kubernetes环境而言,kubectl工具能够部分代替docker-cli且在容器层面之上可展示详细的资源和事件信息,但对于容器本身的调试和排障依然面临同样的尴尬。
■ Kubernetes社区早已意识到此问题也有相应的proposal,目前已存在一些官方和非官方解决方案,本文将详细介绍这些方案如何实现容器调试和诊断。
2
Principle Tools
原理工具
在业务镜像中内置大量调试排障工具显然很不明智,这不仅会导致镜像臃肿,也会带来潜在的安全隐患,因此较为可行的方案是借助功能齐全的工具容器对各个目标业务容器进行调试诊断。
2.1
原理 ▉
容器本质上是宿主机上带有cgroup资源限制和namespace隔离的一组进程,因此只要启动一个进程,并让该进程加入到目标容器的各namespace中,这个进程就能够“进入容器内部”,与容器中的进程“看到”相同的文件系统,虚拟网卡,进程空间等资源(这也正是docker exec和kubectl exec的运行方式)。
注:容器文件系统通过/proc/$pid/root链接对pod中的其他容器可见。
docker本身是支持通过命令行参数实现容器之间各命名空间的共享,因此只要启动一个工具齐全的容器并让该容器共享业务容器的命名空间,就能够利用工具容器中丰富的工具对业务容器进行调试诊断。命令如下:
# busybox容器共享了目标容器的网络命名空间,PID命名空间和IPC命名空间
docker run -it –network=container:$TARGET_ID –pid=container:$TARGET_ID –ipc=container:$TARGET_ID busybox
2.2
工具 ▉
如何生成一个功能齐全的工具镜像,可参考github上一个开源项目nicolaka/netshoot,该项目包含大量网络相关的诊断调试工具:
图片来源于网络
基于该项目,我们对其工具集进行了扩充并进行了部分定制,额外提供了如下工具:
-
ctop: 类似与top命令的开源容器指标实时监测工具
-
htop: 增强的top工具
-
docker: 容器内的docker
-
calicoctl: calico网络命令行
-
kubectl: Kubernetes命令行
-
ipfs: 星际文件系统
-
gofs: 文件上传下载服务器
-
websocketd: 命令行输出转websocket服务器
-
websocat: websocket调试工具
-
jq: 命令行json处理工具
-
yq: 命令行yaml处理工具
-
logview: 更友好的日志显示工具
-
etcd: etcd分布式存储与etcdctl
-
fio: 硬盘性能检测工具
-
git: 分布式代码管理工具
-
mysql-client: mysql数据库客户端
-
postgresql-client: postgresql数据库客户端
-
redis: redis键值数据库与客户端
-
minio: minio对象存储
-
sqlite: sqlite数据库与客户端
-
nginx: nginx web服务器
-
samba: samba服务器与客户端
-
python3: python3
-
openssh: ssh服务
-
其他如tree, sshpass, httpie, bat, exa, zsh等工具
工具镜像名为xo,Dockfile构建文件此处已省略。
3
Solution
解决方案
以下将介绍几种可行的容器调试与排障解决方案,这些解决方案都是基于Kubernetes环境(纯docker环境使用上文的docker命令即可),均需使用工具镜像(可选netshoot[200MB]或我们自己构建的xo[900MB])。
3.1
sidecar简介 ▉
Kubernetes中的业务单元是pod,pod中可以包含多个容器,多个容器共享网络,PID和IPC命名空间,PID命名空间共享需要显式启用。sidecar方式即是在业务pod中同时包含业务容器和工具容器(当然sidecar的应用不止如此),此方式使用简单,但工具容器注入时pod内的容器将重新部署,即容器无法在运行时注入。
● 示例
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
shareProcessNamespace: true
containers:
– name: nginx
image: nginx
– name: tool
image: xo
securityContext:
capabilities:
add:
– SYS_PTRACE
stdin: true
tty: true
3.2
kubectl-debug简介 ▉
kubectl-debug是由PingCAP员工开发的Kubernetes容器调试诊断插件[1]。本地执行kubectl-debug会在目标容器所在节点部署Debug Agent,通过Debug Agent部署共享目标容器命名空间的工具(debug)容器(docker原生特性)。kubectl-debug使用的工具默认是netshoot,但可以配置为其他容器。此方案是非侵入式的,不会对目标容器产生影响。本插件支持CrashLoopBackoff诊断。
注:kubectl插件其实是任意带kubectl-前缀的可执行文件,比如kubectl-debug可执行文件放在/usr/local/bin目录下kubectl就能自动识别,输入kubectl debug命令就相当于执行kubectl-debug命令。通过kubectl plugin list命令可以查看当前kubectl插件列表。
kubectl有一个名为krew的特殊插件,它是kubectl的插件管理工具,通过它可以下载,检索和管理其他kubectl插件。
当前已经有许多开源的kubectl插件,关于容器调试方面的插件除了kubectl-debug外,比较知名的还有ksniff,该工具主要用于容器tcpdump抓包。
其架构如下[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完成。
注:本地插件到Debug Agent的长连接有超时设置,超时后Debug Agent pod会被删除,但生成的Debug容器不会删除。云昊平台环境中需使用port-forward模式。
本插件所需的Debug Agent也可以预先以DaemonSet模式在Kubernetes集群各节点中运行(支持helm chart),以加快后续调试操作。
● 示例
# kubectl 1.12.0 或更高的版本,可以直接使用:
kubectl debug -h
# 假如安装了debug-agent 的 daemonset,可以使用–agentless=false 来加快启动速度
# 之后的命令里会使用默认的agentless模式
kubectl debug POD_NAME
# 假如 Pod 处于CrashLookBackoff 状态无法连接,可以复制一个完全相同的 Pod来进行诊断
kubectl debug POD_NAME –fork
# 当使用fork mode时,如果需要复制出来的pod保留原pod的labels,可以使用 –fork-pod-retain-labels 参数进行设置(注意逗号分隔,且不允许空格)
# 示例如下
# 若不设置,该参数默认为空(既不保留原pod的任何labels,fork出来的新pod的labels为空)
kubectl debug POD_NAME –fork –fork-pod-retain-labels=<labelKeyA>,<labelKeyB>,<labelKeyC>
# 为了使 没有公网 IP 或无法直接访问(防火墙等原因)的 NODE 能够访问,默认开启 port-forward 模式
# 如果不需要开启port-forward模式,可以使用 –port-forward=false 来关闭
kubectl debug POD_NAME –port-forward=false –agentless=false –daemonset-ns=kube-system –daemonset-name=debug-agent
# 老版本的 kubectl 无法自动发现插件,需要直接调用 binary
kubectl-debug POD_NAME
# 使用私有仓库镜像,并设置私有仓库使用的kubernetes secret
# secret data原文请设置为 {Username: <username>, Password: <password>}
# 默认secret_name为kubectl-debug-registry-secret,默认namspace为default
kubectl-debug POD_NAME –image calmkart/netshoot:latest –registry-secret-name <k8s_secret_name> –registry-secret-namespace <namespace>
# 在默认的agentless模式中,你可以设置agent pod的resource资源限制,如下示例
# 若不设置,默认为空
kubectl-debug POD_NAME –agent-pod-cpu-requests=250m –agent-pod-cpu-limits=500m –agent-pod-memory-requests=200Mi –agent-pod-memory-limits=500Mi
3.3
kude介绍 ▉
kude是我们根据kubectl-debug的原理实现的一个简易版的Kubernetes容器调试诊断工具(shell脚本),默认使用xo作为工具容器。本工具对目标容器同样是非侵入式的,通过kubectl向ApiServer查询相关信息,然后利用ssh通过分配tty在目标节点部署Debug容器(与目标容器共享Namespace),并进入Debug容器交互模式。kude架构图如下:
本工具在具有kubectl的主机上即可使用,需要提供目标节点的ssh登录账号和密码(或者提前在本地配置免密登录)。工具中部分默认值与云昊平台当前主机环境契合,配合“自动跳转登录内网主机”甚至可以直接在办公电脑上进行远程调试和诊断。
注:将本工具命名为kubectl-kude并保存到/usr/local/bin/目录下,也能够作为kubectl插件通过kubectl kude命令调用。
本工具由于直接利用ssh远程登录而在Kubernetes中没有server或agent,因此仅在能够直接ssh到容器所在主机的主机上运行才能实现debug容器的部署和命令行交互式debug。其他功能无此限制。
脚本具体内容此处已省略。
● 示例
**USAGE**:
kude -k|-d [<arguments>]
kude -x [<command>] [<ipaddrs>]
kude [<options>] podname
**OPTIONS**:
-a [args] 为kubectl的proxy,cp等命令提供参数
-c [test] kubernetes连接上下文(可选)
-d [args] 执行docker命令
-e [log, logf, inspect, describe, wide, yaml, json] 非交互式指令(可选)
-f [./kube/config] kubeonfig文件路径(可选)
-i [0] 匹配pod内容器index(可选)
-k [args] 执行kubectl命令
-m [xo] 自定义工具容器(可选)
-n [default] 查询的kubernetes命名空间(可选)
-o [xos] podname模糊匹配(可选)
-p [16022] 目标主机ssh端口(可选)
-s [admin123] 目标主机ssh密码(可选)
-t [60] 目标主机ssh连接超时时间(可选)
-u [root] 目标主机ssh账号(可选)
-x [cmd] 目标主机上需要执行的命令
-r Debug容器中是否需要chroot到目标容器的文件系统
-v Debug容器中是否需要挂载目标主机的docker.sock文件
-h 使用说明
**REFERENCES**:
# Linux: https://community.linuxmint.com/tutorial/view/244 https://man.linuxde.net/
# Docker: https://docs.docker.com/engine/reference/commandline/cli/
# Kubectl: https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands
**EXAMPLES**:
# 命令在带kubectl的主机运行
# 不带ssh相关参数的命令默认已经配置了免密登录
# 直接执行docker或kubectl命令
kude -d ps -a #相当于docker ps -a
kude -k get pod #相当于kubectl get pod
# 进行docker镜像推送并删除临时tag
kude -x \’dpush xshrim/xo foo.bar.com/xshrim\’# 远程到目标主机执行命令
kude -x \’ls /etc\’127.0.0.1
kude -p22-s admin -x\’ls /etc\’ 127.0.0.1# 执行本机命令
kude -x\’scp testroot@10.0.0.10:/root/test\’# 最简,所有命名空间中寻找名称带xos字段的pod,部署Debug容器共享该pod下第一个容器的命名空间并进入交互模式
kude xos# xshrim命名空间中寻找名称带xos字段的pod,部署Debug容器共享该pod下第一个容器的命名空间并进入交互模式
kude -n xshrim xos# xshrim命名空间中寻找名称带xos字段的pod,部署Debug容器共享该pod下第一个容器的命名空间进入交互模式并chroot到该容器的文件系统
kude -n xshrim -i0 -r xos# 所有命名空间中寻找名称带xos字段的pod下的第二个容器并显示容器日志
kude -e log -i 1 xos# 所有命名空间中寻找名称带xos字段的pod并显示其描述信息
kude -e describe xos# 所有命名空间中寻找名称带xos字段的pod并将其配置以json格式输出
kude -e json xos# 所有命名空间中寻找名称带xos字段的pod并进入命令行交互模式
kude -e exe xos# 所有命名空间中寻找名称带xos字段的pod并执行命令(不进入交互模式)
kude -e exec xos -a\’ls /root\’# 所有命名空间中寻找名称带xos字段的pod并进行远程文件复制
kude -e cp -a \’test :/root/\’ xos
kude -e cp -a \’:/root/test .\’ xos
# xshrim命名空间中寻找名称带xos字段的pod,部署Debug容器共享该pod下第一个容器的命名空间并进入交互模式,目标主机ssh账号密码端口指定,Debug容器使用netshoot
kude -n xshrim -u root -p 22 -s admin123 -m netshoot xos
3.4
Ephemeral Container介绍 ▉
Ephemeral Container是Kubernetes v1.16以后加入的新资源类型,旨在解决pod内容器调试排障问题,目前处于Alpha阶段。Kubernetes的pod一旦处于运行时状态就无法再向其中添加容器,而Ephemeral Container则可以直接插入到处于运行时状态的pod中并共享该pod中容器的各Namespace,从而实现业务容器的便捷调试与诊断[3]。Ephemeral Container只能在Kubernetes v1.16及以上版本可用,且由于尚处于Alpha阶段,该特性默认是关闭的。此外Ephemeral Container要求PodShareProcessNamespace特性可用,该特性在Kubernetes v1.16版本中默认是启用的。Kubernetes v1.16中启用Ephemeral Container需要在kube-apiserver和kube-scheduler的命令行参数中加入–feature-gates=EphemeralContainers=true。
注:Ephemeral Container不允许暴露端口,不能配置健康检查,也不能作资源限额配置。
● 示例
1.启动业务pod
apiVersion: v1
kind: Podmetadata:
name: rabbit
labels:
role: myrolespec:
shareProcessNamespace: true
containers:
– name: rabbitmq
image: arm32v7/rabbitmq
ports:
– name: rabbit
containerPort: 5672
protocol: TCP
kubectl apply -f ./pod.yaml
2.编辑EphemeralContainer资源
{
\”apiVersion\”: \”v1\”,
\”kind\”: \”EphemeralContainers\”,
\”metadata\”: {
\”name\”: \”rabbit\”
},
\”ephemeralContainers\”: [{
\”command\”: [
\”bash\”
],
\”image\”: \”xo\”,
\”imagePullPolicy\”: \”Always\”,
\”name\”: \”diagtools\”,
\”stdin\”: true,
\”tty\”: true,
\”terminationMessagePolicy\”: \”File\”
}]}
3.添加EphemeralContainer资源到业务pod中
kubectl -n default replace –raw /api/v1/namespaces/default/pods/rabbit/ephemeralcontainers -f ./ephemeral-diagnostic-container.json
4.查看业务pod
kubectl describe pod rabbit
Ephemeral Containers:
diagtools:
Container ID: docker://eb55c71f102ce3d56221934f6ebcabfd2da76204df718bd8d2573da24aecc8e9
Image: xo
Image ID: docker-pullable://xo@sha256:bb00f943d511c29cc2367183630940e797f5e9552683b672613bf4cb602a1c4c
Port:
Host Port:
Command:
bash
State: Running
Started: Sat, 16 Nov 2019 14:49:58 +0000
Ready: False
Restart Count: 0
Environment:
Mounts:
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
5.进入EphemeralContainer进行调试诊断
kubectl attach -it rabbit -c diagtools
4
Debugging cases
调试案例
本部分将列举一些常用的容器调试与排障工具的使用方法,工具容器基于xo,只关注工具容器内部的命令执行,忽略工具容器的创建方式。后续将根据实践经验进行补充。
文件系统挂载
ls /proc/1/rootchroot /proc/1/root/ # chroot后可执行目标容器命令
java -versionexit
网络流量帧听
ip a
iftop -i eth0
DNS域名解析
drill -V 5 xos-svc
nslookup xos-svc
网络报文抓包
ip a
tcpdump -i eth0 -c 1 -Xvv
# xoc上模拟流量
netgen xos 2333# xos上抓包流量
tcpdump -vvvn -i eth0 port 2333
宿主机容器指标查看
# 前提在工具容器中挂载宿主机docker.sock
ctop
端口开放性检测
netstat -tulpn
telnet xos 2333
curl xos:2333
nc xos 2333 -v
主机端口扫描
nmap -p 12376–12390 -dd 172.31.24.25
calico状态查看
calicoctl get weapon
calicoctl get ippool
calicoctl get bgpconfig -o yaml
网络路由跟踪
traceroute -n -m 10 -w 3 xos
应用API请求测试
curl -X POST -k -v https://xos:2333/test -d \”arg1=a\” -d \”arg2=b\”
参考文档
Kubectl debug:https://github.com/aylei/kubectl-debug/blob/master/docs/zh-cn.md
简化Pod故障诊断:kubectl-debug介绍:https://aleiwu.com/post/kubectl-debug-intro/
Ephemeral Containers: https://kubernetes.io/docs/concepts/workloads/pods/ephemeral- containers/
作者/陈盼
整理/吴涛
原创文章,作者:EBCloud,如若转载,请注明出处:https://www.sudun.com/ask/32628.html