Linux容器演变史

在过去的几年里,容器不仅成为开发人员的热门话题,而且成为企业的热门话题。这种日益增长的兴趣导致对安全性改进和加强的需求增加,并为可伸缩性和互操作性做好准备。这就需要大量的工程,以下是红帽公司在企业层面上所做的工程。

2013年秋季,当我第一次见到 Docker Inc. (Docker.io) 的代表时,我们正在研究如何让 Red Hat Enterprise Linux (RHEL) 使用 Docker 容器。(Docker项目的一部分后来改名为Moby。)我们在把这项技术应用到 RHEL 上遇到了一些问题。第一个大障碍是获得支持的写时复制 (COW)文件系统来处理容器映像分层。Red Hat最终提供了一些COW实现,包括设备映射器(Device Mapper)、btrfs和第一个版本的OverlayFS。对于 RHEL,我们默认为 Device Mapper,尽管我们对 OverlayFS 的支持方面已经快完成了。

下一个主要障碍是启动容器的工具。当时,上游docker使用LXC工具来启动容器,我们不希望支持RHEL中设置的LXC工具。在使用upstream docker之前,我一直在与libvirt团队合作开发一个名为virt-sandbox的工具,该工具使用libvirt-lxc启动容器。

当时,Red Hat 的一些人认为,将 LXC 工具集移除,在 Docker 守护进程和 libvirt 之间通过libvirt-lxc来桥接启动容器是个不错的想法。这种做法引起了密切注意。考虑下面使用Docker客户机(Docker -cli)启动容器的例子,以及容器进程(pid1OfContainer)启动之前的调用层:

docker-cli → docker-daemon → libvirt-lxc → pid1OfContainer

我不喜欢在启动容器的工具和最终运行的容器之间有两个守护进程。

我的团队与上游docker开发人员一起,使用原生 Go 编程语言现了一个名为容器运行时库:libcontainer 。这个库最终与 runc 一起作为 OCI 运行时规范的初始实现发布。

docker- cli → docker-daemon @ pid1OfContainer

尽管大多数人错误地认为,当他们执行容器时,容器进程是 docker-cli 的子进程,但实际上他们已经执行了客户机/服务器(C/S)操作,容器进程作为一个完全独立的环境的子进程运行。这种客户机/服务器操作可能导致不稳定和潜在的安全问题,并且会阻塞有用的特性。例如,systemd有一个称为套接字激活的特性,在这个特性中,您可以设置一个守护进程,只有在进程连接到套接字时才运行它。这意味着您的系统使用更少的内存,并且只有在需要时才执行服务。套接字激活的工作方式是systemd在TCP套接字上侦听,当套接字的数据包到达时,systemd激活通常在套接字上侦听的服务。一旦服务被激活,systemd将套接字传递给新启动的守护进程。将此守护进程移动到基于docker的容器中会导致问题。单元文件将使用Docker CLI启动容器,而systemd无法通过Docker CLI将连接的套接字传递给Docker守护进程。

这样的问题使我们意识到我们需要运行容器的替代方法。

容器编排问题

上游docker项目使使用容器变得很容易,并且它仍然是学习Linux容器的一个很好的工具。您可以通过运行一个简单的命令(如docker run -ti fedora sh)快速体验启动容器的过程,并立即进入容器中。

容器的真正优势在于同时启动大量组件,并将它们组装成一个强大的应用程序。设置一个多容器应用的难点在于其复杂度的指数级增长,以及通过简单的 docker 命令将分散的各个部分串联起来。如何在资源有限的集群节点中管理容器布局?如何管理这些容器的生命周期?类似问题还有很多。

在第一届 DockerCon 上,至少有 7 个公司 / 开源项目展示了容器编排的方式。我们演示的是红帽OpenShift的项目geard,它松散的基于 OpenShift v2 容器。红帽决定我们需要在容器编排上重新审视,也可能和其他开源社区的人员合作。

