Kubelet运行时介绍

kubernetes的核心竞争力是对各种workload的抽象和编排,在与计算、存储和网络对接中,分别对应衍生了其对接的事实标准CRI、CNI和CSI。今天我们来重点分析kubelet运行时,另外二者在接下来的文章中再做详细的介绍。

kubelet有以下的启动参数用来指定运行时:

–container-runtime string
The container runtime to use. Possible values: ‘docker’, ‘remote’, ‘rkt(deprecated)’. (default “docker”)
–container-runtime-endpoint string
[Experimental] The endpoint of remote runtime service. Currently unix socket is supported on Linux, and tcp is supported on windows.

当指定-container-runtime为remote时,对应不会在kubelet启动docker-server,而直接调用CRI接口访问通过--container-runtime-endpoint指定的服务,一般这是一个containerd或者CRIO。

部署架构

先从kubelet默认部署的dockershim运行时来分析。下面这张图展现了CRI客户端与服务端、以及服务端与底层服务是如何协调工作的。

  • 客户端
    和服务端之间基于unix socket套接字/run/dockershim.sock,通过gRPC来通信。

  • 服务端
    在kubelet源码中,整个服务端的代码都位于dockershim包中。它实现了对container、image、checkpoint、logs以及sandbox的操作,其本质都是调用docker和CNI plugin来实现的。

    这里值得特殊提到的是:若只想kubelet启动CRI server,而不启动kubelet其他功能,可以在启动参数中指定--experimental-dockershim参数的值为true

客户端

  • crictl
    我们知道,如今安装kubeadm的时候,都会安装对应依赖包,比如kubeletcrictl等。这里的crictl就是一个独立的客户端工具,当kubelet启动之后,可以基于它来调试CRI server。

    为了便于理解cri的各功能,我将crictl的命令提示列出来,后面是对每个命令操作的描述。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    [root@vm ~]# crictl --help
    NAME:
    crictl - client for CRI

    USAGE:
    crictl [global options] command [command options] [arguments...]

    VERSION:
    v1.12.0

    COMMANDS:
    attach Attach to a running container
    create Create a new container
    exec Run a command in a running container
    version Display runtime version information
    images List images
    inspect Display the status of one or more containers
    inspecti Return the status of one or more images
    inspectp Display the status of one or more pods
    logs Fetch the logs of a container
    port-forward Forward local port to a pod
    ps List containers
    pull Pull an image from a registry
    runp Run a new pod
    rm Remove one or more containers
    rmi Remove one or more images
    rmp Remove one or more pods
    pods List pods
    start Start one or more created containers
    info Display information of the container runtime
    stop Stop one or more running containers
    stopp Stop one or more running pods
    update Update one or more running containers
    config Get and set crictl options
    stats List container(s) resource usage statistics
    completion Output bash shell completion code
    help, h Shows a list of commands or help for one command
  • kubelet
    在阅读kubelet源码的时候,有经常看到kubeGenericRuntimeManager这个类。它在kubelet初始化的时候被创建,是整个kubelet的runtime;一旦kubelet需要容器底层操作资源时,就会调动这个接口方法来处理。

这两个客户端都是通过gRPC来访问CRI server的,所以当使用crictl的时候,需要保障其版本与kubelet的版本一致性,否则可能protobuf定义的报文格式有差异,导致无法正常访问。

服务端

服务端是通过dockershim包中的dockerService来实现的。它实现了CRI的所有接口(pod对容器运行时的需求),在这些接口的handler中包含了抽象的业务逻辑,包括网络、日志、流式读写、镜像管理等。

