news 2025/12/30 2:43:20

kubectl exec 的底层原理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
kubectl exec 的底层原理

如果你使用 Kubernetes,那么你对kubectl exec -it <pod-name> -- sh这个命令一定不会陌生。它是我们调试容器、查看日志或排查问题的“瑞士军刀”。我们通常的理解是:“哦,它是在容器内部启动了一个 shell”。

但这个理解并不完全准确,甚至有些误导。更精确的说法是:它在宿主机上启动了一个 shell 进程,并巧妙地让这个进程‘加入’了目标容器所在的全部 Linux 命名空间(Namespace)

今天,我们就来掀开帷幕的一角,看看这个魔法是如何通过 Linux 命名空间实现的。

一、基石:理解 Pod 的共享命名空间

在深入exec之前,必须理解 Pod 的本质。Kubernetes 的一个核心设计是:一个 Pod 内的所有容器共享一组 Linux 命名空间

这意味着什么呢?这意味着同一个 Pod 里的两个容器:

  • 看到的是同样的网络设备(共享netnamespace)
  • 拥有同样的主机名(共享utsnamespace)
  • 可以通过IPC机制通信(共享ipcnamespace)
  • 甚至可以通过pid命名空间看到彼此的进程(如果配置了共享)

当我们说“进入一个容器”,本质上就是想要进入这组被共享的命名空间集合。

二、kubectl exec 的旅程:从客户端到容器运行时

当你敲下kubectl exec -it my-pod -- sh,背后发生了一系列复杂的交互:

  1. API 请求kubectl并非直接联系你的 Node 节点,而是向 Kubernetes API Server 发送一个请求:“请在my-pod中执行sh命令”。
  2. 路由与授权:API Server 进行认证和授权后,知道my-pod运行在哪个节点上,于是将这个请求转发给该节点上的kubelet(节点代理)。
  3. 调用运行时kubelet接收到请求,转而调用本地的容器运行时(如containerdCRI-O)。

至此,所有流程都是标准的 Kubernetes 控制平面通信。真正的魔法发生在容器运行时接下来做的事情上。

三、核心魔法:nsenter 与 setns()

容器运行时(或其 CRI 插件)需要完成最终的任务:在宿主机上启动一个/bin/sh进程,并让它加入到目标容器的命名空间中

它是如何做到的呢?

  1. 定位目标:运行时首先找到目标 Pod 的“暂停容器”(pause)或你指定的业务容器在宿主机上的真实进程 ID(PID)。我们称之为<target-pid>

  2. 加入命名空间:这是最关键的一步。运行时不会在容器内启动进程,而是在宿主机上,通过类似下面的操作(实际是调用setns()系统调用)来启动 shell:

    这是一个概念性类比,实际是代码调用系统调用

nsenter --target <target-pid> \ --mount \ # 加入 Mount NS:看到容器的文件系统 --net \ # 加入 Net NS:看到容器的网络栈 --pid \ # 加入 PID NS:看到容器内的进程 --ipc \ # 加入 IPC NS:可以使用IPC资源 --uts \ # 加入 UTS NS:看到容器的主机名 --cgroup \ # 加入 Cgroup NS:继承容器的资源限制 /bin/sh # 最后,在这个新上下文中执行 shell
* `nsenter`(namespace enter)是一个 Linux 命令行工具,其功能就是让进程加入已有的命名空间。 * `--target` 指定了我们要“附身”的目标进程。 * 后面的一系列 `--<namespace>` 参数指明了我们要加入哪些类型的命名空间。
  1. 建立连接-it参数要求交互式终端。运行时会建立到新sh进程的标准输入(stdin)、输出(stdout)和错误(stderr)的流连接,使得你可以像在本地一样与这个“容器内”的 shell 交互。
四、直观验证:看看宿主机上的进程树

让我们用一个具体的例子来巩固理解。假设一个简单的 Pod,其容器的主进程是/my-app,在宿主机上的 PID 是5678

