公众号关注 「奇妙的 Linux 世界」

设为「星标」,每天带你玩转 Linux !

什么是RunC

Docker、Google、CoreOS 和其他供应商创建了开放容器计划 (OCI),目前主要有两个标准文档:容器运行时标准 (runtime spec)和 容器镜像标准(image spec)。


OCI 对容器 runtime 的标准主要是指定容器的运行状态,和 runtime 需要提供的命令。下图可以是容器状态转换图:

  • init 状态:这个是我自己添加的状态,并不在标准中,表示没有容器存在的初始状态

  • creating:使用 create 命令创建容器,这个过程称为创建中

  • created:容器创建出来,但是还没有运行,表示镜像和配置没有错误,容器能够运行在当前平台

  • running:容器的运行状态,里面的进程处于 up 状态,正在执行用户设定的任务

  • stopped:容器运行完成,或者运行出错,或者 stop 命令之后,容器处于暂停状态。这个状态,容器还有很多信息保存在平台中,并没有完全被删除

Runc的来历

RunC 是从 Docker 的 libcontainer 中迁移而来的,实现了容器启停、资源隔离等功能。Docker将RunC捐赠给 OCI 作为OCI 容器运行时标准的参考实现。Docker 默认提供了 docker-runc 实现。事实上,通过 containerd 的封装,可以在 Docker Daemon 启动的时候指定 RunC的实现。最初,人们对 Docker 对 OCI 的贡献感到困惑。他们贡献的是一种“运行”容器的标准方式,仅此而已。它们不包括镜像格式或注册表推/拉格式。当你运行一个 Docker 容器时,这些是 Docker 实际经历的步骤:

  • 下载镜像

  • 将镜像文件解开为bundle文件,将一个文件系统拆分成多层

  • 从bundle文件运行容器

Docker标准化的仅仅是第三步。在此之前,每个人都认为容器运行时支持Docker支持的所有功能。最终,Docker方面澄清:原始OCI规范指出,只有“运行容器”的部分组成了runtime。这种“概念失联”一直持续到今天,并使“容器运行时”成为一个令人困惑的话题。希望我能证明双方都不是完全错误的,并且在本文中将广泛使用该术语。RunC 就可以按照这个 OCI 文档来创建一个符合规范的容器,既然是标准肯定就有其他 OCI 实现,比如 Kata、gVisor 这些容器运行时都是符合 OCI 标准的。

怎么使用 runc

create the bundle
$ mkdir -p /mycontainer/rootfs# [ab]use Docker to copy a root fs into the bundle
$ docker export $(docker create busybox) | tar -C /mycontainer/rootfs -xvf -# create the specification, by default sh will be the entrypoint of the container
$ cd /mycontainer
$ runc spec# launch the container
$ sudo -i
$ cd /mycontainer
$ runc run mycontainerid# list containers
$ runc list# stop the container
$ runc kill mycontainerid# cleanup
$ runc delete mycontainerid

在命令行中使用 runc,我们可以根据需要启动任意数量的容器。但是,如果我们想自动化这个过程,我们需要一个容器管理器。为什么这样?想象一下,我们需要启动数十个容器来跟踪它们的状态。其中一些需要在失败时重新启动,需要在终止时释放资源,必须从注册表中提取图像,需要配置容器间网络等等。就需要有Low-Level和High-Level容器运行时,runc就是Low-Level实现的实现。

Low-Level和High-Level容器运行时

