20

在 OpenWrt 上玩转 LXC 容器

 2 years ago
source link: https://mlapp.cn/375.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

在 OpenWrt 上玩转 LXC 容器

发表于 2019-06-25

分类于

OpenWrt

本文字数: 3.3k

阅读时长 ≈ 3 分钟

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 的根分区之外,剩余存储空间皆为“空闲空间”。

Partition Guru 分区详情

虽然总空间很大,但因为小苏编译的 OpenWrt 固件根分区只有 500M ,不满足运行 LXC 容器的需求,所以小苏需要将 SD 卡的“空闲空间”新建一个分区,接着把新建好的分区挂载到 LXC 容器所在目录,来他个“狸猫换太子”~

首先我们使用 fdisk命令查看 SD 卡目前的分区状况:

root@OpenWrt:~# fdisk -l /dev/mmcblk0
Disk /dev/mmcblk0: 14.9 GiB, 15931539456 bytes, 31116288 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x5452574f

Device Boot Start End Sectors Size Id Type
/dev/mmcblk0p1 * 8192 110591 102400 50M c W95 FAT32 (LBA)
/dev/mmcblk0p2 114688 1138687 1024000 500M 83 Linux

分区情况与我们刚才在 Partition Guru 中看到的结果一致。另外我们需要记住设备的最后一个分区的终止扇区,在上面 fdisk返回的结果中,我们注意到最后一个分区是 mmcblk0p2,这个分区的终止扇区是 1138687。

接下来我们把 SD 卡的“空闲空间”利用起来,用 fdisk 命令在这部分空间上新建一个分区:

root@OpenWrt:~# fdisk /dev/mmcblk0 # 【进入 fdisk 分区工具】
Welcome to fdisk (util-linux 2.33).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.


Command (m for help): n # 【输入 n 新建分区】
Partition type
p primary (2 primary, 0 extended, 2 free)
e extended (container for logical partitions)


Select (default p): p # 【输入 p 选择新建主分区】
Partition number (3,4, default 3): 3 #【输入 3 选择新建分区号为3的主分区】
First sector (2048-31116287, default 2048): 1140000 # 【下文会讲到】
Last sector, +/-sectors or +/-size{K,M,G,T,P} (1140000-31116287, default 31116287): # 【直接按回车】

Created a new partition 3 of type 'Linux' and of size 14.3 GiB.

Command (m for help): p # 【输入 p 打印当前分区信息,效果等同于 fdisk -l 命令】
Disk /dev/mmcblk0: 14.9 GiB, 15931539456 bytes, 31116288 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x5452574f

Device Boot Start End Sectors Size Id Type
/dev/mmcblk0p1 * 8192 110591 102400 50M c W95 FAT32 (LBA)
/dev/mmcblk0p2 114688 1138687 1024000 500M 83 Linux
/dev/mmcblk0p3 1140000 31116287 29976288 14.3G 83 Linux

Command (m for help): w # 【提交对分区表的修改】
The partition table has been altered.
Syncing disks.

在这一步中:

First sector (2048-31116287, default 2048): 1140000 

小苏输入了 1140000,这个数值需要根据之前最后一个分区的终止扇区来决定,理论上可以输入大于最后一个分区终止扇区(1138687)而小于存储设备的终止扇区(31116287)之间的任意数值,但是为了节省空间,小苏建议输入一个略大于最后一个分区终止扇区的值,在上文中小苏选择了 1140000,当然这个值是区间内任意的,哪怕输入 1138688 也可以~

接下来我们把新建的分区格式化为 ext4 格式:

root@OpenWrt:~# mkfs.ext4 /dev/mmcblk0p3 # 【使用 mkfs.ext4 命令格式化刚刚新建的分区】
mke2fs 1.44.3 (10-July-2018)
Discarding device blocks: done
Creating filesystem with 3747036 4k blocks and 938400 inodes
Filesystem UUID: e07a04f3-933c-4a37-9f23-ed98de55e1f0
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208

Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done

如上信息出现时,代表分区格式化成功~

分区挂载及目录设定

接下来我们需要将刚刚格式化好的分区挂载到 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 模板为例:

新建 LXC 容器

稍等片刻,大概 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
lxc.net.0.flags = up
lxc.net.0.link = br-lan
lxc.net.0.hwaddr = 26:06:06:94:e6:5b

# Init
lxc.init.cmd = /sbin/init

最后点击输入框右下角“CONFIRM”按钮提交(按钮不太显眼,可能需要向右拖动指示条)

LXC 容器网络配置

提交后输入框下方会有“LXC configuration updated”的提示。

顾名思义,点击容器“状态指示灯”右侧的 “启动按钮”可以启动容器,启动后,指示灯会由红色转为绿色,代表容器启动成功。

接下来我们可以在 SSH 或者 TTYD 终端中输入以下命令进入容器:

lxc-attach -n 容器名称

root@OpenWrt:~# lxc-attach -n Ubuntu
root@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

Connected to tty 1
Type <Ctrl+a q> to exit the console, <Ctrl+a Ctrl+a> to enter Ctrl+a itself

Ubuntu 14.04.6 LTS Ubuntu tty1

Ubuntu login: root
Password:
Welcome to Ubuntu 14.04.6 LTS (GNU/Linux 4.14.127 armv7l)

* Documentation: https://help.ubuntu.com/

The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.

root@Ubuntu:~#

启动容器后,我们可以在“状态 - 概况”中看到刚刚创建的容器名称:

容器 IP

这是因为,容器中系统的虚拟网卡可以直接从 OpenWrt 的 DHCP 服务器中获取 IP,也就是说,容器当前的网络状态和一台跑着 Linux 系统并且接入 OpenWrt 所在网络的真机没有什么两样。这样的好处在于,无需在 OpenWrt 与容器间建立复杂的端口映射(Docker:??),在内网下访问容器内的服务十分方便,而即使是在外网,也只需要再做一个简单的端口映射就可以。

因为 SD 卡的第三分区是我们手动新建的,并且为了使 LXC 容器正常运行,第三分区需要挂载到指定挂载点,所以为了使 LXC 容器“可持续运行”,我们需要在设备启动时做些文章。

在“系统 - 启动项”本地启动脚本输入框中输入以下内容以实现 SD 卡第三分区开机自动挂载:

# Umount /dev/mmcblk0p3 from system generated mount point
umount /dev/mmcblk0p3

# Mount /dev/mmcblk0p3 to /srv
mount -v -t ext4 -o rw /dev/mmcblk0p3 /srv

同时因为 LXC 容器默认不会在开机时启动,所以我们还可以在开机挂载代码后输入以下内容实现容器开机启动:

lxc-start -n Ubuntu

其中,Ubuntu 为容器名称。

同时应该注意,所有代码应插入在 exit 0之前。

开机启动脚本

【LEDE】x86 软路由之路 - 10 - 都能用 Docker 了,LXC 还远吗? - CSDN

https://blog.csdn.net/wang805447391/article/details/83542599

容器技术(一)LXC 安装及使用 - leon 的博客

https://blog.leonshadow.com/763482/774.html


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK