专栏首页01ZOO扩展 Kubernetes 之 CRI
原创

扩展 Kubernetes 之 CRI

简介

CRI 是什么

  • 容器运行时插件(Container Runtime Interface,简称 CRI)是 Kubernetes v1.5 引入的容器运行时接口,它将 Kubelet 与容器运行时解耦,将原来完全面向 Pod 级别的内部接口拆分成面向 Sandbox 和 Container 的 gRPC 接口,并将镜像管理和容器管理分离到不同的服务。
  • CRI 主要定义了两个 grpc interface.
    • RuntimeService:容器(container) 和 (Pod)Sandbox 运行时管理
    • ImageService:拉取、查看、和移除镜像
  • OCI (开放容器标准): 定义了 ImageSpec(镜像格式, 比如文件夹结构,压缩方式)和 RuntimeSpec(如何运行,比如支持 create, start, stop, delete)
    • 代表实现有:runC,Kata(以及它的前身 runV 和 Clear Containers),gVisor
  • CRI 区别于 OCI,CRI的定义比较简单直接,只是定义了一套协议(grpc 接口)。
    • 代表实现有 kubernetest 内置的 dockershim, CRI-containerd(或者 containerd with CRI plugin), cri-o

CRI 位于什么位置

在 kubernetes 中:

image
image

在和OCI,调度层的角度看:

graph LR
OrchestrationAPI --> ContainerAPI-criRuntime
ContainerAPI-criRuntime --> KernelAPI-ociRuntime

CRI/CRI Runtime

CRI Runtime 的执行流程

经典的 kubernetes runtime 执行流程

image
  • 执行流程里面核心组件是 kubelet/KubeGenericRuntimeManager 他调用很多其他组件,比如 cm (ContainerManager/podContainerManager/cgroupManager/cpuManager/deviceManager, pod 级别的资源管理), RuntimeService (grpc 调用 CRI 的客户端) 共同完成 SyncPod 的操作。

经典的 dockershim -> containerd 的流程 (称为 docker cri)

image
  1. Kubelet 通过 CRI 接口(gRPC)调用 dockershim,请求创建一个容器。CRI 即容器运行时接口(Container Runtime Interface),这一步中,Kubelet 可以视作一个简单的 CRI Client,而 dockershim 就是接收请求的 Server。目前 dockershim 的代码其实是内嵌在 Kubelet 中的,所以接收调用的凑巧就是 Kubelet 进程
  2. dockershim 收到请求后,转化成 Docker Daemon 能听懂的请求,发到 Docker Daemon 上请求创建一个容器。
  3. Docker Daemon 早在 1.12 版本中就已经将针对容器的操作移到另一个守护进程——containerd 中了,因此 Docker Daemon 仍然不能帮我们创建容器,而是要请求 containerd 创建一个容器;
  4. containerd 收到请求后,并不会自己直接去操作容器,而是创建一个叫做 containerd-shim 的进程,让 containerd-shim 去操作容器。这是因为容器进程需要一个父进程来做诸如收集状态,维持 stdin 等 fd 打开等工作。而假如这个父进程就是 containerd,那每次 containerd 挂掉或升级,整个宿主机上所有的容器都得退出了。而引入了 containerd-shim 就规避了这个问题(containerd 和 shim 并不是父子进程关系);
  5. 我们知道创建容器需要做一些设置 namespaces 和 cgroups,挂载 root filesystem 等等操作,而这些事该怎么做已经有了公开的规范了,那就是 OCI(Open Container Initiative,开放容器标准)。它的一个参考实现叫做 runC。于是,containerd-shim 在这一步需要调用 runC 这个命令行工具,来启动容器;
  6. runC 启动完容器后本身会直接退出,containerd-shim 则会成为容器进程的父进程,负责收集容器进程的状态,上报给 containerd,并在容器中 pid 为 1 的进程退出后接管容器中的子进程进行清理,确保不会出现僵尸进程。

直接对接 cri-containerd/cri-o 的运行时

image
image

使用 cri-containerd 的调用流程更为简洁, 省去了上面的调用流程的 1,2 两步

常见 CRI runtime 实现

image

Cri-containerd

image