谷歌演示了Kubernetes容器编排,该编排基于谷歌在编排自己的内部体系结构时开发的所有知识。OpenShift决定放弃我们的Gear项目,开始在Kubernetes上使用谷歌。Kubernetes现在是GitHub上最大的社区项目之一。

Kubernetes

Kubernetes是为了使用谷歌的lmctfy容器运行时而开发的。2014年夏天,Lmctfy被移植到Docker。Kubernetes在Kubernetes集群的每个节点上运行一个守护进程,称为kubelet。这意味着最初的Kubernetes与Docker 1.8工作流类似:

kubelet → dockerdaemon @ PID1

回到两个守护进程系统。

但情况越来越糟。随着Docker的每一次发布,Kubernetes都被破坏了。Docker 1.10切换了备份存储,导致所有映像的重建。Docker 1.11开始使用runc启动容器:

kubelet → dockerdaemon @ runc @PID1

Docker 1.12添加了一个容器守护进程来启动容器。其主要目的是满足Docker Swarm (Kubernetes的竞争对手):

kubelet → dockerdaemon → containerd @runc @ pid1

如前所述,每个Docker版本都破坏了Kubernetes的功能,这就是为什么Kubernetes和OpenShift要求我们为它们的工作负载提供较老版本的Docker。

现在我们有了一个三个守护进程的系统,如果任何一个守护进程出了问题,整个系统就会崩溃。

走向容器标准化

CoreOS、rkt 其他运行时环境

由于Docker运行时的问题,一些组织正在寻找其他运行时。CoreOS就是这样一个组织。CoreOS为上游docker提供了一个替代的容器运行时,称为rkt (rocket)。他们还引入了一个名为appc (App container)的标准容器规范。基本上,他们想让每个人都使用一个标准规范来描述如何将应用程序存储在容器映像包中。

这就抛出了危险信号。当我第一次开始使用上游 docker处理容器时,我最大的担心是我们最终会得到多个规格。我不希望RPM与debian之间的战争影响未来20年Linux软件的发布。appc引入的一个好的结果是,它说服了upstream docker与开源社区合作,创建了一个名为开放容器计划(open Container Initiative, OCI)的标准机构。

OCI 一直在制定两种规范:

  • OCI运行时规范: OCI运行时规范“旨在指定容器的配置、执行环境和生命周期。”它定义了容器在磁盘上的样子,描述将在容器中运行的应用程序的JSON文件,以及如何生成和执行容器。上游docker贡献了libcontainer的工作,并将runc构建为OCI运行时规范的默认实现。
  • OCI镜像格式规范:镜像格式规范主要基于上游docker镜像格式,并定义了位于容器注册中心的实际容器镜像包。该规范允许应用程序开发人员将其应用程序标准化为单一格式。appc中描述的一些思想虽然仍然存在,但已经添加到OCI镜像格式规范中。这两个OCI规范都接近1.0版本。上游docker已经同意在OCI镜像规范最终确定之后支持它。Rkt现在支持运行OCI镜像以及传统的上游docker镜像。

通过提供容器镜像和运行时的行业规范,OCI 帮助了容器相关工具和编排框架上的革新。

抽象运行时接口

Kubernetes 编排工具是这些标准的受益者之一。作为 Kubernetes 的一大支持者,CoreOS 提交了一系列的补丁为 Kubernetes 增加了通过 rkt 运行容器的支持。Google 和 Kubernetes 社区发现应用了这些补丁之后,将来要为 Kubernetes 增加新的容器运行时支持会使得 Kubernetes 代码变得复杂和臃肿。因此 Kubernetes 团队决定实现一套名为容器运行时接口(Container Runtime Interface,CRI)的 API 协议规范。然后他们重构了 Kubernetes,使其直接调用 CRI,而不用调用 Docker 引擎。如果以后需要添加新的容器运行时接口,只需要实现 CRI 的服务端接口即可。同时,Kubernetes 为 CRI 开发者提供了大量测试集用来验证这些实现是否兼容 Kubernetes。CRI 的抽象,还有一项需要持续进行工作:将原先 Kubernetes 中对 Docker 引擎的直接调用去除,移动到称为 docker-shim 的适配层中去。

