如果想在 V2EX 获得更好的推广效果,欢迎了解 PRO 会员机制:
https://www.v2ex.com/pro/about

如果你经常使用铜币置顶主题,持有 V2EX Solana Token 会在每日签到时获得额外铜币:
https://www.v2ex.com/solana
GrapeCityChina
V2EX  ›  推广

Docker 与 k8s 的恩怨情仇(二)—用最简单的技术实现“容器”

  •  
  •   GrapeCityChina · Jun 23, 2021 · 1140 views
    This topic created in 1879 days ago, the information mentioned may be changed or developed.

    上次我们说到 PaaS 的发展历史,从 Cloud Foundry 黯然退场,到 Docker 加冕,正是 Docker“一点点”的改进,掀起了一场蝴蝶效应,煽动了整个 PaaS 开源项目市场风起云涌。

    为了让大家更好的理解“容器”这个 PaaS 中最核心的技术,本篇将从一个进程开始,为大家讲述容器到底是什么,Cloud Foundry 等 PaaS“前浪”是如何实现容器的。

    进程 vs 容器

    以 Linux 操作系统为例,计算机里运行的进程是程序执行之后,从磁盘的二进制文件,到内存、寄存器、堆栈指令等等所用到的相关设备状态的一个集合,是数据和状态综合的动态表现。而容器技术的目标就是对一个进程的状态和数据进行的隔离和限制。可以说,容器的本质其实就是 Linux 中的一个特殊进程。这个特殊的进程,主要靠 Linux 系统提供的两个机制来实现,这里先回顾一下。

    Namespace

    Linux Namespace 是 Linux 内核的一项功能,该功能对内核资源进行分区,以使一组进程看到一组资源,而另一组进程看到另一组资源。该功能通过为一组资源和进程具有相同的名称空间而起作用,但是这些名称空间引用了不同的资源。资源可能存在于多个空间中。此类资源的示例是进程 ID,主机名,用户 ID,文件名以及与网络访问和进程间通信相关的某些名称。其种类列举如下:

    1. Mount namespaces
    2. UTS namespaces
    3. IPC namespaces
    4. PID namespaces
    5. Network namespaces
    6. User namespaces

    超级进程

    在 Linux 操作系统中,PID==1 的进程被称为超级进程,它是整个进程树的 root,负责产生其他所有用户进程。所有的进程都会被挂在这个进程下,如果这个进程退出了,那么所有的进程都被 kill 。

    隔离 & 限制

    刚才我们提到了隔离和限制,具体指的是什么呢?

    隔离

    以 Docker 为例( Cloud Foundry 同理,我的机器上没有安装后者),我们可以执行下列的命令创建一个简单的镜像: $ docker run -it busybox /bin/sh

    这条语句执行的内容是:用 docker 运行一个容器,容器的镜像名称叫 busybox,并且运行之后需要执行的命令是 /bin/sh,而-it 参数表示需要使用标准输入 stdin 和分配一个文本输入输出环境 tty 与外部交互。通过这个命令,我们就可以进入到一个容器内部了,分别在容器中和宿主机中执行 top 命令,可以看到以下结果:

    7.png

    (在容器内外执行 top 语句的返回结果)

    从中可以发现,容器中的运行进程只剩下了两个。一个是主进程 PID==1 的 /bin/sh 超级进程,另一个是我们运行的 top 。而宿主机中的其余的所有进程在容器中都看不到了——这就是隔离。

    5.jpg

    (被隔离的 top 进程,图片来自网络)

    原本,每当我们在宿主机上运行一个 /bin/sh 程序,操作系统都会给它分配一个进程编号,比如 PID==100 。而现在,我们要通过 Docker 把这个 /bin/sh 程序运行在一个容器中,这时候,Docker 就会在这个 PID==100 创建时施加一个“障眼法”,让他永远看不到之前的 99 个进程,这样运行在容器中的程序就会当自己是 PID==1 的超级进程。

    而这种机制,其实就是对被隔离的程序的进程空间做了手脚,虽然在容器中显示的 PID==1,但是在原本的宿主机中,它其实还是那个 PID==100 的进程。所使用到的技术就是 Linux 中的 Namespace 机制。而这个机制,其实就是 Linux 在创建进程时的一个可选参数。在 Linux 中,创建一个线程的函数是(这里没写错就是线程,Linux 中线程是用进程实现的,所以可以用来描述进程):

    int pid = clone(main_function, stack_size, SIGCHLD, NULL);

    如果我们给这个方法添加一个参数比如 CLONE_NEWPID:

    int pid = clone(main_function, stack_size, CLONE_NEWPID | SIGCHLD, NULL);

    那么这个新的进程就会看到一个全新的进程空间,在这个空间里,因为该空间中仅有这一个进程,所以它自己的 PID 就等于 1 了。

    这样一个过程就是 Linux 容器最基本的隔离实现了。

    限制

    光有 namespace 隔离的容器就和没有电脑的程序员一样,是残缺不全的。

    如果我们只隔离不限制,笼子里面的程序照样占用系统资源,访问依旧自由。为了给有了隔离性的程序添加资源限制,就用到了第二个技术:cgroups

    cgroups 本来是 google 的工程师在 2006 年开发的一个程序,全称是 Linux Control Group,是 Linux 操作系统中用来限制一个进程组能使用资源的上限,包括 CPU 、内存、磁盘、网络带宽等的功能。

    通过 Cgroups 给用户暴露的 API 文件系统,用户可以通过修改文件的值来操作 Cgroups 功能。

    8.png

    (被 cgroup 限制的进程,图片来自网络)

    在 Linux 系统( Ubuntu )中可以执行以下命令查看 CgroupsAPI 文件:

    mount -t Cgroups

    9.png

    ( cgroup 文件系统)

    从上图可以看到,系统中存在包括 cpu 、内存、IO 等多个 Cgroups 配置文件。

    我们以 CPU 为例来说明以下 Cgroups 这个功能。对 CPU 的限制需要引入两个参数 cfs_period 和 cfs_quota,我们为了给活字格公有云 Docker 内的程序限制 CPU 时,会经常操作这两个参数。这两个参数是组合使用的,意思是在长度为 cfs_period 时间内,程序组只能分到总量为 cfs_quota 的 CPU 时间。也就是说 cfs_quota / cfs_period == cpu 使用上限。

    要想限制某个进程的 CPU 使用,可以在 /sys/fs/Cgroups/cpu 目录下,执行以下命令创建一个文件夹 container:

    /sys/fs/Cgroups/cpu/ > mkdir container

    此时,我们可以发现系统自动在 container 目录下生成的一系列 CPU 限制的参数文件,这是 Linux 系统自动生成的,表示我们成功为 CPU 创建了一个控制组 container:

    10.png

    (默认的 CPU 资源文件列表)

    为了展示 CPU 限制的实际效果,让我们执行一个用以下脚本创建的死循环:

    while : ; do : ; done &

    我们在 top 命令结果中会看到返回的进程为 398,因为死循环,cpu 占用率为 100%:

    11.png

    (死循环的进程占了 100% CPU )

    这时,我们再看下 container 目录下的 cpu.cfs_quota_us 和 cpu.cfs_period_us:

    12.png

    (默认情况下 CPU 的限制参数) 这里是没有做过限制时的样子。cfs_quota_us 为-1 说明并没有限制 CPU 的运行上限。现在我们改一下这个值:

    echo 20000 > /sys/fs/Cgroups/cpu/container/cpu.cfs_quota_us

    然后将之前的进程 398 写入这个控制组的 tasks 文件中:

    echo 398 > /sys/fs/Cgroups/cpu/container/tasks

    这时再 top 一下,发现刚才的死循环的 CPU 使用率变成 20%了,CPU 使用资源限制开始生效。

    13.png

    (使用 cgroup 限制 CPU 使用量的死循环进程)

    以上,就是通过 Cgroups 功能对容器做限制的原理了。同理,我们可以用此方法,对一个进程的内存、带宽等做限制,如果这个进程是一个容器进程,一个资源受控的容器基本就可以展现在你面前了事实上,在云时代的早期,Cloud Foundry 等“前浪”都是采用这种方式创建和管理容器。相比于后来者,Cloud Foundry 等在容器的隔离和限制上,虽相对简单、易于理解,但在一些场景下难免会受到制约。

    这里要做一个特别的说明,只有 Linux 中运行的容器是通过对进程进行限制模拟出来的结果,Windows 和 Mac 下的容器,都是通过 Docker Desktop 等容器软件,操作虚拟机模拟出来的“真实”的虚拟容器。

    小结

    本节从容器的原理和 Linux 下实现容器隔离和限制的技术入手,介绍了在云时代早期 Cloud Foundry 等 Paas 平台的容器原理。下一节将继续为大家介绍 Docker 在 Cloud Foundry 容器基础之上又做了什么改动,是如何解决 Cloud Foundry 致命短板的。

    如果您想了解 Docker 如何搅动风云,Docker 的这个容器又和传统虚拟机有何区别?

    敬请期待下篇,我们继续唠。

    No Comments Yet
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   870 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 69ms · UTC 21:15 · PVG 05:15 · LAX 14:15 · JFK 17:15
    ♥ Do have faith in what you're doing.