在HOST上面,一个LXC container包含一个config文件和一个rootfs目录。

早期我们在交叉编译系统上编译出container的rootfs之后,直接在编译系统上将其用tar压缩打包。然后下载到目标系统上解压到container的根文件系统“rootfs”里面。但是这样的话有几个问题:
1. image过于庞大,导致下载到目标系统时间较长。
2. 在目标系统上安装时解压时间过长,且会占用大量flash空间。
3. 当container运行起来后,动态生成的文件跟原有的文件混在一起,不方便管理。例如如果要将container恢复到安装时的状态,这时就无从知道删除哪些文件了。
4. 卸载container时需要删除整个rootfs,耗时较长。
5. 无法防止运行过程中对原始文件系统的修改。

基于以上的缺陷,我们做了以下改良:
1. 在编译系统上压缩打包时,我们将rootfs打包成squashfs文件系统并压缩,这种方式压缩比更高,这样可以减少最后生成的image的大小。

$ sudo -n -- mksquashfs $rootfs_dir $squashfs_image_path -noappend -comp xz

 这样就生成了“rootfs_base.img”,然后再跟其他配置文件和脚本文件一起压缩打包生成最后的image
2.在目标系统上,直接将"rootfs_base.img" 挂载到文件系统目录上,这样就减少了解压的过程,注意在mount时,我们以readonly的方式mount,这样就防止原始的文件系统被篡改。

mount -nt squashfs -o ro $LXC_DIR/rootfs_base.img $LXC_DIR/lowerdir || error_exit "Cannot mount rootfs base image"

3.我们将目标系统上的rootfs设计成overlay文件系统,将原始的rootfs_base部分放在lower layer,然后将系统运行时产生的文件系统的变动放在upper layer。这样就可以将原始文件和运行时文件进行了隔离。
4.为了限制运行时文件系统大小,我们在安装container时,创建了一个固定大小的类型为ex2的image "rootfs_overlay.img",然后将这个image挂载在一个loop文件系统上:

$ rm -f $LXC_DIR/rootfs_overlay.img

$ dd if=/dev/zero of=$LXC_DIR/rootfs_overlay.img bs=1K count=$size
$ mkfs.ext2 -E root_owner="$uid:$gid" $LXC_DIR/rootfs_overlay.img
$ mount -nt ext4 -o loop,nouser_xattr,noacl,nodelalloc,nobarrier,noblock_validity,noatime,sync $LXC_DIR/rootfs_overlay.img $LXC_DIR/overlay || error_exit "Cannot mount overlay rootfs image"

这样,对$LXC_DIR/overlay的写入就受到"rootfs_overlay.img"的大小的限制了。
5.最后我们最终的runtime rootfs是如下组成:


$ mkdir -p $LXC_DIR/overlay/upperdir $LXC_DIR/overlay/workdir 
$ mount -nt overlay overlay -o "noatime,lowerdir=$LXC_DIR/lowerdir,upperdir=$LXC_DIR/overlay/upperdir,workdir=$LXC_DIR/overlay/workdir" "$LXC_ROOTFS_PATH"

6.在stop container的时候,只需要umount相关的文件系统。

$ umount --lazy "$LXC_DIR/overlay"
$ umount --lazy "$LXC_DIR/lowerdir"
$ umount --lazy "$LXC_DIR/basedir"

在uninstall container的时候,只需要删除相应的image和目录即可。如果需要恢复原始状态,只需要清空upper layer的部分。

如果是特权“priv”模式:

mount -nt squashfs -o ro $LXC_DIR/rootfs_base.img $LXC_DIR/lowerdir || error_exit "Cannot mount rootfs base image"
mount -nt ext4 -o loop,nouser_xattr,noacl,nodelalloc,nobarrier,noblock_validity,noatime,sync $LXC_DIR/rootfs_overlay.img $LXC_DIR/overlay || error_exit "Cannot mount overlay rootfs image"
mkdir -p $LXC_DIR/overlay/upperdir $LXC_DIR/overlay/workdir
mount -nt overlay overlay -o "noatime,lowerdir=$LXC_DIR/lowerdir,upperdir=$LXC_DIR/overlay/upperdir,workdir=$LXC_DIR/overlay/workdir" "$LXC_ROOTFS_PATH"

如果是unpriv模式:

mount -nt squashfs -o ro $LXC_DIR/{{ base_image }} $LXC_DIR/basedir || error_exit "Cannot mount rootfs base image"
bindfs -r --uid-offset=$LXC_UID --gid-offset=$LXC_GID $LXC_DIR/basedir $LXC_DIR/lowerdir || error_exit "Cannot mount base rootfs with UID offset"
mkdir -p $LXC_DIR/overlay/upperdir $LXC_DIR/overlay/workdir
mount -nt overlay overlay -o "noatime,lowerdir=$LXC_DIR/lowerdir,upperdir=$LXC_DIR/overlay/upperdir,workdir=$LXC_DIR/overlay/workdir" "$LXC_ROOTFS_PATH"