容器工具创新

镜像仓库创新——skopeo

几年前我们在 Atomic 项目中开发 atomic 命令行接口。其中一个功能是,我们希望能够有一个工具,当镜像还在镜像仓库的时候,就可以验证其中的内容。当时,唯一的变通方式是通过容器镜像关联的 JSON 文件将镜像拉取到本地,然后再通过docker inspect命令来读取镜像信息。这些镜像可能非常大,占用上 G 空间。有了这项功能,就能够让用户能够提前检查镜像内容,以确定是否需要拉取镜像,因此我们为docker inspect命令增加了一个–remote的参数。但是上游 docker 拒绝了这个 pull request,原因是他们不希望将 Docker 命令行接口弄的复杂,并且用户可以通过自己的小工具来实现同样的功能。

Antonio Murdaca领导的我们团队,最终完成了名为skopeo的工具。Antonio 将这个工具定位不仅局限于拉取镜像的配置文件,他还实现了将容器镜像从镜像仓库拉取到本地服务器,从本地服务器推送镜像到镜像仓库的双向协议。

目前,skopeo 已经重度融入在 atomic 命令行接口中,包括检查容器更新、集成入atomic scan命令实现中等。Atomic 也使用 skopeo 来拉取和推送镜像,而非使用上游的 docker 守护进程。

Containers/image 库

我们一直在和 CoreOS 沟通关于在 rkt 中使用 skopeo 的可行性,但是对方一直不愿意通过一个可以执行的帮助应用程序,而是希望能够将 skopeo 功能作为依赖库直接集成进去。因此我们决定将 skopeo 拆分成库和可执行程序,最终创建了image 工程。

containers/image 库和 skopeo 已经被一些上游项目和云服务基础设施工具使用。二者已经能够支持除 Docker 外的其他多种存储后端,并且它们提供了诸如在镜像仓库之间移动镜像等许多特性。skopeo 的一个优势在于,它的工作不依赖任何守护进程。containers/image 库的突破,也让类似容器镜像签名等增强功能的实现成为了可能。

镜像处理和扫描的创新

前文提到了atomic 命令行接口,该工具用来实现 docker 命令行接口不兼容的特性和一些我们觉得上游 docker 不会接受的特性。另外我们还希望它能够可扩展的支持其他容器运行时、工具和存储。前面提到的 skopeo 就验证了这点。

其中一个我们想加入到 atomic 的特性是atomic mount。该命令的需求来源是能够从 Docker 镜像存储(上游 docker 称之为图驱动,graph driver)获取数据,并将其挂载到某个地方,这样可以使用其他工具来检查这个镜像。目前如果使用原生 docker,查看镜像内容的唯一方式是启动容器。如果镜像中包含不受信内容,仅仅为了查看镜像内容而运行其代码是非常危险的。启动容器然后验证内容的另外一个问题是,用于检查的工具通常不会包含在容器镜像中。

大部分容器镜像扫描工具的工作流程通常如下:它们连接到 Docker 套接字,执行docker save命令创建一个压缩包,然后将其解压缩到硬盘上,最后检查这些内容。这个操作流程很慢。

有了atomic mount命令,我们可以直接通过 Docker 图驱动(graph driver)来挂载镜像。如果 Docker 守护进程使用设备映射器(device mapper),它可以挂载这个设备;如果使用的是 overlay 文件系统,它可以挂载 overlay。这样可以快速的满足我们的需求。现在只需要这样做:

# atomic mount fedora /mnt # cd /mnt

然后就可以开始检查内容。当检查完毕之后,执行:

 atomic umount /mnt

我们将该特性融入到了atomic scan命令中,这样就可以构建一个快速镜像扫描仪了。

