Dockerless:Containerd方案

背景介绍

Docker作为最早广泛应用的容器运行时,其普及程度使得用户对其操作方式和功能特性极为熟悉。在Kubernetes的初期,Kubernetes通过内置的dockershim组件与Docker进行适配。然而,随着容器技术的不断发展,Kubernetes推出了CRI(容器运行时接口)标准,以更好地规范和扩展容器的接入能力。这一创新不仅增强了Kubernetes对多种容器运行时的兼容性,也为用户提供了更多选择,提升了灵活性。

CRI的引入使Kubernetes的系统组件能够与各种容器运行时无缝交互。这一转变不仅扩展了Kubernetes的适用范围,使其不再局限于Docker,也降低了对Docker和dockershim的依赖。如今,Kubernetes用户可以放心选择如Containerd、CRI-O等优秀的容器运行时。这种转变简化了Kubernetes项目的架构,并提升了服务运行效率。

自Kubernetes 1.24版本起,官方已正式移除了对Docker(通过dockershim实现的CRI支持)的支持。同时,Amazon EKS也明确将Containerd作为其唯一的容器运行时。因此,我们也将集群的容器运行时切换至Containerd,并用nerdctl替换docker命令行。

在此,有必要介绍一些常见的容器组件,如libcontainerrunccontainerdCRIOCI等。这些组件在容器生态系统中扮演着关键角色,各自发挥独特功能,共同构建了一个高效、灵活的容器运行环境。通过深入了解这些组件,我们将更好地掌握容器技术的运维手段,为未来的应用部署与运维奠定坚实基础。如果想直接了解nerdctl的相关内容,请跳转至相应章节。

从Docker 1.11版本开始,Docker的容器运行除了Docker Daemon,还依赖于多个组件的协作,如containerd、runc等。虽然Docker Daemon守护进程模块不断重构,但其基本功能和定位未发生太大变化,依然保持CS架构,由守护进程负责与Docker Client交互,并管理Docker镜像和容器。

为了应对Docker引擎的复杂性问题,Docker项目将引擎中的部分核心功能抽象出来并开源,这便是Containerd项目的诞生。Containerd专注于容器的基本操作,如镜像管理、容器运行时和生命周期管理等,为容器管理工具提供了统一的接口。

在现代架构中,Containerd负责集群节点上容器的生命周期管理,并通过gRPC接口为Docker Daemon提供支持。

img

技术背景

OCI 标准 (Open Container Initiative)

OCI是由 Docker、CoreOS(被RedHat收购) 等组织对容器格式及运行时建立的统一的行业标准。

OCI主要定义两个规范:

  • 镜像规范(image-spec)定义了镜像的主要格式及内容
  • 运行时规范(runtime-spec) 运行时规范定义镜像文件运行的管理, 而 runC 则是 目前使用最广泛的Low-Level容器运行时(runC 包含libcontainer,包括对namespace和cgroup的调用操作)。

接下去看下目前市面上最主流的3个High-Level容器运行时(High-Level包括镜像传输、镜像管理、镜像解包和 API等高级功能)

  • Containerd :CNCF孵化器的开源项目,由 Docker捐献的
  • Podman():Redhat孵化的项目,Podman 工具在RHEL8中作为完全支持的功能发布。
  • CRI-O : CNCF孵化器的开源项目

我们再继续看一下 容器运行关系:

ocs

基于Containerd作为容器运行时的Docker可以定义为”High-High-Level”容器运行时。

关于 containerd 和 CRI 的关系要注意一些时间点:

  • Containerd 在 2016年初被拆出来
  • CRI 标准在2016 年末出来的 (早于 Containerd)
  • Containerd 在2017年3月进入CNCF之后才添加了CRI支持.

Containerd

Containerd 项目在 2017 年 3 月加入了云原生计算基金会(CNCF),其是一种简单、健壮和可移植性的行业标准容器运行时。它可作为 Linux 和 Windows 的守护进程,可以管理完整容器生命周期,包括镜像传输和存储、容器管理、底层存储和网络管理(通过CNI等)等功能。

containerd

上图展示了 Containerd 的主要组件:

  1. Runtime:Containerd 负责容器的生命周期管理,包括容器的创建、运行和事件管理等。
  2. Storage:Containerd 提供镜像存储管理。它负责容器镜像的存储、检索和管理。
  3. gRPC:Containerd 使用 gRPC 服务器来与上层应用程序通信,为上层提供容器管理服务的接口。gRPC 是一种远程过程调用协议,使容器管理操作可以通过网络进行。
  4. Metrics:Containerd 提供了有关容器性能和资源使用的指标,主要涉及 cgroup(控制组)的性能指标。
  5. Metadata:容器的元数据,如镜像和容器的信息,以及与其相关的元数据,存储在 bootfs 中。这些元数据用于容器的管理和操作。
  6. Tasks:在 Containerd 中,容器结构被管理为任务(tasks)。这包括容器的运行状态、进程信息和其他相关数据。
  7. Events:Containerd 生成事件以通知上层应用程序容器的状态变化。这使上层应用程序能够订阅这些事件,以获知容器的状态变化,以及采取相应的操作。

Containerd 项目在 2017 年 3 月加入了云原生计算基金会(CNCF),下图是Docker跟Containerd的对照表。

