Docker组成原理

本文是阅读《深入浅出Docker》的相关学习笔记

起初简单的以为Docker和容器是一种东西,后来才发现Docker是实现了Linux容器技术(LXC,可以提供轻量级的虚拟化)的一种实现

LXC技术使用Linux 内核和内核功能(Cgroups 和Namespaces)来分隔进程,以便各进程相互独立运行

于是好像有了一个定义:容器是一个相互隔离的和资源受限制的进程

当启动Docker时,会通过Namespaces和Cgropus来创建一个资源隔离的环境,然后将打包的应用程序和关联的文件复制到 Namespace 内的文件系统中

Docker引擎

Docker 引擎是用来运行和管理容器的核心软件

基于OCI标准,采用的模块化结构

主要结构:客户端、守护进程(docker daemon)、containerd和runc

他们共同负责容器的创建和运行

早期的Docker引擎结构由Docker daemon和LXC组成

Docker daemon负责创建容器和运行容器,功能都耦合在一块了

而LXC技术,使用其提供的Namespace和Cgroup来启动容器

为了摆脱LXC的依赖,解决跨平台和核心功能依赖外部工具的问题

Docker公司开发了名为Libcontainer的自研工具,用于替代LXC。Libcontainer的目标是成为与平台无关的工具,可基于不同内核为Docker上层提供必要的容器交互功能

后续Docker公司对Docker daemon进行了重构,将容器执行、容器运行是等功能做成小组件的方式

伴随着重构的进行,Docker公司也在推进着OCI标准(容器开放标准)即规范容器运行时标准(runtime-spec)和容器镜像标准(image-spec)

OCI容器标准

  1. OCI规范了什么?

    规范了容器镜像标准和容器运行时标准,两者通过一个文件系统包(filesystem bundle)来连接

  2. filesystem bundle有什么作用?

    将镜像解包成bundle,bundle包含了一个config.json配置文件和一个根文件夹,Docker引擎会识别这个bundle来运行容器

    config.json的结构包含公共部分和平台适配部分

    公共部分包含了版本信息、rootfs等等,平台部分则是对系统底层的适配,比如Linux的配置是cgroup和namespaces,Windows则是hypervisor

    可以看到Docker是怎么实现跨平台的

  3. 镜像规范规范了什么?

    规定了镜像的构建系统需要输出的内容和格式,并且可以被解包成bundle

    主要包含了以下几种

    1. 文件系统:一个镜像由多个层叠加组成,文件系统负责维护这些层的变更集
    2. manifest:记录层和配置文件的定位信息
    3. 配置文件:记录镜像运行时相关的参数和根目录的变更
    4. image index:记录不同平台的manifests配置

    下边是实例

    关于镜像的组成下文会继续探索

  4. 容器规范规范了什么?什么是容器运行时(runtime)?

    容器规范标准了容器的配置、运行环境和生命周期

    配置文件就是通过解包镜像获得的config.json,并通过这个文件获取支持的平台和创建容器需要的字段

    容器运行时负责运行容器的所有部分,但是实际上并不止运行程序本身;容器运行时负责:通过读取bundle里的配置文件,来准备容器所需要得环境和资源,然后创建容器并且管理他的生命周期

    容器运行时有多种实现,但大多都包含lower-levelhigher-level,需要注意的是,lower-level和higher-level得职责不同,解决不同的问题

    lower-level需要提供容器所需要得资源(如namesapece和cgroup)

    higher-level负责传输和管理镜像,解包镜像并将命令传递给low-level,hight-level还对外提供Api

    runc和containerd就是从docker中分离出来的low-level和hight-level

容器运行时
官方词汇解释
镜像规范
运行时规范

镜像

镜像只会包含必要的应用和服务以及所需要的依赖,以保持较小的体积

镜像由多个层组成,每层叠加之后,从外部看来就如一个独立的对象

当拉取镜像的时候,可以看到这和镜像需要哪些层,结构如下图

也可以使用docker image inspect 镜像 -f "{{.RootFS}}" 命令来查看镜像里的分层,可以看到分层都是使用了sha256来散列过的

所有的Docker镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之上,创建新的镜像层

当添加一个补丁,会继续增加新的分层,因为分层式只读的,不过上层的修改会覆盖下层的

镜像的层会共享,当拉取镜像的时候,Docker可以识别出要拉取的镜像中,哪几层已经在本地存在

启动流程

可以看出Docker将创建和管理容器的逻辑都从Docker daemon中移除,意味着容器运行时与Docker daemon是解耦的,有时称之为“无守护进程的容器(daemonlesscontainer)”,如此,对Docker daemon的维护和升级工作不会影响到运行中的容器

Docker daemon收到请求后会将命令传递给Containerd,Containerd会把镜像解包,将bundle传递给runc,注意到runc之上还有一个shim

shim的作用是允许runc创建完容器后退出,避免每一个容器都创建一个runc,此时会创建一个shim进程,再由shim来调用runc来创建容器、

当runc创建完容器退出后,shim就会成为容器进程的父进程


参考

https://www.qikqiak.com/post/containerd-usage/

https://www.cnblogs.com/michael9/p/13039700.html

https://iximiuz.com/en/posts/container-learning-path/

https://www.modb.pro/db/145438

https://www.waynerv.com/posts/container-fundamentals-learn-container-with-oci-spec/
https://iximiuz.com/en/posts/container-learning-path/

posted @ 2022-03-24 16:52  阿弱  阅读(113)  评论(0编辑  收藏  举报