[Containerd] 01-简介与使用

2,256 阅读8分钟

1. 介绍

  • Containerd 是什么?

    Containerd官网我们可以看到一个很简单明白的解析,一个工业级的容器运行时,着重于简单性健壮性可移植性;通俗点来说:如果你需要开发一个容器编排工具或者要简单地运行一个容器服务,有我就足够了;

    容器运行时: 是一个允许进程在上面运行的一个独立且隔离的虚拟环境

  • Containerd 是如何诞生的?

    Containerd是脱胎于Docker这个软件的,它的诞生也是由于Docker公司的商业模式一直处于摇摆。起初Docker的出现直接吊打了其他容器技术一家独大,连Google也幸免不了,于是Google想着联合一起退出一个容器运行时作为Docker的底层依赖,但Docker公司并不愿意合作。在此之后,Docker为了进一步扩大影响力将libcontainer捐赠给了(OCI,Open Container Intiative)。

    以至于后面Google为了与之抗衡联合了几位行业巨头成立(CNCF,Cloud Native Computing Fundation)想着在底层玩不过就在编排层来抢占市场,后面就上演了前几年的Docker SwarmKubernetes之争,结局大家也知道Docker Swarm全面败北。

    Docker公司并不甘心只当一个底层的容器运行时,于是花了大力气把自己的核心(Contaienrd)依赖剥离出来捐给了CNCF,只为了标榜自己是一个PaaS平台,这波骚操作让一众巨头们都看不懂。 当初让你一起玩你不玩,现在还捐出来了。

    Kubernetes为了表示自己的中立性,故而直接搞了容器运行时标准化(CRI, Container Runtime Interface),后面为了继续突出Docker的重要性继而搞了各种shim来转换接口。其实研究过Containerd的同学们都知道,Containerd完全是可以独立运行容器的况且Kubernetes搞了CNI来应对容器的复杂网络需求。这样做只是为了等待Containerd羽翼丰满之时。

    大家也知道Kubernetesv1.20后弃用Docker,总而言之Docker这个技术是成功的且开创了一个时代。

2. Containerd 的架构

先来一张常见的架构图:

image.png

可以看到Containerd是一个C/S模式的,由containerd client通过containerd提供的GRPC接口进行操作。

Containerd是有不同总类的Plugin组成的如上图看到,Services层即Services Plugin类型,以此类推MetaData PluginContent PluginSnapshotter PluginRuntime Plugin。总之每一个核心模块就是会有一个或多个类型的Plugin组成,如果想知道更多containerd的源码分析可以留意后续推出的文章.

3. Containerd 的安装

大家都比较清楚golang的程序特性,安装起来还是及其方便的。

