我们一直在和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守护进程会进入不可预知的状态。
为了解决这个问题,我们开始将上游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守护进程用来运行容器的工具相同)。
这意味着我们可以使用这些工具和库实现使用容器所需要的能力,而无需依赖一个大型容器守护进程。