工具协调问题

atomic mount命令使用过程中一个大问题是它独立工作。Docker 守护进程无法感知到还有其他进程在使用镜像。这可能导致一些问题(例如,有人首先用前面提到的命令挂载 Fedora 镜像,然后其他人执行了docker rmi fedora命令,Docker 守护进程在试图删除 Fedora 镜像的时候会因为设备忙而失败)。此时 Docker 守护进程会进入不可预知的状态。

Container/storage 库

为了解决这个问题,我们开始将上游 docker daemon 代码中图驱动相关代码拉取到自己的仓库。Docker 守护进程的图驱动将所有锁相关操作都在自己的内存中完成。我们希望能够将锁相关实现移动到文件系统中,这样不同的进程能够同时操作容器存储,而无需都通过守护进程这个单点。

最终该项目被命名为container/storage,它实现了容器在运行、构建和存储时所需要的所有写时复制(COW)特性,而无需使用一个进程来控制和监控(即不需要守护进程)。现在 skopeo 和其他一些工具和项目都能够更好的使用存储。还有一些其他开源项目开始使用 containers/storage 库,在某个时间点我们希望这个项目能够合并回上游的 docker 项目。

启航,进行创新

下面让我们来看下 Kubernetes 在一个节点上通过 Docker 守护进程运行一个容器时,到底发上了什么。首先 Kubernetes 执行一个命令:

kubelet run nginx –image=nginx

该命令告诉 kubelet 在节点上运行 nginx 应用程序。kubelet 调用容器运行时接口,要求其启动 nginx 应用程序。此时,容器运行时接口的实现需要完成以下步骤:

  • 检查本地存储中名为 nginx 的镜像。如果本地不存在,容器运行时将会在镜像仓库中查找对应的镜像。
  • 如果镜像在本地存储中不存在,将其从镜像仓库下载到本地系统中。
  • 将下载的容器镜像释放到容器存储中(通常是一个写时复制存储),并挂载到适当位置。
  • 使用标准化的容器运行时来运行容器。

让我们来看看上述过程中依赖的特性:

  • OCI 镜像格式:用于定义镜像在仓库中的标准存储格式。
  • Containers/image 库:用于实现从镜像仓库拉取镜像所需要的所有特性。
  • Containers/storage 库:提供 OCI 镜像格式解压缩到写时复制存储上所需要的功能。
  • OCI 运行时规范和runc:提供运行容器所需要的工具(和 Docker 守护进程用来运行容器的工具相同)。

这意味着我们可以使用这些工具和库实现使用容器所需要的能力,而无需依赖一个大型容器守护进程。

在一个中等到大型规模基于 DevOps 的持续集成 / 持续交付环境中,效率、速度和安全性是非常重要的。而一旦相关的工具符合 OCI 规范,那么开发和运维就能够在持续集成 / 持续交付管道和生产环境中使用最佳的工具。大部分容器操作工具都隐藏在容器编排框架或者其他上层容器平台技术之下。可以预见到未来容器运行时和镜像工具的选择会成为容器平台的一个安装选项。

系统(独立)容器

Atomic 项目中,我们引入了atomic host,这是一个构建操作系统的新方式,其中的软件可以“原子的”更新,并且大部分应用程序都以容器的方式运行。使用该平台的目的是为了证明大部分软件能够被移植到 OCI 镜像格式,并能够使用标准的协议从镜像仓库下载并安装到系统中。将软件以容器镜像的方式提供,可以让用户的操作系统和应用软件以不同的速度进行更新。传统的 RPM/yum/DNF 方式分发软件包会将应用程序版本限定在主机操作系统的生命周期中。

