前言

相信绝大多数人在安卓上使用容器都是从proot–chroot这么一个过程。那么,在这段经历中,我们也许会对chroot/unshare以外更强的容器技术而感到好奇。
那么,就来看看如何在Android上使用LXC吧。

前期准备

编译内核

这个我帮不了你,你只能靠着网上的教程来自行编译一个支持LXC的内核。

安装LXC

  • 使用 Termux 来获取 LXC:
pkg up -y
pkg i root-repo -y
pkg i tsu lxc -y

在国内你可能需要换源。这里不再详细说,网上都是教程。

检查配置

通过 lxc-checkconfig 检查配置:

sudo lxc-checkconfig

确保它是全绿的。如果不是 那你编译的内核可能要返工了。

配置

配置 cgroup

每次运行 LXC 前执行:

# 挂载 cgroup
mount -t tmpfs -o mode=755 tmpfs /sys/fs/cgroup
mkdir -p /sys/fs/cgroup/devices
mount -t cgroup -o devices cgroup /sys/fs/cgroup/devices
mkdir -p /sys/fs/cgroup/systemd
mount -t cgroup cgroup -o none,name=systemd /sys/fs/cgroup/systemd
# 设置 LXC cgroup
lxc-setup-cgroups

配置网络

本小章(配置网络)内所有的命令只需执行一次。不要重复执行。

运行:

mount|grep cgroup

注意 /sys/fs/cgroup 的挂载选项:

  • 如果它为 cgroup2:
echo "lxc.init.cmd = /sbin/init systemd.unified_cgroup_hierarchy=0" >> $PREFIX/share/lxc/config/common.conf
  • 如果它为 cgroup1:
echo "lxc.init.cmd = /sbin/init systemd.unified_cgroup_hierarchy" >>; $PREFIX/share/lxc/config/common.conf

完成后,运行:

sed -i 's/lxc\.net\.0\.type = empty/lxc.net.0.type = none/g' /data/data/com.termux/files/usr/etc/lxc/default.conf

其他配置

每个容器有一份单独的配置文件。他们的位置一般在 $PREFIX/var/lib/lxc/{NAME}/config,NAME 为你容器的名称。

  • 启用 xterm-256color 获得彩色输出
lxc.environment = TERM="xterm-256color"
  • 限制内存使用量
lxc.cgroup.memory.limit_in_bytes = 2G # 限制为2G
  • 自动设置DNS/挂载NS/udevadm权限修复
lxc.hook.pre-start = bash -c "if ! mountpoint -q /sys/fs/cgroup &>/dev/null; then mkdir -p /sys/fs/cgroup; mount -t cgroup2 -o rw,nosuid,nodev,noexec,relatime cgroup /sys/fs/cgroup; fi; for cg in blkio cpu cpuacct cpuset devices freezer memory pids; do; if ! mountpoint -q /sys/fs/cgroup/${cg} &>/dev/null; then; mkdir -p /sys/fs/cgroup/${cg}; mount -t cgroup2 -o rw,nosuid,nodev,noexec,relatime ${cg} /sys/fs/cgroup/${cg}; fi; mkdir -p /sys/fs/cgroup/systemd; mount -t cgroup -o none,name=systemd systemd /sys/fs/cgroup/systemd; umount -Rl /sys/fs/cgroup/cg2_bpf; umount -Rl /sys/fs/cgroup/schedtune; umount -Rl '${LXC_ROOTFS_PATH}'; sed -i -E 's/^( *# *DNS=.*|DNS=.*)/DNS=223.5.5.5/g' '${LXC_ROOTFS_PATH}/etc/systemd/resolved.conf'; mount -B '${LXC_ROOTFS_PATH}' '${LXC_ROOTFS_PATH}'; mount -i -o remount,suid '${LXC_ROOTFS_PATH}'; if [ ! -e '${LXC_ROOTFS_PATH}/usr/bin/udevadm.' ]; then mv -f '${LXC_ROOTFS_PATH}/usr/bin/udevadm' '${LXC_ROOTFS_PATH}/usr/bin/udevadm.'; fi; echo -e '#!/usr/bin/bash\n/usr/bin/udevadm. \"\$@\" || true' > '${LXC_ROOTFS_PATH}/usr/bin/udevadm'; chmod +x '${LXC_ROOTFS_PATH}/usr/bin/udevadm'; true;"

启动

通过 lxc-create 创建容器:

lxc-create -t download -n {NAME} -- --no-validate -d {DISTRIBUTION} -a {ARCH}

你还可以通过 --server 选项来指定镜像站:

lxc-create -t download -n {NAME} -- --server mirrors.bfsu.edu.cn/lxc-images --no-validate -d {DISTRIBUTION} -a {ARCH}

记得把 {NAME} {DISTRIBUTION} {ARCH} 换成你自己需要的。

这之后,输入:

lxc-start -n {NAME} -F -d

即可进入容器。

常见问题

容器内没有网络

进入容器:

groupadd -g 3003 aid_inet
usermod -g aid_inet 用户名

添加至 aid_inet 组即可。

普通用户无法使用 sudo

容器内重新挂载 / 即可。

mount -n -o remount,suid /

注意,每次打开容器都需要这么做。
或者启动自动挂载:

# /etc/systemd/system/rc.local.service

[Unit]
Description=/etc/rc.local Compatibility
ConditionPathExists=/etc/rc.local

[Service]
Type=forking
ExecStart=/etc/rc.local start
TimeoutSec=0
StandardOutput=tty
RemainAfterExit=yes
SysVStartPriority=99

[Install]
WantedBy=multi-user.target

# /etc/rc.local

#!/bin/sh
mount -n -o remount,suid /

如何运行 Docker

就目前而言,Termux root-repo 中的 Docker 是烂掉的,有很大可能会出现 shim 错误。更建议在 LXC 容器中运行 Docker。