在本文的上篇中,我们为读者介绍了如何配制宿主机和网络,在本文篇中,我们将为读者介绍容器的创建和启动等内容。

创建我们的第一个容器

lxc发行版附带了许多工具来帮助用户创建和管理容器。第一个是lxc-create,该工具可以通过模板为各种Linux平台创建最小化的安装。您可以通过查看/usr/share/lxc/templates目录来查看当前可用的模板。在我的Slackware 14.2宿主机上,我的模板目录如下所示。


我可以选择部署其中的任何一种平台。关于如何在Linux上创建一个带有iptables的家庭路由器,网上可用的教程数不胜数,所以这里就不赘述了,同时,我觉得很多读者可能早就有这方面的经验了。读者可以在计算机上部署dhcpd和bind或dnsmasq,同时为测试环境部署DNS和DHCP。接下来要介绍的是如何设置容器——我会继续使用Slackware,但是,读者可以使用自己喜好的系统。
lxc-create -n router -t slackware -B Btrfs
运行上述命令,并稍等片刻以便从Web上获取所需文件,最终会得到一个新的容器,名为“router”。就像网络名称一样,我总是喜欢保持简洁明了,所以这里也是利用角色为其命名。
当lxc-create生成目录后,将得到/var/lib/lxc/router,其中存放与新容器关联的所有文件。值得注意的是,它将包含两部分内容:定义容器的配置文件,以及保存容器文件系统的rootfs控制器(实际上是Btrfs子卷,稍后将详细介绍)。
配置的具体内容如下所示。其中用红色标识的内容,是我修改过的;对于这些内容,将在后面分别加以介绍。

# Template used to create this container: /usr/share/lxc/templates/lxc-slackware
# Parameters passed to the template:
# Template script checksum (SHA-1): 1d5a84ae1fd3725be88e7d347128402c61000d19
# For additional config options, please look at lxc.container.conf(5)
# Uncomment the following line to support nesting containers:
#lxc.include = /usr/share/lxc/config/nesting.conf
# (Be aware this has security implications)
lxc.start.auto = 1
#Probably change network.0 to dhcp inside the container at some point
lxc.network.0.type = phys
lxc.network.0.link = eth1
lxc.network.0.name = wan
lxc.network.0.flags = up
lxc.network.0.ipv4 = 192.168.1.20/24
lxc.network.0.ipv4.gateway = 192.168.1.1
lxc.network.0.hwaddr=DE:AD:BE:EF:FF:FF
lxc.network.1.type = veth
lxc.network.1.link = lab
lxc.network.1.name = lab0
lxc.network.1.flags = up
lxc.network.1.ipv4 = 192.168.16.254/24
lxc.network.1.hwaddr=DE:AD:BE:EF:00:FF
lxc.rootfs = /var/lib/lxc/router/rootfs
lxc.rootfs.backend = Btrfs
lxc.utsname = router
lxc.mount = /var/lib/lxc/router/fstab
#lxc.console = none
lxc.tty = 4
lxc.pts = 1024
lxc.rootfs = /var/lib/lxc/router/rootfs
lxc.cgroup.devices.deny = a
# /dev/null and zero
lxc.cgroup.devices.allow = c 1:3 rwm
lxc.cgroup.devices.allow = c 1:5 rwm
# consoles
lxc.cgroup.devices.allow = c 5:1 rwm
lxc.cgroup.devices.allow = c 5:0 rwm
lxc.cgroup.devices.allow = c 4:0 rwm
lxc.cgroup.devices.allow = c 4:1 rwm
# /dev/{,u}random
lxc.cgroup.devices.allow = c 1:9 rwm
lxc.cgroup.devices.allow = c 1:8 rwm
lxc.cgroup.devices.allow = c 136:* rwm
lxc.cgroup.devices.allow = c 5:2 rwm
# rtc
lxc.cgroup.devices.allow = c 254:0 rwm
# we don’t trust even the root user in the container, better safe than sorry.
# comment out only if you know what you’re doing.
lxc.cap.drop = sys_module mknod mac_override mac_admin sys_time setfcap setpcap
# you can try also this alternative to the line above, whatever suits you better.
# lxc.cap.drop=sys_admin

