runV

容器技术最近发展迅速,它为应用程序打包和资源的利用改进提供了很多便利。虽然它带来了一些好处,但与此同时,基于 LXC 的容器技术也带来了一定的安全性问题。具体来说,多个容器会在一台机器上共享操作系统内核。一旦一个容器试图攻击内核,该主机上的所有工作负荷都会因此受到影响。 对于一些对安全性敏感且有严格要求的场景,纯容器技术存在明显的缺陷。更重要的是,在云时代,多租户技术已经成为了云客户的刚性需求。因此,容器之间的强隔离性必须要能得到严格的保证。换句话说,容器技术需要得到更多安全性上的提升。 runV 是一个基于虚拟机管理程序(OCI)的运行时。它通过虚拟化 guest kernel,将容器和主机隔离开来,使得其边界更加清晰,这种方式很容易就能帮助加强主机和容器的安全性。虽然基于虚拟机管理程序的容器加强了安全性,但它会因虚拟化技术而损失一些性能。这是因为它需要完成更多的工作,比如 initrd.img 的加载、内核的加载、系统进程的启动等,所以它会需要更多的时间来启动容器。

架构

安装前的准备

在安装之前,我们应该记住一件重要的事情:PouchContainer 中的 runV 仅能在物理机上运行,目前尚不支持在嵌套的虚拟机上运行。此外,我们还应该确保已经安装了 containerdpouchd。在体验基于管理程序的容器之前,除了确保完成了上述的事情外,还需要保证预先安装了以下 3 个部分:

  • QEMU:通用的机器模拟器和虚拟器;

  • runV:与 OCI 兼容的运行时二进制文件;

  • hyperstart:基于管理程序的容器的微型 init 服务。

    安装 QEMU

    运行虚拟机需要安装QEMU。我们可以执行以下命令来轻松安装 QEMU 相关工具。

    在安装了 Ubuntu OS 的物理机上,执行:

    sudo apt-get install -y qemu qemu-kvm

    在安装了 RedHat 系列 OS 的物理机上,执行:

    sudo yum install -y qemu qemu-kvm

    安装 runV

    runV 不提供二进制包,我们需要基于其源代码来创建这个二进制文件。

    首先,从 Github 克隆 runv 项目的 v1.0.0 版本:

    mkdir -p $Home/go/src/github.com/hyperhq
    cd $Home/go/src/github.com/hyperhq
    git clone --branch v1.0.0 https://github.com/hyperhq/runv.git
    export GOPATH=$HOME/go

    其次,基于源代码来创建 runv 的二进制包:

    sudo apt-get install autotools-dev
    sudo apt-get install automake
    cd runv
    ./autogen.sh
    ./configure
    sudo make
    sudo make install

    执行完以上命令,runv 的二进制文件就已经存在于路径中了。

    安装 hyperstart

    hyperstart 为基于管理程序的容器提供了 init 任务。我们还需要基于源代码 v1.0.0 来创建 guest kernel 和 initrd.img:

    cd $Home/go/src/github.com/hyperhq
    git clone --branch v1.0.0 https://github.com/hyperhq/hyperstart.git
    cd hyperstart
    ./autogen.sh
    ./configure
    sudo make

    在成功创建内核和 initrd.img 之后,我们应该把 guest kernelinitrd.img 复制到 runv 将要查找的默认目录中:

    mkdir /var/lib/hyper/
    cp build/{kernel,hyper-initrd.img} /var/lib/hyper/

    启动基于管理程序的容器

    安装完 runv 相关工具后,我们需要启动 pouchd。然后我们可以通过命令行工具 pouch,来创建基于管理程序的容器。这里创建的容器具有与主机隔离的独立内核。

    我们可以通过在 create 命令中添加 --runtime 来创建基于管理程序的容器。我们还可以使用 pouch ps 来列出所有容器,包括基于管理程序的容器,其运行时类型是 runv,以及运行时类型为 runc 的基于 runc 的容器。

    $ pouch create --name hypervisor --runtime runv docker.io/library/busybox:latest
    container ID: 95c8d52154515e58ab267f3c33ef74ff84c901ad77ab18ee6428a1ffac12400d, name: hypervisor
    $
    $ pouch ps
    Name         ID       Status    Image                              Runtime
    hypervisor   95c8d5   created   docker.io/library/busybox:latest   runv
    4945c0       4945c0   stopped   docker.io/library/busybox:latest   runc
    1dad17       1dad17   stopped   docker.io/library/busybox:latest   runv
    fab7ef       fab7ef   created   docker.io/library/busybox:latest   runv
    505571       505571   stopped   docker.io/library/busybox:latest   runc

    此外,我们能够检查主机物理机和基于管理程序的容器的不同内核,命令如下所示:

    # one host physical machine
    $ uname -a
    Linux www 4.4.0-101-generic #124-Ubuntu SMP Fri Nov 10 18:29:59 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
    $
    # gen tunnel into hypervisor-based container
    # check inside kernel
    $ pouch start hypervisor -i
    / # uname -a
    Linux 4.12.4-hyper #18 SMP Mon Sep 4 15:10:13 CST 2017 x86_64 GNU/Linux

    事实证明,在上面的实验中,主机物理机中的内核是 4.4.0-101-generic,而基于管理程序的容器中的内核是 4.12.4-hyper。显然,它们在内核上是相互隔离的。

    运行旧版本的内核

    runV(现在是 katacontainers)提供了一种通用方法,它可以提供仍基于 OCI 兼容映像的隔离的 linux 内核。事实上,在 runV 提供的 Guest OS 中,运行其上的 linux 内核是非常新且高级的。但是在使用 runV 时,如何在 Guest OS 中运行旧版本的 linux 内核仍然是业界面临的巨大挑战。

    场景

    使 runV 能够提供运行旧版本 linux 内核(例如 Linux 内核 2.6.32)的 Guest OS 是一个合理的需求。对于整个工业界,有相当多的工作负载是运行在旧版本 Linux 内核上的。如果所有这些工作负载只能在旧内核上运行(通常企业中的运营团队无法承担升级旧版本 Linux 内核的风险,例如将旧 linux 内核升级到 4.12 或 4.14),则此环境中的应用程序将无法利用到容器和 Kubernetes 等尖端技术的优势。实际上,毫无疑问的是,不能提升应用交付速度,这在互联网时代是致命的。

    旧内核支持

    然而,PouchContainer 提出了一种解决方案,可以在仍然使用 runV 的基础上,容纳必须在旧版本内核上运行的应用程序。尽管为了实现该功能,需要付出的代价是,需要做很多工作来将新内核的功能向后端移植到旧内核中(这部分的技术比较难开源)。以下 demo 演示了如何在 runV 提供的 Guest OS 中运行 linux 内核 2.6.32:在 Guest OS 中创建旧内核

    结论

    PouchContainer 给出了一种常用方法,可以提供基于管理程序的容器。使用 PouchContainer,用户可以根据特定场景,自行选择使用基于管理程序的容器,或是基于 LXC 的容器。

Last updated