执行流程为:

  1. Kubelet 通过 CRI runtime service API 调用 cri plugin 创建 pod
  2. cri 通过 CNI 创建 pod 的网络配置和 namespace
  3. cri 使用 containerd 创建并启动 pause container (sandbox container) 并且把这个 container 置于 pod 的 cgroups/namespace
  4. Kubelet 接着通过 CRI image service API 调用 cri plugin, 获取容器镜像
  5. cri 通过 containerd 获取容器镜像
  6. Kubelet 通过 CRI runtime service API 调用 cri, 在 pod 的空间使用拉取的镜像启动容器
  7. cri 通过 containerd 创建/启动 应用容器, 并且把 container 置于 pod 的 cgroups/namespace. Pod 完成启动.

实践

准备集群

使用 terraform 在腾讯云上创建 tke 测试集群

# Configure the TencentCloud Provider
provider "tencentcloud" {
  secret_id  = var.secret_id
  secret_key = var.secret_key
  region     = var.region
}

# test cluster
resource "tencentcloud_kubernetes_cluster" "managed_cluster" {
  vpc_id                  = var.vpc
  cluster_cidr            = "10.4.0.0/16"
  cluster_max_pod_num     = 32
  cluster_name            = "test"
  cluster_desc            = "test cluster desc"
  cluster_max_service_num = 32
  container_runtime          = "containerd"
  cluster_version            = "1.14.3"

  worker_config {
    count                      = 2
    availability_zone          = var.availability_zone
    instance_type              = var.default_instance_type
    system_disk_size           = 50
    security_group_ids         = [var.sg]
    internet_charge_type       = "TRAFFIC_POSTPAID_BY_HOUR"
    internet_max_bandwidth_out = 100
    public_ip_assigned         = true
    subnet_id                  = var.subnet
    enhanced_security_service = false
    enhanced_monitor_service  = false
    key_ids                   = [var.key_id]
  }

  cluster_deploy_type = "MANAGED_CLUSTER"
}

用 bash 实现一个 CRI runtime

CRI runtime 的实现需要实现大量 API,这里我们做一个简单的 shell 脚本,将请求转发给 runc,同时打印出调用的参数。把这个脚本命名为 runb

$ cat /usr/local/bin/runb
#!/bin/bash -e
echo "["`date --iso-8601=seconds`"] call runb: $@" >> /var/log/runb.log
exec runc $@

使用 ctr/crictl 测试

ctr 是调用containerd 的命令行工具,而 crictl 是调用 cri 相关 api 的工具,一个测试的例子 参考这里

配置 containerd, 使用 k8s yaml 测试

在 /etc/containerd/config.toml 下面添加如下的配置,配置 runtime 为 runb,二进制程序为 runb. containerd 的配置因版本变化有所不同,具体可以参考这里。重启 containerd

      [plugins.cri.containerd.runtimes.runb]
         runtime_type = "io.containerd.runc.v1"
         [plugins.cri.containerd.runtimes.runb.options]
           NoPivotRoot = false
           NoNewKeyring = false
           ShimCgroup = ""
           IoUid = 0
           IoGid = 0
           BinaryName = "runb"
           Root = ""
           CriuPath = ""
           SystemdCgroup = false

用 kubectl 把名为 runb 的 runtimeclass 创建出来

apiVersion: node.k8s.io/v1beta1  # RuntimeClass is defined in the node.k8s.io API group
kind: RuntimeClass
metadata:
  name: runb  # The name the RuntimeClass will be referenced by
  # RuntimeClass is a non-namespaced resource
handler: runb  # The name of the corresponding CRI configuration

创建一个使用 runb 为runtime 的pod

root@VM-8-12-ubuntu:~# cat nginx.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  runtimeClassName: runb
  containers:
    - image: nginx
      name: nginx
      ports:
        - containerPort: 80
          name: http

观察创建结果和 runb 的输出日志

root@VM-8-12-ubuntu:~# crictl ps
CONTAINER ID        IMAGE               CREATED             STATE               NAME                ATTEMPT             POD ID
d0e454f31ad06       2073e0bcb60ee       11 minutes ago      Running             nginx               0                   f640723d57cc2