首先,我将lxc.start.auto改为值1。大多数安装了LXC的系统都提供了启动脚本,该脚本会寻找将该值等于1的容器,并在系统引导时自动启动它们。请记住,要使其正常工作,需要预先提供所需的网桥和文件系统。
下一个修改之处是lxc.network.0.type的值——我已将其设置为“phys”,它代表物理适配器。lxc.network.0.link的值为eth1——这是我想要传递给容器的宿主机的第二个NIC。请注意,它将从正在运行容器的宿主机的ifconfig输出中消失。如果我们使用上行链路桥接器,那么类型将是“veth”,链路将是“uplnk”,以将其连接到宿主机上的桥接器上。最后,该接口在容器内部的名称为“wan”。此外,我还添加了网关和hwaddr条目,这些无需多言。就我而言,这个容器上行连接到我的家用路由器。当容器启动时,内核将这些值作为最初值分配给接口。根据安装的操作系统的不同,其启动脚本之后可能会重新配置该接口,也可能不会这样做。
为了向容器添加更多接口,只需复制lxc.network.[N]节,并根据需要增加索引[N]的值即可。但是,只能包含一个gateway语句。该路由器将提供两个适配器。在我的实际实验室中,路由器有10个接口。为此,只需在主机上创建网桥,并为每个网桥添加相应的lxc.network节就行了。
另外需要注意的是,hwaddr语句是可选的,但是,没有为其赋值的话,那么容器启动时将选择一个随机值。虽然这确实有助于防止MAC地址冲突,但我在测试过程中发现,这会导致容器频繁的重启。因为它们在每次启动时都会创建一个唯一的MAC,所以,很快就耗尽了DHCP地址池。所以,现在为会为容器指定MAC。但是,当IP地址总是在变化时,还是会令人非常抓狂。
最后需要注意的是:网络的lxc.network.[N].name必须与链接名称(网桥)不同,但可以跨容器进行复制。例如,您可以创建一个名为lan的网桥和一个容器,并且它的网络也名为lan,如果在多个容器中的话,这时可以将其命名为lan0。
最后一个修改之处是lxc.mount语句,它指向容器目录中名为fstab的文件。LXC能够理解普通的/etc/fstab格式的文件。所有路径都应该是相对于宿主机的相对路径。容器启动时,它将挂载其中规定的所有文件系统。搞定路由器容器后,我将在/var/lib/lxc/router/fstab创建fstab文件。

lxcpts /var/lib/lxc/router/rootfs/dev/pts devpts defaults,newinstance 0 0
none /var/lib/lxc/router/rootfs/proc proc defaults 0 0
none /var/lib/lxc/router/rootfs/sys sysfs defaults 0 0
none /dev/shm tmpfs defaults 0 0
none /run tmpfs defaults,mode=0755 0 0
/home /var/lib/lxc/router/home none bind,ro 0 0
/etc/hosts /var/lib/lxc/router/rootfs/etc/hosts none bind,ro 0 0
/etc/networks /var/lib/lxc/router/rootfs/etc/networks none bind,ro 0 0
/etc/passwd /var/lib/lxc/router/rootfs/etc/passwd none bind,ro 0 0
/etc/group /var/lib/lxc/router/rootfs/etc/group none bind,ro 0 0
/etc/shadow /var/lib/lxc/router/rootfs/etc/shadow none bind,ro 0 0
/etc/passwd /var/lib/lxc/router/rootfs/etc/passwd none bind,ro 0 0
/etc/shadow- /var/lib/lxc/router/rootfs/etc/shadow- none bind,ro 0 0
/etc/sudoers /var/lib/lxc/router/rootfs/etc/sudoers none bind,ro 0 0
/etc/sudoers.d /var/lib/lxc/router/rootfs/etc/sudoers.d none bind,ro 0 0
/etc/profile /var/lib/lxc/router/rootfs/etc/profile none bind,ro 0 0
/etc/profile.d /var/lib/lxc/router/rootfs/etc/profile.d none bind,ro 0 0
/etc/slackpkg/mirrors /var/lib/lxc/router/rootfs/etc/slackpkg/mirrors none bind,ro 0 0
/etc/resolv.conf /var/lib/lxc/router/rootfs/etc/resolv.conf none bind,ro 0 0

