re.containerbase.startinternal 子容器启动失败_容器发展简史

博客探讨了OS容器如何打包应用程序栈,以LEMP技术栈为例,详细阐述了从Docker引擎到containerd、containerd-shim以及runC的容器启动流程。在容器启动失败的场景下,着重分析了`re.containerbase.startinternal`子容器启动失败的问题。同时,提到了镜像传输、存储、运行时规范和容器监控等关键环节。
摘要由CSDN通过智能技术生成

dec61b5f77fa95cb02c08ea83834fdef.png

在过去四年中(2015-2019),云以及分布式计算成为最受欢迎的技术之一,它们从小众技能逐渐变成更被雇主看重的突出技能。容器化技术是云经济和IT生态系统中最新潮的技术之一。这篇文章可能会帮助您理解有关Docker和容器的一些令人困惑的概念。我们还将看到容器化生态系统在2019年的现状以及演变方向。“不是我们造就了历史,而是历史造就了我们。” ——马丁·路德·金Docker是当今最知名的容器平台之一,它于2013年发布。但是在此之前,隔离和容器化已经被使用。让我们回到1979年,当时我们刚开始使用Chroot Jail,之后便出现了最著名的容器化技术。了解这段历史不但有助于我们理解相关的新概念,也有利于我们理解这项技术。  7775381f6dbdf629078a97e0f3695130.png 时间退回到1979年,Unix版本7在开发过程中引入Chroot Jail以及Chroot系统调用。Chroot jail被用于“Change Root”,它被认为是最早的容器化技术之一。它允许您将进程及其子进程与操作系统的其余部分隔离开来。这种隔离的唯一问题是根进程(root process)可以轻松地退出chroot。它从未考虑实现安全机制。FreeBSD Jail于2000年在FreeBSD OS中引入,旨在为简单的Chroot文件隔离带来更多安全性。与Chroot不同,FreeBSD还实现了将进程及其活动隔离到文件系统的特定视图中。  3880d26823ff42f525142d8fb72fed2f.png A Chroot Jail. 来源:https://linuxhill.wordpress.com/2014/08/09/014-setting-up-a-chroot-jail-in-crunchbang-11debian-wheezy当Linux内核具有操作系统级的虚拟化的功能以后,Linux VServer于2001年被推出,它使用了类似chroot的机制与“安全上下文”(“security context”)以及操作系统虚拟化(容器化)相结合来提供虚拟化解决方案。它比简单的chroot更先进,允许您在单个Linux发行版(VPS)上运行多个Linux发行版。  6b80881d00fa11da8dc1974c1af52e53.png2004年2月,Oracle发布了Oracle Solaris Containers,这是一个用于X86和SPARC处理器的Linux-Vserver版本。SPARC是由Sun Microsystems开发的RISC(精简指令集计算)架构。Solaris Container是由系统资源控制和“区域”(zone)提供的边界隔离组合而成。  073d157829c0a8737d4c91238e62d879.pngOracle Solaris 11.3 与Solaris Containers类似,OpenVZ的第一个版本于2005年推出。OpenVZ与Linux-VServer一样,使用操作系统级虚拟化,许多托管公司采用它来隔离和销售VPS。操作系统级虚拟化有一些限制,因为容器共享相同的体系结构和内核版本,当客户需要不同于主机的内核版本的情况下这种缺点就会显现出来。Linux-VServer和OpenVZ需要为内核打补丁以添加一些用于创建隔离容器的控制机制。 OpenVZ的补丁未集成到内核中。  1c5fed3c3f6fdf52b85e301356a0c711.png2007年,谷歌发布了CGroups,这是一种机制,这种机制能限制和隔离一系列进程的资源使用(CPU,内存,磁盘I / O,网络等)。与OpenVZ 内核相反,CGroups在2007年集成进了Linux内核。2008年,LXC(Linux containers,Linux容器)的第一个版本发布。LXC与OpenVZ,Solaris Containers和Linux-VServer类似,但是它使用的是已经在Linux内核中实现的CGroup。然后,Cloud Foundry于2013年创建了Warden,这是一个管理隔离,短暂存在和被资源控制的环境的API。在其第一个版本中,Warden使用了LXC。  90d79975845fbc95e3fddda0ab505be8.png2013年,Docker推出了的第一个版本。它像OpenVZ和Solaris Containers一样,实现操作系统级虚拟化。2014年,谷歌推出了LMCTFY(Let me contain that for you),谷歌容器栈的开源版本,提供Linux应用程序容器。谷歌工程师一直在与Docker合作libcontainer,并将核心概念和抽象移植到libcontainer。因此没有积极开发LMCTFY项目,未来LMCTFY项目的核心可能会被libcontainer取代。LMCTFY在同一内核上的隔离环境中运行应用程序,并且无需补丁,因为它使用CGroup,命名空间和其他Linux内核功能。 谷歌是容器化行业的领导者。谷歌的一切都在容器上运行。每周有超过20亿个容器在Google基础架构上运行。 2014年12月,CoreOS发布并开始支持rkt(最初作为Rocket发布)作为Docker的替代品。  de40a44c9af1d429a73e488c6f2de1c4.pngJails,虚拟专用服务器(VPS),区域(Zones),容器和VM