特性 Containerd Docker
架构 专注于容器运行时,负责容器生命周期的管理。 提供完整的容器平台,包含运行时和高级管理工具。
镜像管理 提供基础的镜像传输和存储功能。 提供高级镜像构建、版本控制和分发功能。
生态系统 模块化设计,常作为容器生态系统的一部分。 整合多种工具和服务,构建全面的容器生态系统。
安全性 针对核心功能进行小型优化和改进。 提供高级安全功能,如镜像签名和密钥管理。
可扩展性 提供精简机制,支持多种容器运行时场景。 提供插件机制,允许高度自定义和扩展。
部署模式 需要额外工具,如 Kubernetes,提供集成容器解决方案。 提供一体化容器部署管理,支持简单快速启动。
社区支持 获得 CNCF 和云原生基金会的支持。 社区由 Docker 公司主导,资源丰富。
用户案例 常用于构建自定义容器解决方案,如 Kubernetes。 适合构建容器化应用和分布式容器管理。

CRI

Kubernetes 提供了一个名为 CRI 的容器运行时接口。那么,CRI 到底是什么呢?这其实与 Docker 的发展有着密切关系。

在 Kubernetes 的早期阶段,Docker 因其广泛的流行,成为了 Kubernetes 首选支持的容器运行时。Kubernetes 当时通过硬编码的方式直接调用 Docker API。然而,随着 Docker 的不断演进以及 Google 的主导,更多的容器运行时开始涌现。为了支持这些更精简、更灵活的容器运行时,Google 联合红帽推出了 CRI(容器运行时接口)标准,以将 Kubernetes 平台与特定的容器运行时解耦(其中一个目的也是为了减少对 Docker 的依赖)。

CRI(Container Runtime Interface)本质上是一组 Kubernetes 定义的接口,用于与容器运行时进行交互。因此,任何实现了这套接口的容器运行时都可以无缝对接到 Kubernetes。然而,Kubernetes 推出 CRI 标准时,还没有如今的主导地位,因此一些容器运行时并未直接实现 CRI 接口。为了适配这些容器运行时,便引入了 shimshim 作为适配器,将各种容器运行时的接口转换为 Kubernetes 可识别的 CRI 接口。其中,dockershim 就是 Kubernetes 为了对接 Docker 而实现的 CRI 适配器。

dockershim

Kubelet 通过 gRPC 与容器运行时或 shim 进行通信,其中 kubelet 作为客户端,CRI shim(或容器运行时本身)作为服务端。

CRI 定义的 API 主要包括两个 gRPC 服务:ImageServiceRuntimeServiceImageService 负责处理镜像的操作,如拉取、查看和删除镜像;而 RuntimeService 主要管理 Pod 和容器的生命周期,以及与容器交互的操作(如 exec、attach、port-forward)。可以通过 kubelet 的 --container-runtime-endpoint--image-service-endpoint 命令行或者Kubelet配置文件来配置这两个服务的服务地址。

由于 Docker 当时处于主导地位,Kubernetes 直接将 dockershim 内置于 kubelet 中。因此,如果使用 Docker 作为容器运行时,无需额外安装或配置适配器。不过,这种内置支持也在一定程度上让 Docker 公司忽视了后续发展。

当使用 Docker 时,在 Kubernetes 中创建一个 Pod 的过程大致如下:首先,kubelet 通过 CRI 接口调用 dockershim,请求创建容器。此时,kubelet 作为CRI 客户端,而 dockershim 是接收请求的服务端代理,二者都内置于 kubelet 中。

dockershim 收到请求后,会将其转换为 Docker Daemon 能识别的请求,发送给 Docker Daemon,请求创建容器。接下来,Docker Daemon 调用 Containerd,再由 containerd 创建 containerd-shim 进程(Containerd后续版本移除了containerd-shim),通过该进程调用 runc 来实际创建容器。

可以看出,使用 Docker 的调用链较长,而真正与容器相关的操作,Containerd 已经足够。尽管其深受欢迎的一个重要原因是为用户提供了许多友好的功能,但Docker实在是过于复杂和笨重,一些功能对于 Kubernetes 来说并不必要。因此,将容器运行时切换到 Containerd 是一个更为高效的选择。下图展示了插件的发展历程。

cri

从上图可以看出,在 Containerd 1.0 中,CRI 的适配是通过一个独立的 CRI-Containerd 进程完成的。这是因为最初的 Containerd 还需要适配其他系统(如 Swarm),所以并未直接实现 CRI,而是由 CRI-Containerd 这个 shim 来承担。

到了 Containerd 1.1 版本,CRI-Containerd shim 被移除,适配逻辑被作为插件直接集成到 Containerd 主进程中,这使得调用流程更加简洁高效。

随着 CRI 方案的成熟以及其他容器运行时对 CRI 支持的不断完善,Kubernetes 社区在2020年7月启动了移除 dockershim 的计划。在 Kubernetes 1.20 版本中,kubelet 中内置的 dockershim 代码被分离,并将其标记为“维护模式”。尽管此时仍然可以使用 dockershim,但已经在 1.24 版本中彻底移除它,dockershim由

同时社区也提供相应的命令行工具crictl来与CRI进行交互

命令行盘点

其中Docker、ctr、nerdctl 和 crictl 都是容器管理工具,但它们各自的设计目标和使用场景有所不同。以下是它们的对比:

