Docker 容器运行时runtime
容器核心技术
容器核心技术是指能够让 container 在 host 上运行起来的那些技术。
这些技术包括容器规范、容器 runtime、容器管理工具、容器定义工具、Registry 以及 容器 OS,下面分别介绍。
容器规范
容器不光是 Docker,还有其他容器,比如 CoreOS 的 rkt。为了保证容器生态的健康发展,保证不同容器之间能够兼容,包含 Docker、CoreOS、Google在内的若干公司共同成立了一个叫 Open Container Initiative(OCI) 的组织,其目是制定开放的容器规范。
目前 OCI 发布了两个规范:runtime spec 和 image format spec。
有了这两个规范,不同组织和厂商开发的容器能够在不同的 runtime 上运行。这样就保证了容器的可移植性和互操作性。
这个定义是我下的,但它并不是我的个人意志,是基于 OCI 规范这一共识写出来的。这个规范规定了容器之中应用被放到什么样的环境下、如何运行,比如说容器的根文件系统上哪个可执行文件会被执行,是用什么用户执行,需要什么样的 CPU,有什么样的内存资源、外置存储,还有什么样的共享需求等等。
容器 runtime
runtime 是容器真正运行的地方。runtime 需要跟操作系统 kernel 紧密协作,为容器提供运行环境。
如果大家用过 Java,可以这样来理解 runtime 与容器的关系:Java 程序就好比是容器,JVM 则好比是 runtime。JVM 为 Java 程序提供运行环境。同样的道理,容器只有在 runtime 中才能运行。
lxc、runc 和 rkt 是目前主流的三种容器 runtime。
lxc 是 Linux 上老牌的容器 runtime。Docker 最初也是用 lxc 作为 runtime。
runc 是 Docker 自己开发的容器 runtime,符合 oci 规范,也是现在 Docker 的默认 runtime。
rkt 是 CoreOS 开发的容器 runtime,符合 oci 规范,因而能够运行 Docker 的容器。
为什么dockershim被弃用?
维护dockershim已成为Kubernetes维护人员的沉重负担。创建CRI标准就是为了减轻这种负担,并允许不同容器运行时的顺畅互操作性。Docker本身目前没有实现CRI,这就是问题所在。
Dockershim一直是一种临时解决方案(因此得名:shim,垫片)。你可以在Dockershim移除Kubernetes增强建议中阅读更多关于社区讨论和规划的内容。
https://github.com/kubernetes/enhancements/tree/master/keps/sig-node/1985-remove-dockershim
此外,与dockershim不兼容的特性,如cgroups v2和用户命名空间,正在这些新的CRI运行时中实现。取消对dockershim的支持将允许这些领域的进一步发展。
Kubelet会去调用本地的dockershim.sock Unix socket文件
[root@k8s-node1 ~]# find / -name dockershim.sock
/run/dockershim.sock
这个文件并没有叫做dockershim二进制文件,这个dockershim.sock 由kubelet自己创建并且监听的,当有创建容器任务的时候kubelet会去调用这个文件使用dockerd,后续和docker一样
玩过K8S的都知道,K8S默认(如kubeadm安装的集群)是用docker作为容器引擎,kubelet与docker的集成架构是:
kubelet---》dockershim.sock(kubelet创建并监听的)---》dockerd---》contained---》containerd-shim---》runc(执行完就退出)---》容器应用进程(父进程是containerd-shim)
在CRI诞生之前,架构没这么复杂,当时kubelet是能直接调用docker daemon的(kubele---》docker),但后来出现了 rkt容器运行时,K8S又主动对接rkt,随着容器运行时逐渐发展,K8S开发人员意识到容器运行时会越来越多,如果为每个都做开发对接,工作量太大、难以维护。
于是诞生了CRI。在CRI诞生之后,按理来说kubelet就可以废掉docker只保留CRI,但是当时还没有支持CRI的容器运行时(于是诞生了cri-o,但在诞生之前还得先兼容docker),所以为了继续兼容 docker,kubelet自身实现了CRI客户端和CRI服务端,dockershim就是CRI服务端的接口(shim翻译为垫片,其实就是做了一层封装),这是kubelet自身实现的一个unix socket,路径是/var/run/dockershim.sock(由 kubele监听)。工作过程就是kubelet通过CRi访问dockershim.sock,dockershim就会将CRI请求转换为docker daemon能识别的APl请求。
K8s对接docker,rkt容器运行时,因为docker和rkt版本更新要不断对接。就这样工作量太大了。也就是k8s被docker和rkt给绑架了,牵着鼻子走
于是抽象一层解耦,也就是k8s出了一个规范叫CRI,把调用容器调用镜像的方法就行规范化,这样规范化之后就可以让大家docker,rkt来适配他。而不是k8s去适配rkt,docker。这样k8s就成为了主导者,这样容器运行时就变的简单了
K8s弃用的不是docker,弃用的是docker-shim,也就是k8s要将kubelet源码当中docker-shim源码删除掉