这种将基础设置作为容器来分发的一个问题是,有的时候应用程序需要在容器运行时守护进程启动前执行。让我们来看一个使用 Docker 守护进程的 Kubernetes 例子:Kubernetes 在启动前需要先完成网络设置,这样它才能够将 pods 分配到独立的网络环境中。该场景下目前我们使用的默认守护进程是flanneld,它必须在 Docker 守护进程启动之前运行,以设置 Docker 守护进程的网络接口。同时,flanneld 使用etcd作为数据存储。该守护进程需要在 flanneld 之前启动。

如果我们将 etcd 和 flanneld 通过镜像分发,就会遇到先有鸡还是先有蛋的问题。在启动容器化应用程序之前需要一个容器运行时守护进程,但是这些应用程序又需要在容器运行时守护进程之前启动。为了解决这个问题,我找到了一些 hack 的方式,但是没有一种方式是彻底的。同时,Docker 守护进程目前还没有一个像样的方式来设置容器启动的优先级。我看见过这方面的建议,但是目前的实现都是使用类似于老的 SysVInit 方式来启动服务(当然我也知道这带来的复杂性。)

systemd

使用 systemd 来替代 SysVInit 的一个原因是用来处理启动服务的优先级和顺序,为什么容器不能利用这项技术呢?在 Atomic 项目中,我们决定在主机上运行容器的时候不再需要容器运行时守护进程,尤其是那些启动早期需要的服务。因此我们增强了 atomic 命令行接口,允许用户安装容器镜像。当用户执行atomic install --system etcd时,atomic 会使用 skopeo 从镜像仓库拉取 etcd 的 OCI 镜像,然后将镜像释放到 OSTree 存储中。因为我们在生产环境中运行 etcd,因此该镜像是只读的。下一步atomic命令从容器镜像中抓取 systemd 的 unit 文件模板,并在硬盘上创建 unit 文件用以启动镜像。该 unit 文件通常使用runc在主机上启动容器(虽然 runc 不是必须的)。

如果运行atomic install --system flanneld命令会发生类似的事情,除了这次生成 flanneld 的 unit 文件将会指定 etcd 需要在它启动前运行。

当系统启动时,systemd 确保 etcd 会在 flanneld 之前运行,并且容器运行时会在 flanneld 启动之后再运行。该特性可以让用户将 Docker 守护进程和 Kubernetes 放到系统容器中。这意味着用户可以启动一个 atomic host 或者传统基于 rpm 的操作系统,其中的整个容器编排框架以容器的方式运行。这项功能非常强大,因为我们知道客户希望能够独立于这些组件,持续给他们的容器主机打补丁。此外,它能够将主机操作系统占用空间最小化。

当然,关于将传统应用程序放到容器中使其既能够作为独立 / 系统容器运行,又能够成为可编排容器仍然有所争论。考虑一个 Apache 容器,我们可以使用atomic install --system httpd命令安装。这个容器镜像可以和基于 rpm 的 httpd 服务一样的启动(systemctl start httpd,除了 httpd 进程将会启动在容器中)。存储仍然可以使用本地的,意味着可以将主机的 /var/www 目录挂载到容器中,这个容器也监听这本地网络的 80 端口。这表明我们可以在主机的容器中运行传统工作负载,而无需一个容器运行时守护进程。

构建容器镜像

在我看来,最近 4 年来容器创新中最令人悲伤的事情,就是构建容器镜像构建机制缺乏创新。一个容器镜像是一个由镜像内容的压缩包和一些 JSON 文件组成的压缩包。容器的基础镜像是一个完成的根文件系统和一些描述用的 JSON 文件。然后用户可以在上面增加层,每个增加的层会形成一个压缩包和记录变化的 JSON 文件。这些层和基础镜像一起打包组合成了容器镜像。