be00a47f26946d8e6b7e7bd2b242cf7f.png

使用Jails,Zones,VPS,VM和容器都是为了隔离和资源控制,但每种技术是通过不同的方式实现它,每种方式都有其局限性和优势。到目前为止,我们已经简要介绍了Jail如何工作,并介绍了Linux-VServer如何允许运行隔离的用户空间,其中计算机程序直接在主机操作系统的内核上运行,但程序只能访问其资源的受限子集。Linux-VServer允许运行“虚拟专用服务器”(“Virtual Private Servers”),但必须为主机内核打补丁才能使用它。(将VPS视为商业名称。)Solaris容器被称为区域(Zone)。“虚拟机”是在“真实硬件机器”之上模拟虚拟机的通用术语。该术语最初由Popek和Goldberg定义为真实计算机的有效,孤立副本。虚拟机可以是“系统虚拟机”或“过程虚拟机”。在我们日常使用VM这个词时,我们通常指的是“系统虚拟机”,它是模拟的是主机硬件来模拟整个操作系统。但是,“进程虚拟机”(“Process Virtual Machine”,有时称为应用虚拟机)是用于模拟执行单个进程的编程环境:Java Virtual Machine(JVM,Java虚拟机)就是一个例子。操作系统级虚拟化也称为容器化。Linux-VServer和OpenVZ等技术可以运行多个操作系统,同时共享相同的体系架构和内核版本。在guest需要的虚拟机于主机的内核版本的情况时,共享相同的体系结构和内核会有一些限制和缺点。  39266d727f93984af67f8c6e76683f91.png 来源:https://fntlnz.wtf/post/why-containers系统容器(例如LXC)提供的环境非常接近您从虚拟机获得的环境,与此同时又省去了运行单独内核和模拟所有硬件所带来的开销。  4aa68ad10a5ad67d4c62a918820e3ae5.png VM与容器。来源:Docker博客 操作系统容器与应用容器操作系统级虚拟化有助于我们创建容器。LXC和Docker等技术使用这种隔离方式。我们这里有两种类型的容器:
  • OS容器会打包整个应用程序栈的操作系统【示例LEMP技术栈,LEMP是指一组一起使用来运行动态网站或者服务器的开源软件,软件名称的首字母缩写:L代表Linux,E代表Nginx(Engine X),M代表MariaDB或MySQL,P代表PHP】。

  • 应用容器通常每个容器会运行一个进程

而对于App容器来说,它会创建3个容器来构成LEMP技术栈:
  • PHP服务器(或者是PHP FPM)

  • Web服务器(Nginx)

  • MySQL

a0c45d07004062ccbf913db52259c217.png 系统(OS)容器与应用(App)容器

Docker:是容器还是平台?

be00a47f26946d8e6b7e7bd2b242cf7f.png