当然,我们也可以将普通的块设备安装到容器中,尽管这里并没有这么做。我已经使用其特殊名称挂载了proc和sysfs文件,以防容器中的启动脚本没有照顾到它们。它们将收到一个容器名称空间范围内的特殊视图。
在这里,我想为大家介绍一下绑定装载。利用它,我们可以将宿主机的文件系统挂载到容器名称空间内的某个位置。这意味着,可以在宿主机和任意数量的容器间共享/home,而无需设置NFS服务器,只需将相同的文件夹绑定到多个容器即可。当然,这必须作为容器启动的一部分来进行,但是如果在容器运行后这样做,外部文件系统在容器的名称空间中不可见。对于目录的绑定,想必大家已经都很熟悉了,实际上,我们也可以对单个文件执行该操作。如果缺少任何挂载点,容器将无法启动,这个问题将在后文加以介绍。
我认为路由器是可信基础架构的一部分,因此,我选择与宿主机共享许多关键的身份验证文件,尽管是以只读方式。通过绑定passwd、group、shadow和其他文件,我可以使用普通的用户名和密码进行登录。为方便起见,我还可以共享一些配置文件。显然,您可能不想在测试环境中对不受信任的系统执行这些操作,或者您可能需要指定自己的共享的文件。

启动容器

现在我们的路由器已配置完毕,下面开始启动它。从命令行中以手动方式启动容器并不难,具体命令如下所示。

lxc-start -n router:

好吧,但是它能正常工作吗?命令lxc-info -n router会告诉我们答案。如果容器正在运行,会得到这样的结果

lxc-info命令的运行结果
如果启动失败,可以尝试使用以下命令重新启动:

lxc-start -n router -F

执行该命令后,容器将在前台启动,这样就可以看到所有错误信息了。此外,您还可以使用命令lxc-top查看正在运行的容器的相关信息和统计数据。
接下来的步骤,是配置路由器,让它完成路由和防火墙等任务。为了访问它,可以使用lxc-console -n router连接到虚拟控制台。退出时,可以使用<[ctrl]+[a] q>。进入控制台后,可以使用installpkg、apt、yum等添加所需的软件包。然后,就可以使用ssh或xnest来管理它了。

为什么要使用Btrfs?

现在,是Btrfs的表演时间。假设需要将10个Ubuntu工作站加入我的测试域,为此,我将部署一个Ubuntu容器作为模板。

lxc-create -t ubuntu -n TubuntuWS -B Btrfs

然后,还需要进行进行配置,就像前面的路由器配置一样。接下来,用单个veth接口设为lxc.network.0,并将lab设为链接。在这种情况下,就用不着使用特殊fstab了。同时,也不用设置lxc.start.auto。然后,通过lxc-start -n TubuntuWS命令启动容器,并利用lxc-console进行连接,这样就可以构建机器了。
如果一切都符合要求,就可以将其关闭了。为此,可以从宿主机的容器内部执行lxc-stop -n TubuntuWS命令。
现在,我可以使用BTRFS轻松创建大量的低开销的副本。首先,在/var/lib/lxc/下为其他容器创建目录。
然后,克隆模板容器,即TubuntuWS。

cat /var/lib/lxc/TubuntuWS/config | sed ‘s/TubuntuWS/ubuntuWS01/g’ > /var/lib/lxc/ubuntuWS01/config
Btrfs subvolume snapshot /var/lib/lxc/TubuntuWS/rootfs /var/lib/lxc/ubuntuWS01/rootfs

好了,一个克隆完成了!当然,我们可能需要更改一些网络设置。之后,可以使用lxc-create启动它。由于Btrfs已经创建了模板rootfs的写时复制快照,所以至少在进行大量更新之前,几乎不会占用任何磁盘空间。
Btrfs也可以完成其他有用的事情。比如,可以根据需要制作任意数量的卷,创建快照的快照,也可以删除原始源卷。
还记得路由器吗?它所有的配置都是按照这里来的,所以请将其rootfs的快照创建为rootfs.bak。您甚至可以使用它来完成卷的增量式流备份。

原文来自:https://zhuanlan.zhihu.com/p/39853622

本文地址: https://www.linuxprobe.com/linux-test-environment‎.html编辑:贾春华,审核员:逄增宝

本文原创地址:https://www.linuxprobe.com/linux-test-environment%e2%80%8e.html编辑:public,审核员:暂无