1

禁止复制,转载请注明出处和作者
Containerd 模块从 docker 中分离出来后,功能变得更加丰富,依赖者变得更加广泛,不仅仅是docker在使用,可以看到在云原生应用中涉及到容器技术时基本都在依赖 containerd。

来源https://www.slideshare.net/Docker/leveraging-the-power-of-containerd-events-evan-hazlett
图片来源 https://www.slideshare.net/Docker/leveraging-the-power-of-containerd-events-evan-hazlett

一、Containerd 作用

containerd 的下层是runc,containerd 主要在为runc提供 oci(Open Container Initiative)runtime spec,oci 定义了两个标准,一个是关于镜像的,一个是关于容器运行时,容器运行时标准简单来说就是一个config.json文件和一个rootfs,关于oci的详细信息可以看官方github仓库或者文末参考。containerd就是oci一个标准实现。

image

containerd 在docker架构中的位置
二、Container架构分析
Architecture图片.png

containerd本身就提供了ctr命令行工具,grpc接口用来管理容器的生命周期,containerd在镜像管理上进行了创新,不再像docker使用graphdriver来管理镜像,而是使用快照的方式,在容器的世界中存在两种镜像,一种是overlays filesystems(AUFS、OverlayFS), 一种是snapshotting filesystems(devicemapper、btrfs、ZFS)

image

containerd中的数据流

containerd的工作主要分为以下几步
1)拉取镜像,存储到 metadata(metadata是个bolt键值型数据库) 和content中,content中的存储是带标签的存储,metadata中的存储是不带标签,content主要用来进行独立执行或者测试使用,查询的还是metadata中的数据。metadata中的content中的内容主要是 manifests 的index、manifests、config.json、image layer。在拉取完之后同时创建了一个镜像到metadata的image内容空间,还有几本snapshot的构建。
metadata 的schema

// keys.
//  ├──version : <varint>                        - Latest version, see migrations
//  └──v1                                        - Schema version bucket
//     ╘══*namespace*
//        ├──labels
//        │  ╘══*key* : <string>                 - Label value
//        ├──image
//        │  ╘══*image name*
//        │     ├──createdat : <binary time>     - Created at
//        │     ├──updatedat : <binary time>     - Updated at
//        │     ├──target
//        │     │  ├──digest : <digest>          - Descriptor digest
//        │     │  ├──mediatype : <string>       - Descriptor media type
//        │     │  └──size : <varint>            - Descriptor size
//        │     └──labels
//        │        ╘══*key* : <string>           - Label value
//        ├──containers
//        │  ╘══*container id*
//        │     ├──createdat : <binary time>     - Created at
//        │     ├──updatedat : <binary time>     - Updated at
//        │     ├──spec : <binary>               - Proto marshaled spec
//        │     ├──image : <string>              - Image name
//        │     ├──snapshotter : <string>        - Snapshotter name
//        │     ├──snapshotKey : <string>        - Snapshot key
//        │     ├──runtime
//        │     │  ├──name : <string>            - Runtime name
//        │     │  ├──extensions
//        │     │  │  ╘══*name* : <binary>       - Proto marshaled extension
//        │     │  └──options : <binary>         - Proto marshaled options
//        │     └──labels
//        │        ╘══*key* : <string>           - Label value
//        ├──snapshots
//        │  ╘══*snapshotter*
//        │     ╘══*snapshot key*
//        │        ├──name : <string>            - Snapshot name in backend
//        │        ├──createdat : <binary time>  - Created at
//        │        ├──updatedat : <binary time>  - Updated at
//        │        ├──parent : <string>          - Parent snapshot name
//        │        ├──children
//        │        │  ╘══*snapshot key* : <nil>  - Child snapshot reference
//        │        └──labels
//        │           ╘══*key* : <string>        - Label value
//        ├──content
//        │  ├──blob
//        │  │  ╘══*blob digest*
//        │  │     ├──createdat : <binary time>  - Created at
//        │  │     ├──updatedat : <binary time>  - Updated at
//        │  │     ├──size : <varint>            - Blob size
//        │  │     └──labels
//        │  │        ╘══*key* : <string>        - Label value
//        │  └──ingests
//        │     ╘══*ingest reference*
//        │        ├──ref : <string>             - Ingest reference in backend
//        │        ├──expireat : <binary time>   - Time to expire ingest
//        │        └──expected : <digest>        - Expected commit digest
//        └──leases
//           ╘══*lease id*
//              ├──createdat : <binary time>     - Created at
//              ├──labels
//              │  ╘══*key* : <string>           - Label value
//              ├──snapshots
//              │  ╘══*snapshotter*
//              │     ╘══*snapshot key* : <nil>  - Snapshot reference
//              ├──content
//              │  ╘══*blob digest* : <nil>      - Content blob reference
//              └──ingests
//                 ╘══*ingest reference* : <nil> - Content ingest reference