参考:containerd.io/downloads/

  • 下载安装

    cd /usr/local/src
    wget https://github.com/containerd/containerd/releases/download/v1.4.4/containerd-1.4.4-linux-amd64.tar.gz
    tar xvf containerd-1.4.4-linux-amd64.tar.gz
    cd containerd-1.4.4
    copy -av bin/* /usr/local/bin/
    
  • 生成配置文件,配置文件可能看起来比较复杂,但暂时先不太关注,其实都是在配置plugin的参数,这些参数更多的需要跟着源码一起解析;

    mkdir -p /etc/container
    containerd config default > /etc/containerd/config.toml
    
  • 配置一个镜像加速地址,找到"docker.io" 把endpoint换一下

    cat config.toml
    ...
    [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
              endpoint = ["https://dockerhub.mirrors.nwafu.edu.cn"]
    ...
    
  • config.toml文件中,有两个不同的存储路径,一个用来保存持久化数据,一个用来保存运行时状态。后面推出的文章聊到snapshotter, content等源码内容时再详细介绍。

    root = "/var/lib/containerd"
    state = "/run/containerd"
    
  • oom_score, 这个是linux 在内存不足的时候对分数高得分的进程发起kill信号,如果作为生产的容器节点,我们需要保证contaienrd不会被杀死;oom 取值范围 -10001000

    oom_score = -999
    
  • 启动containerd, 输入containerd即可

    -> containerd   
    
    -> ctr version
    Client:
     Version:  v1.4.1
     Revision: c623d1b36f09f8ef6536a057bd658b3aa8632828
     Go version: go1.13.15
    
    Server:
     Version:  v1.4.1
     Revision: c623d1b36f09f8ef6536a057bd658b3aa8632828
     UUID: 2539c5fb-cf92-4dd5-8d77-2f1c39e6959d
    

4. ctr 的使用

  • 先看看ctr 命令总预览,一些比较不好理解的,如contentsnaphostershim等子模块,我们放到后面源码分析的文章中时来说说,这样会更好理解;

    -> ctr -h
    COMMANDS:
      plugins, plugin            provides information about containerd plugins
      version                    print the client and server versions
      containers, c, container   manage containers
      content                    manage content
      events, event              display containerd events
      images, image, i           manage images
      leases                     manage leases
      namespaces, namespace, ns  manage namespaces
      pprof                      provide golang pprof outputs for containerd
      run                        run a container
      snapshots, snapshot        manage snapshots
      tasks, t, task             manage tasks
      install                    install a new package
      oci                        OCI tools
      shim                       interact with a shim directly
      help, h                    Shows a list of commands or help for one command
    
    GLOBAL OPTIONS:
      --debug                      enable debug output in logs
      --address value, -a value    address for containerd's GRPC server (default: "/run/containerd/containerd.sock") [$CONTAINERD_ADDRESS]
      --timeout value              total timeout for ctr commands (default: 0s)
      --connect-timeout value      timeout for connecting to containerd (default: 0s)
      --namespace value, -n value  namespace to use with commands (default: "default") [$CONTAINERD_NAMESPACE]
      --help, -h                   show help
      --version, -v                print the version
    

    从命令的子模块看来,containerd还是偏向于更专注容器的本身,比如网络模块就没见到身影,所以它更适合开发者或者说程序去使用它,而docker明显对人会更加的友好易用。

  • plugins, 查看插件的支持,下面可以看到我的系统不支持btrfsdevmapperzfs这三种snapshotter

    > ctr plugins ls
    TYPE                            ID                       PLATFORMS      STATUS
    io.containerd.content.v1        content                  -              ok
    io.containerd.snapshotter.v1    aufs                     linux/amd64    ok
    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
    io.containerd.grpc.v1           cri                      linux/amd64    ok
    

4.1 images

-> ctr i -h
NAME:
  ctr images - manage images

USAGE:
  ctr images command [command options] [arguments...]

COMMANDS:
  check       check that an image has all content available locally
  export      export images
  import      import images
  list, ls    list images known to containerd
  mount       mount an image to a target path
  unmount     unmount the image from the target
  pull        pull an image from a remote
  push        push an image to a remote
  remove, rm  remove one or more images by reference
  tag         tag an image
  label       set and clear labels for an image

OPTIONS:
  --help, -h  show help
  • 可以看到很多和docker类似的命令, 下面先下载镜像
-> ctr i pull docker.io/library/nginx:alpine
  • 将镜像挂载到目录上
-> ctr i mount docker.io/library/nginx:alpine /mnt
-> tree /mnt
/mnt
├── bin
├── dev
├── docker-entrypoint.d
├── docker-entrypoint.sh
├── etc
├── home
├── lib
├── media
├── mnt
├── opt
├── proc
├── root
├── run
├── sbin
├── srv
├── sys
├── tmp
├── usr
└── var
  • 将镜像从目录上卸载
-> ctr i unmounnt /mnt

4.2 containers

  • 创建容器
-> ctr c create docker.io/library/nginx:alpine nginx
  • 创建容器还有比较多的参数,比如 --mount 指定挂载目录,--privileged 特权容器,--with-ns 指定一个存在的linux namespace并且加入他等,更多可以-h查看。

  • list容器, 后面有些模块的该参数都是类似的效果就不再展示了

-> ctr c ls
  • checkpointrestore 参数其实都是通过runc去提供的一种容器快照功能,保存容器当前状态或恢复当前状态。

4.3 tasks

  • 前面create container 从源码上看到其实只是保存了需要配置的信息并未有任何启动的动作,直至你使用tasks 模块,它类似在管理容器进程的模块。
-> ctr task start -d nginx
-> ctr task ls
TASK                   PID      STATUS
nginx                  26046    RUNNING
  • 它也可以直接创建并运行容器,因为run 子模块其实就是先container create, 然后调用tasks run 进行启动
-> ctr run -d docker.io/library/nginx:alpine nginx
  • 进入容器,--exec-id随便指定一个唯一id即可,其实exec子模块的功能,大部分是有runtime实现的,其实就是runc
-> ctr task exec --exec-id 0 -t nginx sh
  • 暂停容器,这里的暂停容器是由runc 提供的,其实就是通过cgroupfreezer (/sys/fs/cgroup/freezer)让进程冻结,不让其使用计算资源,进程状态会变成不可中断。
->  ctr task pause nginx
  • 恢复容器,也是一样 只是处于不同状态的进程,cgroupfreezer会让进程解冻
-> ctr task resume nginx

想了解更多可以参考,我之前写的runc的源码分析文章:了解一下

  • tasks模块并没有stop功能,我们只能把容器kill掉(说到这我也好奇docker是怎么stop容器的,后面寻找到答案再补上链接)
-> ctr task kill nginx
  • 使用情况
-> ctr task metrics nginx
ID       TIMESTAMP
nginx    2021-03-22 14:19:25.763395731 +0000 UTC

METRIC                   VALUE
memory.usage_in_bytes    3293184
memory.limit_in_bytes    9223372036854771712
memory.stat.cache        0
cpuacct.usage            173628987
cpuacct.usage_percpu     [96660488 76968499]
pids.current             3
pids.limit               0
  • 查看容器中的所有进程PID
-> ctr t ps nginx
PID      INFO
26046    -
26087    -
26088    -

4.4 其他子模块

  • 有些子模块在本篇文章不在详解使用方式及作用,留待后面的文章进行源码分析时的入口;

    • content 内容寻址,可以修改镜像内容
    • snapshots 镜像层的操作
  • events可以监听到containerd内的所有事件,这是在内部实现的一套事件机制,需要事件机制的程序可以借鉴下。

-> ctr events
  • pprof这个熟悉go语言的都应该知道这是个性能监控, 需要在config.toml下的设置debug的socket路径,然后重启服务后就可以使用pprof的命令

  • oci 这个模块主要是展示OCI的规范参数,就是文中开头提到过的OCI,这些规范都会在代码层面有体现;

-> ctr oci spec
  • shim 这个模块不是标准的使用containerd client进行连接,而是通过ttrpc这中低内存的grpc客户端直接拼接出一个容器的连接地址进行连接。它的功能其实在tasks模块下都有,因为它最终远程调用也是tasks模块的service plugin

最后

经历了6小时终于肝完了,在坚持坚持,还有很多坑要填。

后面陆续还有几篇关于containerd的源码分析文章,加油吧~~

肝完就可以进军kubernetes,底层要扎实推进,上层建筑才稳当;