1. 容器概念介绍

对于初学者来说并不太容易理解什么是容器,这里举个例子。想象一下,我们把系统安装在一个U盘里,此系统安装好了MySQL。然后我们把这个U盘插入到一台正在运行的宿主机上,这台宿主机上并没有安装MySQL,如图1-1所示。

图1-1 了解容器和镜像
然后把U盘里的mysqld进程“拽”到物理机上运行。但是这个mysqld进程只能适应U盘里的生态环境(系统环境),并不能适应物理机的生态环境(系统)。所以找一个类似于气球的东西把mysqld包裹保护起来,这个mysqld进程就可以正常运行了,因为它感觉现在所处的生态环境,仍然是U盘里的生态环境。
但是这个进程mysqld运行的时候是需要消耗CPU和内存的,这个U盘里并不提供CPU和内存,所以气球里的mysqld进程可以从物理机里吸收CPU和内存,作为维持它正常运行的“养分”。

那么这个类似气球的东西就是容器,U盘就是镜像。在Linux下安装软件包的时候经常会遇到各种包依赖,或者有人不会在Linux系统(比如 Ubuntu、centos)里安装软件包。这样以后就不需要安装和配mysql了,直接把这个“U盘”插到电脑上,然后生成一个容器出来,这样就有 mysql 对外提供服务了,是不是很方便?

所谓镜像,就是安装了系统的硬盘文件,这个系统里安装了想要运行的程序,比如 mysql、nginx,并规定好使用这个镜像所生成的容器里面运行什么进程。这里假设有一个安装了 mysql的镜像,如图 1-2 所示。

图1-2 了解容器和镜像
在服务器上有一个 mysql 的镜像(即已经安装好了 mysql),然后使用这个镜像生成一个容器。 这个容器里只运行一个 mysqld 进程。容器里的 mysqld 进程直接从物理机吸收 CPU 和内存以维持 它的正常运行。

以后需要什么应用就直接拉取什么镜像下来,然后使用这个镜像生成容器。比如需要对外提供 mysql 服务,那么就拉取一个 mysql 镜像,然后生成一个 mysql 容器。如果需要对外提供 web 服务,那么就拉取一个 nginx 镜像,然后生成一个 nginx 容器。 一个镜像是可以生成很多个容器的,如图 1-3 所示。

图1-3 一个镜像可以生成多个容器

当然了不管是镜像还是容器,都要遵从一个标准,比如镜像的结构是怎么样的,容器的状态、怎么创建和删除容器等都要遵从一个标准,这个标准就是OCI(Open Container Initiative)。

2. 了解什么是runtime

前面说了一大堆,什么是容器什么是创建镜像。如同要聊天得安装微信、QQ,练习打字得安装office一样,那么要管理镜像和容器一定是需要安装一种软件的,这个软件叫做runtime,翻译成中文叫做运行时。

说到了运行时,又涉及到了两类:

  1. 低级别运行时(low-level runtime),比如lxc、runc、gvisor、kata等
    对于低级别的运行时来说,只能简单的管理容器,却是不能管理镜像的。

  2. 高级别运行时(high-level runtime),比如docker、containerd、podman等
    对于高级别运行时来说,他们是通过调用低级别运行时来管理容器,一般可以是runc作为低级别运行时,他们还可以管理镜像。

这里我们重点讲讲containerd的使用。

当我们安装好containerd之后,会有一个服务器进程containerd,他所使用的配置文件是/etc/containerd/config.toml。这个服务器进程会生成一个接口并客户端来连接(通过gRPC协议),这个接口就是/var/run/containerd/containerd.sock。

因为在操作系统里目录/var/run是/run的软连接(快捷方式),所以/run/containerd/containerd.sock和/var/run/containerd/containerd.sock是同一个文件。

图1-4 containerd
kubernetes的kubelet就是通过gRPC协议连接/var/run/containerd/containerd.sock,是它的一个客户端。
那么连接containerd的客户端还有crictl、ctr、dockerd、nerdctl等。对,你没看错,dockerd也是containerd的一个客户端,如下图。

所敲的docker命令,它只是一个客户端工具(命令行接口CLI),需要连接到服务器进程dockerd(这里docker后面有个d,表示守护进程daemon),默认连接到本机的dockerd进程。如果docker客户端需要连接到其他机器的服务器进程dockerd,需要使用docker -H指定远端机器的IP或者通过定义环境变量DOCKER_HOST。

dockerd又作为containerd的客户端连接到/run/containerd/containerd.sock。

1
2
3
[root@vms101 ~]# ps aux | grep -v grep | grep docker
root 1814 0.3 1.5 1088516 60640 ? Ssl 00:48 0:00 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
[root@vms101 ~]#

crictl和ctr也可以作为containerd的客户端,但是这两个客户端工具非常不好用。所以在客户端nerdctl出来之前,可以说containerd对用户层面来说,是非常难用的。

nerdctl就是一个非常好用的客户端工具了,它的命令的使用和docker命令非常相似,所以后续我们就开始使用nerdctl来管理容器和镜像。

不管是哪个客户端连接containerd创建容器,先调用runc把容器创建出来,然后一个进程containerd-shim维护和管理这个容器,如下图。

图1-6 containerd
当然nerdctl也有不方便的地方,比如搭建镜像仓库、构建镜像等方面做的并不完善,比如构建镜像必须要安装buildkitd,且没法基于本地镜像等构建镜像。所以呢,这两块内容我们依然使用docker来完成。