简短回答:都是  c2c509428ea9970c868b843b260fb729.png详细答案:当Docker启动时,它使用LXC作为容器运行时,其想法是创建一个API来管理容器运行时,隔离运行应用程序的单个进程,并监督容器的生命周期及其使用的资源。在2013年初,Docker项目是建立一个“标准容器”,我们可以在这个宣言[1]中看到。  13c97a31f2afcb1689a22a7a8088516c.png现在标准容器宣言已删除。  9f257f36d544b660a6252d2ffbb6e042.pngDocker开始构建一个具有多种功能的单体应用程序,这些功能包括启动云服务器以及构建和运行镜像/容器等等。Docker使用“libcontainer”与Linux内核功能(如:控制组和命名空间)进行交互。  8a7037e5155b040dc16513246187eba8.png Docker,Libcontainer和Linux内核功能 让我们使用命名空间和Cgroup(控制组)创建一个容器我在这个例子中使用的是Ubuntu,但对于大多数发行版来说操作应该类似。首先安装CGroup工具和压力测试工具程序(因为我们将进行一些压力测试)。

sudo apt install cgroup-tools

sudo apt install stress

此命令将创建一个新的执行上下文:

sudo unshare --fork --pid --mount-proc bash

c9d596f2ef2622ed2322181dd4080063.gifunshare命令解除了进程的部分执行上下文的关联。unshare()允许进程(或线程)与当前和其他进程(或线程)共享的执行上下文解除关联。但是部分执行上下文(例如mount > namespace),当使用fork(2)或vfork(2)去创建新进程时,执行上下文,与此同时,其他部分(例如虚拟内存),可能在使用clone(2)创建进程或线程,通过显式请求共享虚拟内存。现在,使用cgcreate我们来创建一个控制组并定义两个控制器,一个在内存上,另一个在CPU上。  c11fa69e79873e8f2573fe1c546da817.gif下一步是定义内存限制并使之生效:

echo 3000000 > /sys/fs/cgroup/memory/mygroup/memory.kmem.limit_in_bytes cgexec -g memory:mygroup bash

fe047231550ae4bdb41f52774ca8a27f.gif现在让我们压力测试一下我们创建的独立命名空间(包含内存限制)。

stress --vm 1 --vm-bytes 1G --timeout 10s

6fb0810e740bb57aee0d7299883cb5a7.gif可以看到执行失败了,于是我们知道内存限制正常工作。如果我们在主机上做同样的事情(不要在16G RAM上模仿),测试永远不会失败,除非你真的没有足够的可用内存:  fe0219ac77d22f7cbf5953e094ee2c3a.gif遵循这些步骤有利于你理解Linux工具(如CGroup和其他资源管理功能)是如何在Linux系统中创建和管理隔离的环境。libcontainer与这些工具交互以管理和运行Docker容器。 runC:在不使用Docker的情况下使用libcontainer2015年,Docker发布runC:一个轻量级,可移植的容器运行时。  482b6e4dbc3f3c763ade9f8efc79e103.pngrunC实际上是一个直接利用libcontainer的命令行工具,无需通过Docker引擎。runC的目标是使标准容器随处可用。这个项目被捐赠给了Open Container Initiative,OCI。libcontainer存储库现已被存档。  4b858bef2f3bf3e73ea08b1aa78229b4.png实际上,libcontainer并没有被抛弃,而是被转移到了runC仓库。  184aa3e710d9598d557c94a0649cba03.png让我们回到实践部分并使用runC创建一个容器。首先安装runC运行时:  3af99d7c5bc100dce86ad0e78142dd2d.gif让我们创建一个目录(/mycontainer),我们将导出镜像Busybox的内容。BusyBox[2]的大小介于1到5 Mb之间(取决于变体),在制作高效利用空间发行版的时候,是一个非常好的组成部分。BusyBox将许多常见UNIX实用程序的微小版本组合到一个小的可执行文件中。它提供了你通常在GNU fileutils,shellutils等中找到的大多数实用程序的替代工具。BusyBox中的工具程序通常比它们包含所有功能的GNU版本有更少的选项;但包含的选项提供了大部分你所需要的功能,与GNU的对应版本非常相似。BusyBox为任何小型或嵌入式系统提供了相当完善的环境。 来源:Docker Hub。 5da102967664e1b4adafa6604b362e35.gif使用runC命令,我们可以用提取的镜像和spec文件(config.json)来运行BusyBox容器。runc spec命令一开始会创建以下JSON文件:

