日期: July 22, 2022


关于pause容器

众所周知,在k8s中每创建一个pod都有自带一个pause的容器,有时也称为infra容器,这个东西到底有什么用呢?

在k8s中,pause是pod内所有容器的父容器,它的作用有两个

  • 作为pod中共享的namespace(network,uts)的基础
  • pid namespace共享 ,在每个pod中充当1号进程的角色,并在子进程变成孤儿进程时收割这些僵尸进程

关于snadbox(沙箱)

K8s在创建Pod时kubelet会先调用CRI接口创建一个沙箱(sandbox)环境,之后kubelet就可以在里面创建用户容器了,删除的时候先把sandbox移除,再停止里面的所有容器

在linux CRI体系里,pod sandbox就是pause容器,kubelet代码引用的defaultSandboxImage 就是官方的pause镜像

关于1号进程/pid namespace

在 Linux 系统中运行新进程时,新进程会继承父进程的namespace,在 namespace 中运行进程的方法是通过取消与父进程的共享 namespace,从而创建一个新的 namespace(?)

在linux中,pid为1的进程是init进程,是所有进程的父进程,它会维护一张进程表并不断检查其他进程的状态,如果某个子进程变成孤儿进程,init就来接管它,然后在它退出时回收资源

容器使用pid namespace对pid进行隔离,每个容器中均可以有独立的 init 进程。当在主机上发送 SIGKILL 或者 SIGSTOP(即 docker kill 或者 docker stop)强制终止容器的运行时,其实就是在终止容器内的 init 进程。一旦 init 进程被销毁,同一 PID namespace 下的进程也随之被销毁。

容器必须有一个充当init进程,Docker 中ENTRYPOINT 进程就是 init 进程。如果用共享pid namespace的方式,父容器就是init进程

当然父容器不一定非得是pause,随便其他一个应用也可以作为父容器,但除了pause其他应用未必会有回收僵尸进程的功能

如何在docker中实现类似k8s的pod内容器组共享

在k8s中一个pod就是一组被约束的容器,pod中的不同容器如同在同一台主机上,它们可以通过localhost通信

在docker中同样可以实现这种容器组间namespace的共享,只需要:

  • 创建一个父容器
  • 创建与父容器共享资源的子容器

操作

  • 创建pause作为父容器

docker run -d —name pause gcr.io/google_containers/pause-amd64:3.0

  • 创建应用容器作为子容器,并共享pause容器的网络/ips/pid

docker run -d —name ghost —net = container:pause —ipc = container:pause —pid = container:pause ghost

此时进入pause容器通过localhost:port可以访问到ghost服务(network namespace共享)

ps查看进程会发现1号进程为/pause,进入ghost容器1号进程也是它(pid namespace共享)

kubernetes中的pid namespace 共享/隔离

在 Kubernetes 1.8 版本之前,默认是启用 PID namespace 共享的,除非使用 kubelet 标志 —docker-disable-shared-pid=true 禁用。而在 Kubernetes 1.8 版本以后,情况刚好相反,默认情况下  —docker-disable-shared-pid=true,如果要开启,还要设置成 false

Kubernetes 提供的关于是否共享 PID namespace 的 downward API

apiVersion: v1
kind: Pod
metadata:
	name: nginx
spec:
	shareProcessNamespace: true
containers:
- name: nginx
	image: nginx
- name: shell
	image: busybox

为什么kubernetes中要默认禁止pid namespace共享呢

因为如果一个pod中跑一个应用,僵尸进程几乎可以忽略不计,而由于pause成为了PID 1 ,在子容器里如果要重启服务kill  -HUP 1 就去kill  pause容器了,服务的pid如果不是1 ,可能会每次pid不一致,也无法完成某些固定统一的操作,systemd 这类镜像要求获得 PID 1,否则无法正常启动

PID namespace 共享使 Pod 内不同容器的进程对其他容器是可见的,包括 /proc 中可见的所有信息,如果有环境变量传递的密码,也是所有容器共享的,有安全隐患