当人们想到容器运行时,可能会想到一系列示例;runc、lxc、lmctfy、Docker(容器)、rkt、cri-o。这些中的每一个都是为不同的情况而构建的,并实现了不同的功能。有些,如 containerd 和 cri-o,实际上使用 runc 来运行容器,在High-Level实现镜像管理和 API。与 runc 的Low-Level实现相比,可以将这些功能(包括镜像传输、镜像管理、镜像解包和 API)视为High-Level功能。考虑到这一点,您可以看到容器运行时空间相当复杂。每个运行时都涵盖了这个Low-Level到High-Level频谱的不同部分。这是一个非常主观的图表:因此,从实际出发,通常只专注于正在运行的容器的runtime通常称为“Low-Level容器运行时”,支持更多高级功能(如镜像管理和gRPC / Web API)的运行时通常称为“High-Level容器运行时”,“High-Level容器运行时”或通常仅称为“容器运行时”,我将它们称为“High-Level容器运行时”。值得注意的是,Low-Level容器运行时和High-Level容器运行时是解决不同问题的、从根本上不同的事物。

  • Low-Level容器运行时:容器是通过Linux nanespace和Cgroups实现的,Namespace能让你为每个容器提供虚拟化系统资源,像是文件系统和网络,Cgroups提供了限制每个容器所能使用的资源的如内存和CPU使用量的方法。在最低级别的运行时中,容器运行时负责为容器建立namespaces和cgroups,然后在其中运行命令,Low-Level容器运行时支持在容器中使用这些操作系统特性。目前来看低级容器运行时有:runc :我们最熟悉也是被广泛使用的容器运行时,代表实现Docker。runv:runV 是一个基于虚拟机管理程序(OCI)的运行时。它通过虚拟化 guest kernel,将容器和主机隔离开来,使得其边界更加清晰,这种方式很容易就能帮助加强主机和容器的安全性。代表实现是kata和Firecracker。runsc:runsc = runc + safety ,典型实现就是谷歌的gvisor,通过拦截应用程序的所有系统调用,提供安全隔离的轻量级容器运行时沙箱。截止目前,貌似并不没有生产环境使用案例。wasm : Wasm的沙箱机制带来的隔离性和安全性,都比Docker做的更好。但是wasm 容器处于草案阶段,距离生产环境尚有很长的一段路。

  • High-Level容器运行时:通常情况下,开发人员想要运行一个容器不仅仅需要Low-Level容器运行时提供的这些特性,同时也需要与镜像格式、镜像管理和共享镜像相关的API接口和特性,而这些特性一般由High-Level容器运行时提供。就日常使用来说,Low-Level容器运行时提供的这些特性可能满足不了日常所需,因为这个缘故,唯一会使用Low-Level容器运行时的人是那些实现High-Level容器运行时以及容器工具的开发人员。那些实现Low-Level容器运行时的开发者会说High-Level容器运行时比如containerd和cri-o不像真正的容器运行时,因为从他们的角度来看,他们将容器运行的实现外包给了runc。但是从用户的角度来看,它们只是提供容器功能的单个组件,可以被另一个的实现替换,因此从这个角度将其称为runtime仍然是有意义的。即使containerd和cri-o都使用runc,但是它们是截然不同的项目,支持的特性也是非常不同的。dockershim, containerd 和cri-o都是遵循CRI的容器运行时,我们称他们为高层级运行时(High-level Runtime)。

Kubernetes 只需支持 containerd 等high-level container runtime即可。由containerd 按照OCI 规范去对接不同的low-level container runtime,比如通用的runc,安全增强的gvisor,隔离性更好的runv。

containerd

与RunC_一样_,我们又可以在这里看到一个docker公司的开源产品containerd曾经是开源docker项目的一部分。尽管_containerd_是另一个自给自足的软件。

  • 一方面,它称自己为容器运行时,但是与运行时__RunC_不同_。不仅_containerd_和_runc_的职责不同,组织形式也不同。显然_runc_是只是一个命令行工具,_containerd_是一个长期居住守护进程。_runc_的实例不能超过底层容器进程。通常它在create调用时开始它的生命,然后只是在容器的 rootfs 中的指定文件去运行。

  • 另一方面,_containerd _可以管理超过数千个_runc_容器。它更像是一个服务器,它侦听传入请求以启动、停止或报告容器的状态。在引擎盖下_containerd_使用RunC。然而,_containerd_不仅仅是一个容器生命周期管理器。它还负责镜像管理(从注册中心拉取和推送镜像,在本地存储镜像等)、跨容器网络管理和其他一些功能。

image.png

containerd 是一个工业级标准的容器运行时,它强调简单性健壮性可移植性,containerd 可以负责干下面这些事情:

  • 管理容器的生命周期(从创建容器到销毁容器)

  • 拉取/推送容器镜像

  • 存储管理(管理镜像及容器数据的存储)

  • 调用 runc 运行容器(与 runc 等容器运行时交互)

  • 管理容器网络接口及网络

上图是 Containerd 整体的架构。由下往上,Containerd支持的操作系统和架构有 Linux、Windows 以及像 ARM 的一些平台。在这些底层的操作系统之上运行的就是底层容器运行时,其中有上文提到的runc、gVisor 等。在底层容器运行时之上的是Containerd 相关的组件,比如 Containerd 的 runtime、core、API、backend、store 还有metadata 等等。构筑在 Containerd 组件之上以及跟这些组件做交互的都是 Containerd 的 client,Kubernetes 跟 Containerd 通过 CRI 做交互时,本身也作为 Containerd 的一个 client。Containerd 本身有提供了一个 CRI,叫 ctr,不过这个命令行工具并不是很好用。