基本上所有人都通过docker build和 Dockerfile 格式文件来构建镜像。上游 docker 在几年前就已经停止接受修改和增强 Dockerfile 格式和构建方式的 pull request。在容器进化过程中,Dockerfile 扮演了重要的角色。开发和运维可以简单直接的方式构建镜像。然而在我看来,Dockerfile 只是一个简化的 bash 脚本,到目前为止还有好多问题没有解决。例如:

  • 为了构建容器镜像,Dockerfile 必须依赖 Docker 守护进程。
    • 目前为止还没有人创建出标准的工具,可以独立于 Docker 命令来创建 OCI 格式镜像。
    • 诸如ansible-containers和 OpenShift S2I(Source2Image)等工具,仍然在底层使用了 Docker 引擎。
  • Dockerfile 中的每一行都会创建一个新的镜像,这能够提升开发阶段构建镜像的效率,因为工具可以知道 Dockerfile 中的每一行是否有修改,如果没有修改,之前构建的镜像可以被复用而无需重新构建。但是这会导致产生大量的层。
    • 因为这个问题许多人都要求提供一个合并层的方式以限制层的数量。最终上游 docker 已经接受了部分建议满足了该需求。
  • 为了能够从安全的站点拉取镜像内容,通常用户需要一些安全措施。例如用户需要有 RHEL 认证和订阅授权才能够在镜像中增加 RHEL 内容。
    • 这些密钥最终会保存在镜像的层中,开发者需要在这些层中移除它们。
    • 为了能够在构建镜像的时候能够挂载卷,我们在自己分发的 atomic 项目和 docker 包中增加了-v参数,但是上游 docker 没有接受这些补丁。
  • 构建出来的组件最终被放置在容器镜像中。因此,虽然 Dockerfile 对于刚开始构建的开发者,可以更好的了解整个构建过程,但是这对于大规模企业环境来说不是一个高效的方式。另外,在使用自动化容器平台之后,用户不会关注构建 OCI 标准镜像的方式是否高效。

出发吧 buildah

在 2017 年 DevConf.cz 上,我让我们团队的Nalin Dahyabhai看看称之为 containers-coreutils 的构建工具,本质上这是一个使用了 containers/storage 库和 containers/image 库的一系列命令行工具,它们可以模仿 Dockerfile 的语法。Nalin 将其命令为buildah,用来取笑我的波士顿口音。使用一些 buildah 原语,我们就可以构建一个容器镜像:

  • 关于安全性的一个主要思想是确保操作系统的镜像尽可能小,以限制不需要的工具。该想法的来源是黑客需要依赖工具来攻破应用程序,如果注入 gcc、make、dnf 等工具不存在,攻击可能会停止或者受到限制。
  • 另外,由于这些镜像的拉取和推送都需要经过互联网,缩小镜像尺寸总是没错的。
  • Docker 构建镜像的主要方式是通过命令将软件安装或者编译到容器的buildroot中。
  • 执行run命令需要这些可执行程序包含在容器镜像中。例如在镜像中使用dnf命令需要安装整个 Python 栈,即使最终的应用程序不会使用 Python。
  • ctr=$(buildah from fedora):
    • 使用 containers/image 库从镜像仓库拉取 Fedora 镜像
    • 返回一个容器 ID(ctr)
  • mnt=$(buildah mount $ctr):
    • 挂载刚创建的容器镜像($ctr)。
    • 返回挂载点路径。
    • 现在可以使用这个挂载点写入内容。
  • dnf install httpd –installroot=$mnt:
    • 我们可以使用主机系统上的命令将内容写入到容器中,这意味着我们可以把密钥放在主机上,而不用放入到容器中,另外构建工具也可以保存在主机上。
    • dnf 等命令也不用事先安装到容器中,Python 等依赖也是,除非应用程序依赖。
  • cp foobar $mnt/dir:
    • 我们可以使用 bash 支持的任何命令来填充容器。
  • buildah commit $ctr:
    • 我们可以在需要的时候创建层。层的创建由用户控制而不是工具。
  • buildah config --env container=oci --entrypoint /usr/bin/httpd $ctr:
    • 在 Dockerfile 中支持的命令也可以指定。
  • buildah run $ctr dnf -y install httpd:
    • buildah 同样支持run命令,它不依赖容器运行时守护进程,而是通过执行runc直接在锁定的容器中执行命令。
  • buildah build-using-dockerfile -f Dockerfile .:
    • buildah 同样支持使用 Dockerfile 来构建镜像。