{

"ociVersion": "1.0.1-dev",

"process": {

"terminal": true,

"user": {

"uid": 0,

"gid": 0

},

"args": [

"sh"

],

"env": [

"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",

"TERM=xterm"

],

"cwd": "/",

"capabilities": {

"bounding": [

"CAP_AUDIT_WRITE",

"CAP_KILL",

"CAP_NET_BIND_SERVICE"

],

"effective": [

"CAP_AUDIT_WRITE",

"CAP_KILL",

"CAP_NET_BIND_SERVICE"

],

"inheritable": [

"CAP_AUDIT_WRITE",

"CAP_KILL",

"CAP_NET_BIND_SERVICE"

],

"permitted": [

"CAP_AUDIT_WRITE",

"CAP_KILL",

"CAP_NET_BIND_SERVICE"

],

"ambient": [

"CAP_AUDIT_WRITE",

"CAP_KILL",

"CAP_NET_BIND_SERVICE"

]

},

"rlimits": [

{

"type": "RLIMIT_NOFILE",

"hard": 1024,

"soft": 1024

}

],

"noNewPrivileges": true

},

"root": {

"path": "rootfs",

"readonly": true

},

"hostname": "runc",

"mounts": [

{

"destination": "/proc",

"type": "proc",

"source": "proc"

},

{

"destination": "/dev",

"type": "tmpfs",

"source": "tmpfs",

"options": [

"nosuid",

"strictatime",

"mode=755",

"size=65536k"

]

},

{

"destination": "/dev/pts",

"type": "devpts",

"source": "devpts",

"options": [

"nosuid",

"noexec",

"newinstance",

"ptmxmode=0666",

"mode=0620",

"gid=5"

]

},

{

"destination": "/dev/shm",

"type": "tmpfs",

"source": "shm",

"options": [

"nosuid",

"noexec",

"nodev",

"mode=1777",

"size=65536k"

]

},

{

"destination": "/dev/mqueue",

"type": "mqueue",

"source": "mqueue",

"options": [

"nosuid",

"noexec",

"nodev"

]

},

{

"destination": "/sys",

"type": "sysfs",

"source": "sysfs",

"options": [

"nosuid",

"noexec",

"nodev",

"ro"

]

},

{

"destination": "/sys/fs/cgroup",

"type": "cgroup",

"source": "cgroup",

"options": [

"nosuid",

"noexec",

"nodev",

"relatime",

"ro"

]

}

],

"linux": {

"resources": {

"devices": [

{

"allow": false,

"access": "rwm"

}

]

},

"namespaces": [

{

"type": "pid"

},

{

"type": "network"

},

{

"type": "ipc"

},

{

"type": "uts"

},

{

"type": "mount"

}

],

"maskedPaths": [

"/proc/kcore",

"/proc/latency_stats",

"/proc/timer_list",

"/proc/timer_stats",

"/proc/sched_debug",

"/sys/firmware",

"/proc/scsi"

],

"readonlyPaths": [

"/proc/asound",

"/proc/bus",

"/proc/fs",

"/proc/irq",

"/proc/sys",

"/proc/sysrq-trigger"

]

}

}

68cb8fb2e823e7c34e117a7c7a3793dd.gif另一个替代方案是使用“oci-runtime-tool”的子命令“oci-runtime-tool generate”,它包含很多选项帮助你做更多自定义配置。更多信息,请参阅runtime-tools[3]。使用生成的规范JSON文件,您可以自定义容器的运行时。例如,我们可以更改要执行的应用程序的参数。  233f71c26dcf50296d547a44db08c664.png让我们来看看原始config.json文件和新文件之间的差别:  72bf2e587f29f4902455f9e5ed6c3825.png现在让我们再次运行容器并观察到它在退出前休眠了10秒。行业标准的容器运行时