Docker

  • 简介:Docker 是一个流行的容器管理工具,提供了从构建、运行到管理容器的完整生态系统。
  • 功能:支持容器镜像构建、镜像分发(Docker Hub 集成)、容器生命周期管理(启动、停止、删除等)。
  • 接口:提供强大且用户友好的 CLI 和 API 接口,适合开发人员和运维人员快速上手。
  • 特点
    • 有丰富的生态系统(如 Docker Compose)。
    • 支持复杂的容器编排功能(如 Swarm)。
    • 包含额外的守护进程(dockerd),需要与容器运行时协作。

ctr

  • 简介ctr 是 containerd 提供的原生 CLI,用于直接与 containerd 交互。
  • 功能ctr 可以拉取镜像、运行容器、管理容器和镜像存储等基本功能,但功能相对简化,更适合低级操作和调试。
  • 接口:与 Docker 不同,ctr 面向 containerd 用户,没有像 Docker 那样的高级 API 和生态支持。
  • 特点
    • 适合需要直接操作 containerd 的开发者和系统集成商。
    • 不支持镜像构建等高级功能(containerd 专注于容器运行时管理)。

nerdctl

  • 简介nerdctl 是一个基于 containerd 的命令行工具,支持 Docker 风格的 CLI 命令。
  • 功能:提供 Docker 类似的用户体验,可以直接用来运行容器、构建镜像、支持 CNI 网络插件和卷等。
  • 接口:与 Docker 的 CLI 非常相似,使得从 Docker 迁移到 nerdctl 相对简单。
  • 特点
    • 不需要 Docker 守护进程,仅依赖 containerd。
    • 可以与 Kubernetes(使用 containerd 作为运行时)很好地集成,且支持与 nerdctl 的 Docker Compose 类似功能。

crictl

  • 简介crictl 是一个用于容器运行时接口(CRI)的命令行工具,专为 Kubernetes 容器管理设计。
  • 功能:主要用于与 CRI 兼容的容器运行时(如 containerd 和 CRI-O)进行交互,包括管理 Pod、镜像和容器。
  • 接口crictl 的命令集更偏向 Kubernetes 集群的调试和管理,与 Docker 和 nerdctl 相比,更专注于 Kubernetes 场景。
  • 特点
    • 集成和兼容 Kubernetes,通常用来调试或管理容器和 Pod 运行状态。
    • 不能用来构建镜像,专注于容器和 Pod 的生命周期管理。

前面我们了解了可以使用 docker、ctr、crictl 这些命令行工具进行容器管理,接下来我们针对以下几个场景做横向对比:

  1. 镜像相关操作
镜像相关功能 docker CLI *containerd ctr crictl nerdctl
显示本地镜像列表 docker images ctr i ls crictl images nerdctl images
下载镜像 docker pull ctr i pull crictl pull nerdctl pull
上传镜像 docker push ctr i push nerdctl push
删除本地镜像 docker rmi ctr i rm crictl rmi nerdctl rmi
查看镜像详情 docker inspect crictl inspecti nerdctl inspect
重命名镜像 docker tag ctr i tag nerdctl tag
导出镜像 docker export ctr i export nerdctl save
导入镜像 docker import ctr i import nerdctl load
构建镜像 docker build nerdctl build
  1. 容器相关操作
容器相关功能 docker CLI containerd ctr crictl nerdctl
显示容器列表 docker ps ctr c ls crictl ps nerdctl ps
创建容器 docker create ctr c create crictl create nerdctl create
启动容器 docker start ctr t start crictl start nerdctl start
停止容器 docker stop crictl stop nerdctl stop
删除容器 docker rm ctr c rm crictl rm nerdctl rm
查看容器详情 docker inspect ctr c info crictl inspect nerdctl inspect
attach docker attach ctr t attach crictl attach nerdctl attach
exec docker exec ctr t exec crictl exec nerdctl exec
logs docker logs crictl logs nerdctl logs
stats docker stats ctr t metrics crictl stats nerdctl stats
  1. Pod 相关操作
Pod相关功能 docker CLI containerd ctr crictl nerdctl
显示Pod列表 crictl pods
查看Pod详情 crictl inspectp
运行Pod crictl runp
停止Pod crictl stopp
删除Pod crictl rmp

总结

  • Docker:用户友好,功能全面,适合独立容器管理和开发环境。
  • ctr:轻量、低级工具,适合直接与 containerd 交互,主要用于调试和底层管理。
  • nerdctl:兼容 Docker CLI,适合那些想使用 Docker 命令但又希望直接使用 containerd 的用户。
  • crictl:Kubernetes 环境下的调试工具,专注于 CRI 兼容的运行时,不适合一般的开发场景。

其中nerdctl 是轻量化的 containerd 替代方案,crictl 是基于 Kubernetes CRI交互的Pod管理工具。

Containerd使用指南

nerdctl 是一个与 docker cli 风格兼容的 containerd 客户端工具,而且直接兼容 docker compose 的语法的,这就大大提高了直接将 containerd 作为本地开发、测试或者单机容器部署使用的效率。

1. 安装

同样直接在 GitHub Release 页面下载对应的压缩包解压到 PATH 路径下即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 如果没有安装 containerd,则可以下载 nerdctl-full-<VERSION>-linux-amd64.tar.gz 包进行安装
➜ ~ wget https://github.com/containerd/nerdctl/releases/download/v1.7.5/nerdctl-1.7.5-linux-amd64.tar.gz

