容器

容器术语解析

容器(Container):容器是一种轻量级、可移植、自包含的软件包装方式,它包含了运行一个软件所需要的所有内容,包括代码、运行时环境、库、环境变量和配置文件。

镜像(Image):镜像是创建容器的模板,它是一个只读的文件,包含了运行一个容器所需要的代码以及依赖的环境。可以把它理解为容器的“蓝图”。

容器镜像(Container Image):这是“镜像”和“容器”两个概念的结合,指的是用来创建容器的镜像。

镜像层(Image Layer):Docker使用联合文件系统(Union File System)来构建一个镜像,每一层都是只读的,每一层都代表镜像构建过程的一部分。每一层都会增加镜像的大小。

注册中心(Registry):注册中心是存储镜像的地方。Docker Hub是最常用的公开注册中心,也可以设置私有注册中心。

仓库(Repository):在特定的注册中心中,仓库是用来存储和组织镜像的地方。一个仓库可以包含多个版本的同一个镜像,每个版本都有一个不同的标签。

标签(Tag):标签是指向特定镜像版本的可读名称。一个镜像可以有多个标签,最常见的标签是“latest”,指向仓库中最新的镜像。

基础镜像(Base Image):基础镜像是一个没有父镜像的镜像,通常包含一个操作系统。其他镜像可以基于基础镜像来创建,添加更多的层。

平台镜像(Platform Image):平台镜像通常是指为特定平台或者框架预先构建的镜像,比如Node.js镜像、Python镜像等。这些镜像通常基于某个操作系统的基础镜像,然后添加了运行特定平台或框架所需要的软件和库。

层(Layer):在Docker的上下文中,层通常指的是镜像层。在Kubernetes的上下文中,层可能指的是网络层、应用层等概念,具体含义取决于上下文。

Docker基本概念

Docker Engine 和containerd 以及 runc 的关系是什么?

Docker Engine、containerd 和 runc 是容器技术栈中的三个重要组件,它们之间的关系如下:

Docker Engine:Docker Engine 是 Docker 的核心组件,它提供了一个完整的容器运行时环境和管理工具。Docker Engine 包括了容器的构建、分发和运行等功能,并且提供了 Docker CLI(命令行界面)来管理和操作容器。在 Docker Engine 中,它内置了 containerd 和 runc。

containerd:containerd 是一个轻量级的容器运行时,它负责管理容器的生命周期,包括容器的创建、启动、停止、销毁等操作。containerd 提供了一组 API 接口,供上层工具(如 Docker Engine)调用,以实现对容器的管理和控制。containerd 的设计目标是为了提供高性能和可扩展性,同时保持简洁和可靠性。

runc:runc 是一个用于运行容器的工具,它是由 Open Container Initiative(OCI)开发的容器运行时规范的参考实现之一。runc 负责根据容器的配置和参数,创建和管理容器的隔离环境,并运行容器中的应用程序。runc 提供了一个轻量级的进程隔离工具,它使用 Linux 内核提供的命名空间(namespace)和控制组(cgroup)等特性来实现容器的隔离和资源管理。

因此,Docker Engine 使用 containerd 作为其底层容器运行时,containerd 则使用 runc 来运行容器。Docker Engine 提供了更高级的容器管理功能,如镜像管理、网络管理、存储管理等,而 containerd 和 runc 则提供了底层的容器运行时支持,负责容器的生命周期管理和隔离环境的创建与管理。通过这种层级关系,Docker Engine 在 containerd 和 runc 的基础上提供了更丰富的功能和用户友好的接口。

如何理解 Linux 内核提供的命名空间(namespace)和控制组(cgroup)

Linux 内核提供的命名空间(namespace)和控制组(cgroup)是两个关键的特性,用于实现容器化环境中的隔离和资源管理。

命名空间(namespace)是一种隔离机制,它将操作系统的全局资源分割成多个独立的部分,每个部分都有自己独立的视图。通过使用不同类型的命名空间,可以实现对进程、网络、文件系统、用户、IPC(进程间通信)等资源的隔离。每个命名空间中的进程只能看到和访问属于同一命名空间的资源,对其他命名空间的资源是不可见的。这种隔离机制使得容器内的进程能够在一个相对独立的运行环境中执行,互不干扰。

以下是常见的命名空间类型:

