热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

kubespy用bash实现的k8s动态调试工具

原文位于 https:github.comhuazhihaokubespyblobmasterimplement-a-k8s-debug-plugin-in-bash.md背景Ku

原文位于 https://github.com/huazhihao/kubespy/blob/master/implement-a-k8s-debug-plugin-in-bash.md


背景

Kubernetes调试的最大痛点是精简过的容器镜像里没有日常的调试工具。背后的原因是精简容器镜像本身就是容器技术的最佳实践之一。nginx的容器镜像甚至不包含ps和curl这种最基础的工具。这种完全服务于生产环境的策略无异于过早优化,但受制于immutable infrastructure的基本思想和CI/CD实际操作的双重制约,你无法在生产环境发布一个和开发环境不同的容器镜像。这使得这一过早优化的结果更加灾难化。解决这个问题的关键在于,能否在不侵入式的修改容器镜像的情况下,向目标容器里加载需要的调试工具。例如,类似于istio之类的解决方案可以向目标pod插入一个sidecar容器。当然这里的权限要求是高于sidecar容器的,因为pod中的各个容器虽然共享network,但pid和ipc是不共享的。此外,sidecar容器是无法被加入一个已经创建出来的pod,而我们希望工具容器可以在运行时被动态插入,因为问题的产生是随机的,你不能完全预测需要加载哪些工具。

Kubernetes社区很早有相关的issue和proposal但并没有最后最终被upstream接受。

目前官方给出最接近的方案是Ephemeral Containers和shareProcessNamespace。前者允许你在运行时在一个pod里插入一个短生命周期的容器(无法拥有livenessProbe, readinessProbe),而后者允许你共享目标pod内容器的network,pid,ipc等cgroups namespace(注意,此namespace非Kubernetes的namespace)甚至修改其中的环境。目前Ephemeral Containers还在v1.17 alpha阶段。而且shareProcessNamespace这个spec要求在创建pod的时候就必须显式启用,否则运行时无法修改。

