Kubernetes控制平面组件:Kubelet详解(六):pod sandbox(pause)容器
云原生学习路线导航页(持续更新中)
- kubernetes学习系列快捷链接
- Kubernetes架构原则和对象设计(一)
- Kubernetes架构原则和对象设计(二)
- Kubernetes架构原则和对象设计(三)
- Kubernetes控制平面组件:etcd(一)
- Kubernetes控制平面组件:etcd(二)
- Kubernetes控制平面组件:API Server详解(一)
- Kubernetes控制平面组件:API Server详解(二)
- Kubernetes控制平面组件:调度器Scheduler(一)
- Kubernetes控制平面组件:调度器Scheduler(二)
- Kubernetes控制平面组件:Controller Manager 之 内置Controller详解
- Kubernetes控制平面组件:Controller Manager 之 NamespaceController 全方位讲解
- Kubernetes控制平面组件:Kubelet详解(一):架构 及 API接口层介绍
- Kubernetes控制平面组件:Kubelet详解(二):核心功能层
- Kubernetes控制平面组件:Kubelet详解(三):CRI 容器运行时接口层
- Kubernetes控制平面组件:Kubelet详解(四):gRPC 与 CRI gRPC实现
- Kubernetes控制平面组件:Kubelet详解(五):切换docker运行时为containerd
- Kubernetes控制平面组件:Kubelet 之 Static 静态 Pod
本文是 kubernetes 的控制面组件 kubelet 系列文章第六篇,主要讲解了 pod 的 沙箱sandbox容器,也称为pause容器,介绍了pause容器的核心作用、技术细节、工作原理、常用运维命令等,最后还对pause容器的源码进行了分析
- 希望大家多多 点赞 关注 评论 收藏,作者会更有动力继续编写技术文章
1.pod 的容器组成:sandbox+业务containers
- 在kubernetes中,pod容器由
sandbox + 业务containers
组成,除了业务自己的功能容器,每一个pod还包含一个sandbox容器,镜像一般为 pause,所以也称为 pause 容器 - 在 Kubernetes控制平面组件:Kubelet详解(三):CRI 容器运行时接口层 中我们介绍了crictl的基本使用。使用crictl可以查看pod的sandbox容器
- crictl ps:查看pod 的业务容器
- crictl pods:查看pod 的sandbox pause容器
- 下面例子可以看出,虽然都是nginx-sts-0这一个pod,但是找到两个不同的容器
# 查看pod的sandbox容器 [root@VM-226-235-tencentos ~]# crictl pods | grep nginx-sts-0 da081a3c7ba60 10 hours ago Ready nginx-sts-0 default 0 (default) # 查看pod的业务容器 [root@VM-226-235-tencentos ~]# crictl ps | grep nginx-sts-0 d99ea5d921d87 a830707172e80 10 hours ago Running nginx 0 da081a3c7ba60 nginx-sts-0
2.pod 的 sandbox 容器
2.1.pause
容器的核心作用
- 在 Kubernetes 中,Pod 的沙箱容器(Sandbox Container),通常被称为
pause
容器,是 Pod 实现多容器共享命名空间的核心组件。它的存在对 Pod 的正常运作至关重要,但往往被开发者忽视。 pause
容器是 Kubernetes 为每个 Pod 创建的第一个容器,它的核心作用是为 Pod 提供共享的 Linux 命名空间(如网络、IPC、PID 等),并作为 Pod 生命周期的锚点
功能 | 说明 |
---|---|
维持命名空间 | 确保 Pod 内的所有容器共享相同的网络、IPC 等命名空间。 |
防止 Pod 崩溃 | 若所有应用容器退出,pause 容器仍存活,避免 Pod 被误判为终止。 |
处理僵尸进程 | 作为 PID 1 进程,负责回收孤儿进程(僵尸进程),避免资源泄漏。 |
Pod 生命周期管理 | Kubelet 通过监控 pause 容器的状态来判断 Pod 是否存活。 |
2.2.pause
容器的技术细节
2.2.1.镜像来源
- 默认镜像:
registry.k8s.io/pause:<version>
(例如registry.k8s.io/pause:3.9
) - 镜像体积极小(约 700KB),仅包含一个极简的静态编译的
pause
进程。
2.2.2.进程行为
pause
进程启动后会无限休眠(通过调用pause()
系统调用),不执行任何业务逻辑。- 代码开源地址:https://github.com/kubernetes/kubernetes/tree/master/build/pause
2.2.3.资源占用
- CPU 和内存消耗极低,通常可忽略不计。
- 示例
crictl pods
输出:[root@VM-226-235-tencentos ~]# crictl pods | grep nginx-sts-0da081a3c7ba60 10 hours ago Ready nginx-sts-0 default 0 (default)
2.3.pause
容器的工作原理
2.3.1.步骤 1:Pod 创建
- Kubelet 收到创建 Pod 的指令。
- 先创建
pause
容器,该容器初始化 Pod 的共享命名空间(如网络命名空间)。 - 其他应用容器(如 Nginx、Redis)加入
pause
容器的命名空间。
2.3.2.步骤 2:命名空间共享
- 网络命名空间:所有容器共享同一个 IP 和端口空间,因此可以通过
localhost
互相通信。 - IPC 命名空间:允许容器通过 System V IPC 或 POSIX 消息队列通信。
- PID 命名空间:容器可以看到彼此的进程(通过
ps -ef
)。
2.3.3.步骤 3:Pod 终止
- 当 Pod 被删除时,Kubelet 先终止
pause
容器,触发共享命名空间的释放。 - 所有关联的应用容器会被自动终止。
2.4.查看 pause
容器
-
命令
# 列出所有 Pod 沙箱容器(包括 pause 容器) crictl pods# 查看 pause 容器的详细信息,会包含:pause在主机上的进程号、pause镜像等 crictl inspectp <pause_container_id>
-
我们知道每一个容器,在主机上都是一个进程,pause容器也不例外。从进程输出看,我的主机上跑了很多个pause进程,每个pod都会有一个
[root@VM-226-235-tencentos ~]# ps -ef | grep /pause root 4754 1 3 00:13 ? 00:23:43 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf --config=/var/lib/kubelet/config.yaml --cgroup-driver=systemd --network-plugin=cni --pod-infra-container-image=registry.aliyuncs.com/google_containers/pause:3.1 --container-runtime=remote --container-runtime-endpoint=unix:///run/containerd/containerd.sock 65535 5598 5497 0 00:13 ? 00:00:00 /pause 65535 5600 5520 0 00:13 ? 00:00:00 /pause 65535 5614 5546 0 00:13 ? 00:00:00 /pause 65535 5625 5537 0 00:13 ? 00:00:00 /pause 65535 7203 7146 0 00:14 ? 00:00:00 /pause 65535 7220 7182 0 00:14 ? 00:00:00 /pause 65535 7339 7286 0 00:14 ? 00:00:00 /pause 65535 7346 7298 0 00:14 ? 00:00:00 /pause 65535 8796 8723 0 00:14 ? 00:00:00 /pause 65535 8921 8784 0 00:14 ? 00:00:00 /pause 65535 9013 8866 0 00:14 ? 00:00:00 /pause 65535 9028 8945 0 00:14 ? 00:00:00 /pause 65535 9057 8999 0 00:14 ? 00:00:00 /pause 65535 9063 8980 0 00:14 ? 00:00:00 /pause root 9160 9137 0 00:14 ? 00:00:00 /pause 65535 14471 14451 0 00:16 ? 00:00:00 /pause
2.5.常见问题与解答
2.5.1.能否不用 pause
容器?
- 不能。Kubernetes 依赖
pause
容器实现命名空间共享和生命周期管理。若直接删除pause
容器,Pod 内的其他容器将无法正常工作。
2.5.2.为什么 pause
容器一直处于运行状态?
- 这是设计行为。
pause
容器需要持续运行以维持命名空间,直到 Pod 被显式删除。
2.5.3.如何自定义 pause
镜像?
- 修改 Kubelet 的启动参数:
注意:自定义镜像需兼容 Kubernetes 的 CRI 接口。--pod-infra-container-image=my-registry/pause:custom
2.5.4.pause
容器占用资源异常?
- 正常情况下,
pause
容器几乎不消耗资源。若发现异常:- 检查是否被攻击者利用(例如恶意进程注入)。
- 使用
crictl inspectp
查看容器状态。 - 排查节点上的安全漏洞。
2.6.深入理解:pause
容器与僵尸进程
- 问题:在 Linux 中,孤儿进程会被 PID 1 进程接管。若 PID 1 进程未正确处理
SIGCHLD
信号,僵尸进程会累积。 pause
容器的解决方案:pause
进程会主动回收子进程,因此即使应用容器内的进程产生僵尸进程,也会被pause
容器清理。
3.为什么kubernetes要引入pause容器?
- 首先,pause容器镜像极小,几乎不占用资源,且处于长期sleep状态,不处理业务,极度稳定,基本不会主动退出
- 此时就可以把一些 net namespace、pid namespace 等挂载给它,使得pod的网络非常稳定。只要pause容器存活,不论业务容器重启多少次,网络namespace都会存在,即使pod是crash的,也可以ping通,只是业务端口可能断掉了
- 另外,sandbox容器的启动在所有业务容器之前,可以为业务容器提供一个就绪的网络环境等 容器前置依赖
4.pause 容器源码分析
https://github.com/kubernetes/kubernetes/tree/master/build/pause
- 我们以linux为例:https://github.com/kubernetes/kubernetes/tree/master/build/pause/linux
4.1.pause.c
/*
Copyright 2016 The Kubernetes Authors.Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License athttp://www.apache.org/licenses/LICENSE-2.0Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>#define STRINGIFY(x) #x
#define VERSION_STRING(x) STRINGIFY(x)#ifndef VERSION
#define VERSION HEAD
#endif// 信号处理函数:当收到 SIGINT/SIGTERM 时优雅终止容器
static void sigdown(int signo) {psignal(signo, "Shutting down, got signal");exit(0);
}// 信号处理函数:回收僵尸进程(SIGCHLD 信号触发)
static void sigreap(int signo) {// 循环调用 waitpid 回收所有已终止的子进程while (waitpid(-1, NULL, WNOHANG) > 0);
}int main(int argc, char **argv) {int i;// 处理命令行参数 "-v":打印版本信息for (i = 1; i < argc; ++i) {if (!strcasecmp(argv[i], "-v")) {printf("pause.c %s\n", VERSION_STRING(VERSION));return 0;}}// 警告:如果 pause 不是 PID 1(即不是容器第一个进程)if (getpid() != 1)fprintf(stderr, "Warning: pause should be the first process\n");// 注册信号处理器if (sigaction(SIGINT, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0)return 1; // 处理 Ctrl+C/SIGINTif (sigaction(SIGTERM, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0)return 2; // 处理 Kubernetes 发送的终止信号(如 Pod 删除)if (sigaction(SIGCHLD, &(struct sigaction){.sa_handler = sigreap,.sa_flags = SA_NOCLDSTOP},NULL) < 0)return 3; // 处理子进程退出(SA_NOCLDSTOP 表示不处理停止事件)// 主循环:通过 pause() 挂起进程,保持容器运行for (;;)pause(); // pause() 会阻塞直到收到信号// 此处代码理论上不会执行fprintf(stderr, "Error: infinite loop terminated\n");return 42;
}
4.2.orphan.c
/*
Copyright 2016 The Kubernetes Authors.Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License athttp://www.apache.org/licenses/LICENSE-2.0Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*//* 测试工具:生成一个孤儿进程,验证 pause 容器的僵尸进程回收能力 */#include <stdio.h>
#include <unistd.h>int main() {pid_t pid;pid = fork();if (pid == 0) { // 子进程分支// 等待父进程退出(父进程 PID 变为 1)while (getppid() > 1);printf("Child exiting: pid=%d ppid=%d\n", getpid(), getppid());return 0;} else if (pid > 0) { // 父进程分支// 父进程立即退出,使子进程成为孤儿进程printf("Parent exiting: pid=%d ppid=%d\n", getpid(), getppid());return 0;}// fork 失败处理perror("Could not create child");return 1;
}