在这些抽象之下,最终依然是调用容器和CNI接口实现的功能。

  • 使用dockerlib库,基于/run/docker.sock来访问docker engine;
  • /etc/cni/*下查询pod所使用的CNI网络类型,然后调用/opt/cni/*下的CNI插件来为pod添加、卸载网络。

源码分析

kubelet中,对容器运行时的源码主要包含以下几块(结合下图来分析):

  • 服务端(dockershim包中)
    dockershim.NewDockerService创建出dockerService类,它实现了CRI的所有接口,对应代码在action指定的文件中实现。同时,该类的属性包含了CNI、streamServicer、containerManager、checkPointHandler等,分别为CRI接口提供各个方面的功能。

  • 启动服务端(kubelet中)
    图中上半部分是kubelet启动的时候,指定了--experimental-dockershim=true参数后,只启动CRI server的流程;而下半部分是完整的kubelet启动后的流程。

  • protobuf接口定义(api)
    也就是定义CRI的方法和消息结构的定义了,主要位于kubernetes项目的/pkg/kubelet/apis/cri/目录下。

  • 客户端
    kubelet的客户端代码其实就是调用gRPC的接口了,主要位于GenericRuntimeManager中。在上图中可以看到,kubelet会通过klet.runtime调用操作pod各种资源的方法。

演变方向

从基于dockerd到基于containerd,最后希望直接基于OCI标准,K8S试图取消中间所有环节,直接到达RunC。当然,k8s必须提供直接调试底层runC的工具来取代docker,否则kubelet已挂,整个机器就瘫痪了。当前crictl就是一个比较好的工具!

Containerd

CRI-Containerd

在containerd早期的版本中,对CRI的支持是通过外部适配服务来实现的;自v1.1之后,containerd的势力逐渐往外扩展,将CRI作为plugin来运行,该plugin意图替代dockershim,除了提供CRI的支持之外,还提供对CNI的调用。

下图是containerd的架构图。从北向看,containerd在保留其对原有API的情况下,还对外向k8s提供了CRI API。从南向看,containerd除了支持runc外,也支持runhcs和kata等。

containerd的配置文件/etc/containerd/config.toml, 可以看到,其中包含了关于CNI的配置信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[plugins]
[plugins.cri]
[plugins.cri.containerd]
[plugins.cri.containerd.default_runtime]
[plugins.cri.cni]
# bin_dir is the directory in which the binaries for the plugin is kept.
bin_dir = "/opt/cni/bin"

# conf_dir is the directory in which the admin places a CNI conf.
conf_dir = "/etc/cni/net.d"

# max_conf_num specifies the maximum number of CNI plugin config files to
# load from the CNI config directory. By default, only 1 CNI plugin config
# file will be loaded. If you want to load multiple CNI plugin config files
# set max_conf_num to the number desired. Setting max_config_num to 0 is
# interpreted as no limit is desired and will result in all CNI plugin
# config files being loaded from the CNI config directory.
max_conf_num = 1

# conf_template is the file path of golang template used to generate
# cni config.
# If this is set, containerd will generate a cni config file from the
# template. Otherwise, containerd will wait for the system admin or cni
# daemon to drop the config file into the conf_dir.
# This is a temporary backward-compatible solution for kubenet users
# who don't have a cni daemonset in production yet.
# This will be deprecated when kubenet is deprecated.
conf_template = ""

现在使用CRI-Containerd来安装kubelet不再需要那么繁琐了,需要安装的,只有以下三个包。在使用docker的时候,其实是docker的安装包帮我们一并安装的以下三个可执行文件。

Binary Name Support OS Architecture
containerd seccomp, apparmor,
overlay, btrfs
linux amd64
containerd-shim overlay, btrfs linux amd64
runc seccomp, apparmor linux amd64

其最终运行之后,相互之间调用的通信是这样的:

nvidia-docker

Nvidia提供了k8s pod使用GPU的一整套解决方案。运行时方面,nvidia提供了特定的运行时,主要的功能是为了让container访问从node节点上分配的GPU资源。

如上图所示,libnvidia-container被整合进docker的runc中。通过在runc的prestart hook 中调用nvidia-container-runtime-hook来控制GPU。启动容器时,prestart hook会校验环境变量GPU-enabled来校验该容器是否需要启用GPU,一旦确认需要启用,就调用nvidia定制的运行时来启动容器,从而为容器分配limit指定个数的GPU。

  • 环境变量

    NVIDIA_VISIBLE_DEVICES : controls which GPUs will be accessible inside the container. By default, all GPUs are accessible to the container.
    NVIDIA_DRIVER_CAPABILITIES : controls which driver features (e.g. compute, graphics) are exposed to the container.
    NVIDIA_REQUIRE_* : a logical expression to define the constraints (e.g. minimum CUDA, driver or compute capability) on the configurations supported by the container.

下面是简单使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
$ sudo docker run -it --runtime=nvidia --shm-size=1g -e NVIDIA_VISIBLE_DEVICES=0,1 --rm nvcr.io/nvidia/pytorch:18.05-py3

Copyright (c) 2006 Idiap Research Institute (Samy Bengio)
Copyright (c) 2001-2004 Idiap Research Institute (Ronan Collobert, Samy Bengio, Johnny Mariethoz)

All rights reserved.

Various files include modifications (c) NVIDIA CORPORATION. All rights reserved.
NVIDIA modifications are covered by the license terms that apply to the underlying project or file.


root@45cebefa1480:/workspace# nvidia-smi
Mon May 28 07:15:39 2018

+-----------------------------------------------------------------------------+
| NVIDIA-SMI 396.26 Driver Version: 396.26 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
|===============================+======================+======================|
| 0 Tesla V100-SXM2... On | 00000000:00:1B.0 Off | 0 |
| N/A 39C P0 36W / 300W | 0MiB / 16160MiB | 0% Default |
+-------------------------------+----------------------+----------------------+
| 1 Tesla V100-SXM2... On | 00000000:00:1C.0 Off | 0 |
| N/A 38C P0 35W / 300W | 0MiB / 16160MiB | 0% Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes: GPU Memory |
| GPU PID Type Process name Usage |
|=============================================================================|
| No running processes found |
+-----------------------------------------------------------------------------+

在k8s的调度方面,nvidia基于k8s的device plugin来实现了kubelet层GPU资源的上报,通过在pod的spec中指定对应的limit来声明对GPU个数的申请情况。在spec中必须指定limit的值(且必须为整数),reqire的值要么不设置,要么等于limit的值。

kata

kata因为其底层使用hypervisor的虚拟化技术,在安全和隔离性方面对很多场景都有较强的吸引力,其发展也很不错;下面是kata的适配图。由于篇幅所限,后续再抽个机会,专门写一篇关于kata的文章。

0%