在 OpenWrt 上玩转 LXC 容器
文章更新
20190625 初次成文
写在前面
LXC (LinuX Contains) 是操作系统级别的虚拟化技术,它可以提供轻量级的虚拟化、以便隔离进程和资源。容器有效地将操作系统管理的资源划分到独立的组中,并把各个独立的组进行隔离,可以让各自的组占用独立的资源,完成自己独立的任务。
LXC 容器已经成为 OpenWrt 项目的一部分,简单来说,LXC 允许你在 OpenWrt 中运行多个不同的系统,比如 Ubuntu、CentOS、Alpine Linux 等。
如此一来,很多 OpenWrt 上跑不起来或者暂时未适配的应用现在就都能跑啦~
LXC 的 Luci APP 源码托管在:
https://github.com/openwrt/luci/tree/master/applications/luci-app-lxc
在小苏的“自编译 OpenWrt 固件”中也加入了 LXC 及其 Luci APP 的支持,但是在国内范围内介绍 OpenWrt LXC 特性的文章不是很多,而 LXC 自身又是极其强大的一个东西,所以值得 鸽子王 小苏特地写一篇文章来介绍~
以下小苏以自己编译的适用于树莓派的 OpenWrt 固件为例:
分区新建及格式化
在使用 LXC 容器之前,我们需要做一些准备工作。因为 LXC 容器的建立过程中需要从网络上下载模板资源(包含容器自身的 RootFS 以及一些配置文件)并解压到机身存储中,不论是在容器的建立还是容器的运行过程中都会占用大量存储空间(几十至数百兆)。而运行 OpenWrt 的设备的存储根分区往往不足以承载这些“大文件”,怎么办呢?最简单的方法就是将 U 盘插入运行 OpenWrt 的设备,将 LXC 容器涉及到的文件“转移到 U 盘中”。在这篇文章中,小苏使用以 SD 卡为存储介质的树莓派做演示,因为 SD 卡的总容量完全满足 LXC 对于存储空间的要求,所以不必使用 U 盘来“转移文件”。
默认情况下,写入小苏编译的 OpenWrt 固件的 SD 卡除了 50M 的引导分区和 500M 的根分区之外,剩余存储空间皆为“空闲空间”。
虽然总空间很大,但因为小苏编译的 OpenWrt 固件根分区只有 500M ,不满足运行 LXC 容器的需求,所以小苏需要将 SD 卡的“空闲空间”新建一个分区,接着把新建好的分区挂载到 LXC 容器所在目录,来他个“狸猫换太子”~
首先我们使用 fdisk
命令查看 SD 卡目前的分区状况:
root@OpenWrt:~# fdisk -l /dev/mmcblk0 |
分区情况与我们刚才在 Partition Guru 中看到的结果一致。另外我们需要记住设备的最后一个分区的终止扇区,在上面 fdisk
返回的结果中,我们注意到最后一个分区是 mmcblk0p2,这个分区的终止扇区是 1138687。
接下来我们把 SD 卡的“空闲空间”利用起来,用 fdisk
命令在这部分空间上新建一个分区:
root@OpenWrt:~# fdisk /dev/mmcblk0 # 【进入 fdisk 分区工具】 |
在这一步中:
First sector (2048-31116287, default 2048): 1140000 |
小苏输入了 1140000,这个数值需要根据之前最后一个分区的终止扇区来决定,理论上可以输入大于最后一个分区终止扇区(1138687)而小于存储设备的终止扇区(31116287)之间的任意数值,但是为了节省空间,小苏建议输入一个略大于最后一个分区终止扇区的值,在上文中小苏选择了 1140000,当然这个值是区间内任意的,哪怕输入 1138688 也可以~
接下来我们把新建的分区格式化为 ext4 格式:
root@OpenWrt:~# mkfs.ext4 /dev/mmcblk0p3 # 【使用 mkfs.ext4 命令格式化刚刚新建的分区】 |
如上信息出现时,代表分区格式化成功~
分区挂载及目录设定
接下来我们需要将刚刚格式化好的分区挂载到 LXC 相关目录上,在 OpenWrt 中,与 LXC 相关的目录都在 /srv
文件夹下,如果根目录下没有这个文件夹,我们需要先新建(若文件夹已存在可忽略此步)。
mkdir /srv
接着将 SD 卡的第三分区挂载到 /srv
下:
mount -v -t ext4 -o rw /dev/mmcblk0p3 /srv
在 /srv
目录下新建 lxc 文件夹:
mkdir /srv/lxc
至此准备工作完成~
镜像源设定
进入 OpenWrt 的控制面板,在 “服务 - LXC Containers”我们可以进入 LXC 容器的控制界面。之前小苏提到过新建容器过程中需要从网络下载大量数据,为了提高下载速度,我们可以把 “Containers URL” 中的软件源替换成清华大学的 “LXC Images” 源:
mirrors.tuna.tsinghua.edu.cn/lxc-images |
“保存&应用”之后,刷新界面等一两秒,在“Create New Container”处的 “Template”下拉框中便可以加载出可用的 LXC 模板。
容器新建
在“Create New Container”的“共享名”处填入你想要给容器起的名字,在“Template”处选择容器模板,点击右侧的“Create”按钮即可启动容器新建任务,以 Ubuntu Trusty 模板为例:
稍等片刻,大概 5 分钟左右的时间(视网络而定),在 “Available Containers”即可出现新建完成的容器。
注意:
不是所有模板创建的容器都可成功运行!
目前测试成功的模板为:
Alpine Linux 全系列
CentOS (有一些小 Bug*)
Devuan ASCII
Ubuntu Trusty
*CentOS 容器启动后可能会遇到网络无法连接的问题,可以参照 这篇文章 解决。
存在问题的模板为:
ArchLinux:无法启动
Debian 全系列:高版本发行版无法启动,低版本发行版无法联网
Ubuntu 高版本(除 Trusty):无法启动
待测试:
Fedora、Gentoo、Kali
网络配置
依次点击“MORE - CONFIGURE”打开容器配置界面,注释此行:
lxc.net.0.type = empty # 注释此行 |
并且在在末尾添加以下内容:
lxc.net.0.type = veth |
最后点击输入框右下角“CONFIRM”按钮提交(按钮不太显眼,可能需要向右拖动指示条)
提交后输入框下方会有“LXC configuration updated”的提示。
启动容器
顾名思义,点击容器“状态指示灯”右侧的 “启动按钮”可以启动容器,启动后,指示灯会由红色转为绿色,代表容器启动成功。
接下来我们可以在 SSH 或者 TTYD 终端中输入以下命令进入容器:
lxc-attach -n 容器名称
root@OpenWrt:~# lxc-attach -n Ubuntu |
当命令指示符从root@OpenWrt:
变为 root@Ubuntu:
时,代表我们成功进入容器。
除了使用 lxc-attach
的方式进入容器外,我们还可以使用 lxc-console
的方式进入容器。这种进入方式更接近于实机交互,需要验证用户名和密码才可进入,所以在使用lxc-console
前需要事先使用lxc-attach
进入容器并设置一个用户密码。
lxc-console -n 容器名称 -t 1
其中,-t 参数后面的数字代表控制台编号,如果 -t 后跟随的数字是 1 ,那么命令执行后将会进入第一个控制台。进入控制台后,按下 Ctrl+a 后再按下 q 即可暂时离开控制台(而不退出),再次进入相同编号的控制台后可以恢复之前的会话,如果想要在终端中输入 Ctrl+a,那么按下两次 Ctrl+a 即可。
root@OpenWrt:~# lxc-console -n Ubuntu -t 1 |
启动容器后,我们可以在“状态 - 概况”中看到刚刚创建的容器名称:
这是因为,容器中系统的虚拟网卡可以直接从 OpenWrt 的 DHCP 服务器中获取 IP,也就是说,容器当前的网络状态和一台跑着 Linux 系统并且接入 OpenWrt 所在网络的真机没有什么两样。这样的好处在于,无需在 OpenWrt 与容器间建立复杂的端口映射(Docker:??),在内网下访问容器内的服务十分方便,而即使是在外网,也只需要再做一个简单的端口映射就可以。
其他设置
因为 SD 卡的第三分区是我们手动新建的,并且为了使 LXC 容器正常运行,第三分区需要挂载到指定挂载点,所以为了使 LXC 容器“可持续运行”,我们需要在设备启动时做些文章。
在“系统 - 启动项”本地启动脚本输入框中输入以下内容以实现 SD 卡第三分区开机自动挂载:
# Umount /dev/mmcblk0p3 from system generated mount point |
同时因为 LXC 容器默认不会在开机时启动,所以我们还可以在开机挂载代码后输入以下内容实现容器开机启动:
lxc-start -n Ubuntu |
其中,Ubuntu 为容器名称。
同时应该注意,所有代码应插入在 exit 0
之前。
参考资料
【LEDE】x86 软路由之路 - 10 - 都能用 Docker 了,LXC 还远吗? - CSDN
https://blog.csdn.net/wang805447391/article/details/83542599
容器技术(一)LXC 安装及使用 - leon 的博客