be00a47f26946d8e6b7e7bd2b242cf7f.png

鉴于容器逐渐成为主流,容器生态系统中的不同参与者一直致力于标准化。标准化是自动化和总结最佳实践的关键。在将runC项目提供给OCI的同时,Docker在2016年开始使用containerd作为容器运行时,containerd可以与更底层运行时runC进行交互。  598001961176cd4ad22a27974b63fb13.gifCotnainerd完全支持启动OCI下的软件并管理这些软件的生命周期。 Containerd(以及其他像cri-o这样的运行时)都是使用runC来运行容器,但Containerd实现了其他更高级功能,如镜像管理和更高级API。  2aeb1a13fa3afcceef6b05dee246626b.png containerd与Docker和OCI运行时集成Containerd,Shim和RunC,这一切是如何一起工作的

be00a47f26946d8e6b7e7bd2b242cf7f.png

runC建立在libcontainer之上,libcontainer是以前为Docker引擎提供动力的容器库。在1.11版之前,Docker引擎被用于管理卷,网络,容器,图像等。 现在,Docker架构被拆分成四个部分:
  • Docker引擎

  • containerd运行时

  • containerd-shim

  • 和runC运行时。

二进制文件分别称为docker,docker-containerd,docker-containerd-shim和docker-runc。让我们用Docker的新架构来列举运行容器的步骤:
  1. Docker引擎创建容器(来自镜像)并将其传递给containerd

  2. Containerd调用containerd-shim

  3. Containerd-shim使用runC来运行容器

  4. Containerd-shim可以让运行时(此处为runC)在启动容器后退出

使用新架构,我们可以运行“无守护容器”(“daemon-less containers” ),它有两个优点:
  1. runC可以在启动容器后退出,我们不必运行整个运行时进程。

  2. 即使Docker和/或容器死亡,containerd-shim也会保证stdin,stdout和stderr这些文件描述符为打开状态。

4193cbf6d54057858452b0c960996f5c.png “既然runC和Containerd都是运行时,为什么运行单个容器时需要两个运行时?”这可能是被问的最多的问题之一。在之前讲解为什么Docker将其架构拆分为runC和Containerd时,您会发现这两个都是运行时。如果您是从头开始阅读的,那么您一定注意到有高层级和低层级的运行时。这就是runC和Containerd两者之间的实际差异。两者都可以称为运行时,但每个运行时都有不同的用途和功能。为了使容器生态系统保持标准化,低层级的容器运行时只允许运行容器。低级运行时(如runC)应该轻巧,快速,并且不会与其他更高层级的容器管理发生冲突。当你创建一个容器时,这个容器实际上同时被containerd和runC两个运行时同时管理。您会发现有很多容器运行时,其中一些是遵循OCI标准的而另一些不是,一些是低层级的运行时,而另一些不仅仅是运行时,它们同时实现了工具层来管理容器的生命周期等等:
  • 镜像传输和存储

  • 容器运行和监控,

  • 底层存储

  • 网络附件

  • 等等

e176a0ebddc18383dea99e4bfd249028.png 来源:https://www.ianlewis.org/en/container-runtimes-part-1-introduction-container-r我们可以为Docker添加新的运行时,只需运行:

sudo dockerd --add-runtime==

示例:

sudo apt-get install nvidia-container-runtime

sudo dockerd --add-runtime=nvidia=/usr/bin/nvidia-container-runtime

容器运行时接口

be00a47f26946d8e6b7e7bd2b242cf7f.png