我们希望将类似ansible-containers和 OpenShift S2I 这样的工具改成buildah,而非依赖一个容器运行时守护进程。

构建容器镜像和运行容器采用相同的运行时环境,对于生产环境还会遇到一个大问题,既针对容器安全需要同时满足二者的权限需求。通常构建容器镜像的时候需要远多于运行容器所需要的权限。例如,默认情况下我们会允许mknod能力。mknod能力允许进程能够创建设备节点,但是在生产环境几乎没有应用程序需要这个能力。在生产环境中移除mknod能力能够让系统变得更加安全。

另一个例子是我们会默认给容器镜像以读写权限,因为安装进程需要将软件包安装到/usr目录中。但是在生产环境中,我个人建议应该将所有容器运行在只读模式。容器中的进程应该只能允许写入tmpfs或者挂载到容器内的卷上。通过将构建镜像和运行容器分离,我们可以修改这些默认设置,让运行环境更加安全。

Kubernetes 的运行时抽象:CRI-O

Kubernetes 增加了一套用于插拔任何 pods 运行时的 API,称为容器运行时接口(Container Runtime Interface,CRI)。我不愿意在我的系统上运行太多的守护进程,但是我们基于此增加了一种。由我们团队中Mrunal Patel领导的小组,在 2016 年晚些时候开始实现CRI-O守护进程。这是一个用于运行基于 OCI 应用程序的容器运行时接口守护进程。理论上,未来我们可以将 CRI-O 代码直接编译如 kubelet,以减少一个守护进程。

和其他容器运行时不同的是,CRI-O 的唯一目的是满足 Kubernetes 的需求。回忆一下前面提到的关于 Kubernetes 运行一个容器所需要的步骤。

Kubernetes 向 kubelet 发送一个消息,通知它需要启动一个 nginx 服务:

  • kubelet 调用 CRI-O 告知其运行 nginx。
  • CRI-O 响应 CRI 请求。
  • CRI-O 在镜像仓库中找到对应的 OCI 镜像。
  • CRI-O 使用 containers/image 将镜像从仓库拉取到本地。
  • CRI-O 使用 containers/storage 将镜像解压到本地存储中。
  • CRI-O 使用 OCI 运行时规范启动容器,通常使用runc。我前面提到过,这和 Docker
    守护进程使用runc来启动容器的方式相同。
  • 如果需要,kubelet 也可以使用其他的运行时来启动容器,例如Clear Containers的runv。

CRI-O 旨在成为运行 Kubernetes 的稳定平台,我们只会在通过了所有 Kubernetes 的测试集之后才会发布新的版本。所有提交到 CRI-O 上的 pull request 都需要运行整个 Kubernetes 测试集。开发者不能提交一个无法通过这些测试的 pull request。CRI-O 是一个完全开放的项目,目前已经有来自 Intel、SUSE、IBM、Google、Hyper.sh 等公司的贡献者。只要 CRI-O 的大多数维护者同意,pull request 就会被接受,即使这些补丁功能不是红帽公司需要的。

总结

我希望本文的深入探讨能够帮助读者了解 Linux 容器的演化史。Linux 一度处于每个厂商有自己标准的情形。Docker 公司专注于建立镜像构建的事实标准,并且简化了容器的相关工具。Open Container Initiative 的建立意味着行业正在围绕着核心镜像格式和运行时制定规范,围绕着让工具更有效率、更安全、高可扩展性和可用性进行创新。容器允许我们能够用新颖的方式验证软件安装,无论其是运行在主机上的传统软件还是运行在云端经过编排的微服务。在很多方面,这还仅仅是个开始。

英文原文:https://opensource.com/article/17/7/how-linux-containers-evolved

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值