一 前传
2002年 namespace技术发布,并在2.4.19linux内核中被引入,提供了7种资源的隔离,着重体现隔离
• Mount: 隔离文件系统挂载点
• UTS: 隔离主机名和域名信息
• IPC: 隔离进程间通信
• PID: 隔离进程的 ID
• Network: 隔离网络资源
• User: 隔离用户和用户组的 ID
2007年 谷歌发布了CGroup 着重体现限制,限制cpu、内存、磁盘、网络等等一系列资源。CGroups在2007年集成进了Linux内核
2008年 lxc (Linux Container)发布,这是一种内核虚拟化技术,可以提供轻量级的虚拟化,以便隔离进程和资源。lxc依赖于linux的cgroup和namespace github.com/lxc/lxc
二 docker的时代
2010年 dotCloud公司成立,主要做pass云计算服务,dotCloud 平台利用了 Linux 容器技术(lxc),他们将自己的的技术命名为docker.
2013年 docker开源 并且他们也将公司正式改名为Docker, docker每个月就会更新一个版本. 2014年4月docker1.0发布.
2014年6月 DockerCon 2014大会开幕,Kubernetes等一系列容器工具在大会中崭露头角,cAdvisor 是 Google 用来分析运行中容器的资源。docker发布libcontainer(runc前生)github.com/docker-arch…
2014年12月 DockerSwarm发布,并在Docker 1.12.0(2016年8月)成为内置功能
2014年12月 CoreOS宣布终止与docker合作,发布并开始支持rkt(最初作为Rocket发布)作为Docker的替代品,CoreOS 是一个基础设施领域创业公司,由于看准了docker可以集成到自己的产品,成为了docker早期的代码提供者,而由于理念不同,最终与docker决裂。 github.com/rkt/rkt
2015年6月 Docker发布OCI(Open Container Initiative)规范,旨在制定并维护容器镜像格式和容器运行时的正式规范(OCI Specifications)。同时docker将Libcontainer捐出,封装为符合OCI规范的为runC项目。runC本质上是启动容器的一些命令行工具 github.com/opencontain…
但是容器本身没有“价值”,有价值的是容器编排,容器最终只能成为开发者手中的小工具,编排到生产环境才是终极目标,这也是docker作为paas型公司的目标。
三 编排的战争
2015年7月 Google联合 Linux 基金会成立 CNCF (Cloud Native Computing Foundation)云原生计算基金会,Kubernetes作为CNCF的第一个项目 github.com/cncf
2015年8月 kubernetes1.0正式发布
2016年7月 K8S在1.3版本宣布支持Rkt容器,但是这对于K8S的维护人员来说,这是一项巨大的挑战,同一套功能需要面对Rkt和Docker做两套实现,这也促使了CRI的产生。
2016年9月 Google和红帽主导了CRI(Container Runtime Interface)标准 github.com/kubernetes/…
// Runtime service defines the public APIs for remote container runtimes
service RuntimeService {
rpc Version(VersionRequest) returns (VersionResponse) {}
rpc RunPodSandbox(RunPodSandboxRequest) returns (RunPodSandboxResponse) {}
rpc StopPodSandbox(StopPodSandboxRequest) returns (StopPodSandboxResponse) {}
rpc RemovePodSandbox(RemovePodSandboxRequest) returns (RemovePodSandboxResponse) {}
rpc PodSandboxStatus(PodSandboxStatusRequest) returns (PodSandboxStatusResponse) {}
rpc ListPodSandbox(ListPodSandboxRequest) returns (ListPodSandboxResponse) {}
rpc CreateContainer(CreateContainerRequest) returns (CreateContainerResponse) {}
rpc StartContainer(StartContainerRequest) returns (StartContainerResponse) {}
rpc StopContainer(StopContainerRequest) returns (StopContainerResponse) {}
rpc RemoveContainer(RemoveContainerRequest) returns (RemoveContainerResponse) {}
rpc ListContainers(ListContainersRequest) returns (ListContainersResponse) {}
rpc ContainerStatus(ContainerStatusRequest) returns (ContainerStatusResponse) {}
rpc UpdateContainerResources(UpdateContainerResourcesRequest) returns (UpdateContainerResourcesResponse) {}
rpc ExecSync(ExecSyncRequest) returns (ExecSyncResponse) {}
rpc Exec(ExecRequest) returns (ExecResponse) {}
rpc Attach(AttachRequest) returns (AttachResponse) {}
rpc PortForward(PortForwardRequest) returns (PortForwardResponse) {}
rpc ContainerStats(ContainerStatsRequest) returns (ContainerStatsResponse) {}
rpc ListContainerStats(ListContainerStatsRequest) returns (ListContainerStatsResponse) {}
rpc UpdateRuntimeConfig(UpdateRuntimeConfigRequest) returns (UpdateRuntimeConfigResponse) {}
rpc Status(StatusRequest) returns (StatusResponse) {}
}
service ImageService {
rpc ListImages(ListImagesRequest) returns (ListImagesResponse) {}
rpc ImageStatus(ImageStatusRequest) returns (ImageStatusResponse) {}
rpc PullImage(PullImageRequest) returns (PullImageResponse) {}
rpc RemoveImage(RemoveImageRequest) returns (RemoveImageResponse) {}
rpc ImageFsInfo(ImageFsInfoRequest) returns (ImageFsInfoResponse) {}
}
这里列举了CRI的一些代码,CRI定义了操作Pod、容器、镜像的一系列接口,具体实现需要容器厂商进行实现,作为容器运行时接口,我倒是认为,这是专门为K8S设计的规范,我们真的需要pod这个概念嘛?在我看来容器关注点应该在自身,而不应该为Pod负责,Pod作为上层K8s的概念,为什么要由下层实现。
2016年12月 k8S在1.5版本引入CRI,虽然提出了CRI,但是docker作为先出的产品,必然是不支持的,所以K8S提供了符合OCI规范的docker-shim,k8s通过OCI调用docker-shim,再由docker-shim调用docker
调用链 K8s → docker-shim → docker → containerd → runC
2017年3月 Google、RedHat、Intel、SUSE、IBM联合发起的项目CRI-O诞生,如同官网所说OCI-based implementation of Kubernetes Container Runtime Interface,它完全遵循OCI,CIR规范,至此docker再也不是K8S的必要模块,因为他有了替代品,并且执行链如下:K8s → CRI-O → runC github.com/cri-o/cri-o
2017年 Docker拆分了containerd,捐献给了CNCF,为了将containerd接入到CRI标准中,k8s又搞出了cri-containerd项目,cri-containerd是一个守护进程用来实现kubelet和containerd之间的交互 github.com/containerd
调用链 K8s → cri-containerd → containerd → runC
2018年3月 K8S成为有史以来CNCF的第一个毕业项目
2018年4月 在CNCF管理下的containerd发布V1.1.0,正式兼容CRI规范,并且把CRIPugin内置到了containerd
调用链 K8s → containerd1.1 → runC
四 k8s的胜利
2019年2月 托管在CNCF的containerd正式毕业,一些云容器的厂商也逐渐把Docker替换为containerd
2019年8月 托管在CNCF的rkt被宣布归档
2020年9月 Docker宣布放弃更新Swarm
2020年12月 K8S宣布停止维护dockershim, 在v1.20之后,Kubernetes将弃用Docker作为容器运行时,并移除内置dockershim。
2022年1月 docker接手dockershim的维护工作,并命名为cri-dockerd github.com/Mirantis/cr…
2022年5月3日 Kubernetes 1.24移除docker-shim的支持,如果需要适配docker,则需要安装cri-docker,至此k8s彻底摆脱对docker的强依赖,成为了真正的容器管理者,从面向docker编程,转为了面向CRI编程。如果容器厂商想要在容器领域分一杯羹,也必然绕不开CRI规范。
五 总结
此时相信不少读者还是对此十分迷惑,什么是CRI,OCI。containerd和runc区别在哪。
我们先来捋一下docker的调用链
runc是最底层,但是不要被其他博客所误导,正如官网所说
Please note that runc is a low level tool not designed with an end user in mind. It is mostly employed by other higher level container software.
runc仅仅是基于namespace和cgroup的低级命令行工具,和cd pwd这些命令一样,runc创建完容器就会退出,需要有高级的容器对镜像、容器、版本进行管理,containerd作为高级的容器,管理所有容器的增删改查和镜像的增删改查,但是仅仅作为管理员。而docker,虽然我们习惯性的称他为容器,但是我更愿意称之为容器引擎,把containerd管理的信息整理并输出给用户。
如果希望更加详细的了解这一历史,推荐大家看下这部纪录片 Kubernetes: The Documentary www.bilibili.com/video/BV13q…