在这些组件之上就是真正的平台,Google Cloud、Docker、IBM、阿里云、微软云还有RANCHER等等都是,这些平台目前都已经支持 containerd, 并且有些已经作为自己的默认容器运行时了。

从 k8s 的角度看,选择 containerd作为运行时的组件,它调用链更短,组件更少,更稳定,占用节点资源更少。

Docker

Docker 于 2013 年发布,解决了开发人员在端到端运行容器时遇到的许多问题。这里是他包含的所有东西:

  • 容器镜像格式

  • 一种构建容器镜像的方法(Dockerfile/docker build);

  • 一种管理容器镜像(docker image、docker rm等);

  • 一种管理容器实例的方法(docker ps, docker rm 等);

  • 一种共享容器镜像的方法(docker push/pull);

  • 一种运行容器的方式(docker run);

当时,Docker 是一个单体系统。但是,这些功能中没有一个是真正相互依赖的。这些中的每一个都可以在可以一起使用的更小、更集中的工具中实现。每个工具都可以通过使用一种通用格式、一种容器标准来协同工作。从 Docker 1.11 之后,Docker Daemon 被分成了多个模块以适应 OCI 标准。拆分之后,结构分成了以下几个部分。
其中,containerd 独立负责容器运行时和生命周期(如创建、启动、停止、中止、信号处理、删除等),其他一些如镜像构建、卷管理、日志等由 Docker Daemon 的其他模块处理。

Docker 的模块块拥抱了开放标准,希望通过 OCI 的标准化,容器技术能够有很快的发展。

现在创建一个docker容器的时候,Docker Daemon 并不能直接帮我们创建了,而是请求 containerd 来创建一个容器。当containerd 收到请求后,也不会直接去操作容器,而是创建一个叫做 containerd-shim 的进程。让这个进程去操作容器,我们指定容器进程是需要一个父进程来做状态收集、维持 stdin 等 fd 打开等工作的,假如这个父进程就是 containerd,那如果 containerd 挂掉的话,整个宿主机上所有的容器都得退出了,而引入 containerd-shim 这个垫片就可以来规避这个问题了,就是提供的live-restore的功能。这里需要注意systemd的 MountFlags=slave。

然后创建容器需要做一些 namespaces 和 cgroups 的配置,以及挂载 root 文件系统等操作。runc 就可以按照这个 OCI 文档来创建一个符合规范的容器。

真正启动容器是通过 containerd-shim 去调用 runc 来启动容器的,runc 启动完容器后本身会直接退出,containerd-shim 则会成为容器进程的父进程, 负责收集容器进程的状态, 上报给 containerd, 并在容器中 pid 为 1 的进程退出后接管容器中的子进程进行清理, 确保不会出现僵尸进程。containerd,containerd-shim和容器进程(即容器主进程)三个进程,是有依赖关系的。可以参考《containerd,containerd-shim和runc的依存关系》[1],查看怎么保证live-restore的功能的。

reference

https://www.ianlewis.org/en/container-runtimes-part-1-introduction-container-r

https://iximiuz.com/en/posts/journey-from-containerization-to-orchestration-and-beyond/#container-management

https://github.com/moby/moby/issues/35873#issuecomment-386467562

[1]https://fankangbest.github.io/2017/11/24/containerd-containerd-shim%E5%92%8Crunc%E7%9A%84%E4%BE%9D%E5%AD%98%E5%85%B3%E7%B3%BB/

本文转载自:「运维开发故事」,原文:https://tinyurl.com/skbbpr4j,版权归原作者所有。欢迎投稿,投稿邮箱: editor@hi-linux.com。

你可能还喜欢

点击下方图片即可阅读

GitHub 星标 2.1 K,可能是最简单好用的纯文本流程图制作工具


点击上方图片,『美团|饿了么』外卖红包天天免费领

更多有趣的互联网新鲜事,关注「奇妙的互联网」视频号全了解!

