深入浅出 Kubernetes Pod 实现原理

在 Kubernetes 中,Pod 是容器组的概念,一个 Pod 可以包含多个容器。这些容器共享网络、存储等资源,为应用程序提供了一个更加灵活的运行环境。

容器的本质是一个特殊的进程,特殊在为其创建了 NameSpace 隔离运行环境,并用 Cgroups 控制资源开销,还借助了一些 Linux 网络虚拟化技术解决了网络通信的问题

Pod 所做的则是让多个容器加入同一个 NameSpace 以实现资源共享。

07582eb51089036b6f9c3889ac8d2e16.png
Pod 的形象表达

接下来,我们可以使用 Docker 来还原 Pod 的实现原理。

容器共享 NameSpace

现在我们要部署一个 Pod ,其中包含 nginx 和 busybox 两个容器,前者作为主应用提供 Web 服务,后者作为 Sidecar 调试容器。

首先启动 nginx 容器:

docker run -d --name nginx --ipc="shareable" -v $PWD/log:/var/log/nginx -v $PWD/html:/usr/share/nginx/html nginx

默认情况下,Docker 的 IPC Namespace 是私有的,我们可以使用 --ipc="shareable" 来指定允许共享。-v 参数的作用就不多讲了。

接下来启动 busybox 容器,并加入到 nginx 容器的 NET、IPC、PID NameSpace 中,同时,我们共享 nginx 容器的 Volume ,以便可以访问 nginx 的日志文件:

docker run -d --name busybox --net=container:nginx --ipc=container:nginx --pid=container:nginx -v $PWD/log:/var/log/nginx yauritux/busybox-curl /bin/sh -c 'while true; do sleep 1h; done;'

两个容器都启动后,就可以在 busybox 容器中直接调试 nginx 容器的资源了:

[root@vm ~]# echo "hello pod" > $PWD/html/index.html
[root@vm ~]# docker exec -it busybox ps
PID   USER     TIME  COMMAND
    1 root      0:00 nginx: master process nginx -g daemon off;
   29 101       0:00 nginx: worker process
   30 101       0:00 nginx: worker process
   31 root      0:00 /bin/sh -c while true; do sleep 1h; done;
   37 root      0:00 sleep 1h
   38 root      0:00 ps
[root@vm ~]# docker exec -it busybox curl localhost
hello pod
[root@vm ~]# docker exec -it busybox tail /var/log/nginx/access.log
127.0.0.1 - - [30/Mar/2023:08:07:58 +0000] "GET / HTTP/1.1" 200 10 "-" "curl/7.81.0" "-"
[root@vm ~]#

在 busybox 容器中,不仅可以看到 nginx 容器的进程,还可以直接访问 nginx 服务和共享的日志文件目录。

但是,由于 Namespace 是由 nginx 容器创建的,如果 nginx 意外崩溃,那么所有 Namespace 都会一同被删除,busybox 容器也会被终止:

[root@vm ~]# docker stop nginx
nginx
[root@vm ~]# docker exec -it busybox ps
Error response from daemon: Container fca6ea0fe9f177f62d4b2d5d7db5bf64766d605f0414a8995c8d93159efeed4a is not running
[root@vm ~]#

显然,让业务容器充当共享基础容器是不可取的,必须保证每个容器都是对等的关系,而不是父子关系。Kubernetes 也考虑到了这一点。

Pause 容器

Pause 容器,又叫 Infra 容器。为了解决共享基础容器的安全问题, Kubernetes 会在每个 Pod 里,额外起一个 Infra 容器来共享整个 Pod 的 Namespace 。

Pause 容器会在 Pod 创建时首先启动,并创建 Namespace 、配置网络 IP 地址及路由等相关信息。可以说,Pause 容器的生命周期就相当于是整个 Pod 的生命周期。

等 Pause 容器启动完成后,其它容器才接着启动,并与 Pause 容器共享 Namespace。这样,每个容器就都可以访问 Pod 中其他容器的资源了。

具体的创建过程可以查看 kubelet 的源码:pkg/kubelet/kuberuntime/kuberuntime_manager.go#L678[1]

ee8f0c9582b642325cecaf303e449d65.png

其中第 4 步的创建 Sandbox 沙盒容器,实际就是创建 Pause 容器。之后才继续后面的 init 容器、正常容器的创建。

