Linux有容器技术LXC,LXD对LXC进行了封装。与Docker类似,但LXC容器比Docker容器完整,且不像Docker是一次性容器设计运行结束后即销毁实例。使用LXD操作容器,体验介于Docker与虚拟机之间。(其实LXD不只提供封装LXC容器的功能,还提供封装虚拟机)

开始使用LXD——初始设置

  1. 从自己的发行版仓库安装LXD。LXD与Docker一样有一个守护进程lxd,因此有

    sudo systemctl enable/disable/start/stop lxd
    

    如果用systemctl停止lxd时,仍有正在运行的容器,那些容器不会被停止。所以应该先停止所有容器,再systemctl stop

    lxd是守护进程,而平时操作LXD则是用lxc命令(此lxc非属于LXC容器,是LXD的客户端的意思)

  2. 安装LXD后自动创建lxd用户组,我们手动把自己的日常用户添加进这个组里:

    usermod -aG lxd <username>
    
  3. 添加清华LXC镜像(下文都用这个tuna-images源):

    lxc remote add tuna-images https://mirrors.tuna.tsinghua.edu.cn/lxc-images/ --protocol=simplestreams --public
    

    若想在连接镜像时使用代理:

    lxc config set core.proxy_http http://ip:port
    lxc config set core.proxy_https http://ip:port
    lxc config set core.proxy_ignore_hosts image-server.local 
    
  4. /etc/subuid/etc/subgid都写

    1000:1000000:65536
    root:1000000:65536
    lxd:1000000:65536
    

    让容器进程运行时,宿主看到的容器进程所属UID以一百万算起 (外一百万=内0)。且容器的rootfs/下的文件在宿主机看来UID、GID也都是一样map过的。这种叫非特权容器,比特权容器安全。

  5. 初始化LXD。运行

    sudo lxd init
    

    交互问答式的。

    • 其中有一个项让我们选择储存方式,我们自用选最简单的dir方式就可以,简单实用(这样容器内文件可以直接访问/var/lib/lxd/storage-pools/default/containers/容器名/rootfs/查看修改)。
  6. (可选)缓存与自动刷新相关设置

    lxc config set images.remote_cache_expiry 30
    lxc config set images.auto_update_interval 24
    lxc config set images.auto_update_cached false 
    
  7. 重启

  8. 若宿主机有防火墙,可能需要手动把lxdbr0加入白名单,以使容器能够联网和与宿主机网络通信

使用LXD的常用操作

  • 查看网络源目前可下载的镜像

    lxc image list tuna-images:
    
  • 查看本地已缓存镜像

    lxc image list local:
    
  • 下载镜像到本地缓存

    lxc image copy ubuntu:14.04 local:
  • 使用镜像创建并运行容器

    lxc launch tuna-images:发布版/版本/架构 容器名
    

    发布版/版本/架构也可以用它的一串十六进制数字id替代表示)

  • 运行或停止容器

    lxc start 容器名
    
    lxc stop [-f] 容器名
    
  • 获得容器的bash shell

    lxc exec 容器名 bash
    

    上面这个是non-login shell。它支持stdin传入,如:

    cat 宿主机上的某shell脚本 | lxc exec 容器名 bash -
    

    (可选)在容器内,用passwd设置root密码。内账户有密码后,可登录容器获得login shell:

    lxc console 容器名
    
  • 挂载主机路径到容器

    lxc config|profile device add <容器名|profile名> share-from-host(设备名) disk source=/tmp/share-to-lxd/ path=/media/share-from-host [shift=true]
    

    shift=true启用指非特权容器需要的UID、GID map。这个要求内核支持shiftfs。

    若无,非特权容器中看到的宿主机共享进去的文件都是nobody:nobody的,而主机中看到的容器内创建的文件都是1000000:1000000

  • 查看挂载

    lxc config device show <容器名>
    
  • 手动带UID、GID map的挂载

    前面说到,以非特权方式运行容器,UID、GID是map过的。如果内核不支持shiftfs,可以考虑一些workaround方案,用其他方式来做map(选其中一个):

    • bindfs:

      bindfs -u 1000000 -g 1000000 --create-for-user=1000 --create-for-group=1000  /something-on-host /tmp/share-to-lxd/subdir
      

      (注意:这样mount中套mount,有时有看不见的可能,有时要先启动容器,进入了share文件夹,再从主机中bindfs)

    • fuse-overlayfs