kubespy (https://github.com/huazhihao/kubespy)是一个完全用bash实现的Kubernetes调试工具,它不但完美的解决了上面提到的如何在运行时向目标容器加载工具的问题,而且并不依赖任何最新版本的Kubernetes的特性。这篇文章稍后会介绍如何使用kubespy来动态调试,以及kubespy是如何通过kubectl,docker以及chrootl来构建这条调试链的,最后会简单分析一下关键的代码实现。


安装

你可以直接从代码安装,因为kubespy是完全bash实现的,所以可以直接拷贝文件来执行。

$ curl -so kubectl-spy https://raw.githubusercontent.com/huazhihao/kubespy/master/kubespy
$ sudo install kubectl-spy /usr/local/bin/

你也可以从krew来安装。krew是一个kubernetes-sigs孵化中的kubectl插件包管理工具,带有准官方性质。

$ kubectl krew install spy

使用

安装过后,kubespy可以成为一个kubectl的子命令被执行。你可以指定目标pod为参数。如果目标pod有多个容器,你可以通过-c指定具体的容器。你也可以指定加载的工具容器的镜像,默认是busybox:latest

$ kubectl spy POD [-c CONTAINER] [--spy-image SPY_IMAGE]

你可以通过以下这个demo来快速体验如何调试一个镜像为nginx的pod。nginx镜像不包含ps或者任何网络工具。而加载的工具容器的镜像为busybox,不但可以访问原容器的文件系统和进程树,甚至可以杀进程,修改文件。当然发http请求更是没有问题。

asciicast


工作原理

kubespy的工作原理大致可以用以下这个流程图来展示。

local machine: kubectl spy [1]
|
v
master node: kube-apiserver [2]
|
v
worker node: kubelet [3]
|
v
spy pod (eg. busybox) [4]
| (chroot)
v
docker runtime [5]
| (run)
v
spy container [6]
| (join docker namespace: pid/net/ipc)
v
application pod (eg. nginx) [7]

概要的看,kubespy是通过以下这些步骤构建调试连的,上图的步骤数字可以与下文对应

[1] `kubespy`作为`kubectl`的插件被执行,可以向`master node`上的`kube-apiserver`发出api请求,会先取得目标容器的关键信息,如其所在的`worker node`和pid/net/ipc 等cgroups namespace
[2] `kube-apiserver`将具体的命令分发给目标容器所在的`worker node`上的agent`kubelet`执行
[3] `kubelet`创建一个`busybox`作为`spy pod`
[4] `spy pod`mount了`worker node`的根目录,并通过`chroot`取得了worker node的控制权
[5] `spy pod`控制了docker cli创建了工具容器(`spy container `)
[6] 工具容器被加入目标容器的pid/net/ipc等cgroups namespace
[7] 用户通过`kubectl`被attach到工具容器的tty里,可以对目标容器进行调试甚至是修改

关键代码


如何取得目标容器的pid/net/ipc等cgroups namespace

if [[ "${co}" == "" ]]; then
cid=$(kubectl get pod "${po}" -o='jsOnpath={.status.containerStatuses[0].containerID}' | sed 's/docker:\/\///')
else
cid=$(kubectl get pod "${po}" -o='jsOnpath={.status.containerStatuses[?(@.name=="'"${co}"'")].containerID}' | sed 's/docker:\/\///')
fi

根据kubectl的convention,如果用户未指定容器,我们默认以目标pod的第一个容器作为目标容器。kubelet在为kubernetes集群创建容器时,会相应的创建以containerID命名的pid/net/ipc等cgroups namespace。cgroups namespace是docker实现user space隔离的基础原理,可以参考https://docs.docker.com/engine/docker-overview/#namespaces 进行了解。


如何获得目标容器所在worker node以及其docker cli的控制权

"volumes": [
{
"name": "node",
"hostPath": {
"path": "/"
}
}
]

spy pod会将worker node的根目录作为volume来mount在/host

{
"name": "spy",
"image": "busybox",
"command": [ "/bin/chroot", "/host"],
"args": [
"docker",
"run",
"-it",
"--network=container:'"${cid}"'",
"--pid=container:'"${cid}"'",
"--ipc=container:'"${cid}"'",
"'"${ep}"'"
],
"stdin": true,
"stdinOnce": true,
"tty": true,
"volumeMounts": [
{
"mountPath": "/host",
"name": "node"
}
]
}

然后在通过busybox里的chroot,将worker node的根目录作为自己的根目录。而docker cli也将被直接暴露出来。

在获取了目标容器的pid/net/ipc等cgroups namespace之后,即可直接创建工具容器共享目标容器的cgroups namespace。此时,目标容器和目标容器在进程树,网络空间,内部进程通讯等,都是没有任何隔离的。


如何将用户的terminal带入目标容器中

kubectl run -itchrootdocker run -it都是可以attach到目标的tty中的,这些命令链接起来,像一系列跳板,把用户的terminal一层层带入下一个,最终带入目标容器中。


小结

kubespy (https://github.com/huazhihao/kubespy) 用bash实现对kubernetes集群中的pod通过动态加载工具容器来调试,弥补了目前kubernetes版本上功能的缺失,并展示了一些对kubernetes本身有深度的技巧。



推荐阅读
  • 本文总结了近年来在实际项目中使用消息中间件的经验和常见问题,旨在为Java初学者和中级开发者提供实用的参考。文章详细介绍了消息中间件在分布式系统中的作用,以及如何通过消息中间件实现高可用性和可扩展性。 ... [详细]
  • DirectShow Filter 开发指南
    本文总结了 DirectShow Filter 的开发经验,重点介绍了 Source Filter、In-Place Transform Filter 和 Render Filter 的实现方法。通过使用 DirectShow 提供的类,可以简化 Filter 的开发过程。 ... [详细]
  • Spring 中策略模式的应用:Resource 接口详解
    本文探讨了在 Spring 框架中如何利用 Resource 接口实现资源访问策略。Resource 接口作为资源访问策略的抽象,通过多种实现类支持不同类型的资源访问。 ... [详细]
  • 我正致力于利用Azure Functions和System.IO.Compression库,将大量文件高效地压缩并存储到Azure Blob容器中。这种方法不仅提高了存储效率,还优化了数据管理流程。通过这种方式,可以显著减少存储成本,并提升数据访问速度。 ... [详细]
  • vue引入echarts地图的四种方式
    一、vue中引入echart1、安装echarts:npminstallecharts--save2、在main.js文件中引入echarts实例:  Vue.prototype.$echartsecharts3、在需要用到echart图形的vue文件中引入:   importechartsfrom"echarts";4、如果用到map(地图),还 ... [详细]
  • Docker 环境下 MySQL 双主同步配置指南
    本文介绍了如何在 Docker 环境中配置 MySQL 的双主同步,包括目录结构的创建、配置文件的编写、容器的创建与设置以及最终的验证步骤。 ... [详细]
  • 解决针织难题:R语言编程技巧与常见错误分析 ... [详细]
  • C++ 开发实战:实用技巧与经验分享
    C++ 开发实战:实用技巧与经验分享 ... [详细]
  • 【前端开发】深入探讨 RequireJS 与性能优化策略
    随着前端技术的迅速发展,RequireJS虽然不再像以往那样吸引关注,但其在模块化加载方面的优势仍然值得深入探讨。本文将详细介绍RequireJS的基本概念及其作为模块加载工具的核心功能,并重点分析其性能优化策略,帮助开发者更好地理解和应用这一工具,提升前端项目的加载速度和整体性能。 ... [详细]
  • 探讨 jBPM 数据库表结构设计的精要与实践
    探讨 jBPM 数据库表结构设计的精要与实践 ... [详细]
  • 本文详细介绍如何在华为鲲鹏平台上构建和使用适配ARM架构的Redis Docker镜像,解决常见错误并提供优化建议。 ... [详细]
  • Flutter 核心技术与混合开发模式深入解析
    本文深入探讨了 Flutter 的核心技术,特别是其混合开发模式,包括统一管理模式和三端分离模式,以及混合栈原理。通过对比不同模式的优缺点,帮助开发者选择最适合项目的混合开发策略。 ... [详细]
  • 使用Echarts for Weixin 小程序实现中国地图及区域点击事件
    本文介绍了如何使用Echarts for Weixin在微信小程序中构建中国地图,并实现区域点击事件。包括效果展示、条件准备和逻辑实现的具体步骤。 ... [详细]
  • RTThread线程间通信
    线程中通信在裸机编程中,经常会使用全局变量进行功能间的通信,如某些功能可能由于一些操作而改变全局变量的值,另一个功能对此全局变量进行读取& ... [详细]
  • React 实现 Post 请求下载 PDF 文件的解决方案
    在 React 应用中实现通过 POST 请求下载 PDF 文件的功能,本文提供了完整的代码示例。具体实现包括设置状态以显示加载提示,并通过控制台日志记录下载索引,确保请求的正确性和用户体验。此外,还详细介绍了如何处理响应流并将其转换为可下载的 PDF 文件,适用于需要安全传输数据的场景。 ... [详细]
author-avatar
Eva---LiuJ
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有