Kubernetes之“暂停”容器

本文讲的是Kubernetes之“暂停”容器【编者的话】希望这篇文章可以帮助大家更好的了解Kubernetes的相关核心内容。

当检查你的Kubernetes集群的节点时,在节点上执行命令docker ps,你可能会注意到一些被称为“暂停(/pause)”的容器。

$ docker ps
CONTAINER ID IMAGE COMMAND ...
...
3b45e983c859 gcr.io/google_containers/pause-amd64:3.0  “/pause” ...
...
dbfc35b00062 gcr.io/google_containers/pause-amd64:3.0  “/pause” ...
...
c4e998ec4d5d gcr.io/google_containers/pause-amd64:3.0  “/pause” ...
...
508102acf1e7 gcr.io/google_containers/pause-amd64:3.0  “/pause” ...

这些“暂停”容器是啥,而且还这么多暂停的?这到底是什么情况?

为了回答这些问题,我们需要退一步看看Kubernetes中的pods如何实现,特别是在Docker/containerd运行时。如果你还不知道怎么做,可以先阅读我以前发表的关于Kubernetes pods 的文章

Docker支持容器,这非常适合部署单个软件单元。但是,当你想要一起运行多个软件时,这种模式会变得有点麻烦。当开发人员创建使用supervisord作为入口点的Docker镜像来启动和管理多个进程时,你经常会看到这一点。对于生产系统,许多人发现,将这些应用程序部署在部分隔离并部分共享环境的容器组中更为有用。

Kubernetes为这种使用场景提供了一个称为Pod的干净的抽象。它在隐藏Docker标志的复杂性的同时会保留容器,共享卷等。它也隐藏了容器运行时的差异。例如,rkt原生支持Pod,所以Kubernetes的工作要少一些,但是不管怎样,作为Kubernetes用户的你都不用担心(Pod会帮你搞定这些)。

原则上,任何人都可以配置Docker来控制容器组之间的共享级别——你只需创建一个父容器,在知道正确的标志配置的情况下来设置并创建共享相同环境的新容器,然后管理这些容器的生命周期。而管理所有这些片段的生命周期可能会变得相当复杂。

在Kubernetes中,“暂停”容器用作你的Pod中所有容器的“父容器”。“暂停”容器有两个核心职责。首先,在Pod中它作为Linux命名空间共享的基础。其次,启用PID(进程ID)命名空间共享,它为每个Pod提供PID 1,并收集僵尸进程。

共享命名空间

在Linux中,当你运行新进程时,该进程从父进程继承其命名空间。在新命名空间中运行进程的方法是通过“取消共享”命名空间(与父进程),从而创建一个新的命名空间。以下是使用该unshare工具在新的PID,UTS,IPC和装载命名空间中运行shell的示例。

sudo unshare --pid --uts --ipc --mount -f chroot rootfs / bin / sh

一旦进程运行,你可以将其他进程添加到进程的命名空间中以形成一个Pod。可以使用setns系统调用将新进程添加到现有命名空间。

Pod中的容器在其中共享命名空间。Docker可让你自动执行此过程,因此,让我们来看一下如何使用“暂停”容器和共享命名空间从头开始创建Pod的示例。首先,我们将需要使用Docker启动“暂停”容器,以便我们可以将容器添加到Pod中。

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

然后我们可以运行我们的Pod的容器。首先我们将运行Nginx。这将在端口2368上设置Nginx到其localhost的代理请求。

$ cat <<EOF >> nginx.conf
> error_log stderr;
> events { worker_connections  1024; }
> http {
>     access_log /dev/stdout combined;
>     server {
>         listen 80 default_server;
>         server_name example.com www.example.com;
>         location / {
>             proxy_pass http://127.0.0.1:2368;
>         }
>     }
> }
> EOF
$ docker run -d --name nginx -v `pwd`/nginx.conf:/etc/nginx/nginx.conf -p 8080:80 --net=container:pause --ipc=container:pause --pid=container:pause nginx 

然后,我们将为作为我们的应用服务器的ghost博客应用程序创建另一个容器。

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

在这两种情况下,我们将“暂停”容器指定为我们要加入其命名空间的容器。这将有效地创建我们的Pod。如果你访问 http://localhost:8080/ 你应该能够看到ghost通过Nginx代理运行,因为网络命名空间在pause,nginx和ghost容器之间共享。