虚拟X server

容器镜像一般都不会带有GUI相关内容的,若想在容器内运行GUI程序,除了安装相应的包外,还需要一个X server。可以把宿主机的X server共享给容器用,但这需要处理好X授权,且安全性低些。可以在容器内运行虚拟的X server,可以通过截图、VNC、录屏方式查看和操作这个虚拟X server,这些方法在CI自动化测试时也有用。

虚拟的X server有:

  • Xvfb
  • Xdummy

Xdummy更晩出现,声称比Xvfb功能更多。选择其中的一个使用

启动虚拟X server

二个中选一

  • Xvfb

    apt-get install xvfb
    
    Xvfb -ac :3 [-listen tcp]  [-screen 0 1280x1024x24 ]
    

    (它可能不会有任何输出)

  • Xdummy

    • 第一种情况:需要编译
    apt-get install x11vnc xserver-xorg-video-dummy gcc
    
    Xdummy -install 
    

    Xdummy是一个脚本,这一步实际上是用cc编译一个.so出来,准备结合LD_PRELOAD运行X server。Xdummy也可以调用X

    启动:

    Xdummy :3  [-listen tcp]
    

    若报错 dlsym No such file ,则在上一步编译时,加一个环境变量 CFLAGS="-ldl"

    • 第二种情况:不需要编译

      这种情况下可以没有Xdummy这个脚本文件。

      创建/tmp/dummy.conf写入:

      Section "Monitor"
        Identifier "Monitor0"
        HorizSync 28.0-80.0
        VertRefresh 48.0-75.0
        # https://arachnoid.com/modelines/
        # 1920x1080 @ 60.00 Hz (GTF) hsync: 67.08 kHz; pclk: 172.80 MHz
        # Modeline "1920x1080_60.00" 172.80 1920 2040 2248 2576 1080 1081 1084 1118 -HSync +Vsync
        Modeline "1280x720_60.00"   74.50  1280 1344 1472 1664  720 723 728 748 -hsync +vsync
      EndSection
      
      Section "Device"
        Identifier "Card0"
        Driver "dummy"
        VideoRam 256000
      EndSection
      
      Section "Screen"
        DefaultDepth 24
        Identifier "Screen0"
        Device "Card0"
        Monitor "Monitor0"
        SubSection "Display"
          Depth 24
          Modes "1280x720_60.00"
        EndSubSection
      EndSection
      

      然后运行:

      X -config /tmp/dummy.conf :3 -listen tcp
      

启动了虚拟的X server在:3后,就可以

env DISPLAY=:3 某GUI程序

也可以把Xvfb运行在宿主机上。这样要在容器内设定export DISPLAY=宿主机IP:3

如果X server与client(client即那些GUI程序)不是运行在一个系统上的,虚拟X server那边则需要执行:

xhost +<另一系统的IP>

通过shell对虚拟X server操作和截图

apt-get install xdotool xwd scrot x11vnc

其中:

  • xdotool,模拟键盘和鼠标动作

  • x11vnc,VNC服务器,把X server内容和操作提供给VNC客户端

  • scrot,截图工具(推荐)

  • xwd,截图工具(不推荐)

模拟鼠标操作

容器内运行xdotool

env DISPLAY=:3 xdotool getmouselocation
env DISPLAY=:3 xdotool mousemove <x> <y>
env DISPLAY=:3 xdotool click 1    # (1是左键)

VNC

容器内运行VNC服务

x11vnc -display :3 -N -forever -shared -reopen [-passwd 123456] -desktop 1

宿主机可用Remmina连接容器IP:5903(因为容器内用了:3这个DISPLAY,所以默认的x11vnc服务端口是5903),密码为上面设置的123456

注意在Redmina中挥动鼠标可能会使截图中的鼠标隐藏。用xdotool mousemove可恢复

截图

在容器内运行CLI截图工具。

xwd(不推荐):

xwd -display :3 -root -out /media/share-from-host/shot.xwd

(主机中或容器中) convert shot.xwd shot.png

上面的convert是ImageMagick的命令

scrot(推荐):

env DISPLAY=:3 scrot /media/share-from-host/shot.png -p   # (-p : 截图包括鼠标)

(如果在CI中,截图后要上传artifacts)

也可以运行simplescreenrecorder或ffmpeg等对虚拟X server录屏(录屏工具与虚拟X server要运行在同一系统上)。