# 深入了解runc和OCI规范
# 背景
# runc
参考资料:
- 真正运行容器的工具:深入了解 runc 和 OCI 规范 (opens new window)
- k8s搭配containerd (opens new window)
- 深入理解 OCI 标准 (opens new window)
从用户命令到容器启动,流程可能如下:
对于Docker、Podman、CRI-O 和 Containerd有一个核心的工具是runc。runc 是一个命令行客户端,用于运行根据 Open Container Initiative (OCI) 格式打包的应用程序,并且是 Open Container Initiative 规范的兼容实现。
runc 符合OCI规范(runtime-spec),意味着它可以从一个"OCI 包"中运行一个容器,注意这些包并不是"容器镜像",它只是一个根文件系统和一个config.json 文件。而不是Podman 或 Docker 那样有"镜像"概念,所以不能只执行runc run nginx:latest这样来启动一个容器。runc的简单执行流程如下:
Container image -> Root filesystem and config.json -> runc
注意层、标签、容器注册表和存储库等功能,所有这些都不是 OCI 包甚至运行时规范的一部分。有一个单独的 OCI-spec(image-spec)定义镜像。
# OCI
# 产生背景
Docker拥有的功能:
- 镜像构建
- 镜像存储
- 镜像解压
- 容器运行
- 容器网络
随着Kubernetes成熟并成为容器编排的事实标准,Docker对于Kubernetes来说功能太多了,比如k8s不需要镜像构建和网络功能,它只需要镜像拉取。于是2015年,在 Linux 基金会的支持下有了 Open Container Initiative (OCI):
The Open Container Initiative (OCI) is a lightweight, open governance structure (project), formed under the auspices of the Linux Foundation, for the express purpose of creating open industry standards around container formats and runtime. The OCI was launched on June 22nd 2015 by Docker, CoreOS and other leaders in the container industry.
Docker将自己的容器格式和运行时runC捐给了OCI,OCI在此基础上制定了两个标准:
- 运行时标准 Runtime Specification (runtime-spec):介绍如何运行一个已经解压的"filesystem bundle"。
- 镜像标准 Image Specification (image-spec):介绍如何通过一个构建系统创建一个OCI镜像,这个OCI镜像是由以下几部分组成:
- Image Manifest:列明了镜像所需的文件压缩包,同时指明了每种压缩包使用的压缩算法、哈希值和文件大小,以及每个层之间的关系。
- Image Configuration:包含了程序运行所需的硬件架构、操作系统、系统环境变量、启动命令、启动参数、工作目录等。
- Filesystem Serialization:镜像是一个tar,里面还包含很多layer.tar,每个layer都包含一个json文件,用于表明当前层的配置和父层。
注意,每个layer本质上也是一个小镜像,它的 hash 值的唯一性,镜像存储设施在交互时,只需根据文件清单检查本地存储,相同的压缩包只需存储一份即可,大幅提高了镜像分发的效率。
# OCI意义
在 OCI 之前,容器生态发展百花齐放,Docker 一骑绝尘瞩目,但大小社区各自为政,开发人员兼容疲惫,用户使用痛苦。有了 OCI 镜像标准之后,不同平台在沿着各自方向优化镜像的存储和传输,同时也能够使用同一套标准下实现互通,用户因此得以在不同平台自由迁移。
借助 OCI Runtime 标准,客户端只需提供 rootfs 和 config.json 声明,便可借助不用的 OCI Runtime 实现,将应用跑到不同操作系统上,且达到不同的隔离效果。如只需达到 namespace 级别隔离,Linux 使用 runC,Windows 使用 runhcs,这也是传统容器的隔离级别,隔离资源但并不隔离内核。如果需要达到 VM 级的强隔离,可以使用 gVisor runsc 实现用户态内核隔离,也可以使用 kata-runtime 实现 hypervisor VM 级别隔离。
# containerd
# containerd产生
- 2016年12月,docker在容器生态中竞争失败,宣布将其containerd捐赠给CNCF。2017年3月containerd正式加入CNCF。
- 2017年10月,CRI-O的1.0发布,支持将runc和Kata容器作为容器运行时。CRI-O 能够让 Kubernetes 使用任意兼容 OCI 的运行时作为运行 Pod 的容器运行时,CRI-O 本身也是 CRI-runtime。操控 CRI-O 的常用命令行工具包括:
- crictl:用于排除故障和直接与 CRI-O 容器引擎一起工作(需安装 cri-tools,较常用)。
- runc:用于运行容器镜像(CRI-O 自带,几乎不用)。
- podman:用于在容器引擎之外管理 pod 和容器镜像(run, stop, start, ps, attach, exec 等,需安装 podman,经常用)。一些 Docker 功能被包含在其他工具中,而不是 CRI-O 中。例如,podman提供了与许多 docker 命令功能完全兼容的命令行,并将这些功能也扩展到管理 pod 上。一些 Docker 功能被包含在其他工具中,而不是 CRI-O 中。例如,podman提供了与许多 docker 命令功能完全兼容的命令行,并将这些功能也扩展到管理 pod 上。
- 2018年7月,发布Containerd1.1.2正式将CRI-containerd以插件形式cri-plugion集成到了Containerd中。理论上来说,从此刻开始Containerd就可以独立支持Kubelete CRI了。
# containerd作用
Containerd 可以在宿主机中管理完整的容器生命周期:容器镜像的传输和存储、容器的执行和管理、存储和网络等。下面是具体功能:
- 管理容器的生命周期(从创建容器到销毁容器)
- 拉取/推送容器镜像
- 存储管理(管理镜像及容器数据的存储)
- 调用 runC 运行容器(与 runC 等容器运行时交互)
- 管理容器网络接口及网络
# containerd常用命令
containerd主要使用ctr,常用的镜像相关命令如下:
# 查看命名空间
ctr namespace ls
# 查看默认命名空间有哪些镜像
ctr image ls
ctr -n default images ls
# 查看k8s命名空间有哪些镜像
ctr -n=k8s.io images ls
# 拉取镜像
ctr image pull docker.io/library/busybox:latest
# 给镜像打标签
ctr i tag docker.io/library/busybox:latest docker.io/library/busybox:v1
ctr i tag --force docker.io/library/busybox:latest docker.io/library/busybox:v1
# 删除镜像
ctr images rm imageID
# 导出镜像
ctr images export mysql.tar.gz docker.io/library/mysql:latest
# 注意上面的镜像和下面导出镜像是一样的
docker save -o 镜像全称
# 导入镜像
ctr images import mysql.tar.gz
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 运行容器
containerd中有两个概念:container和task,container是镜像实例化的一个虚拟环境,提供一个磁盘和模拟空间,task是将容器运行起来,初始化进程这样的形式。在docker里面,将task的概念弱化了,docker run就是container和task一块启动。
# 创建task,运行容器
ctr run -d docker.io/library/busybox:latest busybox-v1
# 显示创建的task
ctr task ls
# 进入容器
ctr task exec --exec-id 3118 -t busybox-v1 sh
# 停掉并删除task
ctr tasks kill --signal 9 busybox-v1
ctr task rm -f busybox-v1
# 查看容器
ctr c ls
# 删除容器
ctr c rm busybox-v1
ctr c delete busybox-v1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Docker对比
- ctr 并没有创建原始镜像的能力,也就是说无法进行 docker 的 build 和 commit 操作。docker build 基于 dockerfile 可以构建镜像,ctr 命令无法构建镜像。
- containerd 相比于docker , 多了 namespace 概念,每个 image 和 container 都会在各自的namespace下可见。
他们常见的区别如下:
# CRI-O
参考资料如下:
- Mapping from dockercli to crictl (opens new window)
- Debugging Kubernetes nodes with crictl (opens new window)
CRI-O (opens new window)是Kubernetes CRI (Container Runtime Interface),它能兼容OCI (Open Container Initiative)的运行时。它是一个轻量级替代Docker的容器运行时。这个规范让kubelet能够使用各种容器运行时,当每个节点上都有一个正常工作的容器运行时,kubelet通过该接口去调用相应容器运行时,并启动pod及容器。
下面是containerd,docker以及k8s的常用命令对比:
# Podman容器
# 出现背景
Podman主要是红帽主导的,同Docker相比,它有如下特点:
Podman | Docker |
---|---|
Podman 是无守护进程的 | Docker 有一个守护进程(containerd)。docker CLI 与守护进程交互以管理容器。 |
Podman 直接通过 runc 与 Linux 内核交互 | Docker daemon 拥有正在运行的容器的所有子进程 |
Podman 可以使用多个容器部署 pod。相同的 pod manifest 可以在 Kubernetes 中使用。此外,你可以将 Kubernetes pod 清单部署为 Podman pod。 | Docker 中没有 Pod 的概念 |
无需任何额外配置即可运行无根容器。你可以使用 root 或非特权用户运行容器。 | Docker 无根模式需要额外的配置。 |
# Podman常用命令
# 查看版本信息
podman version
# 查看环境信息
podman info
# 搜索镜像
podman search busybox
# 拉取镜像
podman pull docker.io/library/busybox
# 查看当前镜像列表
podman images
# 给镜像打标签
podman tag busybox busybox:v1
# 删除镜像
podman rmi -f busybox:v1
# 运行镜像
podman run -it busybox sh
podman run busybox /bin/echo "Hello,Welcome to xianchao"
podman run --name nginx-v1 --restart=always -itd -p 30090:80 nginx
# 查看正在运行的镜像
podman ps
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Compose
先看下docker compose的历史:
- 2014年,启动Docker Compose,它是一个基于YAML定义管理容器组的项目。这个 YAML 后来被赋予了正式的规范,即Compose规范 (opens new window)。该版是使用python编写。并使用Rest API与Docker守护进程通信。
- 2018年,Podman发布,起初它只专注于Docker cli的兼容性,不包括对API的支持,因此Podman无法与Docker Compose一起使用。很多人想使用Podman并保留现有的Compose YAML文件,因此一个Podman Compose的社区项目启动,该项目处理Compose规范,并转换为Podman CLI的命令。
- 2019年,Podman添加了一个与Docker兼容的API,并于2020年支持Docker Compose。
- 2021年,Docker Compose项目用Go完全重写,称为Docker Compose 2.0,命令行从docker-compose变为docker compose。
那么应该是选择哪个使用?
- Podman Compose 是更原生、更轻量级的解决方案。Podman Compose 直接执行 Podman 命令,而不是与 Podman 的 API 套接字通信。这消除了运行 Podman 服务来提供 API 的需要,节省了资源。因为它使用了 Podman 的常规命令行和 fork-exec 模型,所以更容易在系统上进行跟踪和管理。
- Docker Compose 具有更多功能。它与 Docker 和 Podman 兼容,因此更加通用。此外,它比 Podman Compose 拥有更多的用户,经过更广泛的测试,并且可能更稳定。
那么未来是啥样的呢?
- Podman 团队并不专注于 Compose YAML。相反,我们正在努力开发podman generate kube和podman play kube,它允许Kubernetes YAML 以类似于 Compose 的方式直接与 Podman 一起使用。有了这些工具,我们就有了与更广泛的 Kubernetes 生态系统集成的优势。例如,在 Podman 上运行的容器podman play kube可以很容易地移动到 OpenShift (Kubernetes) 集群上。
- Red Hat 通常推荐Kubernetes YAML而不是Compose,我们正在努力制定一个路线图,通过使用 podman-play/generate-kube 功能(图像构建、应用程序拆卸)创建和使用 Kubernetes YAML 来提供越来越多类似 Compose 的功能、intiContainers 以及对 Kubernetes 原语的扩展支持)。
← k8s基础入门2