如果你觉得这一切好复杂,恭喜你,大家都这么就觉得;它确实很复杂(感觉像句废话)。而且我们甚至还没有了解如何监控和管理这些容器的生命周期。不过,值得庆幸的事,通过Pod,Kubernetes会为你很好地管理所有这些。

收割僵尸

在Linux中,PID命名空间中的所有进程会形成一个树结构,每个进程都会有一个父进程。只有在树的根部的进程没有父进程。这个进程就是“init”进程,即PID 1。

进程可以使用fork和exec syscalls启动其他进程。当启动了其他进程,新进程的父进程就是调用fork syscall的进程。fork用于启动正在运行的进程的另一个副本,而exec则用于启动不同的进程。每个进程在OS进程表中都有一个条目。这将记录有关进程的状态和退出代码。当子进程运行完成,它的进程表条目仍然将保留直到父进程使用wait syscall检索其退出代码将其退出。这被称为“收割”僵尸进程。

僵尸进程是已停止运行但进程表条目仍然存在的进程,因为父进程尚未通过wait syscall进行检索。从技术层面来说,终止的每个进程都算是一个僵尸进程,尽管只是在很短的时间内发生的,但只要不终止他们就可以存活更久。

当父进程wait在子进程完成后不调用syscall时,会发生较长的生存僵尸进程。这样的情况有很多,比如:当父进程写得不好并且简单地省略wait call时,或者当父进程在子进程之前死机,并且新的父进程没有调用wait去检索子进程时。当进程的父进程在子进程之前死机时,OS将子进程分配给“init”进程即PID 1。init进程“收养”子进程并成为其父进程。这意味着现在当子进程退出新的父进程(init)时,必须调用wait 来获取其退出代码否则其进程表项将保持永远,并且它也将成为一个僵尸进程。

在容器中,一个进程必须是每个PID命名空间的init进程。使用Docker,每个容器通常都有自己的PID命名空间,ENTRYPOINT进程是init进程。然而,正如我在上一篇关于Kubernetes Pods的文章中所指出的,某个容器可以在另一个容器的命名空间中运行。在这种情况下,这个容器必须承担init进程的角色,而其他容器则作为init进程的子进程添加到命名空间中。

在Kubernetes Pods的文章中,我在一个容器中运行Nginx,并将ghost添加到了Nginx容器的PID命名空间。

$ docker run -d --name nginx -v `pwd`/nginx.conf:/etc/nginx/nginx.conf -p 8080:80 nginx
$ docker run -d --name ghost --net=container:nginx --ipc=container:nginx --pid=container:nginx ghost

在这种情况下,Nginx将承担PID 1的作用,并将ghost添加为Nginx的子进程。虽然这样貌似不错,但从技术上来看,Nginx现在需要负责任何ghost进程的子进程。例如,如果ghost分身或者使用子进程运行exec,并在子进程完成之前崩溃,那么这些子进程将被Nginx收养。但是,Nginx并不是设计用来作为一个init进程运行并收割僵尸进程的。这意味着将会有很多的这种僵尸进程,并且在整个容器的生命周期,他们都将持续存活。

在Kubernetes Pods中,容器的运行方式与上述方式大致相同,但是每个Pod都有一个特殊的“暂停”容器。这个“暂停”容器运行一个非常简单的进程,它不执行任何功能,基本上是永远睡觉的(见pause()下面的调用)。因为它比较简单,在这里写下完整的源代码,如下:

/*
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 at
http://www.apache.org/licenses/LICENSE-2.0
Unless 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 <sys/types.h>

include <sys/wait.h>

include <unistd.h>

static void sigdown(int signo) {
psignal(signo, "Shutting down, got signal");
exit(0);
}

static void sigreap(int signo) {
while (waitpid(-1, NULL, WNOHANG) > 0);
}

int main() {
if (getpid() != 1)
/* Not an error because pause sees use outside of infra containers. */
fprintf(stderr, "Warning: pause should be the first process\n");

