2015年(100)
分类: LINUX
2015-07-03 00:15:28
容器有效地将由单个操作系统管理的资源划分到孤立的组中,以更好地在孤立的组之间平衡有冲突的资源使用需求。与虚拟化相比,这样既不需要指令级模拟,也不需要即时编译。容器可以在核心 CPU 本地运行指令,而不需要任何专门的解释机制。此外,也避免了准虚拟化(paravirtualization)和系统调用替换中的复杂性。
通过提供一种创建和进入容器的方式,操作系统让应用程序就像在独立的机器上运行一样,但又能共享很多底层的资源。例如,可以有效地共享公共文件(比如 glibc)的页缓存,因为所有容器都使用相同的内核,而且所有容器还常常共享相同的 libc 库(取决于容器配置)。这种共享常常可以扩展到目录中其他不需要写入内容的文件。
容器在提供隔离的同时,还通过共享这些资源节省开销,这意味着容器比真正的虚拟化的开销要小得多。
容器技术早就出现。例如,Solaris Zones 和 BSD jails 就是非 Linux 操作系统上的容器。用于 Linux 的容器技术也有丰富的遗产,例如 Linux-Vserver、OpenVZ 和 FreeVPS。虽然这些技术都已经成熟,但是这些解决方案还没有将它们的容器支持集成到主流 Linux 内核。(要了解更多关于这些技术的信息,请查看 参考资料 小节)。
相比之下,Linux Resource Containers 项目(见 参考资料)则通过为主流 Linux 内核作贡献来实现容器。与此同时,这些贡献可能对成熟的 Linux 容器解决方案有用处 — 为更成熟的容器项目提供公共后端。本文简要介绍如何使用由 LXC 项目创建的工具。
为了充分利用本文,您应该熟悉使用命令行运行程序,例如 make、gcc 和 patch。此外,还应该熟悉 tarball(*.tar.gz 文件)的解压。
LXC 项目由一个 Linux 内核补丁和一些 userspace 工具组成。这些 userspace 工具使用由补丁增加的内核新特性,提供一套简化的工具来维护容器。
在使用 LXC 之前,首先需要下载 Linux 内核源代码,应用适当的 LXC 补丁,然后构建、安装和启动它。最后再下载、构建和安装 LXC 工具。
我使用一个打了补丁的 Linux 2.6.27 内核(见 参考资料)。虽然 2.6.27 Linux 内核的 lxc 补丁可能不适用于您喜欢的发行版的内核源代码,但是 2.6.27 以后的 Linux 版本可能已经包含该补丁提供的大部分功能。所以,强烈建议使用最新的补丁和主流内核源代码。而且,除了下载内核源代码并添加补丁外,还可以使用 git
获取代码:
git clone git://git.kernel.org/pub/scm/linux/kernel/git/daveh/linux-2.6-lxc.git
在 kernelnewbies.org 可以找到关于如何为内核添加补丁,如何配置、构建安装和启动内核的指导说明(见 参考资料)。
LXC 需要一些特定的内核配置。为 LXC 适当配置内核的最容易的方式是使用 make menuconfig
,然后选择 Container support。取决于内核所支持的特性,这样做会进一步选择一组其他配置选项。
除了一个支持容器的内核外,还需要一些工具才能够简单地启动和管理容器。本文的容器管理工具来自 liblxc(从 参考资料 获取链接。另外,还可以使用 libvirt)。这个小节讨论:
下载并解压缩 liblxc (见 参考资料),然后从 liblxc 目录中:
./configure --prefix=/ make make install
如果您习惯于构建源 RPM,可以从网上下载一个(见 参考资料)。
为了在容器中管理网络接口,需要 2.6.26 或更高版本的 iproute2 包(见 参考资料)。如果您的 Linux 发行版没有这个包,请下载并按照说明配置和安装它。
很多实用的容器的另一个关键部分是网络访问。目前,桥接(连接多个以太网区段,使它们成为一个单独的以太网区段)是将一个容器连接到网络的最佳方法。为了准备使用 LXC,我们将创建一个桥(见 参考资料),并使用它将我们真正的网络接口与容器的网络接口连接起来。
创建一个名为 br0 的桥:
brctl addbr br0 brctl setfd br0 0
用一个已有网络接口中的 IP(在本例中是 10.0.2.15
)连接桥接口:ifconfig br0 10.0.2.15 promisc up
。将已有的网络接口(在本例中是 eth0
)添加到桥,并取消它与它的 IP 地址的直接关联:
brctl addif br0 eth0 ifconfig eth0 0.0.0.0 up
任何添加到桥 br0
的接口都将对那个 IP 地址作出响应。最后,确保默认的路由用 route add -net default gw 10.0.2.2 br0
将数据包发送到网关。以后,在配置容器时,指定 br0
作为通往外界的链接。
除了网络外,容器常常需要它们自己的文件系统。取决于您的需要,有几种填充容器文件系统的方法。我将讨论其中两种:
使用 debootstrap
命令构建一个定制的 Debian 容器 非常简单:
debootstrap sid rootfs
如果要构建大量的容器,首先将包下载到一个 tarball 中可以节省时间,例如 debootstrap --make-tarball sid.packages.tgz sid 。这将产生一个 .tar 文件,这个文件约 71MB(压缩了 52MB),而一个根目录约 200MB。然后开始在 rootfs 中构建根目录:
debootstrap --unpack-tarball sid.packages.tgz sid rootfs
。(debootstrap
主页上有更多关于构建更小的或更适合的容器的信息)。
这将生成一个宿主容器高度冗余的环境(见 参考资料)。
运行 ssh 容器 可以大大减少容器文件系统占用的磁盘空间。例如,这种方法仅仅使用数 KB 就能在不同容器的 22 端口上运行多个 ssh 守护进程(参考资料 中提供了一个例子)。容器通过使用关键根目录来实现这一点,例如 /bin、/sbin 和 /lib 等的只读绑定挂载共享来自已有 Linux 系统的 sshd 包内容。这里使用一个网络名称空间,并创建基本的读写内容。
用于生成那些轻量级容器的方法与用于生成 chroot 环境的方法基本一样。不同之处在于只读绑定挂载和使用名称空间增强 chroot 环境的隔离性,使之成为有效的容器。
接下来,需要选择一种连接到容器的方法。
接下来的步骤是连接到容器。根据配置容器的不同方式,有几种方法可供选择:
如果不需要用于容器的 GUI 接口,那么 通过 SSH 连接 就可以了。在此情况下,一个简单的 ssh 连接就足够了(参见上面的 “运行一个 ssh 容器”)。这种方法的优点是依靠 IP 寻址来支持创建任意数量的容器。
如果 ssh 连接花很长的时间才到达密码提示,那么在 DNS 查找期间 Avahi multicast DNS/Service Discovery 守护进程就可能超时。
通过 Virtual Network Computing(VNC)连接,这种方法可以为容器增加一个 GUI 接口。
使用 vnc4server 启动一个 X 服务器,该服务器只为 VNC 客户机服务。需要安装 vnc4server,以便从容器的 /etc/rc.local 文件运行它,如下所示:echo '/usr/bin/vnc4server :0 -geometry 1024x768 -depth 24' >> rootfs/etc/rc.local
。当容器启动时,将创建一个分辨率为 1024×768 的 24 位色的 X 屏幕。接下来的连接很简单,如下所示:
vncviewer <ip>:<display>
如果容器与它的宿主共享 tty,那么 通过 VT: tty(文本)连接 就很有用。在这情况下,可以使用 Linux Virtual Terminals(VT)连接到容器。使用 VT 的简单用法是登录 tty 设备之一,然后这个 tty 设备将与 Linux VT 通信。登录进程被称作 getty
。使用 VT 8:
echo '8:2345:respawn:/sbin/getty 38400 tty8' >> rootfs/etc/inittab
一旦容器被启动,它将在 tty8 上运行 getty
,以允许用户登录到容器中。可以通过类似的技巧,使用 LXC 工具重新启动容器。
这种方法不支持容器的图形化界面。而且,由于每次只有一个进程可以连接到 tty8,若要启用多个容器,则需要进一步配置。
通过 VT: X 连接 让您可以运行一个 GUI。在 VT 9 上运行 GNOME Display Manager(gdm),然后编辑 rootfs/usr/share/gdm/defaults.conf,将FirstVT=7
替换为 FirstVT=9
,以及将 VTAllocation=true
替换为 VTAllocation=false
。
虽然这样便可以使用一个图形化界面,但是仍然只能使用有限的几种 Linux 虚拟终端之一。
至此,您正在运行一个适当的内核,安装了 LXC 实用程序,并且有了一个可用的环境,接下来便可以学习管理该环境的实例了。(提示:LXC README 中更加详细地描述了这方面的大部分内容)。
LXC 使用 cgroup 文件系统来管理容器。在使用 LXC 之前,首先必须挂载这个文件系统:mount -t cgroup cgroup /cgroup
。可以将 cgroup 文件系统挂载到任何地方。LXC 将使用 /etc/mtab 中挂载的第一个 cgroup 文件系统。
本文的后面将展示一些 LXC 基础知识和杂项内容,并讨论低级访问。
对于使用 LXC 工具的基础知识,我们将看看:
创建容器就是将一个名称与一个配置文件关联起来。该名称将用于管理容器:
lxc-create -n name -f configfile
这使得多个容器可以同时使用相同的配置文件。在配置文件中,可以指定容器的属性,例如它的主机名、网络、root 文件系统和 fstab。运行 lxc-sshd 脚本(该脚本创建一个配置)之后,ssh 容器配置如下所示:
lxc.utsname = my_ssh_container lxc.network.type = veth lxc.network.flags = up lxc.network.link = br0 lxc.network.ipv4 = 10.0.2.16/24 lxc.network.name = eth0 lxc.mount = ./fstab lxc.rootfs = ./rootfs
无论配置文件如何,用 LXC 工具启动的容器有自己的系统进程视图,以及自己的挂载树和可用的进程间通信(IPC)资源视图。
除了这些以外,当一个容器启动时,配置中未提到的任何类型的资源都被认为是与主机共享。这使管理员可以简洁地指定容器与其主机之间的关键不同点,并且使配置具有可移植性。
列出关于已有容器的信息对于管理已有容器非常重要。显示一个特定容器的状态:
lxc-info -n name
显示属于一个容器的进程:
lxc-ps
启动
LXC 根据容器类型的不同而有所不同:一种是系统容器,一种是应用程序容器。系统容器类似于虚拟机。与真正的虚拟化相比,虽然它们的隔离性要低一些,但是开销也降低了。直接原因是每个容器使用相同的 Linux 内核。为了类似于虚拟机,系统容器和 Linux 发行版一样在同一个地方启动,即通过运行 init 程序:
lxc-start -n name init
与系统容器相比,应用程序容器只是创建用于隔离一个应用程序的单独的名称空间。启动一个应用程序容器:
lxc-execute -n name cmd
发信号
将一个信号发送到在一个容器中运行的所有进程:
lxc-kill -n name -s SIGNAL
暂停
暂停一个容器在概念上类似于将 SIGSTOP
信号发送到一个容器中的所有进程。但是,发送虚假的 SIGSTOP
信号可能会迷惑一些程序。所以,LXC 通过 cgroup 接口使用 Linux 进程冻结器(process freezer):
lxc-freeze -n name
恢复
要恢复一个被冻结的容器:
lxc-unfreeze -n name
停止
停止一个容器将导致该容器中启动的所有进程全体死亡,并且清理容器:
lxc-stop -n name
销毁
销毁容器是指删除通过 lxc-create
步骤与名称关联的配置文件和元数据:
lxc-destroy -n name
下面是您可能想知道的一些其他内容(有些与监视有关)。
查看和调整容器的优先级:
lxc-priority -n name lxc-priority -n name -p priority
持续观察容器的状态和优先级变化:
lxc-monitor -n name
按 Ctrl-C 停止监视容器。
还可以等待容器进入以 |
分隔的一组状态之一:
lxc-wait -n name -s states
等待除了 RUNNING
之外的所有状态:
lxc-wait -n name -s 'STOPPED|STARTING|STOPPING|ABORTING|FREEZING|FROZEN'
当然,这样将会立即返回。如果没有遇到意外错误,您应该期望只有当容器进入给定的状态时 lxc-wait
才返回。
LXC 使用 cgroup 文件系统管理容器。可以通过 LXC 读和操纵 cgroup 文件系统的一些部分。要管理每个容器对 cpu 的使用,则可以通过读取和调整容器的 cpu.shares 来进行,如下所示:
lxc-cgroup -n name cpu.shares lxc-cgroup -n name cpu.shares howmany