➜ ~ mkdir -p /usr/local/containerd/bin/ && tar -zxvf nerdctl-1.7.5-linux-amd64.tar.gz nerdctl && mv nerdctl /usr/local/containerd/bin/
➜ ~ ln -s /usr/local/containerd/bin/nerdctl /usr/local/bin/nerdctl
➜ ~ nerdctl version
WARN[0000] unable to determine buildctl version: exec: "buildctl": executable file not found in $PATH
Client:
Version: v1.7.5
OS/Arch: linux/amd64
Git commit: cffed372371dcbea3dc9a646ce5a913fc1c09513
buildctl:
Version:

Server:
containerd:
Version: v2.0.0-beta.2-33-g96bf529cb
GitCommit: 96bf529cbf55940ddb96bb8adc8be51b11922ebb
runc:
Version: 1.1.4
GitCommit: v1.1.4-0-g5fd4c4d

安装完成后接下来学习下 nerdctl 命令行工具的使用。

2. 命令简介

容器管理

run

docker run 类似可以使用 nerdctl run 命令运行容器,例如:

1
2
3
➜  ~ nerdctl run -d -p 80:80 --name=nginx --restart=always nginx:alpine
docker.io/library/nginx:alpine: resolved |++++++++++++++++++++++++++++++++++++++|
index-sha256:bead42240255ae1485653a956ef41c9e458eb077fcb6dc664cbc3aa9701a05ce: done |++++++++++++++++++++++++++++++++++++++| manifest-sha256:ce6ca11a3fa7e0e6b44813901e3289212fc2f327ee8b1366176666e8fb470f24: done |++++++++++++++++++++++++++++++++++++++| config-sha256:7ce0143dee376bfd2937b499a46fb110bda3c629c195b84b1cf6e19be1a9e23b: done |++++++++++++++++++++++++++++++++++++++| elapsed: 5.3 s total: 3.1 Ki (606.0 B/s) 6e489777d2f73dda8a310cdf8da9df38353c1aa2021d3c2270b30eff1806bcf8

可选的参数使用和 docker run 基本一直,比如 -i-t--cpus--memory 等选项,可以使用 nerdctl run --help 获取可使用的命令选项:

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
[root@192 containerd]# nerdctl run --help
Run a command in a new container. Optionally specify "ipfs://" or "ipns://" scheme to pull image from IPFS.

Usage: nerdctl run [flags] IMAGE [COMMAND] [ARG...]