Kubernetes是当下最流行的容器编排系统之一。随着容器运行时数量的不断增加,Kubernetes目标是变得更加灵活,并且与更多的容器运行时(不仅仅是Docker)进行交互。最初,Kubernetes使用Docker运行时来运行容器,并且现在它仍然是默认的运行时。但是,CoreOS希望将Kubernetes与RKT运行时一起使用,并为Kubernetes提供补丁,以便将来此运行时可以用来替代Docker运行时。在添加新的容器运行时,Kubernetes不希望改变自己的代码库,于是它决定创建容器运行时接口(CRI或Container Runtime Interface),这是一组API和库,允许在Kubernetes中运行不同的容器运行时。Kubernetes通过CRI API与它支持的运行时进行交互。  682696182ac69d7e73c0b485239fd55c.png以下是一些CRI插件:CRI-O:是为Kubernetes CRI接口创建的第一个容器运行时。 cri-o不是为了取代Docker,而是可以在Kubernetes的特定上下文中使用它而不是Docker运行时。  42798cc1313fa52f55832b632e7990cd.pngContainerd CRI:使用cri-containerd,用户可以使用containerd作为底层运行时运行Kubernetes集群,而无需安装Docker。  0921db9855fa5d7157486389260824be.pnggVisor CRI:gVisor是由Google开发的项目,它在用户空间中实现了大约200个Linux系统调用,与直接在Linux内核上运行的Docker容器(使用命名空间隔离)相比,具有更高的安全性。Google Cloud App Engine使用gVisor CRI实现客户之间的隔离。  2690ab416afa868ee5b6f01d6131dca9.pnggVisro运行时与Docker和Kubernetes集成,使得运行沙盒容器变得简单。CRI-O Kata容器:Kata Containers是一个开源项目,用于构建轻量级虚拟机,可插入容器生态系统。 CRI-O Kata容器允许在Kubernetes上运行Kata Containers来替代默认的Docker运行时。  f20ac0a94d0399165197fb581a2c819c.pngMoby项目

be00a47f26946d8e6b7e7bd2b242cf7f.png

建立一个单体的Docker平台的项目在某种程度上已被抛弃,并催生了Moby项目,在这个项目中,Docker被拆分成多个组件,例如RunC。  94585551100a1db6caa23ced76680994.png 来源:Solomon Hykes TwitterMoby是一个将Docker开发进行组织和模块化的项目。它是一个开发和生产的生态系统。 Docker的常规用户很难意识到变化。  a64951dabb0fd37e5cab104bcfbbe902.png 资料来源:Solomon Hykes TwitterMoby帮助开发和运行Docker CE和EE(Moby是Docker上游)以及为其他运行时和平台创建开发和生产环境。开放容器计划(The Open Containers Initiative)

be00a47f26946d8e6b7e7bd2b242cf7f.png

正如我们所看到的,Docker将RunC捐赠给Open Container Initiative(OCI),但这项计划是什么?OCI是一个轻量级,开放型治理架构的组织,由Docker,CoreOS和容器行业的其他领导者于2015年发起。开放容器计划(OCI)旨在建立软件容器的通用标准,以避免容器生态系统内部可能出现的分裂和分化。  11060cb05f004f0508b6bab8d03fad3c.png它包含两个规范:
  • runtime-spec:运行时规范

  • image-spec:镜像规范

使用不同运行时的容器可以与Docker API一起使用。使用Docker创建的容器可以能在任何其他引擎上运行。相关链接:
  1. https://github.com/moby/moby/commit/0db56e6c519b19ec16c6fbd12e3cee7dfa6018c5

  2. http://www.busybox.net/

  3. https://github.com/opencontainers/runtime-tools

原文链接:https://medium.com/faun/the-missing-introduction-to-containerization-de1fbb73efc5Kubernetes实战培训

b3e545afd0a8bcd99b94c8dbd832ec8d.png

Kubernetes实战培训将于2019年6月28日在深圳开课,3天时间带你系统掌握Kubernetes,学习效果不好可以继续学习。本次培训包括:云原生介绍、微服务;Docker基础、Docker工作原理、镜像、网络、存储、数据卷、安全;Kubernetes架构、核心组件、常用对象、网络、存储、认证、服务发现、调度和服务质量保证、日志、监控、告警、Helm、实践案例等,点击下方图片或者点击阅读原文了解详情。 74f0c70bf90281ae215c08b9e5163847.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值