本文基于 ubuntu 16.04 操作。
很多人可能不知道 LXD,但可能听说过老牌容器 LXC(远早于 docker)。
[LXC][] 由 [Canonical Ltd][] 和 [Ubuntu][] 开发维护,其灵感可能来自 [OpenVZ][] 等轻量级虚拟机(容器)。
原有的 LXC 工具比较难用(需要用户了解一些底层知识),同时开发团队想要修改(优化)一些默认配置和特性(如安全增强,默认创建非特权容器)。
为了保持兼容性,不宜在旧的已有 LXC 工具(如 lxc-create
, lxc-start
等)上动刀,于是新设计封装了一套上层运维操作工具,即 [LXD][]。
LXC 使用 C 开发,LXD 使用 golang 开发。早期版本的 docker 其实也是基于 LXC 封装,LXD 可能也借鉴了 docker 的一些思想。
LXD 拆分为 daemon(命令为 lxd
)和客户端(命令为 lxc
)两部分。
LXD 的定位很清晰:系统容器,直接对标虚拟机 ,甚至可以直接运行虚拟机镜像(但是不启动内核)。
系统容器运行整套操作系统(再说一次,除了内核),应用容器(如 docker)运行应用,两者不冲突。
可以在 LXD 容器里安装和使用 docker,跟在物理机和虚拟机上没什么两样。
LXD 还支持与 OpenStack 集成(nova-lxd 项目,可替代 OpenStack 上的虚拟机?)。
[LXD][] 远没有 docker 流行,网上资料不多。
但 通过 LXD 官方文档 和命令行帮助,已经足够轻松了解和使用 LXD,读来感觉非常可爱。
ubuntu 下可直接从软件仓库安装 LXD:
sudo aptitude install lxd lxd-client -y
lxd-client
是客户端软件包,安装后得到 lxc
命令。当前版本 lxd 默认会拉起 lxdbr0 网桥,可修改 lxd.service 不要依赖 lxd-bridge.service,避免拉起这个默认网桥:
sudo systemctl stop lxd-bridge.service
sudo rsync -ai /{lib,etc}/systemd/system/lxd.service
sudo sed -r -e '/^(After|Requires)=/ s#lxd-bridge.service\s*##g' /etc/systemd/system/lxd.service -i
sudo systemctl daemon-reload
另外 /etc/default/lxd-bridge
文件包含 lxd 网桥相关配置,可配置 lxd 软件包修改此配置文件。
echo 'lxd lxd/setup-bridge boolean false' | sudo debconf-set-selections
echo 'lxd lxd/use-existing-bridge boolean true' | sudo debconf-set-selections
echo 'lxd lxd/bridge-name string br0' | sudo debconf-set-selections
sudo dpkg-reconfigure -f noninteractive lxd
为了使用新版 LXD 功能和特性,可从源码编译安装新版本 LXD。
安装开发依赖。建议手动下载安装 golang 最新版本,其他依赖可直接从软件仓库安装:
sudo aptitude install acl dnsmasq-base git liblxc1 lxc-dev libacl1-dev make pkg-config rsync squashfs-tools tar xz-utils -y
从源码编译最新 release 版本:
mkdir lxd.gopath
cd lxd.gopath/
cat > .envrc <<< $'export GOPATH="${PWD}"\nGOROOT="$(readlink -f /opt/go1.10)"\nPATH="${GOROOT}/bin:${GOPATH}/bin:$PATH"'
direnv allow .
git clone git@github.com:lxc/lxd.git src/github.com/lxc/lxd
( cd src/github.com/lxc/lxd/ && git tag --sort=version:refname | tail )
( cd src/github.com/lxc/lxd/ && git reset --hard lxd-3.0.0 )
make -C src/github.com/lxc/lxd/
安装到 local 目录:
$ sudo install bin/lxd bin/lxc -t /usr/local/bin/ -v
'bin/lxd' -> '/usr/local/bin/lxd'
'bin/lxc' -> '/usr/local/bin/lxc'
转移软件包 lxd 可执行文件,使用本地新版本替代:
sudo systemctl stop lxd
sudo dpkg-divert --rename --add /usr/bin/lxc
sudo dpkg-divert --rename --add /usr/bin/lxd
sudo ln -sfT /usr/{local/,}bin/lxc
sudo ln -sfT /usr/{local/,}bin/lxd
其他办法:修改 lxd.service 使用新版本 lxd 可执行文件。
sudo systemctl stop lxd
sudo sed -r -e 's#(/usr/)(bin/lxd)b#1local/2#g' /etc/systemd/system/lxd.service -i
sudo systemctl daemon-reload
如果 `lxc` 命令被 hash 到系统路径,则需要解除 hash 以使用 local 下的新版 lxc 命令。
hash -d lxc
LXD daemon, 有时也称作服务器(server)。
LXD 相关操作通常通过客户端 lxc 命令执行,但 lxd 命令也包含一些特殊操作。
一个用户可能会用到的操作是 lxd init
,初始化 lxd daemon。
$ lxd init --help
Description:
Configure the LXD daemon
Usage:
lxd init [flags]
Examples:
init --preseed
init --auto [--network-address=IP] [--network-port=8443] [--storage-backend=dir]
[--storage-create-device=DEVICE] [--storage-create-loop=SIZE]
[--storage-pool=POOL] [--trust-password=PASSWORD]
Flags:
--auto Automatic (non-interactive) mode
--network-address Address to bind LXD to (default: none)
--network-port Port to bind LXD to (default: 8443) (default -1)
--preseed Pre-seed mode, expects YAML config from stdin
--storage-backend Storage backend to use (btrfs, dir, lvm or zfs, default: dir)
--storage-create-device Setup device based storage using DEVICE
--storage-create-loop Setup loop based storage with SIZE in GB (default -1)
--storage-pool Storage pool to use or create
--trust-password Password required to add new clients
Global Flags:
# ... ...
lxd init 也是与 daemon 通信完成操作,相当于一个特殊的 lxc 命令。其主要有两个操作,配置网络和存储。
lxd 与 git 类似,采用分布式的架构,任意两个节点都可以相互通信。配置网络地址将其暴露到网络上,其他节点可使用密码连接。
lxd init --auto --network-address=0.0.0.0 --trust-password=1234
也可以直接使用 lxc 命令修改和查看网络地址(及其他 server 配置):
$ lxc config set core.https_address 0.0.0.0:8443
$ lxc config show
config:
core.https_address: 0.0.0.0:8443
core.trust_password: true
提供一个简便的命令,创建一个名为 default 的("默认")存储,并配置(名为 default 的)默认 profile 使用此存储创建容器。
创建一个名为 lxd 的 lvm vg,使用此 vg 创建 "默认" 存储。
lxd init --auto --storage-backend=lvm --storage-pool=lxd
当然也可以直接使用 lxc 命令查看和执行相关操作:
$ lxc storage list
+---------+-------------+--------+--------+---------+
| NAME | DESCRIPTION | DRIVER | SOURCE | USED BY |
+---------+-------------+--------+--------+---------+
| default | | lvm | lxd | 1 |
+---------+-------------+--------+--------+---------+
$ lxc profile show default
config: {}
description: Default LXD profile
devices:
root:
path: /
pool: default
type: disk
name: default
used_by: []
参考:https://lxd.readthedocs.io/en/latest/storage/ 。
与 docker 不同,LXD 不区分容器和数据卷。
换句话说,容器根文件系统(容器卷)和数据卷在 LXD 使用完全相同的存储方案,统称为存储卷(storage volume),
唯一区别是容器卷会使用镜像进行初始化。
这有个好处,如设计一套存储计算分离的存储方案,则天然同时适用于容器卷和数据卷。
LXD 内置支持多种存储后端,如 dir, btrfs, lvm, zfs, ceph 等,推荐使用 zfs 和 btrfs。
为了方便我们可以使用熟悉的 lvm (thinpool)。前面已经介绍过创建和查看存储,在此不再敖述。
LXD 采用类似 git 分布式架构管理和分发镜像(和容器?),
任意两个节点都可以互相通信,并且许多操作天然支持访问和操作 remote 节点。
$ lxc remote list
+-----------------+------------------------------------------+---------------+-----------+--------+--------+
| NAME | URL | PROTOCOL | AUTH TYPE | PUBLIC | STATIC |
+-----------------+------------------------------------------+---------------+-----------+--------+--------+
| images | https://images.linuxcontainers.org | simplestreams | | YES | NO |
+-----------------+------------------------------------------+---------------+-----------+--------+--------+
| local (default) | unix:// | lxd | tls | NO | YES |
+-----------------+------------------------------------------+---------------+-----------+--------+--------+
| ubuntu | https://cloud-images.ubuntu.com/releases | simplestreams | | YES | YES |
+-----------------+------------------------------------------+---------------+-----------+--------+--------+
| ubuntu-daily | https://cloud-images.ubuntu.com/daily | simplestreams | | YES | YES |
+-----------------+------------------------------------------+---------------+-----------+--------+--------+
添加远程节点:
lxc remote add han2017 https://han2017:8443/ --accept-certificate --password=1234
可以直接使用远程镜像创建容器,LXD 会自动拷贝镜像到本地 cache(私有镜像),并自动管理(自动更新,过期清理)。
也可以显式手动从远程节点拷贝镜像到本地:
lxc image copy ubuntu:16.04 local: --alias ubuntu/16.04 --public
lxc image copy han2017:ubuntu/16.04 local: --copy-aliases --public
$ lxc image list
+--------------+--------------+--------+---------------------------------------------+--------+----------+-----------------------------+
| ALIAS | FINGERPRINT | PUBLIC | DESCRIPTION | ARCH | SIZE | UPLOAD DATE |
+--------------+--------------+--------+---------------------------------------------+--------+----------+-----------------------------+
| ubuntu/16.04 | be7cec7c9489 | yes | ubuntu 16.04 LTS amd64 (release) (20180405) | x86_64 | 156.27MB | Apr 7, 2018 at 8:17am (UTC) |
+--------------+--------------+--------+---------------------------------------------+--------+----------+-----------------------------+
$ sudo tree -L 3 -a /var/lib/lxd/images/
/var/lib/lxd/images/
├── be7cec7c948958adfbb9bc7dbd292762d2388cc883466815fc2b6bc06bf06f5a
└── be7cec7c948958adfbb9bc7dbd292762d2388cc883466815fc2b6bc06bf06f5a.rootfs
/var/lib/lxd/images/
目录下。参考:https://lxd.readthedocs.io/en/latest/containers/ 。
LXD 容器配置可划分为两部分:
lxc config
或 lxc profile
管理。lxc config device
或 lxc profile device
管理。如磁盘设备,网络设备等。为了方便管理容器配置,LXD 支持使用 profile 预设管理容器配置模板。lxc config
管理已创建容器实例配置,lxc profile
管理容器 profile 配置。
创建容器时可指定多个 profile,多个 profile 与容器自身配置叠加覆盖得到最终有效配置。
使用 lxc init
命令创建容器:
$ lxc init --help
Description:
Create containers from images
Usage:
lxc init [<remote>:]<image> [<remote>:][<name>] [flags]
Examples:
lxc init ubuntu:16.04 u1
Flags:
-c, --config Config key/value to apply to the new container
-e, --ephemeral Ephemeral container
-n, --network Network name
-p, --profile Profile to apply to the new container
-s, --storage Storage pool name
--target Node name
-t, --type Instance type
$ lxc network list
+--------+----------+---------+-------------+---------+
| NAME | TYPE | MANAGED | DESCRIPTION | USED BY |
+--------+----------+---------+-------------+---------+
| br0 | bridge | NO | | 0 |
+--------+----------+---------+-------------+---------+
| enp5s0 | physical | NO | | 0 |
+--------+----------+---------+-------------+---------+
-p
指定 profile, 可重复多次以指定多个 profile 叠加覆盖。不指定时默认使用 default
。-c
指定容器 key/value 配置。-s
使用指定存储池创建容器卷。容器卷未指定大小时默认与镜像大小相同。-n
创建网卡设备连接到指定网络。可指定外部(手动管理的)网桥(或网卡等网络设备?)名。可简化起见,可设置创建特权容器。
一个原因是,LXD 非特权容器默认开启了用户 idmap,虽然 LXD 对 idmap 做了很好的支持,
但 idmap 解决的问题比带来的问题更多,如与宿主机共享文件系统问题等。
修改默认 profile:
lxc profile set default security.privileged true
创建容器:
$ lxc list
+------+-------+------+------+------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+------+-------+------+------+------+-----------+
$ lxc storage volume list default
+------+------+-------------+---------+
| TYPE | NAME | DESCRIPTION | USED BY |
+------+------+-------------+---------+
$ sudo lvs lxd
$ lxc init ubuntu/16.04 test -s default -n br0
Creating test
$ lxc list
+------+---------+------+------+------------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+------+---------+------+------+------------+-----------+
| test | STOPPED | | | PERSISTENT | 0 |
+------+---------+------+------+------------+-----------+
$ lxc config show test
architecture: x86_64
# ... ...
devices:
br0:
nictype: bridged
parent: br0
type: nic
root:
path: /
pool: default
type: disk
ephemeral: false
profiles:
- default
stateful: false
description: ""
$ lxc storage volume list default
+-----------+------------------------------------------------------------------+-------------+---------+
| TYPE | NAME | DESCRIPTION | USED BY |
+-----------+------------------------------------------------------------------+-------------+---------+
| container | test | | 1 |
+-----------+------------------------------------------------------------------+-------------+---------+
| image | be7cec7c948958adfbb9bc7dbd292762d2388cc883466815fc2b6bc06bf06f5a | | 1 |
+-----------+------------------------------------------------------------------+-------------+---------+
$ sudo lvs lxd
LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
LXDThinpool lxd twi-aotz-- 98.00g 0.91 0.09
containers_test lxd Vwi-a-tz-- 10.00g LXDThinpool images_be7cec7c948958adfbb9bc7dbd292762d2388cc883466815fc2b6bc06bf06f5a 8.90
images_be7cec7c948958adfbb9bc7dbd292762d2388cc883466815fc2b6bc06bf06f5a lxd Vwi-a-tz-- 10.00g LXDThinpool 8.90
创建容器时即完成创建容器卷(作为容器设备)(网卡等容器设备则是启动容器时才创建)。
启动容器:
$ brctl show
bridge name bridge id STP enabled interfaces
br0 8000.0227625084a8 yes enp5s0
tap0
$ lxc start test
$ brctl show
bridge name bridge id STP enabled interfaces
br0 8000.0227625084a8 yes enp5s0
tap0
vethKFYJSY
$ ip link show dev vethKFYJSY
6: vethKFYJSY@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br0 state UP mode DEFAULT group default qlen 1000
link/ether fe:69:14:43:db:75 brd ff:ff:ff:ff:ff:ff link-netnsid 0
$ lxc exec -t test /bin/bash
root@test:~# ip -4 addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
5: eth0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000 link-netnsid 0
inet 192.168.100.77/24 brd 192.168.100.255 scope global eth0
valid_lft forever preferred_lft forever
另外发现 lxc exec
的进程报 "not a tty":
root@test:~# tty
not a tty
root@test:~# ll /proc/$$/fd/0
lrwx------ 1 root root 64 Apr 7 09:24 /proc/445/fd/0 -> /dev/pts/2
root@test:~# findmnt /dev/
TARGET SOURCE FSTYPE OPTIONS
/dev none tmpfs rw,relatime,size=492k,mode=755
root@test:~# findmnt /dev/pts/
TARGET SOURCE FSTYPE OPTIONS
/dev/pts devpts devpts rw,relatime,gid=5,mode=620,ptmxmode=666
root@test:~# ls /dev/pts/
ptmx
lxc exec
进程使用的应该是从宿主机上继承的伪终端(上例即 /dev/pts/2
)。宿主机上可看到相关进程信息如下:
$ pschain -C bash -fww f
UID PID PPID C STIME TTY STAT TIME CMD
# ... ...
root 24225 1 0 4月06 ? Ssl 11:43 /usr/bin/lxd --group lxd --logfile=/var/log/lxd/lxd.log
root 30251 24225 0 17:23 ? Sl 0:00 \_ /usr/local/bin/lxd forkexec test /var/lib/lxd/containers /var/log/lxd/test/lxc.conf -- env TERM=xterm PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin HOME=/root USER=root LANG=C.UTF-8 -- cmd /bin/bash
root 30259 30251 0 17:23 pts/2 Ss+ 0:00 \_ /bin/bash
$ sudo ls -l /proc/30259/fd/0 --color
lrwx------ 1 root root 64 4月 7 17:30 /proc/30259/fd/0 -> /dev/pts/2
如果要执行的命令强依赖正确设置 tty,一个简单的解决办法是 在容器内重新分配伪终端,如使用 script
命令包装要执行的命令。
$ lxc exec -t test /bin/bash
root@test:~# tty
not a tty
root@test:~# script -c /bin/bash /dev/null
Script started, file is /dev/null
root@test:~# tty
/dev/pts/0
可以在 lxc exec 时直接执行 script 命令:
$ lxc exec -t test -- script -c /bin/bash /dev/null
Script started, file is /dev/null
root@test:~# tty
/dev/pts/0
容器添加磁盘(文件系统)设备,设置 source 为宿主机文件系统路径,即使用路径绑定,即可轻松访问宿主机文件系统。
LXD 支持动态添加路径绑定,操作立即生效,非常方便。
新建 profile 方便管理相关配置:
lxc profile create share-host
lxc profile set share-host security.privileged true
lxc profile device add share-host /etc/apt/ disk {source,path}=/etc/apt/
$ lxc profile show share-host
config:
security.privileged: "true"
description: ""
devices:
/etc/apt/:
path: /etc/apt/
source: /etc/apt/
type: disk
name: share-host
used_by: []
security.privileged=true
。容器添加 profile:
$ lxc exec -t test findmnt /etc/apt/
$ lxc profile add test share-host
Profile share-host added to test
$ lxc config show test
# ... ...
profiles:
- default
- share-host
stateful: false
description: ""
$ lxc exec -t test findmnt /etc/apt/
TARGET SOURCE FSTYPE OPTIONS
/etc/apt /dev/mapper/vg-ubu1604[/etc/apt] ext4 rw,relatime,errors=remount-ro,data=ordered
$ lxc profile device add share-host /var/cache/apt/ disk {source,path}=/var/cache/apt/
Device /var/cache/apt/ added to share-host
$ lxc exec -t test findmnt /var/cache/apt/
TARGET SOURCE FSTYPE OPTIONS
/var/cache/apt /dev/mapper/vg-ubu1604var[/cache/apt] ext4 rw,relatime,data=ordered
LXD 不支持分层镜像,镜像制作工具也没有 docker 完善。如何制作 LXD 镜像呢?
参考:https://lxd.readthedocs.io/en/latest/image-handling/ 。
将容器发布为镜像:
$ lxc image list
+--------------+--------------+--------+---------------------------------------------+--------+----------+-----------------------------+
| ALIAS | FINGERPRINT | PUBLIC | DESCRIPTION | ARCH | SIZE | UPLOAD DATE |
+--------------+--------------+--------+---------------------------------------------+--------+----------+-----------------------------+
| ubuntu/16.04 | be7cec7c9489 | yes | ubuntu 16.04 LTS amd64 (release) (20180405) | x86_64 | 156.27MB | Apr 7, 2018 at 8:17am (UTC) |
+--------------+--------------+--------+---------------------------------------------+--------+----------+-----------------------------+
$ sudo tree -L 3 -a /var/lib/lxd/images/
/var/lib/lxd/images/
├── be7cec7c948958adfbb9bc7dbd292762d2388cc883466815fc2b6bc06bf06f5a
└── be7cec7c948958adfbb9bc7dbd292762d2388cc883466815fc2b6bc06bf06f5a.rootfs
$ lxc publish test --alias test --public
Container published with fingerprint: cd1d4f8ce11dc9b6ff2b0a2c45e3cf1bc1370bbb45724b245880b757636537d3
$ lxc image list
+--------------+--------------+--------+---------------------------------------------+--------+----------+-----------------------------+
| ALIAS | FINGERPRINT | PUBLIC | DESCRIPTION | ARCH | SIZE | UPLOAD DATE |
+--------------+--------------+--------+---------------------------------------------+--------+----------+-----------------------------+
| test | cd1d4f8ce11d | yes | | x86_64 | 243.98MB | Apr 7, 2018 at 4:40pm (UTC) |
+--------------+--------------+--------+---------------------------------------------+--------+----------+-----------------------------+
| ubuntu/16.04 | be7cec7c9489 | yes | ubuntu 16.04 LTS amd64 (release) (20180405) | x86_64 | 156.27MB | Apr 7, 2018 at 8:17am (UTC) |
+--------------+--------------+--------+---------------------------------------------+--------+----------+-----------------------------+
$ sudo tree -L 3 -a /var/lib/lxd/images/
/var/lib/lxd/images/
├── be7cec7c948958adfbb9bc7dbd292762d2388cc883466815fc2b6bc06bf06f5a
├── be7cec7c948958adfbb9bc7dbd292762d2388cc883466815fc2b6bc06bf06f5a.rootfs
└── cd1d4f8ce11dc9b6ff2b0a2c45e3cf1bc1370bbb45724b245880b757636537d3
0 directories, 3 files
$ lxc storage volume list default
+-----------+------------------------------------------------------------------+-------------+---------+
| TYPE | NAME | DESCRIPTION | USED BY |
+-----------+------------------------------------------------------------------+-------------+---------+
| container | test | | 1 |
+-----------+------------------------------------------------------------------+-------------+---------+
| image | be7cec7c948958adfbb9bc7dbd292762d2388cc883466815fc2b6bc06bf06f5a | | 1 |
+-----------+------------------------------------------------------------------+-------------+---------+
使用新镜像创建容器:
$ lxc init test test2 -p share-host -p default -s default -n br0
Creating test2
$ lxc list
+-------+---------+------+------+------------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+-------+---------+------+------+------------+-----------+
| test | STOPPED | | | PERSISTENT | 0 |
+-------+---------+------+------+------------+-----------+
| test2 | STOPPED | | | PERSISTENT | 0 |
+-------+---------+------+------+------------+-----------+
$ lxc storage volume list default
+-----------+------------------------------------------------------------------+-------------+---------+
| TYPE | NAME | DESCRIPTION | USED BY |
+-----------+------------------------------------------------------------------+-------------+---------+
| container | test | | 1 |
+-----------+------------------------------------------------------------------+-------------+---------+
| container | test2 | | 1 |
+-----------+------------------------------------------------------------------+-------------+---------+
| image | be7cec7c948958adfbb9bc7dbd292762d2388cc883466815fc2b6bc06bf06f5a | | 1 |
+-----------+------------------------------------------------------------------+-------------+---------+
| image | cd1d4f8ce11dc9b6ff2b0a2c45e3cf1bc1370bbb45724b245880b757636537d3 | | 1 |
+-----------+------------------------------------------------------------------+-------------+---------+
友好排版请 阅读原文 。