if (sigaction(SIGINT, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0)
return 1;
if (sigaction(SIGTERM, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0)
return 2;
if (sigaction(SIGCHLD, &(struct sigaction){.sa_handler = sigreap,
                                         .sa_flags = SA_NOCLDSTOP},
            NULL) < 0)
return 3;

for (;;)
pause();
fprintf(stderr, "Error: infinite loop terminated\n");
return 42;


正如你所看到的,它当然不会只知道睡觉。它执行另一个重要的功能——即它扮演PID 1的角色,并在子进程被父进程孤立的时候通过调用wait 来收割这些僵尸子进程(参见sigreap)。这样我们就不用担心我们的Kubernetes Pods的PID命名空间里会堆满僵尸了。

PID命名空间共享的一些上下文

值得注意的是,PID命名空间共享已经有了很多的前后关系。如果你启用了PID命名空间共享,那么只能通过暂停容器来收割僵尸,并且目前这一功能仅在Kubernetes 1.7+以上的版本中可用。如果使用Docker 1.13.1+运行Kubernetes 1.7,这一功能默认是开启的,除非使用kubelet标志(--docker-disable-shared-pid=true)禁用。这在Kubernetes 1.8 中正好相反的,现在默认情况下是禁用的,除非由kubelet标志(--docker-disable-shared-pid=false)启用。感兴趣的话,可以看看在GitHub issue中对增加支持PID命名空间共享的有关讨论。

如果没有启用PID命名空间共享,则Kubernetes Pod中的每个容器都将具有自己的PID 1,并且每个容器将需要收集僵尸进程本身。很多时候,这不是一个问题,因为应用程序不会产生其他进程,但僵尸进程使用内存是一个经常被忽视的问题。因此,由于PID命名空间共享使你能够在同一个Pod中的容器之间发送信号,我衷心的希望PID命名空间共享可以成为Kubernetes中的默认选项。

加入我们的社区

希望这篇文章有助于揭示Kubernetes的核心部分。如果这篇文章在下面的评论或Twitter上有帮助,请告诉我们。如果你对Kubernetes感兴趣,并想加入我们的社区,你可以通过多种方式:

  • Stack Overflow上发布和回答问题
  • 在Twitter上跟随@Kubernetesio(你也可@me!)
  • 加入Kubernetes Slack并与我们讨论。(我是ianlewis一个屌丝!)
  • 贡献于GitHub的Kubernetes项目

希望很快能见到你!

原文链接:The Almighty Pause Container(翻译:王杰)

原文发布时间为:2017-10-13

本文作者:ds_sky2008

本文来自合作伙伴Dockerone.io,了解相关信息可以关注Dockerone.io。

原文标题:Kubernetes之“暂停”容器

时间: 2025-01-09 07:02:34

Kubernetes之“暂停”容器的相关文章

DockOne微信分享(一二〇):基于Kubernetes的私有容器云建设实践

本文讲的是DockOne微信分享(一二〇):基于Kubernetes的私有容器云建设实践[编者的话]本次分享将为大家介绍易宝支付私有容器云从0到1的建设之路.包括技术选型.理论基础.基于Kubernetes的容器云和CI/CD落地过程中的挑战和踩过的坑. 建设背景及目标 在Docker技术流行开来之前,保证软件交付的质量和速度对于大多数企业来说都是困难的.业务的复杂性带来了应用的复杂性,面对成千上万的不同应用,运维部门需要时刻应对来自不同应用.不同环境的挑战.特别是在自动化运维程度不高的企业,"

DockOne微信分享(一三七):Kubernetes主机和容器的监控方案

本文讲的是DockOne微信分享(一三七):Kubernetes主机和容器的监控方案[编者的话]随着Docker容器云的广泛应用,大量的业务软件运行在容器中,这使得对Docker容器的监控越来越重要.传统的监控系统大多数是针对物理机或者虚拟机设计的,而容器的特点不同与传统的物理机或者虚拟机,如果还是采用传统的监控系统,则会增加监控复杂程度,那么如何对容器进行监控呢? [3 天烧脑式基于Docker的CI/CD实战训练营 | 北京站]本次培训围绕基于Docker的CI/CD实战展开,具体内容包括:

CRI-O将如何把Kubernetes推上容器生态系统的中心位置

本文讲的是CRI-O将如何把Kubernetes推上容器生态系统的中心位置[编者的话]CRI-O是什么?它将给容器世界带来哪些改变?开源项目CRI-O,即之前的OCID,旨在不依赖传统容器引擎的前提下,使开源Kubernetes调度框架可以管理和启动容器化的工作负载. 使用Google发起.Kubernetes工程师开发的容器运行时接口(CRI),通过与Kubernetes或Kubernetes的商业实例(如CoreOS Tectonic)进行交互,该软件可以帮助DevOps专家管理整个"容器生

Kubernetes可以为容器编排做点什么?

本文讲的是Kubernetes可以为容器编排做点什么?[编者的话]毋庸置疑,Kubernetes目前已成为业内最炙手可热的容器编排框架.本文主要从宏观上阐述了Kubernetes是什么,有什么功能和特性,以及能为容器编排带来什么好处.本文只写了一个概览,有很多细节并未提及,只希望可以给正在Kubernetes道路上探索的同学一点启发. [烧脑式Kubernetes实战训练营]本次培训理论结合实践,主要包括:Kubernetes架构和资源调度原理.Kubernetes DNS与服务发现.基于Kub

Kubernetes – Google分布式容器技术初体验

Kubernetes – Google分布式容器技术初体验 Kubernetes是Google开源的容器集群管理系统.前几天写的 分布式服务框架的4项特性 中提到一个良好的分布式服务框架需要实现 服务的配置管理.包括服务发现.负载均衡及服务依赖管理. 服务之间的调度及生命周期管理. 由于Kubernetes包含了上述部分特性,加上最近Google新推出的Container Engine也是基于Kubernetes基础上实现,因此最近对Kubernetes进行了一些尝试与体验. 运行环境 Kube

Kubernetes让Docker容器如虎添翼

本文讲的是Kubernetes让Docker容器如虎添翼[编者的话]本文主要讲述作者看好Kubernetes与Docker结合的未来. 一年前,我开始学习Docker容器.几个月下来,我意识到我正在学习的是一项革命性技术,原因如下: 快速学习:对于任何我想学习的工具.框架或者编程语言,按需使用Docker容器可以加快我探索性学习环境的搭建. 按需自助服务环境:Docker容器可以用来搭建按需自助服务的开发和测试环境.对于开发和测试人员来说,这是巨大生产力的助推器. 自动部署:使用Docker容器

网易云基于Kubernetes+Docker的容器服务研发实践

网易从2012年春开始云计算研发,陆续上线私有云IaaS.PaaS服务,并实现网易95%以上的互联网业务迁移上云.在近日的网易云技术布道系列活动中,张晓龙分享了网易云基础服务团队在研发容器服务过程中的实战经验.   一.网易云技术架构   首先看到网易云的研发历程和整体架构,如下:   下图是网易云的简单架构:     技术架构从底到上可分为三层:   基础设施层主要采用虚拟化技术将服务器.交换机/路由器以及硬盘等物理设备虚拟成为可以按需分配的计算/存储/网络资源.基础设施层主要包括:云主机.云

《Docker容器:利用Kubernetes、Flannel、Cockpit和Atomic构建和部署》——1.2 了解容器的组成

1.2 了解容器的组成 Docker是Docker项目开发的一种容器格式.docker命令能够运行.停止.启动.调查容器,还能操纵容器.docker命令也可以作为服务守护进程运行,处理管理Docker容器的请求.默认情况下,这个Docker服务会从Docker Hub Registry获取你请求的镜像.虽然你无需知道更多就可以开始使用,但接下来会依次给出一些额外的信息. 1.2.1 Docker项目 Docker项目为Docker开发提供了一个中心.它将Docker称为"一个针对分布式应用开发者

编排管理成容器云关键,Kubernetes和Swarm该选谁

  不论是公有云还是私有云环境,Docker 在新一代技术架构中的重要地位已经毋庸多言,甚至已经有企业在探索完全 Docker 化.在此背景下,如何选择容器技术栈就成为了企业实践的关键.回答这个问题,首先需要厘清技术体系更新的逻辑,再看可选技术是否符合需求.本文认为,容器的管理和编排将是容器云的关键,而 Kubernetes 是最为成熟的编排技术. 容器管理和编排将成云计算主战场 从云化的诱因说起.中国云计算实践八年多,市场认知逐渐提升,锐意创新企业对云的期待已经不是资源弹性.成本优势那么简单,