Flags:
--add-host strings Add a custom host-to-IP mapping (host:ip)
--blkio-weight uint16 Block IO (relative weight), between 10 and 1000, or 0 to disable (default 0)
--cap-add strings Add Linux capabilities
--cap-drop strings Drop Linux capabilities
--cgroup-conf strings Configure cgroup v2 (key=value)
--cgroup-parent string Optional parent cgroup for the container
--cgroupns string Cgroup namespace to use, the default depends on the cgroup version ("host"|"private") (default "private")
--cidfile string Write the container ID to the file
--cosign-certificate-identity string The identity expected in a valid Fulcio certificate for --verify=cosign. Valid values include email address, DNS names, IP addresses, and URIs. Either --cosign-certificate-identity or --cosign-certificate-identity-regexp must be set for keyless flows
--cosign-certificate-identity-regexp string A regular expression alternative to --cosign-certificate-identity for --verify=cosign. Accepts the Go regular expression syntax described at https://golang.org/s/re2syntax. Either --cosign-certificate-identity or --cosign-certificate-identity-regexp must be set for keyless flows
--cosign-certificate-oidc-issuer string The OIDC issuer expected in a valid Fulcio certificate for --verify=cosign, e.g. https://token.actions.githubusercontent.com or https://oauth2.sigstore.dev/auth. Either --cosign-certificate-oidc-issuer or --cosign-certificate-oidc-issuer-regexp must be set for keyless flows
--cosign-certificate-oidc-issuer-regexp string A regular expression alternative to --certificate-oidc-issuer for --verify=cosign. Accepts the Go regular expression syntax described at https://golang.org/s/re2syntax. Either --cosign-certificate-oidc-issuer or --cosign-certificate-oidc-issuer-regexp must be set for keyless flows
--cosign-key string Path to the public key file, KMS, URI or Kubernetes Secret for --verify=cosign
--cpu-period uint Limit CPU CFS (Completely Fair Scheduler) period
--cpu-quota int Limit CPU CFS (Completely Fair Scheduler) quota (default -1)
--cpu-shares uint CPU shares (relative weight)
--cpus float Number of CPUs
--cpuset-cpus string CPUs in which to allow execution (0-3, 0,1)
--cpuset-mems string MEMs in which to allow execution (0-3, 0,1)
-d, --detach Run container in background and print container ID
--detach-keys string Override the default detach keys (default "ctrl-p,ctrl-q")
--device strings Add a host device to the container
--dns strings Set custom DNS servers
--dns-opt strings Set DNS options
--dns-option strings Set DNS options
--dns-search strings Set custom DNS search domains
--entrypoint stringArray Overwrite the default ENTRYPOINT of the image
-e, --env stringArray Set environment variables
--env-file strings Set environment variables from file
--gpus stringArray GPU devices to add to the container ('all' to pass all GPUs)
--group-add strings Add additional groups to join
--help show help
-h, --hostname string Container host name
--init Run an init process inside the container, Default to use tini
--init-binary string The custom binary to use as the init process (default "tini")
-i, --interactive Keep STDIN open even if not attached
--ip string IPv4 address to assign to the container
--ip6 string IPv6 address to assign to the container
--ipc string IPC namespace to use ("host"|"private")
--ipfs-address string multiaddr of IPFS API (default uses $IPFS_PATH env variable if defined or local directory ~/.ipfs)
--isolation string Specify isolation technology for container. On Linux the only valid value is default. Windows options are host, process and hyperv with process isolation as the default (default "default")
--kernel-memory string Kernel memory limit (deprecated)
-l, --label stringArray Set metadata on container
--label-file strings Set metadata on container from file
--log-driver string Logging driver for the container. Default is json-file. It also supports logURI (eg: --log-driver binary://<path>) (default "json-file")
--log-opt stringArray Log driver options
--mac-address string MAC address to assign to the container
-m, --memory string Memory limit
--memory-reservation string Memory soft limit
--memory-swap string Swap limit equal to memory plus swap: '-1' to enable unlimited swap
--memory-swappiness int Tune container memory swappiness (0 to 100) (default -1) (default -1)
--mount stringArray Attach a filesystem mount to the container
--name string Assign a name to the container
--net strings Connect a container to a network ("bridge"|"host"|"none"|<CNI>) (default [bridge])
--network strings Connect a container to a network ("bridge"|"host"|"none"|"container:<container>"|<CNI>) (default [bridge])
--oom-kill-disable Disable OOM Killer
--oom-score-adj int Tune container’s OOM preferences (-1000 to 1000, rootless: 100 to 1000)
--pid string PID namespace to use
--pidfile string file path to write the task's pid
--pids-limit int Tune container pids limit (set -1 for unlimited) (default -1)
--platform string Set platform (e.g. "amd64", "arm64")
--privileged Give extended privileges to this container
-p, --publish strings Publish a container's port(s) to the host
--pull string Pull image before running ("always"|"missing"|"never") (default "missing")
--rdt-class string Name of the RDT class (or CLOS) to associate the container with
--read-only Mount the container's root filesystem as read only
--restart string Restart policy to apply when a container exits (implemented values: "no"|"always|on-failure:n|unless-stopped") (default "no")
--rm Automatically remove the container when it exits
--rootfs The first argument is not an image but the rootfs to the exploded container
--runtime string Runtime to use for this container, e.g. "crun", or "io.containerd.runsc.v1" (default "io.containerd.runc.v2")
--security-opt stringArray Security options
--shm-size string Size of /dev/shm
--stop-signal string Signal to stop a container (default "SIGTERM")
--stop-timeout int Timeout (in seconds) to stop a container
--sysctl stringArray Sysctl options
--tmpfs stringArray Mount a tmpfs directory
-t, --tty Allocate a pseudo-TTY
--ulimit strings Ulimit options
--umask string Set the umask inside the container. Defaults to 0022
-u, --user string Username or UID (format: <name|uid>[:<group|gid>])
--uts string UTS namespace to use
--verify string Verify the image (none|cosign|notation) (default "none")
-v, --volume stringArray Bind mount a volume
--volumes-from stringArray Mount volumes from the specified container(s)
-w, --workdir string Working directory inside the container

See also 'nerdctl --help' for the global flags such as '--namespace', '--snapshotter', and '--cgroup-manager'.

exec

同样也可以使用 exec 命令执行容器相关命令,例如:

1
2
➜  ~ nerdctl exec -it ea07355852eb date
Mon Nov 18 03:15:06 UTC 2024

ps

使用 nerdctl ps 命令可以列出所有容器,与docker不同的是,其提供了基于namespace的隔离,如果需要查看k8s的容器,需要增加-n k8s.io

1
2
3
4
➜  ~ nerdctl -n k8s.io ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e7f156d31942 docker.io/prom/node-exporter:v1.4.0 "/bin/node_exporter …" 2 days ago Up k8s://node-exporter/node-exporter-5zhjt/main
bf1704937991 hub.cloud.ctripcorp.com/k8s-mirror/pause-amd64:3.1 "/pause" 2 days ago Up k8s://node-exporter/node-exporter-5zhjt

同样可以使用 -a 选项显示所有的容器列表,默认只显示正在运行的容器,不过需要注意的是 nerdctl ps 命令并没有实现 docker ps 下面的 --filter--format--last--size 等选项。

inspect

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
➜  ~ nerdctl -n k8s.io inspect e7f156d31942
[
{
"Id": "e7f156d31942ffec027b48d50d787e18c59e24ae8134f06ee25dab6c74220d83",
"Created": "2024-11-15T08:37:07.845392486Z",
"Path": "/bin/node_exporter",
"Args": [
...
],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"Pid": 22800,
"ExitCode": 0,
"Error": "",
"StartedAt": "",
"FinishedAt": ""
},
"Image": "docker.io/prom/node-exporter:v1.4.0",
...

可以看到显示结果和 docker inspect 也基本一致的。

logs

查看容器日志是我们平时经常会使用到的一个功能,同样我们可以使用 nerdctl logs 来获取日志数据:

1
2
3
4
➜  ~ nerdctl -n k8s.io logs e7f156d31942 |more
ts=2024-11-15T08:37:07.980Z caller=node_exporter.go:182 level=info msg="Starting node_exporter" version="(version=1.4.0, branch=HEAD, revision=7da1321761b3b8dfc9e496e1a60e6a476fec6018)"
ts=2024-11-15T08:37:07.980Z caller=node_exporter.go:183 level=info msg="Build context" build_context="(go=go1.19.1, user=root@83d90983e89c, date=20220926-12:32:56)"
...

同样支持 -f-t-n--since--until 这些选项。

stop

1
2
3
➜  ~ nerdctl -n k8s.io stop e7f156d31942
FATA[0000] 1 errors:
unable to cleanup network for container: e7f156d31942

rm

1
2
3
4
5
6
➜  ~ nerdctl -n k8s.io rm e7f156d31942 
FATA[0000] 1 errors:
container e7f156d31942ffec027b48d50d787e18c59e24ae8134f06ee25dab6c74220d83 is in running status. unpause/stop container first or force removal
➜ ~ nerdctl -n k8s.io rm -f e7f156d31942
ERRO[0000] 1 errors:
failed to load container networking options from specs: unexpected end of JSON input

要强制删除同样可以使用 -f--force 选项来操作。

镜像管理

images

1
2
3
4
➜  ~ nerdctl -n k8s.io images|grep 4a2c72aa0e18
prom/node-exporter <none> 4a2c72aa0e18 2 days ago linux/amd64 24.56MB 11.47MB
<none> <none> 4a2c72aa0e18 2 days ago linux/amd64 24.56MB 11.47MB
prom/node-exporter v1.4.0 4a2c72aa0e18 2 days ago linux/amd64 24.56MB 11.47MB

通过cri方式获取的镜像,containerd会生成三个镜像,分为为tag、sha256以及digest,另外需要注意的是docker images 的一些选项暂未实现,比如 --filter--format

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[root@192 containerd]# nerdctl image --help
Manage images

Usage: nerdctl image [flags]

Commands:
build Build an image from a Dockerfile. Needs buildkitd to be running.
convert convert an image
decrypt decrypt an image
encrypt encrypt image layers
history Show the history of an image
inspect Display detailed information on one or more images.
load Load an image from a tar archive or STDIN
ls List images
prune Remove unused images
pull Pull an image from a registry. Optionally specify "ipfs://" or "ipns://" scheme to pull image from IPFS.
push Push an image or a repository to a registry. Optionally specify "ipfs://" or "ipns://" scheme to push image to IPFS.
rm Remove one or more images
save Save one or more images to a tar archive (streamed to STDOUT by default)
tag Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE

Flags:
-h, --help help for image

pull

1
2
3
4
5
6
➜  ~ nerdctl -n k8s.io pull prom/node-exporter:v1.4.0
docker.io/prom/node-exporter:v1.4.0: resolved |++++++++++++++++++++++++++++++++++++++|
index-sha256:4a2c72aa0e18fcedfa86e4a2ca5cf8e33010246e3125449015b586f4fcde7f01: exists |++++++++++++++++++++++++++++++++++++++|
manifest-sha256:2d9dcdf0b2226f0c3d550a64d2667710265462350a3ba9ebe37d0302bc64af0f: exists |++++++++++++++++++++++++++++++++++++++|
config-sha256:d3e443c987ef405e1be101647873d86b5729c9c47bb1dd1ab59ccb24bc9e322c: exists |++++++++++++++++++++++++++++++++++++++|
elapsed: 0.5 s total: 0.0 B (0.0 B/s)

push

当然在推送镜像之前也可以使用 nerdctl login 命令登录到镜像仓库,然后再执行 push 操作。

可以使用 nerdctl login 进行登录,使用 nerdctl logout 可以注销退出登录。

tag

使用 tag 命令可以为一个镜像创建一个别名镜像:

1
2
3
4
5
6
7
8
9
10
11
➜  ~ nerdctl -n k8s.io images
REPOSITORY TAG IMAGE ID CREATED SIZE
prom/node-exporter <none> 4a2c72aa0e18 2 days ago linux/amd64 24.56MB 11.47MB
<none> <none> 4a2c72aa0e18 2 days ago linux/amd64 24.56MB 11.47MB
prom/node-exporter v1.4.0 4a2c72aa0e18 2 days ago linux/amd64 24.56MB 11.47MB
➜ ~ nerdctl -n k8s.io tag prom/node-exporter:v1.4.0 prom/node-exporter:v1.4.1
➜ ~ nerdctl -n k8s.io images|grep 4a2c72aa0e18
prom/node-exporter v1.4.1 4a2c72aa0e18 4 seconds ago linux/amd64 24.56MB 11.47MB
prom/node-exporter <none> 4a2c72aa0e18 2 days ago linux/amd64 24.56MB 11.47MB
<none> <none> 4a2c72aa0e18 2 days ago linux/amd64 24.56MB 11.47MB
prom/node-exporter v1.4.0 4a2c72aa0e18 2 days ago linux/amd64 24.56MB 11.47MB

save

使用 save 命令可以导出镜像为一个 tar 压缩包。

1
2
3
➜  ~ nerdctl save -o busybox.tar.gz busybox:latest
➜ ~ ls -lh busybox.tar.gz
-rw-r--r-- 1 root root 761K Aug 19 15:19 busybox.tar.gz

rmi

1
2
3
4
5
➜  ~ nerdctl -n k8s.io rmi prom/node-exporter:v1.4.1
Untagged: docker.io/prom/node-exporter:v1.4.1@sha256:4a2c72aa0e18fcedfa86e4a2ca5cf8e33010246e3125449015b586f4fcde7f01
Deleted: sha256:084326605ab6715ca698453e530e4d0319d4e402b468894a06affef944b4ef04
Deleted: sha256:5295faa045209ff9800d03fe1ccc94431dac6a54dac2edc7f6d06ee1e58bb0be
Deleted: sha256:bebdf9d4bf7bd2bd40a7e73e5f09a86a87e34578b622328d5a58bf01353f9def

load

使用 load 命令可以将上面导出的镜像再次导入:

1
2
➜  ~ nerdctl load -i busybox.tar.gz
unpacking docker.io/library/busybox:latest (sha256:0f354ec1728d9ff32edcd7d1b8bbdfc798277ad36120dc3dc683be44524c8b60)...done

使用 -i--input 选项指定需要导入的压缩包。

build

nerdctl build 需要依赖 buildkit 工具。

buildkit 是 Docker 公司开源的一个构建工具包,支持 OCI 标准的镜像构建。它主要包含以下部分:

  • 服务端 buildkitd:当前支持 runc 和 containerd 作为 worker,默认是 runc,我们这里使用 containerd
  • 客户端 buildctl:负责解析 Dockerfile,并向服务端 buildkitd 发出构建请求

构建完成后查看镜像是否构建成功:

1
2
3
4
5
➜  ~ ➜  ~ nerdctl -n k8s.io images
REPOSITORY TAG IMAGE ID CREATED SIZE
prom/node-exporter <none> 4a2c72aa0e18 2 days ago linux/amd64 24.56MB 11.47MB
<none> <none> 4a2c72aa0e18 2 days ago linux/amd64 24.56MB 11.47MB
prom/node-exporter v1.4.0 4a2c72aa0e18 2 days ago linux/amd64 24.56MB 11.47MB

这样我们就使用 nerdctl + buildkitd 轻松完成了容器镜像的构建。

compose

当然,如果你希望在单机环境下使用 Docker Compose,但想使用 containerdnerdctl 提供了对 Compose 功能的兼容支持。我们可以使用以下 nerdctl compose 系列命令来管理 Compose 服务:

  1. 启动 Compose 服务

    1
    nerdctl compose up
  2. 查看服务日志

    1
    nerdctl compose logs
  3. 构建镜像

    1
    nerdctl compose build
  4. 停止并删除服务

    1
    nerdctl compose down

通过结合 containerdnerdctlbuildkit,您可以在镜像构建和容器管理方面完全替代 Docker,提供高效和轻量的容器管理体验。这种方式特别适合希望利用 containerd 的轻量特性,同时又不想放弃 Docker Compose 的用户。

CRICTL使用指南

crictl 可以追溯到 Kubernetes 中的容器运行时接口(CRI)。在 Kubernetes 1.5 版本中引入了 CRI,它定义了 Kubernetes 和容器运行时之间的标准化接口,以便 Kubernetes 可以与不同的容器运行时(如 Docker、containerd、CRI-O 等)进行交互。

由于 Kubernetes 对容器运行时的要求日益严格,需要更多的运行时特性和更高的性能,因此出现了对 CRI 的更高需求。而且,CRI 也为容器运行时的实现提供了标准化的接口,使得开发人员可以更轻松地实现自己的容器运行时。

在这样的背景下,crictl 作为一个命令行工具被开发出来,用于与符合 CRI 标准的容器运行时进行交互。它使得用户可以方便地管理容器和容器镜像,查询容器状态和元数据,并进行容器日志查看等操作。crictl 的出现使得容器运行时的管理和调试变得更加便捷,同时也促进了 CRI 标准的推广和应用。

以下是 crictl 常见的用法和功能:

  1. 容器管理crictl 可以用来启动、停止、删除和查询容器的状态。
  2. 容器镜像管理crictl 可以用来拉取、推送、删除和查询容器镜像。
  3. 容器日志查看crictl 可以用来查看容器的标准输出和标准错误日志。
  4. 容器信息查询crictl 可以用来查询容器的详细信息,如 ID、名称、状态、IP 等。
  5. 容器元数据操作crictl 还可以进行容器元数据的操作,如注解和标签的添加和删除。

crictl 是一个强大的工具,可以帮助您管理容器运行时中的容器和容器镜像,以及了解容器的状态和元数据。它通常用于调试和管理容器化应用程序,并与 Kubernetes 等容器编排系统集成使用。

1. 安装

首先我们需要先安装 crictl 工具,直接从 cri-tools 的 release 页面下载对应的二进制包,解压放入 PATH 路径下即可:

1
2
3
4
5
6
7
8
➜  ~ VERSION="v1.22.0"
➜ ~ wget https://github.com/kubernetes-sigs/cri-tools/releases/download/$VERSION/crictl-$VERSION-linux-amd64.tar.gz
# 如果有限制,也可以替换成下面的 URL 加速下载
# wget https://download.fastgit.org/kubernetes-sigs/cri-tools/releases/download/$VERSION/crictl-$VERSION-linux-amd64.tar.gz
➜ ~ tar zxvf crictl-$VERSION-linux-amd64.tar.gz -C /usr/local/bin
➜ ~ rm -f crictl-$VERSION-linux-amd64.tar.gz
➜ ~ crictl -v
crictl version v1.22.0

到这里证明 crictl 工具安装成功了。

2. 命令简介

crictl 安装完成后,接下来我们来了解下该工具的一些常见使用方法。

首先需要修改下默认的配置文件,默认为 /etc/crictl.yaml,在文件中指定容器运行时和镜像的 endpoint 地址,内容如下所示:

1
2
3
4
5
runtime-endpoint: unix:///var/run/containerd/containerd.sock
image-endpoint: unix:///var/run/containerd/containerd.sock
debug: false
pull-image-on-create: false
disable-pull-on-run: false

配置完成后就可以使用 crictl 命令了。

Pod 管理

通过 crictl pods 命令可以获取当前节点上运行的 Pods 列表,如下所示:

1
2
3
4
5
➜  ~ crictl pods
POD ID CREATED STATE NAME NAMESPACE ATTEMPT RUNTIME
1f617ebf0524c 8 weeks ago Ready node-exporter-ggjwf node-exporter 0 (default)
4b93e3d6e8d1f 2 months ago Ready node-problem-detector-r6ps6 kube-system 0 (default)

还可以使用 --name 参数获取指定的 Pod,也可以根据标签来筛选 Pod 列表:

1
2
3
➜  ~ crictl pods --label app=node-exporter
POD ID CREATED STATE NAME NAMESPACE ATTEMPT RUNTIME
1f617ebf0524c 8 weeks ago Ready node-exporter-ggjwf node-exporter 0 (default)

镜像管理

使用 crictl images 命令可以获取所有的镜像:

1
2
3
➜  ~ crictl images
IMAGE TAG IMAGE ID SIZE
hub.cloud.ctripcorp.com/prom/node-exporter v1.4.0 d3e443c987ef4 11.5MB

同样在命令后面可以加上 -v 参数来显示镜像的详细信息:

1
2
3
4
5
6
7
8
9
➜  ~ crictl images -v
ID: sha256:d3e443c987ef405e1be101647873d86b5729c9c47bb1dd1ab59ccb24bc9e322c
RepoTags: docker.io/prom/node-exporter:v1.4.0
RepoDigests: docker.io/prom/node-exporter@sha256:4a2c72aa0e18fcedfa86e4a2ca5cf8e33010246e3125449015b586f4fcde7f01
Size: 11474514
Username: nobody


......

容器管理

使用 crictl ps 命令可以获取正在运行的容器列表:

1
2
3
➜  ~ crictl ps
CONTAINER IMAGE CREATED STATE NAME ATTEMPT POD ID
e7f156d31942f d3e443c987ef4 2 days ago Running main 2 bf1704937991a node-exporter-5zhjt

还有更多其他可选参数,使用 -s 选项按照状态进行过滤:

1
2
3
➜  ~ crictl ps -s Exited
CONTAINER IMAGE CREATED STATE NAME ATTEMPT POD ID POD
22a0adacf702f d3e443c987ef4 2 days ago Exited main 1 bf1704937991a node-exporter-5zhjt

crictl 也有类似 exec 的命令支持,比如在容器 ID 为 e7f156d31942f 的容器中执行一个 whoami 命令:

1
2
➜  ~  crictl exec -it e7f156d31942f whoami
root

还可以获取容器日志信息:

1
2
3
4
5
➜  ~ crictl logs e7f156d31942f 
ts=2024-11-15T08:37:07.980Z caller=node_exporter.go:182 level=info msg="Starting node_exporter" version="(version=1.4.0, branch=HEAD, revision=7da1321761b3b8dfc9e496e1a60e6a476fec6018)"
ts=2024-11-15T08:37:07.980Z caller=node_exporter.go:183 level=info msg="Build context" build_context="(go=go1.19.1, user=root@83d90983e89c, date=20220926-12:32:56)"
ts=2024-11-15T08:37:07.980Z caller=node_exporter.go:185 level=warn msg="Node Exporter is running as root user. This exporter is designed to run as unprivileged user, root is not required."
...

kubectl logs 类似于,还可以使用 -f 选项来 Follow 日志输出,--tail N 也可以指定输出最近的 N 行日志。

使用 crictl stats 命令可以列出所有或者单一容器资源的使用情况:

1
2
3
4
➜  ~ crictl stats e7f156d31942f 

CONTAINER NAME CPU % MEM DISK INODES
e7f156d31942f main 0.00 37.13MB 0B 18

此外镜像和容器相关的一些操作也都支持,更多信息请参考 kubernetes-sigs/cri-tools

链接

  1. https://kubernetes.io/zh-cn/docs/setup/production-environment/container-runtimes/
  2. https://zhuanlan.zhihu.com/p/666200234
  3. https://www.qikqiak.com/k8strain2/containerd/runtime/
  4. https://github.com/kubernetes/cri-api/tree/master/pkg/apis
  5. http://www.opennaru.com/kubernetes/containerd/
  6. https://www.slideshare.net/AkihiroSuda/container-plumbing-days-2023-why-was-nerdctl-made
  7. https://www.alibabacloud.com/blog/a-discussion-on-container-runtime---starting-with-dockershim-being-deleted-by-kubernetes_600118