作用如此重要的 Pause 容器也决定了它的特点:

1、镜像非常小:gcr.io/google_containers/pause-amd64[2]

f21d5e88abd8e2c140fd2e486b7ee3eb.png

2、性能开销几乎可以忽略:pause.c[3]

5a895dc6c780bd6ea436aa41f2c40e95.png

了解了 Pause 容器后,重新开始我们的 Pod 原理试验。

从新环境开始,这次我们首先启动的应该是 Pause 容器了:

docker run -d --name pause --ipc="shareable" gcr.io/google_containers/pause-amd64:3.2

然后再启动 nginx 容器,加入到 Pause 容器的 Namespace :

docker run -d --name nginx --net=container:pause --ipc=container:pause --pid=container:pause -v $PWD/log:/var/log/nginx -v $PWD/html:/usr/share/nginx/html nginx

同理 busybox 容器:

docker run -d --name busybox --net=container:pause --ipc=container:pause --pid=container:pause -v $PWD/log:/var/log/nginx yauritux/busybox-curl /bin/sh -c 'while true; do sleep 1h; done;'

现在在 busybox 容器中将可以同时看到 pause 和 nginx 容器的进程:

[root@vm ~]# echo "hello pod" > $PWD/html/index.html
[root@vm ~]# docker exec -it busybox ps
PID   USER     TIME  COMMAND
    1 root      0:00 /pause
    8 root      0:00 nginx: master process nginx -g daemon off;
   36 101       0:00 nginx: worker process
   37 101       0:00 nginx: worker process
   38 root      0:00 /bin/sh -c while true; do sleep 1h; done;
   45 root      0:00 sleep 1h
   46 root      0:00 ps
[root@vm ~]# docker exec -it busybox curl localhost
hello pod
[root@vm ~]# docker exec -it busybox tail /var/log/nginx/access.log
127.0.0.1 - - [30/Mar/2023:08:49:49 +0000] "GET / HTTP/1.1" 200 10 "-" "curl/7.81.0" "-"
[root@vm ~]#

而且即使 nginx 意外崩溃了,也不会影响到 busybox 容器:

[root@vm ~]# docker stop nginx
nginx
[root@vm ~]# docker exec -it busybox ps
PID   USER     TIME  COMMAND
    1 root      0:00 /pause
   38 root      0:00 /bin/sh -c while true; do sleep 1h; done;
   45 root      0:00 sleep 1h
   66 root      0:00 ps
[root@vm ~]#

回到 Kubernetes

相信你现在已经理解,在 Kubernetes 集群中执行 kubectl apply -f nginx-busybox-pod.yaml 命令后所发生的事情:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-busybox-pod
spec:
  shareProcessNamespace: true
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: nginx-log
      mountPath: /var/log/nginx
    - name: nginx-html
      mountPath: /usr/share/nginx/html
    ports:
    - containerPort: 80
  - name: busybox
    image: yauritux/busybox-curl
    command: ["/bin/sh", "-c", "while true; do sleep 1h; done;"]
    volumeMounts:
    - name: nginx-log
      mountPath: /var/log/nginx
  volumes:
  - name: nginx-log
    emptyDir: {}
  - name: nginx-html
    emptyDir: {}
75bc1804ad105940255f9fab9d886e48.png
(改:应为 -c busybox ps)第一个进程就是 pause

原理就是这么简单。

参考资料

[1]

pkg/kubelet/kuberuntime/kuberuntime_manager.go#L678: https://github.com/kubernetes/kubernetes/blob/v1.26.1/pkg/kubelet/kuberuntime/kuberuntime_manager.go#L678

[2]

gcr.io/google_containers/pause-amd64: https://console.cloud.google.com/gcr/images/google-containers/GLOBAL/pause-amd64

[3]

pause.c: https://github.com/kubernetes/kubernetes/blob/master/build/pause/linux/pause.c

- END -

扫码关注公众号「网管叨bi叨」

给网管个星标,第一时间吸我的知识 👆

网管整理了一本《Go 开发参考书》收集了70多条开发实践。去公众号回复【gocookbook】领取!还有一本《k8s 入门实践》讲解了常用软件在K8s上的部署过程,公众号回复【k8s】即可领取!

觉得有用就点个在看  👇👇👇

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值