root@VM-8-12-ubuntu:~# head /var/log/runb.log
starting with runb -namespace default -address /run/containerd/containerd.sock -publish-binary /usr/local/bin/containerd -id hello -debug start
starting with runb: --root /run/containerd/runc/k8s.io --log /run/containerd/io.containerd.runtime.v2.task/k8s.io/5dd814edd58826c3ebcfa87ae96bb972b1541d6007f7f7ed5c01a4da639b0fac/log.json --log-format json create --bundle /run/containerd/io.containerd.runtime.v2.task/k8s.io/5dd814edd58826c3ebcfa87ae96bb972b1541d6007f7f7ed5c01a4da639b0fac --pid-file /run/containerd/io.containerd.runtime.v2.task/k8s.io/5dd814edd58826c3ebcfa87ae96bb972b1541d6007f7f7ed5c01a4da639b0fac/init.pid 5dd814edd58826c3ebcfa87ae96bb972b1541d6007f7f7ed5c01a4da639b0fac
starting with runb: --root /run/containerd/runc/k8s.io --log /run/containerd/io.containerd.runtime.v2.task/k8s.io/5dd814edd58826c3ebcfa87ae96bb972b1541d6007f7f7ed5c01a4da639b0fac/log.json --log-format json state 5dd814edd58826c3ebcfa87ae96bb972b1541d6007f7f7ed5c01a4da639b0fac
starting with runb: --root /run/containerd/runc/k8s.io --log /run/containerd/io.containerd.runtime.v2.task/k8s.io/5dd814edd58826c3ebcfa87ae96bb972b1541d6007f7f7ed5c01a4da639b0fac/log.json --log-format json start 5dd814edd58826c3ebcfa87ae96bb972b1541d6007f7f7ed5c01a4da639b0fac
starting with runb: --root /run/containerd/runc/k8s.io --log /run/containerd/io.containerd.runtime.v2.task/k8s.io/5dd814edd58826c3ebcfa87ae96bb972b1541d6007f7f7ed5c01a4da639b0fac/log.json --log-format json state 5dd814edd58826c3ebcfa87ae96bb972b1541d6007f7f7ed5c01a4da639b0fac

参考

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 一些kubernetes的开发/实现/使用技巧-2

    JobController的实现逻辑比较简单,用它来示例 Controller的实现方式

    王磊-AI基础
  • 用 go 实现 lua 虚拟机

    下面依次介绍上面的一些步骤,本文旨在一篇文章写清楚大概流程,具体的细节将会忽略,实际的实现也会尽可能的简化,本文主要参考 自己动手实现 lua,和 gopher...

    王磊-AI基础
  • ffmpeg 命令的 golang binding 工具

    王磊-AI基础
  • containerd与kubernetes集成

    由于docker嵌入了太多自身内容,为了减轻容器负担。此次选用containerd作为kubernetes的容器实现方案。本文将带大家讲述如何搭建一个集成了co...

    sealyun
  • 弃用docker之后你的k8s用哪种容器runtime?

    在k8s取消内置dockershim代码,取消了对docker的支持后,用户无非重新选择一个运行时,不必过度惊慌!

    有点技术
  • Docker 组件基本介绍

    供用户使用的命令行工具,负责请求 docker API 与 dockerd 交互,使得用户可以便捷友好的操作 docker。

    我是阳明
  • Containerd 1.1.0 尝鲜记

    下载 cri-containerd 1.1.0,并解压,其中包含 /usr、/etc 以及 opt 三个目录,这里我们只是用前两个目录的内容,目录结构如下,直接...

    崔秀龙
  • containerd项目

    说明:containerd是一个行业标准的容器运行引擎,强调简单、健全和可移植性。它可用作Linux和Windows的守护程序,管理主机系统的完整容器生命周期:...

    CNCF
  • DCOS番外篇之Docker概念快递

    早前推出DCOS番外篇Docker系列,主要介绍docker相关技术,请阅读新的文章:DCOS番外篇之Dcoker概念快递

    zouyee
  • CNCF发布containerd项目旅程报告

    今天我们非常兴奋地发布我们的containerd项目旅程报告。这是我们为毕业阶段项目发布的第四份这样的报告。

    CNCF

扫码关注云+社区

领取腾讯云代金券

,,