PID 命名空间:隔离进程 ID(PID),使得容器内的进程在不同的命名空间中具有不同的进程 ID。 网络命名空间:隔离网络栈,每个容器有自己的网络接口、IP 地址、路由表和防火墙规则。 挂载命名空间:隔离文件系统挂载点,使得容器内的文件系统与主机或其他容器的文件系统相互隔离。 IPC 命名空间:隔离进程间通信资源,如消息队列、信号量和共享内存。 UTS 命名空间:隔离主机名和域名。 用户命名空间:隔离用户和用户组标识。 控制组(cgroup)是一种资源管理机制,用于限制和控制进程组的资源使用。通过将进程组绑定到特定的控制组,可以对其资源使用进行限制,例如 CPU、内存、磁盘、网络带宽等。控制组使得容器可以对资源进行精确的管理和分配,确保各个容器之间的资源不会互相干扰。

控制组支持层次化结构,可以将进程组组织成树状结构,并为每个层次设置资源限制。这样,容器内的进程可以根据其所在的控制组获取相应的资源限制,实现资源的隔离和管理。

通过命名空间和控制组的组合使用,Linux 内核提供了强大的容器化支持,使得容器能够在隔离的运行环境中执行,并对资源进行有效的管理和控制。这为容器技术的实现和广泛应用提供了基础。

docker-init?

然而,Docker 在其容器中使用了一个称为 “docker-init” 的进程作为容器的初始进程。

“Docker init” 进程是一个特殊的进程,它在容器启动时作为容器的第一个进程运行。它的主要功能是启动容器中的其他进程,并负责处理容器的生命周期管理。

具体来说,”docker-init” 进程会执行以下操作:

创建 PID 命名空间并切换到该命名空间,以隔离容器内的进程 ID。

设置正确的进程信号处理程序,以处理来自 Docker 主进程的信号(如 SIGTERM)。

通过启动容器的主进程,将控制转移到容器中的应用程序。

在容器的主进程退出时,清理容器内的资源,并确保容器内的所有进程都正确终止。

“Docker init” 进程的目的是确保容器中的进程能够正确处理信号、以正确的方式启动和停止,并避免孤儿进程的产生。

请注意,”docker-init” 进程并不是一个独立的进程管理器,而是 Docker 引擎在容器运行时启动的一部分。它提供了容器进程管理的基础功能,但没有提供像 Tini 进程管理器那样的复杂信号处理和进程管理功能。如果需要更高级的进程管理功能,可以选择使用像 Tini 这样的第三方进程管理器来增强容器的生命周期管理。

Docker 存储

使用 Docker NFS 卷的方式来挂载 NFS 共享到容器

Docker可以使用NFS卷来挂载NFS共享到容器中,这样可以让容器直接访问NFS共享中的文件和目录。这种方式的优点是可以让多个容器共享同一份数据,或者让容器和宿主机共享数据。

以下是使用Docker NFS卷来挂载NFS共享到容器的步骤:

首先,需要在宿主机上安装NFS客户端。在Ubuntu上,可以使用以下命令来安装:

sudo apt-get update
sudo apt-get install nfs-common

然后,需要创建一个Docker卷来挂载NFS共享。可以使用docker volume create命令来创建一个NFS卷:

docker volume create --driver local \
    --opt type=nfs \
    --opt o=addr=nfs_server,rw \
    --opt device=:/path/to/dir \
    nfs_volume

这个命令会创建一个名为nfs_volume的Docker卷,这个卷会挂载NFS服务器上的/path/to/dir目录。需要将nfs_server和/path/to/dir替换为实际的NFS服务器地址和目录。

最后,可以在创建容器时使用-v参数来挂载这个NFS卷:

docker run -d -v nfs_volume:/data some_image

这个命令会启动一个新的容器,这个容器会挂载nfs_volume卷到/data目录。容器中的应用可以通过/data目录来访问NFS共享中的文件和目录。

这样做的原因是,使用NFS卷可以让容器直接访问NFS共享中的文件和目录,而不需要将这些文件和目录复制到容器中。这样可以让多个容器共享同一份数据,或者让容器和宿主机共享数据。此外,使用NFS卷还可以让在不修改容器的情况下,动态地改变容器可以访问的文件和目录。

Tags: Kubernetes
Share: X (Twitter) Facebook LinkedIn