当你执行kubectl exec后,在宿主机上使用pstree -p 5678查看,你可能会看到这样的结构:

containerd(1234)───my-app(5678) # 容器原本的主进程
└─sh(7788) # kubectl exec 创建的进程!

看到了吗?sh(7788)这个进程本身就运行在宿主机上,是containerd的子进程。但它通过setns()系统调用,加入了my-app(5678)进程的所有命名空间。

因此,在这个sh进程里:

  • ps aux看到的是容器内部的进程列表(因为加入了pidnamespace)。
  • ip addr看到的是容器的网络接口(因为加入了netnamespace)。
  • ls /看到的是容器的根文件系统(因为加入了mountnamespace)。
  • hostname看到的是容器的主机名(因为加入了utsnamespace)。

它完美地“扮演”了一个容器内部的进程。

结论

所以,下次当你使用kubectl exec时,可以这样理解:

你不是在启动一个“容器内的进程”,而是在启动一个“拥有容器视角的宿主机进程”。Kubernetes 和容器运行时通过 Linux 命名空间这面“镜子”,让这个外部进程看到了一个完全不同的、属于容器内部的世界。

这种基于命名空间的“附身”能力,正是容器技术轻量、高效和可调试性的完美体现。它模糊了容器内外的边界,让我们能从一个更高的维度去观察和管理这些被隔离的环境。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2025/12/13 17:38:54

事件触发机制下的四容水箱系统控制和Matlab仿真 针对离散事件触发机制下的四容水箱系统控制和...

事件触发机制下的四容水箱系统控制和Matlab仿真 针对离散事件触发机制下的四容水箱系统控制和Matlab仿真研究&#xff0c;从整个系统控制架构的角度出发&#xff0c;对数据传输方案、控制方法和实验平台进行了较为系统的研究。 本文的研究重点是离散事件触发机制下的四容水箱控…

作者头像 李华
网站建设 2025/12/24 19:02:03

【JavaSE】十三、枚举类Enum Lambda表达式 列表排序常见写法

文章目录Ⅰ. 枚举类定义与使用Ⅱ. 枚举类的构造方法默认就是 private总结Ⅲ. 什么是 Lambda 表达式Ⅳ. Lambda 表达式的使用列表排序的常用写法1. **基本比较&#xff08;数值&#xff09;**Integer.compare(a, b)2. **方法引用 ** **Comparator.comparing**3. **多条件排序**4…

作者头像 李华
网站建设 2025/12/28 21:01:07

concurrent.futures 全面教程:常用 API 串联与实战指南

大家好&#xff0c;我是jobleap.cn的小九。 concurrent.futures 是 Python 标准库中用于简化并发编程的核心模块&#xff0c;基于抽象的 Executor 类封装了 ThreadPoolExecutor&#xff08;线程池&#xff09;和 ProcessPoolExecutor&#xff08;进程池&#xff09;&#xff0c…

作者头像 李华
网站建设 2025/12/13 17:35:52

Dijkstra - 单源最短路径

算法:Dijkstra [堆优化(优先队列)]求解:单源最短路径核心思想&#xff1a;贪心,每次从未确定最短距离的节点中,选择距离源点最近的节点,用该节点更新其邻接点的距离。这是一个堆优化的Dijkstra最短路径算法实现。让我为您详细解析每个部分&#xff1a;一、数据结构解析1. 邻接表…

作者头像 李华
网站建设 2025/12/13 17:35:51

亲手搭建原子级观测设备:OpenSTM终极指南

亲手搭建原子级观测设备&#xff1a;OpenSTM终极指南 【免费下载链接】OpenSTM OpenSTM - 一个扫描隧道显微镜项目&#xff0c;可能用于科研或精密工程领域。 项目地址: https://gitcode.com/gh_mirrors/op/OpenSTM 想要亲眼看到原子的排列吗&#xff1f;现在&#xff0…

作者头像 李华