经过一段时间对lxc容器部署的实践和理解,阶段性总结如下:

对于arm,按LXC提供工具的正常的流程:

checkconfig:配置内核,使lxc-checconfig的所有项变绿色。(其实lxc-checkconfig只是一个对内核.config压缩包进行检查的脚本)

create:使用template,从模板的发行版(如alpine,ubuntu)的官网镜像源下载对应的rooft,放到/var或者/usr/local/var目录;

start:将rootfs mount到/usr/local/lib目录,建立必要的网络服务(因此在此之前先建立虚拟网桥服务);

对于csky:

checkconfig:因为rootfs里没有包含congfig.gz文件,可以手动打包然后运行lxc-checkconfig检查;

create:因为生态问题,目前发行版的镜像源还不支持arch=csky,因此需要手动将rootfs和config放置到指定目录,然后通过lxc-ls确认;

start:基本和arm一致,会碰到因为工具链和rootfs支持不完善导致的问题,具体见下面的详细笔记;

==========   详细笔记  =========

1. LXC 容器 部署( imx6

LXC Linux Container容器,是一种内核虚拟化技术,可以提供轻量级的虚拟化,以便隔离进程和资源。Linux 容器功能是基于 cgroups Namespace 来实现的 . 所以要了解 Linux 容器必须先了解 cgroup Namespace

1.1 虚拟机环境

1.1.1 安装 LXC 服务

1 a pt-get install lxc。

2 lxc-checkconfig ,检查当前 Linux 内核支持 LXC 的情况。要是一切都已被启用,内核对 LXC 的支持已准备就绪。

1.1.2 使用 LXC

1、lxc-create -n lxc-ubuntu -t ubuntu:

-n 指定容器名

-t 指定模板名,这里必须为 ubuntu。

其中配置模板在 /usr/share/

默认 创建与本地主机同一版本号和同一架构的最小 Ubuntu 安装系统 容器存储在 /var/lib/lxc/<container-name> ,根文件系统则位于 /var/lib/lxc/<container-name>/rootfs 。LXC 创建过程中下载的所有程序包则缓存在 /var/cache/lxc 里面

2、 lxc-ls --fancy:查看容器列表,

3、lxc-start -n lxc-ubuntu :启动 lxc 容器。后面加 -F 可以把启动过程放在前台,会打印更多的调试信息。

4、brctl show lxcbr0 :确认 容器的接口( vethSS7RB5 )自动连接到 LXC 的内部网桥( lxcbr0

5、lxc-console -n lxc-ubuntu :登入容器的控制台,提示需要输入用户名和密码,默认都是 ubuntu ,退出控制台使用 exit

6、lxc-stop -n lxc-ubuntu :停止容器,注意需要在另一个终端输入。

1.1.3 遇到过的问题:

1、 在虚拟机上重新 start 容器时:不知道是之前使用了 destroy ,还是强制关闭虚拟机了, lxc-start 的时候提示“ lxc_ovs_attach_bridge: 1817 Failed to attach "lxcbr0" to openvswitch bridge "vethDSDW29" ”,通过 ifconfig 发现 lxcbr0 网卡没有了,通过 apt-get 卸载,重新安装才解决。 dpkg --list apt-get --purge remove 包名( --purge 是可选项,写上这个属性是将软件及其配置文件一并删除)

1.2 Imx6 目标板环境

1.2.1 编译 lxc

下载 lxc2.0.0 版本

1、设置环境变量:

. /opt/fsl-imx-x11/4.1.15-2.1.0/environment-setup-cortexa9hf-neon-poky-linux-gnueabi

利用 nxp 自带的环境变量配置脚本,设置当前环境变量: CC LD LIB

2、 配置检查: ./configure --build=x86_64-linux --host= --target=arm-poky-linux --bindir=/home/cql/imx6/lxc-2.0.0/bin 。检查通过后会执行

. /configure 相关概念

--build= 编译该软件所使用的平台

--host= 该软件将运行的平台

--target= 该软件所处理的目标平台

3、 编译 make

4、 安装: make install

5、 将生成的工具集打包通过 ssh 放到目标板: scp -r lxc_bin root@192.168.199.102:/usr/ s bin /lxc_bin

6、执行lxc-checkconfig看内核还缺哪些服务,如下如所示,需要将内核的对应配置打开,重新编译内核。

1.2.2 配置内核

根据lxc-checkconfig的运行结果,查看当前内核还差哪些服务,根据服务重新配置内核。

1、 执行 make menuconfig 。如果虚拟机缺少 ncurses ,需要先安装 apt-get install libncurses5-dev。

2、 如果在 menuconfig 中搜索不到选项的关键字,需要查看 lxc-checkconfig脚本代码,确认错误打印的实际条件。

1.2.3 编译内核

1 make menuconfig

如果提示 make menuconfig   fatal error ncurses.h ;是因为缺少图形化动态库 ncurses ;通过 apt-get install libncurses5-dev 解决。又提示: recipe for target  'script/kconfig/dochecklx ;重启虚拟机解决。

2 make distclean

3 make imx_v7_defconfig

4 make zImage 提示: make /bin/sh: 1: lzop: not found, 虚拟机缺少 lzop 压缩工具,使用 apt-get install lzop 后解决

5 make dtbs

6 make modules

1.2.4 构建容器

1、 将模板拷贝到目标板: scp  -r templates/ root@192.168.199.228:/usr/local/share/lxc/ 。并设置可执行权限。

2、 将配置文件从 lxc-2.0.0/config/templates 拷贝到目标板: scp  -r config/templates/* root@192.168.199.228:/usr/local/share/lxc/config/

3、 lxc 工具集拷贝到目标板: scp  ../bin/* root@192.168.199.129:/sbin/

4、 将动态库拷贝到目标板: scp  src/lxc/liblxc.so root@192.168.199.129:/usr/local/lib ;并创建软连接: ln -s /usr/local/lib/liblxc.so /usr/lib/liblxc.so.1 。后面工具集使用的需要用到软连接。

5、 修改 /usr/local/share/lxc/template/lxc-xxx 编辑模板:将 fetch 函数 wget 超时增加到 100

6、 创建容器: lxc_create -n lxc0 -t alpine

7、启动容器:lxc-start -n lxc0  -o /dev/stdout -l debug -F。

8、 查看 lxc 版本: lxc-device --version

1.2.5 重要目录说明:

1、 容器的模板路劲: /usr/local/share/lxc/templates 。不同的 lxc 版本路径有变化,这个对应的是 lxc2.0.0, 比如笔者虚拟机上 lxc2.0.11 的目录是 /usr/local/share/lxc/templates 。通过阅读模板脚本可以看到其他目录的差异。

2、 容器的文件系统路径: /usr/local/var/lib/lxc$(lxcname)/rootfs ,虚拟机是: /var/lib/lxc/$(lxcname)/rootfs/

3、 容器的配置文件路径: /usr/local/var/lib/lxc$(lxcname)/config ,虚拟机是: /var/lib/lxc/$(lxcname)/config

1.2.6 遇到过的问题:

1 、配置 lxc 编译脚本的时候: ./configure 一直有问题, cannot find crt1.o ”等各种错误, config.log 上看是链接出错,反复调整 CC --build --host --target 还是没有效果 ,最后还是使用 nxp 的环境变量脚本更方便一些。

2、 编译内核前配置的时候: make menuconfig ,提示“ fatal error ncurses.h ”;是因为缺少图形化动态库 ncurses ;通过 apt-get install libncurses5-dev 解决。又提示: recipe for target  'script/kconfig/dochecklx ;重启虚拟机解决。

3、 创建容器的时候, lxc-create -n lxc0 -t alpine -l debug 提示: lxc-create: utils.c: get_template_path: 1427 Permission denied - bad template: alpine 。查看模板的路径和内容均确认是正确的。因为 template 文件是脚本,需要可执行权限, chmod 777 /usr/local/share/lxc/templates/* ,提高权限,解决。

4、 创建 lxc 容器的时候: lxc-create -n lxc0 -t alpine ,提示“ wget: server returned error: HTTP/1.1 400 Bad Request ”,直接使用 wget 工具测试“ wget http://www.bai.com”,发现是可以下载网页的,但是使用 wget https://alpinelinux.org/keys/alpine-devel@lists.alpinelinux.org-4a6a0840.rsa.pub ,是不行的,提示相同的错误。应该是 wget 软件问题,去 https://www.busybox.net/ 查看busybox 的版本发布说明, wget 在笔者使用的 busybox_1.27.2 这个版本之后确实有修复 wget 的问题。

5、 继续创建容器:提示 sha256sum: WARNING: 1 computed checksum did NOT match ”。直接使用 sha256sum 相应的 .pub 文件,确实有问题,查看文件内容,和虚拟机相应文件对比,明显不对,应该是之前 wget 下载错误导致,删除 .pub ,解决。

6、 继续创建容器:提示下载 apk_tool 超时, 再次创建的时候直接解压 apk_tool 失败,想起 alpine 的模板脚本的 fetch 函数里配置的是 wget -T 10 ,超时时间可能不够,修改超时时间到 100 ,解决。

7、 创建容器的时候,提示错误: lxc-create: utils.c: get_template_path: 1340 Permission denied - bad template: alpine ”。阅读代码发现是 access 错误,应是没有权限,给 templates config 都加上操作权限,解决。

8、 启动容器的时候:提示 lxc-start 20200714015606.656 ERROR    lxc_start - start.c:lxc_spawn:1093 - failed initializing cgroup support ”,放开调试打印: lxc-start -n lxc0  -o /dev/stdout -l debug -F lxc-start 20200714041349.939 ERROR    lxc_cgfs - cgfs.c:do_setup_cgroup_limits:1993 - No such file or directory - Error setting devices.deny to a for lxc0 ”。官网查到应该是 2.0.0 的版本对不支持 systemd 的内核有 bug ,这个在后面的版本中有修复。通过在 /etc/fstab 文件中加入挂载信息解决:

cgroup               /sys/fs/cgroup         cgroup     defaults              0  0

重启后系统会自动挂载文件系统。

9、Linux 内核配置文件 打包在/proc/config.gz

1. LXC 容器部署(csky)

1.1 编译 lxc

1、 设置环境变量: . /opt/csky-toolchain/environment-setup-csky 。利用通用的环境变量配置脚本,设置当前环境变量: CC LD LIB

2、 配置检查: /configure --build=x86_64-linux --host=csky-abiv2-linux --target=csky-abiv2-linux --bindir=/home/cql/fuxi_h/lxc-2.x.x/bin

--build= 编译该软件所使用的平台

--host= 该软件将运行的平台

--target= 该软件所处理的目标平台

3、 编译 make

4、 安装: make install

5、执行lxc-checkconfig看内核还缺哪些服务,如下如所示,需要将内核的对应配置打开,重新编译内核。

1.2 内核配置

1、 将默认配置覆盖到 ../obj/.config ,在内核目录执行: make ARCH=csky CROSS_COMPILE=/opt/csky-toolchain/bin/csky-linux-gnuabiv2- O=../obj/ fx6evb_defconfig

2、根据上面lxc-checkconfig 的检测结果配置内核: Make ARCH=csky  O=../obj/ menuconfig 。(注:配置 CONFIG_NF_NAT_IPV4 需要先打开 NF_CONNTRACK ,其他的根据宏名称搜索即可)

3、 编译内核并把内核和 roofs 一起打包到 image make ARCH=csky CROSS_COMPILE=/opt/csky-toolchain/bin/csky-abiv2-linux- CONFIG_INITRAMFS_SOURCE=/home/cql/fuxi_h/rootfs/work/fx6/tmp_fs O=../obj -j8

1.3 创建容器

因为通过模板来创建 lxc 的时候,需要从发行版官网下载和 arch 相对应的镜像, cksy 的生态不健全,各个发行版还不支持 csky ,所以不能使用 lxc_create -n lxc0 -t alpine 来创建容器。

手动将 rootfs config 文件拷贝到 /usr/local/var/lib/lxc/lxcXXX/ 或者 /var/lib/lxc/lxcXXX 目录下(自己编译的库 liblxc.so.1.0.0 使用的前者路径,虚拟机和默认的 liblxc.so.l.6.0 使用的是后者,具体什么原因还确定),这个时候就可以通过 lxc-ls 看看有没有 lxcXXX

环境构建:

1 mount -t cgroup none /root/cgroupfs( 目录可以随意,这样在 /proc/1/cgroup 文件才有内容 )

2 、执行 lxc-net ,创建虚拟网桥,通过 ifconfig 确认网桥 lxcbr0 是否 OK

1.4 启动容器

1 、输入命令 lxc-start -n lxc0 -l debug  -o /tmp/lxclog12

1.5 进入容器

1、lxc-attach -n lxc0

1.6 退出容器

键入 exit 即可退出容器

1.7 配置网络

1.7.1 主机网络配置:

1、 配置 dns :更改 /etc/resolv.conf search lan nameserver 192.168.199.1

2、重启网络服务:/etc/init.d/S40network restart。

3、 确认能 ping 通外网: ping www.baidu.com

1.7.2 容器网络配置:

1、 修改启动脚本 /et/init.d/rcS

/sbin/ifconfig eth0 10.0.3.100 netmask 255.255.255.0

/sbin/route add default gw 10.0.3.1 eth0

2 、重启容器:验证可以 ping 通外网。

1.8 遇到过的问题

1 ## Booting kernel from Legacy Image at 00080000 ...

Image Name:   fx6-kernel-iamge

Image Type:   CSKY Linux Kernel Image (uncompressed)

Data Size:    10435776 Bytes = 10 MiB

Load Address: 00080000

Entry Point:  00080000

Verifying Checksum ... Bad Data CRC

ERROR: can't get kernel image!

== 》内核大小超过了扇区大小,检查 uboot 的配置和 dd 命令的大小。

Uboot 查看分区大小: pr ;修改内核分区大小命令: env set flash_kernel_size 0x900000。

== 》使用浙大的 rootfs rcS 替换南网自制的,实现启动。

2 lxc-start: lxc0: cgroups/cgroup.c: cgroup_init: 54 Failed to initialize cgroup driver

== cg_hybrid_init 函数中需要访问 read_file("/proc/1/cgroup"); 内核的这个文件是空的,导致 lxc start 异常退出。

a. meuconfig:General setup-->Control Group support 下选择你们想要的 cgroup

b. 重新编译,启动,在 shell 界面把 cgroup 挂载到一个目录 (path/dir) 下,即运行命令 mount -t cgroup none path/dir

c. 就能 /proc/1/cgroup 文件中看到内容了

3 lxc-start: lxc0: storage/dir.c: dir_mount: 198 No such file or directory - Failed to mount "/var/lib/lxc/lxc-busybox/rootfs" on "/usr/local/lib/lxc/rootfs"

== 》在 /var 路劲中配置 rootfs 路径, lxc 启动会将这个目录 mount /usr 目录。

4 lxc-start lxc0 19700101001039.724 ERROR    conf - conf.c:lxc_allocate_ttys:1001 - No such file or directory - Failed to create tty 0

lxc-start lxc0 19700101001039.728 ERROR    conf - conf.c:lxc_create_ttys:1102 - Failed to allocate ttys

== 》参考 lxc 源码自带的重新 openpty.c 文件实现 openpty 函数

4 conf - conf.c:setup_caps:2567 - unknown capability sys_module

lxc-start lxc0 19700101005037.568 ERROR    conf - conf.c:lxc_setup:3806 - Failed to drop capabilities

== 》在 lxc_setup 函数中屏蔽掉 setup_caps

根本原因分析: configure 脚本通过检查工具链是否包含 libcap 相关的库和头文件来决定是否在 config.h 中打开宏 HAVE_LIBCAP ,如果这个宏没有打开,将会导致了 setup_caps 的匹配失败。

待优化解决方案:构建工具链的时候需要开启 libcap 选项,因为默认使用动态库, rootfs 也需要 libcap.so.