一文搞懂 Docker、Containerd、RunC 间的联系和区别相关推荐

  1. 一文搞懂 Service Mesh 和 API Gateway 关系和区别

    公众号关注 「奇妙的 Linux 世界」 设为「星标」,每天带你玩转 Linux ! 关于Service Mesh和API Gateway之间的关系,这个问题过去两年间经常被问起,社区也有不少文章和资 ...

  2. 一文搞懂 Promise、Genarator、 Async 三者的区别和联系

    非985/211大学毕业,软件工程专业,前端,坐标:北京 工作三年多,第一家人数 30 多人的创业公司,1 年多. 第二家属于前端技术不错的公司,2 年多. 01 我是一个喜欢在技术领域"折 ...

  3. 服务器千兆网卡接百兆交换机不通_一文搞懂监控工程中百兆交换机和千兆交换机的区别在哪?...

    安防监控系统工程现在都是用的网络摄像机,那么就肯定会经常和网络设备--交换机打交道,很多人在做监控方案的时候犯难,多少台摄像机该选用百兆交换机还是千兆交换机呢?关于这个问题除了需要掌握理论知识还是结合 ...

  4. 一文搞懂VOS费率前缀、地区前缀的区别和使用

    登录VOS3000客户端 进入费率管理 "VOS费率前缀"和"地区前缀"的主要区别如下:  VOS费率前缀:VOS(Voice Over Service)费率前 ...

  5. 一文搞懂AWS EC2, IGW, RT, NAT, SG 基础篇下

    B站实操视频更新 跟着拉面学习AWS--EC2, IGW, RT, NAT, SG 简介 长文多图预警,看结论可以直接拖到"总结"部分 本文承接上一篇文章介绍以下 AWS 基础概念 ...

  6. So easy ! 两句口号轻松搞懂Docker

    作为云计算IT圈里人, 如果还不知道Docker,那就算out啦.不过,在搞懂Docker之前,得先来了解一下Docker的成名之路! 从dotCloud到Docker,一"开"鸣 ...

  7. 一文搞懂 Traefik2.1 的使用

    原文链接:一文搞懂 Traefik2.1 的使用 一文搞懂 Traefik2.1 的使用 核心概念 安装 ACME 中间件 灰度发布 流量复制 TCP 简单 TCP 服务 带 TLS 证书的 TCP ...

  8. 都2021年了,再不学ES6你就out了 —— 一文搞懂ES6

    JS干货分享 -- 一文搞懂ES6 导语:ES6是什么?用来做什么? 1. let 与 const 2. 解构赋值 3. 模板字符串 4. ES6 函数(升级后更爽) 5. Class类 6. Map ...

  9. 【显卡】一文搞懂显卡

    [显卡]一文搞懂显卡 文章目录 [显卡]一文搞懂显卡 1. 前言介绍 1.1 CPU和显卡的区别 1.1.1 作用不同 1.1.2 结构不同 1.1.3 应用场景不同 1.2 三个著名的显卡公司 2. ...

最新文章

  1. 操作SQLite数据库
  2. 怎样得到select所有option里的值
  3. PLSQL Developer中几个功能
  4. java.lang.NumberFormatException: multiple points错误问题
  5. VS2008+OpenCL环境配置
  6. POJ3420 Quad Tiling(模板+矩阵快速幂)
  7. pyqt5 获取Qlabel中的图片并对其进行处理(包括Qimage转换为Mat)
  8. 链表冒泡排序java_055-冒泡排序算法代码实现
  9. python元组读取到列表_python中读入二维csv格式的表格方法详解(以元组/列表形式表示)...
  10. python计算机视觉编程——sift特征提取和ransac减少错配
  11. Scrapy0.24.1_中文文档
  12. 影响我一生的两本书(02)_huadingjin_新浪博客
  13. Linux的10个彩蛋
  14. (邱维声)高等代数课程笔记:数域
  15. 粉丝测试题的软件,套路得粉丝的答题类H5推荐
  16. 我的世界服务器盖亚3缴械修改,我的世界盖亚魔典3mod教程怪物系统介绍
  17. html 颜色 excel,Excel~常用颜色对照表
  18. 物联网+阿里云+小程序开发的一些工具和方法总结
  19. echarts如何设置平均线
  20. 数据库软件Toad安装使用教程-详细教程

热门文章

  1. linux发送邮件指定发件人
  2. 东南大学计算机类学什么,东南大学王牌专业有哪些?最全解析来了!
  3. 使用AndroidStudio优雅的敲代码
  4. 中等职业学校计算机等级考试模拟题,2017中等职业学校计算机等级考试题库计算机基础题库...
  5. php微信公众平台API接口(thinkphp5)
  6. 创意影响:为什么色彩准确的视频编辑监视器很重要
  7. p2p的UDP打洞原理
  8. ESP32/ESP8266安装arduino开发板失败的方法(附合宙esp32用blinker固件连接小爱同学提示“要操作的设备好像出问题了等一会再试吧”的解决方法
  9. php plog使用,pLog安装在PHP5下无法进入管理界面问题的解决
  10. Wmic 使用中的一些问题