在contained中是存在namespace的概念的
2)当运行容器时,利用metadata中的content、snapshot、image信息进行active snapshot(可以理解为docker中的容器层)的构建,构建bundle,调用runc启动容器。

containerd中的插件机制

containerd对外接口有ctr命令行和有grpc,ctr也是通过grpc协议来与containerd server通信,grpc server不是像我们平时使用的那样将各个service导入到某个入口包中进行注册,containerd使用了插件注册机制,将task、content、snapshot、namespace、event、containers等服务以插件的方式注册然后提供服务.
这是containerd的内部插件

[root@master containerd]# ctr plugin ls
TYPE                            ID                       PLATFORMS      STATUS
io.containerd.content.v1        content                  -              ok
io.containerd.snapshotter.v1    aufs                     linux/amd64    error
io.containerd.snapshotter.v1    btrfs                    linux/amd64    error
io.containerd.snapshotter.v1    devmapper                linux/amd64    error
io.containerd.snapshotter.v1    native                   linux/amd64    ok
io.containerd.snapshotter.v1    overlayfs                linux/amd64    ok
io.containerd.snapshotter.v1    zfs                      linux/amd64    error
io.containerd.metadata.v1       bolt                     -              ok
io.containerd.differ.v1         walking                  linux/amd64    ok
io.containerd.gc.v1             scheduler                -              ok
io.containerd.service.v1        introspection-service    -              ok
io.containerd.service.v1        containers-service       -              ok
io.containerd.service.v1        content-service          -              ok
io.containerd.service.v1        diff-service             -              ok
io.containerd.service.v1        images-service           -              ok
io.containerd.service.v1        leases-service           -              ok
io.containerd.service.v1        namespaces-service       -              ok
io.containerd.service.v1        snapshots-service        -              ok
io.containerd.runtime.v1        linux                    linux/amd64    ok
io.containerd.runtime.v2        task                     linux/amd64    ok
io.containerd.monitor.v1        cgroups                  linux/amd64    ok
io.containerd.service.v1        tasks-service            -              ok
io.containerd.internal.v1       restart                  -              ok
io.containerd.grpc.v1           containers               -              ok
io.containerd.grpc.v1           content                  -              ok
io.containerd.grpc.v1           diff                     -              ok
io.containerd.grpc.v1           events                   -              ok
io.containerd.grpc.v1           healthcheck              -              ok
io.containerd.grpc.v1           images                   -              ok
io.containerd.grpc.v1           leases                   -              ok
io.containerd.grpc.v1           namespaces               -              ok
io.containerd.internal.v1       opt                      -              ok
io.containerd.grpc.v1           snapshots                -              ok
io.containerd.grpc.v1           tasks                    -              ok
io.containerd.grpc.v1           version                  -              ok

扩展插件的两种方式
1、通过二进制方式在containerd的命令行传入
2、通过配置containerd的配置文件来设置proxy pligin
runtime在containerd中有v1和v2两个版本,可以在执行ctr run命令中通过命令行传入

ctr run --runtime io.containerd.runc.v1

自定义快照插件,/etc/containerd/config.toml 是containerd的默认配置文件,

[proxy_plugins]
  [proxy_plugins.customsnapshot]
    type = "snapshot"
    address = "/var/run/mysnapshotter.sock"

proxy plugin会在containerd启动时随着内部插件一起启动。

containerd中的event

containerd中实现了event的发订阅功能,在对每个资源操作后都会推送相关事件给订阅者,利用这个功能可以事件对containerd的监控和实现一些hook功能。

containerd中的namespace

通过上文中metadata中的scame可以看到在containerd中是存在命名空间的概念以及实现,类似opensatck中多租户一样,可以将不同的业务和应用进行隔离,比如kubernetes中使用containerd和docker在使用containerd时就使用了不同的namespace。

三、总结

containerd代码从docker中分离出来后,功能变得丰富强大以至于基本所有云计算中容器相关的底层都在利用containerd,在镜像管理上作者实现了微创新,docker中使用的是graph driver管理镜像,containerd中使用的snapshot,为什么会使用snapshot模型,因为snapshot在增量快照上是有严格的父子关系,这种关系和镜像的分层模型很一致,在容器世界有两种文件系统一种是块级别的一种是文件系统级别,container的将块级别的进行分层化,将文件系统级别的进行快照化。

四、参考

1、runtime spec
2、https://github.com/containerd/containerd/blob/master/PLUGINS.md
3、https://github.com/containerd/containerd/blob/master/design/data-flow.md
4、https://www.slideshare.net/Docker/assessing-container-runtime-performance-with-bucketbench-by-phil-estes-ibm
5、https://cizixs.com/2017/11/05/oci-and-runc/
6、https://heychenbin.github.io/post/containerd_intro/
7、https://www.jianshu.com/p/86296691ca49
8、https://blog.mobyproject.org/where-are-containerds-graph-drivers-145fc9b7255
9、https://container42.com/2017/10/14/containerd-deep-dive-intro/


朱伟
4 声望1 粉丝