Ubuntu Core 是什么？Ubuntu Core 是完全基于 snap 包构建，并且完全事务化的 Ubuntu 版本。该系统大部分是只读的，所有已安装的应用全部来自 snap 包，完全使用事务化更新。这意味着不管在系统更新还是安装软件的时候遇到问题，整个系统都可以回退到之前的状态并且记录这个错误。最新版是在 2016 年 11 月发布的 Ubuntu Core 16。注意，Ubuntu Core 限制只能够安装 snap 包（而非 “传统” 软件包），并且有相当数量的 snap 包在当前环境下不能正常运行，或者需要人工干预（创建用户和用户组等）才能正常运行。随着新版的 snapd 和 “core” snap 包发布，Ubuntu Core 每周都会得到改进。环境需求就 LXD 而言，Ubuntu Core 仅仅相当于另一个 Linux 发行版。也就是说，snapd 需要挂载无特权的 FUSE 和 AppArmor 命名空间以及软件栈，像下面这样：一个新版的使用 Ubuntu 官方内核的系统一个新版的 LXD创建一个 Ubuntu Core 容器当前 Ubuntu Core 镜像发布在社区的镜像服务器。你可以像这样启动一个新的容器：stgraber@dakara:~$ lxc launch images:ubuntu-core/16 ubuntu-core Creating ubuntu-core Starting ubuntu-core 这个容器启动需要一点点时间，它会先执行第一阶段的加载程序，加载程序会确定使用哪一个镜像（镜像是只读的），并且在系统上设置一个可读层，你不要在这一阶段中断容器执行，这个时候什么都没有，所以执行 lxc exec 将会出错。几秒钟之后，执行 lxc list 将会展示容器的 IP 地址，这表明已经启动了 Ubuntu Core：stgraber@dakara:~$ lxc list +-------------+---------+----------------------+----------------------------------------------+------------+-----------+ | NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | +-------------+---------+----------------------+----------------------------------------------+------------+-----------+ | ubuntu-core | RUNNING | 10.90.151.104 (eth0) | 2001:470:b368:b2b5:216:3eff:fee1:296f (eth0) | PERSISTENT | 0 | +-------------+---------+----------------------+----------------------------------------------+------------+-----------+ 之后你就可以像使用其他的交互一样和这个容器进行交互：stgraber@dakara:~$ lxc exec ubuntu-core bash root@ubuntu-core:~# snap list Name Version Rev Developer Notes core 16.04.1 394 canonical - pc 16.04-0.8 9 canonical - pc-kernel 4.4.0-45-4 37 canonical - root@ubuntu-core:~# 更新容器如果你一直关注着 Ubuntu Core 的开发，你应该知道上面的版本已经很老了。这是因为被用作 Ubuntu LXD 镜像的代码每隔几个月才会更新。Ubuntu Core 系统在重启时会检查更新并进行自动更新（更新失败会回退）。如果你想现在强制更新，你可以这样做：stgraber@dakara:~$ lxc exec ubuntu-core bash root@ubuntu-core:~# snap refresh pc-kernel (stable) 4.4.0-53-1 from 'canonical' upgraded core (stable) 16.04.1 from 'canonical' upgraded root@ubuntu-core:~# snap version snap 2.17 snapd 2.17 series 16 root@ubuntu-core:~# 然后重启一下 Ubuntu Core 系统，然后看看 snapd 的版本。root@ubuntu-core:~# reboot root@ubuntu-core:~# stgraber@dakara:~$ lxc exec ubuntu-core bash root@ubuntu-core:~# snap version snap 2.21 snapd 2.21 series 16 root@ubuntu-core:~# 你也可以像下面这样查看所有 snapd 的历史记录：stgraber@dakara:~$ lxc exec ubuntu-core snap changes ID Status Spawn Ready Summary 1 Done 2017-01-31T05:14:38Z 2017-01-31T05:14:44Z Initialize system state 2 Done 2017-01-31T05:14:40Z 2017-01-31T05:14:45Z Initialize device 3 Done 2017-01-31T05:21:30Z 2017-01-31T05:22:45Z Refresh all snaps in the system 安装 Snap 软件包以一个最简单的例子开始，经典的 Hello World：stgraber@dakara:~$ lxc exec ubuntu-core bash root@ubuntu-core:~# snap install hello-world hello-world 6.3 from 'canonical' installed root@ubuntu-core:~# hello-world Hello World! 接下来让我们看一些更有用的：stgraber@dakara:~$ lxc exec ubuntu-core bash root@ubuntu-core:~# snap install nextcloud nextcloud 11.0.1snap2 from 'nextcloud' installed 之后通过 HTTP 访问你的容器就可以看到刚才部署的 Nextcloud 实例。如果你想直接通过 git 测试最新版 LXD，你可以这样做：stgraber@dakara:~$ lxc config set ubuntu-core security.nesting true stgraber@dakara:~$ lxc exec ubuntu-core bash root@ubuntu-core:~# snap install lxd --edge lxd (edge) git-c6006fb from 'canonical' installed root@ubuntu-core:~# lxd init Name of the storage backend to use (dir or zfs) [default=dir]: We detected that you are running inside an unprivileged container. This means that unless you manually configured your host otherwise, you will not have enough uid and gid to allocate to your containers. LXD can re-use your container's own allocation to avoid the problem. Doing so makes your nested containers slightly less safe as they could in theory attack their parent container and gain more privileges than they otherwise would. Would you like to have your containers share their parent's allocation (yes/no) [default=yes]? Would you like LXD to be available over the network (yes/no) [default=no]? Would you like stale cached images to be updated automatically (yes/no) [default=yes]? Would you like to create a new network bridge (yes/no) [default=yes]? What should the new bridge be called [default=lxdbr0]? What IPv4 address should be used (CIDR subnet notation, “auto” or “none”) [default=auto]? What IPv6 address should be used (CIDR subnet notation, “auto” or “none”) [default=auto]? LXD has been successfully configured. 已经设置过的容器不能回退版本，但是可以在 Ubuntu Core 16 中运行另一个 Ubuntu Core 16 容器：root@ubuntu-core:~# lxc launch images:ubuntu-core/16 nested-core Creating nested-core Starting nested-core root@ubuntu-core:~# lxc list +-------------+---------+---------------------+-----------------------------------------------+------------+-----------+ | NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | +-------------+---------+---------------------+-----------------------------------------------+------------+-----------+ | nested-core | RUNNING | 10.71.135.21 (eth0) | fd42:2861:5aad:3842:216:3eff:feaf:e6bd (eth0) | PERSISTENT | 0 | +-------------+---------+---------------------+-----------------------------------------------+------------+-----------+ 写在最后如果你只是想试用一下 Ubuntu Core，这是一个不错的方法。对于 snap 包开发者来说，这也是一个不错的工具来测试你的 snap 包能否在不同的环境下正常运行。如果你希望你的系统总是最新的，并且整体可复制，Ubuntu Core 是一个很不错的方案，不过这也会带来一些相应的限制，所以可能不太适合你。最后是一个警告，对于测试来说，这些镜像是足够的，但是当前并没有被正式的支持。在不久的将来，官方的 Ubuntu server 可以完整的支持 Ubuntu Core LXD 镜像。附录LXD 主站：https://linuxcontainers.org/lxdGithub：https://github.com/lxc/lxd邮件列表：https://lists.linuxcontainers.orgIRC：#lxcontainers on irc.freenode.net在线试用：https://linuxcontainers.org/lxd/try-it来自: https://insights.ubuntu.com/2017/02/27/ubuntu-core-in-lxd-containers/作者：Stéphane Graber 译者：aiwhj 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
介绍当 LXD 2.0 随着 Ubuntu 16.04 一起发布时，LXD 联网就简单了。要么你可以使用 lxd init 来配置，为你的容器自己提供一个 “lxdbr0” 网桥，要么使用一个已存在的物理接口。虽然这确实有效，但是有点混乱，因为大部分的桥接配置发生在 Ubuntu 打包的 LXD 之外。那些脚本只能支持一个桥接，并且没有通过 API 暴露，这使得远程配置有点痛苦。直到 LXD 2.3，LXD 终于发展了自己的网络管理 API ，并有相应的命令行工具。这篇文章试图来简述这些新的功能。基础联网在初始情况下，LXD 2.3 没有定义任何网络。lxd init 会为你设置一个，并且默认情况下将所有新的容器连接到它，但是让我们亲手尝试看下究竟发生了些什么。要创建一个新的带有随机 IPv4 和 IP6 子网，并启用 NAT 的网络，只需要运行：stgraber@castiana:~$ lxc network create testbr0 Network testbr0 created 你可以如下查看它的配置：stgraber@castiana:~$ lxc network show testbr0 name: testbr0 config: ipv4.address: 10.150.19.1/24 ipv4.nat: "true" ipv6.address: fd42:474b:622d:259d::1/64 ipv6.nat: "true" managed: true type: bridge usedby: [] 如果你不想要那些自动配置的子网，你可以这么做：stgraber@castiana:~$ lxc network create testbr0 ipv6.address=none ipv4.address=10.0.3.1/24 ipv4.nat=true Network testbr0 created 那就会这样：stgraber@castiana:~$ lxc network show testbr0 name: testbr0 config: ipv4.address: 10.0.3.1/24 ipv4.nat: "true" ipv6.address: none managed: true type: bridge usedby: [] 如果你的容器没有使用它，那么创建的网络对你也没什么用。要将你新创建的网络连接到所有容器，你可以这么做：stgraber@castiana:~$ lxc network attach-profile testbr0 default eth0 要将一个网络连接到一个已存在的容器中，你可以这么做：stgraber@castiana:~$ lxc network attach my-container default eth0 现在，假设你已经在机器中安装了 openvswitch，并且要将这个网桥转换成 OVS 网桥，只需更改为正确的驱动：stgraber@castiana:~$ lxc network set testbr0 bridge.driver openvswitch 如果你想要一次性做一系列修改。lxc network edit 可以让你在编辑器中交互编辑网络配置。静态租约及端口安全使用 LXD 管理 DHCP 服务器的一个好处是可以使得管理 DHCP 租约很简单。你所需要的是一个容器特定的网卡设备以及正确的属性设置。root@yak:~# lxc init ubuntu:16.04 c1 Creating c1 root@yak:~# lxc network attach testbr0 c1 eth0 root@yak:~# lxc config device set c1 eth0 ipv4.address 10.0.3.123 root@yak:~# lxc start c1 root@yak:~# lxc list c1 +------+---------+-------------------+------+------------+-----------+ | NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | +------+---------+-------------------+------+------------+-----------+ | c1 | RUNNING | 10.0.3.123 (eth0) | | PERSISTENT | 0 | +------+---------+-------------------+------+------------+-----------+ IPv6 也是相同的方法，但是换成 ipv6.address 属性。相似地，如果你想要阻止你的容器更改它的 MAC 地址或者为其他 MAC 地址转发流量（比如嵌套），你可以用下面的命令启用端口安全：root@yak:~# lxc config device set c1 eth0 security.mac_filtering true DNSLXD 在网桥上运行 DNS 服务器。除了设置网桥的 DNS 域（ dns.domain 网络属性）之外，还支持 3 种不同的操作模式（dns.mode）：managed ：每个容器都会有一条 DNS 记录，匹配它的名字以及已知的 IP 地址。容器无法通过 DHCP 改变这条记录。dynamic ：允许容器通过 DHCP 在 DNS 中自行注册。因此，在 DHCP 协商期间容器发送的任何主机名最终都出现在 DNS 中。none ： 针对那些没有任何本地 DNS 记录的递归 DNS 服务器。默认的模式是 managed，并且典型的是最安全以及最方便的，因为它为容器提供了 DNS 记录，但是不允许它们通过 DHCP 发送虚假主机名嗅探其他的记录。使用隧道除了这些，LXD 还支持使用 GRE 或者 VXLAN 隧道连接到其他主机。LXD 网络可以连接任何数量的隧道，从而轻松地创建跨多个主机的网络。这对于开发、测试和演示非常有用，生产环境通常更喜欢使用 VLAN 进行分割。所以说，你想在主机 “edfu” 上有一个运行 IPv4 和 IPv6 的基础 “testbr0” 网络，并希望在主机 “djanet” 上使用它来生成容器。最简单的方法是使用组播 VXLAN 隧道。这种类型的隧道仅在两个主机位于同一物理段上时才起作用。root@edfu:~# lxc network create testbr0 tunnel.lan.protocol=vxlan Network testbr0 created root@edfu:~# lxc network attach-profile testbr0 default eth0 它在主机 “edfu” 上定义了一个 “testbr0” 桥接，并为其他主机能加入它设置了一个组播 VXLAN。在这个设置中，“edfu” 为这个网络扮演了一个路由器角色，提供 DHCP、DNS 等等，其他主机只是通过隧道转发流量。root@djanet:~# lxc network create testbr0 ipv4.address=none ipv6.address=none tunnel.lan.protocol=vxlan Network testbr0 created root@djanet:~# lxc network attach-profile testbr0 default eth0 现在你可以在任何一台主机上启动容器，并看到它们从同一个地址池中获取 IP，通过隧道直接互相通讯。如先前所述，这个使用了组播，它通常在跨越路由器时无法很好工作。在这些情况下，你可以用单播模式使用 VXLAN 或者 GRE 隧道。要使用 GRE 加入另一台主机，首先配置服务主机：root@edfu:~# lxc network set testbr0 tunnel.nuturo.protocol gre root@edfu:~# lxc network set testbr0 tunnel.nuturo.local 172.17.16.2 root@edfu:~# lxc network set testbr0 tunnel.nuturo.remote 172.17.16.9 接着是“客户端”主机：root@nuturo:~# lxc network create testbr0 ipv4.address=none ipv6.address=none tunnel.edfu.protocol=gre tunnel.edfu.local=172.17.16.9 tunnel.edfu.remote=172.17.16.2 Network testbr0 created root@nuturo:~# lxc network attach-profile testbr0 default eth0 如果你像使用 VXLAN，只要这么做：root@edfu:~# lxc network set testbr0 tunnel.edfu.id 10 root@edfu:~# lxc network set testbr0 tunnel.edfu.protocol vxlan 还有：root@nuturo:~# lxc network set testbr0 tunnel.edfu.id 10 root@nuturo:~# lxc network set testbr0 tunnel.edfu.protocol vxlan 这里需要隧道 id 以防与已经配置的多播 VXLAN 隧道冲突。这就是如何使用最近的 LXD 简化跨主机联网了！总结LXD 使得从简单的单主机网络到数千个容器的非常复杂的跨主机网络的定义变得更加容易。它也使为一些容器定义一个新网络或者给容器添加第二个设备，并连接到隔离的私有网络变得很简单。虽然这篇文章介绍了支持的大部分功能，但仍有一些可以微调 LXD 网络体验的窍门。可以在这里找到完整的列表：https://github.com/lxc/lxd/blob/master/doc/configuration.md 。额外信息LXD 主站：https://linuxcontainers.org/lxdGithub 地址： https://github.com/lxc/lxd邮件列表支持：https://lists.linuxcontainers.orgIRC 频道：#lxcontainers on irc.freenode.net在线尝试 LXD：https://linuxcontainers.org/lxd/try-itvia: https://www.stgraber.org/2016/10/27/network-management-with-lxd-2-3/作者：Stéphane Graber 译者：geekpi 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
介绍终于要结束了！这个大约一年前开始的这系列文章的最后一篇博文。LXD 入门安装与配置你的第一个 LXD 容器资源控制镜像管理远程主机及容器迁移LXD 中的 DockerLXD 中的 LXD实时迁移LXD 和 JujuLXD 和 OpenStack调试，及给 LXD 做贡献如果你从一开始就关注了这个系列，你应该已经使用了 LXD 相当长的时间了，并且非常熟悉它的日常操作和功能。但如果出现问题怎么办？你可以做什么来自己跟踪问题？如果你不能，你应该记录什么信息，以便上游可以跟踪问题？如果你想自己解决问题或通过实现你需要的功能来帮助改善LXD怎么办？如何构建，测试和贡献 LXD 代码库？调试 LXD 并填写 bug 报告LXD 日志文件/var/log/lxd/lxd.log：这是 LXD 日志的主文件。为了避免它快速充满你的磁盘，默认只会记录 INFO、WARNING 或者 ERROR 级别的日志。你可以在 LXD 守护进程中使用 –debug 改变其行为。/var/log/lxd/CONTAINER/lxc.conf：每当你启动容器时，此文件将更新为传递给 LXC 的配置。这里会展示容器将如何配置，包括其所有的设备、绑定挂载等等。/var/log/lxd/CONTAINER/forkexec.log：这个文件包含 LXC 命令执行失败时产生的错误。这个情况是非常罕见的，因为 LXD 通常会在发生之前处理大多数错误。/var/log/lxd/CONTAINER/forkstart.log：这个文件包含 LXC 在启动容器时的错误信息。含 LXC 命令执行失败时产生的错误。CRIU 日志 (对于实时迁移)如果使用 CRIU 进行容器实时迁移或实时快照，则每次生成 CRIU 转储或恢复转储时都会记录额外的日志文件。这些日志也可以在 /var/log/lxd/CONTAINER/ 中找到，并且有时间戳，以便你可以找到与你最近的操作所匹配的那些日志。它们包含 CRIU 转储和恢复的所有内容的详细记录，并且比典型的迁移/快照错误消息更容器理解。LXD 调试消息如上所述，你可以使用 -debug 选项将守护进程切换为执行调试日志记录。另一种方法是连接到守护进程的事件接口，它将显示所有日志条目，而不管配置的日志级别（即使是远程工作）。举例说，对于 lxc init ubuntu:16.04 xen 来说，lxd.log 会是这样：INFO[02-24|18:14:09] Starting container action=start created=2017-02-24T23:11:45+0000 ephemeral=false name=xen stateful=false used=1970-01-01T00:00:00+0000 INFO[02-24|18:14:10] Started container action=start created=2017-02-24T23:11:45+0000 ephemeral=false name=xen stateful=false used=1970-01-01T00:00:00+0000 而 lxc monitor –type=logging 会是：metadata: context: {} level: dbug message: 'New events listener: 9b725741-ffe7-4bfc-8d3e-fe620fc6e00a' timestamp: 2017-02-24T18:14:01.025989062-05:00 type: logging metadata: context: ip: '@' method: GET url: /1.0 level: dbug message: handling timestamp: 2017-02-24T18:14:09.341283344-05:00 type: logging metadata: context: driver: storage/zfs level: dbug message: StorageCoreInit timestamp: 2017-02-24T18:14:09.341536477-05:00 type: logging metadata: context: ip: '@' method: GET url: /1.0/containers/xen level: dbug message: handling timestamp: 2017-02-24T18:14:09.347709394-05:00 type: logging metadata: context: ip: '@' method: PUT url: /1.0/containers/xen/state level: dbug message: handling timestamp: 2017-02-24T18:14:09.357046302-05:00 type: logging metadata: context: {} level: dbug message: 'New task operation: 2e2cf904-c4c4-4693-881f-57897d602ad3' timestamp: 2017-02-24T18:14:09.358387853-05:00 type: logging metadata: context: {} level: dbug message: 'Started task operation: 2e2cf904-c4c4-4693-881f-57897d602ad3' timestamp: 2017-02-24T18:14:09.358578599-05:00 type: logging metadata: context: ip: '@' method: GET url: /1.0/operations/2e2cf904-c4c4-4693-881f-57897d602ad3/wait level: dbug message: handling timestamp: 2017-02-24T18:14:09.366213106-05:00 type: logging metadata: context: driver: storage/zfs level: dbug message: StoragePoolInit timestamp: 2017-02-24T18:14:09.369636451-05:00 type: logging metadata: context: driver: storage/zfs level: dbug message: StoragePoolCheck timestamp: 2017-02-24T18:14:09.369771164-05:00 type: logging metadata: context: container: xen driver: storage/zfs level: dbug message: ContainerMount timestamp: 2017-02-24T18:14:09.424696767-05:00 type: logging metadata: context: driver: storage/zfs name: xen level: dbug message: ContainerUmount timestamp: 2017-02-24T18:14:09.432723719-05:00 type: logging metadata: context: container: xen driver: storage/zfs level: dbug message: ContainerMount timestamp: 2017-02-24T18:14:09.721067917-05:00 type: logging metadata: context: action: start created: 2017-02-24 23:11:45 +0000 UTC ephemeral: "false" name: xen stateful: "false" used: 1970-01-01 00:00:00 +0000 UTC level: info message: Starting container timestamp: 2017-02-24T18:14:09.749808518-05:00 type: logging metadata: context: ip: '@' method: GET url: /1.0 level: dbug message: handling timestamp: 2017-02-24T18:14:09.792551375-05:00 type: logging metadata: context: driver: storage/zfs level: dbug message: StorageCoreInit timestamp: 2017-02-24T18:14:09.792961032-05:00 type: logging metadata: context: ip: '@' method: GET url: /internal/containers/23/onstart level: dbug message: handling timestamp: 2017-02-24T18:14:09.800803501-05:00 type: logging metadata: context: driver: storage/zfs level: dbug message: StoragePoolInit timestamp: 2017-02-24T18:14:09.803190248-05:00 type: logging metadata: context: driver: storage/zfs level: dbug message: StoragePoolCheck timestamp: 2017-02-24T18:14:09.803251188-05:00 type: logging metadata: context: container: xen driver: storage/zfs level: dbug message: ContainerMount timestamp: 2017-02-24T18:14:09.803306055-05:00 type: logging metadata: context: {} level: dbug message: 'Scheduler: container xen started: re-balancing' timestamp: 2017-02-24T18:14:09.965080432-05:00 type: logging metadata: context: action: start created: 2017-02-24 23:11:45 +0000 UTC ephemeral: "false" name: xen stateful: "false" used: 1970-01-01 00:00:00 +0000 UTC level: info message: Started container timestamp: 2017-02-24T18:14:10.162965059-05:00 type: logging metadata: context: {} level: dbug message: 'Success for task operation: 2e2cf904-c4c4-4693-881f-57897d602ad3' timestamp: 2017-02-24T18:14:10.163072893-05:00 type: logging lxc monitor 的格式有点不同于每个条目都缩合成一行的日志文件，但更重要的是，你可以看到所有 level：dbug 条目。如何报告 bugLXD 的 bug最好报告 bug 的地方是 https://github.com/lxc/lxd/issues。确保完整填写了 bug 报告模板中的内容，这些信息可以节省我们我们时间来复现环境。Ubuntu 的 bug如果你发现 Ubuntu 包本身有问题，无法安装、升级或删除。或者遇到 LXD init 脚本的问题。报告此类错误的最好是在 Launchpad 上。在 Ubuntu 系统上，你可以使用：ubuntu-bug lxd ，它将自动包括一些日志文件和包信息供我们查看。CRIU 的 bug与 CRIU 相关的 Bug，你可以通过 CRIU 的错误输出发现，你应该在 Launchpad 上报告这些：ubuntu-bug criu请注意，通过 LXD 使用 CRIU 属于测试版功能，除非你愿意通过 Canonical 的支持合同付费支持，要么可能需要一段时间才能查看你的错误报告。贡献给 LXDLXD 用 Go 写成并托管在 Github。我们欢迎任外部的贡献。为 LXD 贡献不需要 CLA 或类似的法律协议签署，只是通常的开发者所有权证书（Signed-off-by: 行）。在我们的问题追踪器工具中，我们列有许多潜在的功能需求，新的贡献者可以以此作为良好的起点。通常最好在开始处理代码先发出 issue，这样每个人都知道你正在做这项工作，以便我们可以提供一些早期反馈。从源码源码构建 LXD这里有上游的维护说明：https://github.com/lxc/lxd#building-from-source你需要在 Github 上 fork 上游仓库，然后将你的更改推送到你的分支。我们建议每天 rebase 上游的 LXD，因为我们倾向于定期合并更改。运行测试套件LXD 维护了两套测试集，单元测试和集成测试。你可以用下面的命令测试所有：sudo -E make check 要只运行单元测试，使用：sudo -E go test ./... 要运行集成测试，使用：cd test sudo -E ./main.sh 后者支持相当多的环境变量来测试各种存储后端、禁用网络测试、使用 ramdisk 或只是调整日志输出。其中一些是：LXD_BACKEND：btrfs、dir、lvm 或 zfs” 之一（默认为 dir） 运行 LXD 存储驱动程序相关的所有测试。LXD_CONCURRENT：true 或 false（默认为 false） 这启用一些额外的并发测试。LXD_DEBUG：true 或 false（默认为 false） 记录所有 shell 命令，并在调试模式下运行所有​​ LXD 命令。LXD_INSPECT：true 或 false（默认为 false） 测试程序会在故障时挂起，以便你可以检查环境。LXD_LOGS：将所有 LXD 日志文件转储到的目录（默认为 “”） 所有生成的 LXD 守护进程的 logs 目录将被复制到此路径。LXD_OFFLINE：true 或 false（默认为 false） 禁用任何依赖于外部网络连接的测试。LXD_TEST_IMAGE： unified 格式的 LXD 镜像的路径（默认为 “”） 可以使用自定义测试镜像，而不是默认的最小 busybox 镜像。LXD_TMPFS：true 或 false（默认为 false） 在 tmpfs 安装中运行整个测试套件，这会使用相当多的内存，但会使测试速度明显更快。LXD_VERBOSE：true 或 false（默认为 false） 不太极端的 LXD_DEBUG 版本。shell 命令仍然会记录，但 -debug 不会传递给 LXC 命令，LXD 守护进程只能使用 -verbose 运行。测试程序将在实际运行之前提醒你任何缺失的依赖项。在相当快的机器上运行该测试可在 10 分钟内完成。发送你的分支发送拉取请求（PR）之前，你需要确认：你已经 rebase 了上游分支你的所有提交信息都包括 Signed-off-by: First Last <email> 这行已删除任何你的临时调试代码你已经将相关的提交 squash 在一起，以保持你的分支容易审查单元和集成测试全部通过一切完成后，在 Github 上发起一个拉取请求。我们的 Jenkins 将验证提交是否全部有 signed-off，在 MacOS 和 Windows 上的测试将自动执行，如果看起来不错，我们将触发一个完整的 Jenkins 测试，它将在所有存储后端、32 位和 64 位以及我们关心的所有 Go 版本上测试你的分支。假设我们有人触发了 Jenkins，这通常需要不到一个小时的时间。一旦所有测试完成，我们对代码本身感到满意，你的分支将会被合并，你的代码会出现在下一个 LXD 发布中。如果更改适用于 LXD stable-2.0 分支，我们将为你向后移植。总结我希望这个系列的博客文章有助于你了解什么是 LXD，以及它可以做什么！本系列的范围仅限于 LXD（2.0.x），但我们也为那些想要最新功能的用户提供每月功能版本。你可以找到一些其他涵盖了原来的 LXD 2.0系列文章中列出的功能的博客文章。额外的信息LXD 的主站在： https://linuxcontainers.org/lxdLXD 的 GitHub 开发仓库： https://github.com/lxc/lxdLXD 的邮件列表： https://lists.linuxcontainers.orgLXD 的 IRC 频道：#lxcontainers on irc.freenode.net在线尝试 LXD： https://linuxcontainers.org/lxd/try-it作者简介：我是 Stéphane Graber。我是 LXC 和 LXD 项目的领导者，目前在加拿大魁北克蒙特利尔的家所在的 Canonical 有限公司担任 LXD 的技术主管。via: https://stgraber.org/2017/02/27/lxd-2-0-debugging-and-contributing-to-lxd-1212/作者：Stéphane Graber 译者：geekpi 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
这是 LXD 2.0 系列介绍文章的第十一篇。LXD 入门安装与配置你的第一个 LXD 容器资源控制镜像管理远程主机及容器迁移LXD 中的 DockerLXD 中的 LXD实时迁移LXD 和 JujuLXD 和 OpenStack调试，及给 LXD 做贡献介绍首先对这次的延期抱歉。为了让一切正常我花了很长时间。我第一次尝试是使用 devstack 时遇到了一些必须解决问题。 然而即使这样，我还是不能够使网络正常。我终于放弃了 devstack，并使用用户友好的 Juju 尝试使用 “conjure-up” 部署完整的 Ubuntu OpenStack。它终于工作了！下面是如何运行一个完整的 OpenStack，使用 LXD 容器而不是 VM，并在 LXD 容器中运行所有这些（嵌套的！）。要求这篇文章假设你有一个可以工作的 LXD 设置，提供容器网络访问，并且你有一个非常强大的 CPU，大约 50GB 给容器空间和至少 16G B的内存。记住，我们在这里运行一个完整的 OpenStack，这东西不是很轻量！设置容器OpenStack 由大量不同做不同事情的组件组成。 一些需要一些额外的特权，为了可以使设置更简单，我们将使用特权容器。我们将配置支持嵌套的容器，预加载所有需要的内核模块，并允许它访问 /dev/mem（显然是需要的）。请注意，这意味着 LXD 容器的大部分安全特性对该容器被禁用。 然而由 OpenStack 自身产生的容器将是无特权的，并且可以正常使用 LXD 的安全特性。lxc launch ubuntu:16.04 openstack -c security.privileged=true -c security.nesting=true -c "linux.kernel_modules=iptable_nat, ip6table_nat, ebtables, openvswitch" lxc config device add openstack mem unix-char path=/dev/mem LXD 中有一个小 bug，它会尝试加载已经加载到主机上的内核模块。这已在LXD 2.5中得到修复，并将在LXD 2.0.6 中修复，但在此之前，可以使用以下方法：lxc exec openstack -- ln -s /bin/true /usr/local/bin/modprobe 我们需要加几条 PPA 并安装 conjure-up，它是我们用来安装 OpenStack 的部署工具。lxc exec openstack -- apt-add-repository ppa:conjure-up/next -y lxc exec openstack -- apt-add-repository ppa:juju/stable -y lxc exec openstack -- apt update lxc exec openstack -- apt dist-upgrade -y lxc exec openstack -- apt install conjure-up -y 最后一步是在容器内部配置 LXD 网络。所有问题都选择默认，除了：使用 dir 存储后端（ zfs 不在嵌套容器中用）不要配置 IPv6 网络（conjure-up/juju 不太兼容它）lxc exec openstack -- lxd init 现在配置完容器了，现在我们部署 OpenStack！用 conjure-up 部署 OpenStack如先前提到的，我们用 conjure-up 部署 OpenStack。这是一个很棒的用户友好的可以与 Juju 交互来部署复杂服务的工具。首先：lxc exec openstack -- sudo -u ubuntu -i conjure-up 选择 “OpenStack with NovaLXD”选择 “localhost” 作为部署目标（使用 LXD）点击 “Deploy all remaining applications”接下来会部署 OpenStack。整个过程会花费一个多小时，这取决于你运行的机器。你将看到所有服务会被分配一个容器，然后部署并最终互连。部署完成后会显示一个安装完成的界面。它会导入一些初始镜像、设置 SSH 权限、配置网络最后会显示面板的 IP 地址。访问面板并生成一个容器面板运行在一个容器中，因此你不能直接从浏览器中访问。最简单的方法是设置一条 NAT 规则：lxc exec openstack -- iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to <IP> 其中 <ip> 是 conjure-up 在安装结束时给你的面板 IP 地址。你现在可以获取 “openstack” 容器的 IP 地址（来自 lxc info openstack），并将浏览器指向：http:///horizon 。第一次加载可能需要几分钟。 一旦显示了登录界面，输入默认登录名和密码（admin/openstack），你就会看到OpenStack的欢迎面板！现在可以选择左边的 “Project” 选项卡，进入 “Instances” 页面。 要启动一个使用 nova-lxd 的新实例，点击 “Launch instance”，选择你想要的镜像，网络等，接着你的实例就产生了。一旦它运行后，你可以为它分配一个浮动 IP，它将允许你从你的 “openstack” 容器中访问你的实例。总结OpenStack 是一个非常复杂的软件，你也不会想在家里或在单个服务器上运行它。 但是，不管怎样在你的机器上包含这些服务在一个容器中都是非常有趣的。conjure-up 是部署这种复杂软件的一个很好的工具，背后使用 Juju 驱动部署，为每个单独的服务使用 LXD 容器，最后是实例本身。它也是少数几个容器嵌套多层并实际上有意义的情况之一！额外信息conjure-up 网站： http://conjure-up.ioJuju 网站： http://www.ubuntu.com/cloud/jujuLXD 的主站在： https://linuxcontainers.org/lxdLXD 的 GitHub 仓库： https://github.com/lxc/lxdLXD 的邮件列表： https://lists.linuxcontainers.orgLXD 的 IRC 频道： #lxcontainers on irc.freenode.net如果你不想或者不能在你的机器上安装 LXD ，你可以在 web 上试试在线版的 LXD。作者简介：我是 Stéphane Graber。我是 LXC 和 LXD 项目的领导者，目前在加拿大魁北克蒙特利尔的家所在的 Canonical 有限公司担任 LXD 的技术主管。via: https://www.stgraber.org/2016/10/26/lxd-2-0-lxd-and-openstack-1112/作者：Stéphane Graber 译者：geekpi 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
这是 LXD 2.0 系列介绍文章的第十篇。LXD 入门安装与配置你的第一个 LXD 容器资源控制镜像管理远程主机及容器迁移LXD 中的 DockerLXD 中的 LXD实时迁移LXD 和 JujuLXD 和 OpenStack调试，及给 LXD 做贡献介绍Juju 是 Canonical 的服务建模和部署工具。 它支持非常广泛的云服务提供商，使您能够轻松地在任何云上部署任何您想要的服务。此外，Juju 2.0 还支持 LXD，既适用于本地部署，也适合开发，并且可以在云实例或物理机上共同协作。本篇文章将关注本地使用，通过一个没有任何Juju经验的LXD用户来体验。要求本篇文章假设你已经安装了 LXD 2.0 并且配置完毕（看前面的文章），并且是在 Ubuntu 16.04 LTS 上运行的。设置 Juju第一件事是在 Ubuntu 16.04 上安装 Juju 2.0。这个很简单：stgraber@dakara:~$ sudo apt install juju Reading package lists... Done Building dependency tree Reading state information... Done The following additional packages will be installed: juju-2.0 Suggested packages: juju-core The following NEW packages will be installed: juju juju-2.0 0 upgraded, 2 newly installed, 0 to remove and 0 not upgraded. Need to get 39.7 MB of archives. After this operation, 269 MB of additional disk space will be used. Do you want to continue? [Y/n] Get:1 http://us.archive.ubuntu.com/ubuntu xenial-updates/main amd64 juju-2.0 amd64 2.0~beta7-0ubuntu1.16.04.1 [39.6 MB] Get:2 http://us.archive.ubuntu.com/ubuntu xenial-updates/main amd64 juju all 2.0~beta7-0ubuntu1.16.04.1 [9,556 B] Fetched 39.7 MB in 0s (53.4 MB/s) Selecting previously unselected package juju-2.0. (Reading database ... 255132 files and directories currently installed.) Preparing to unpack .../juju-2.0_2.0~beta7-0ubuntu1.16.04.1_amd64.deb ... Unpacking juju-2.0 (2.0~beta7-0ubuntu1.16.04.1) ... Selecting previously unselected package juju. Preparing to unpack .../juju_2.0~beta7-0ubuntu1.16.04.1_all.deb ... Unpacking juju (2.0~beta7-0ubuntu1.16.04.1) ... Processing triggers for man-db (2.7.5-1) ... Setting up juju-2.0 (2.0~beta7-0ubuntu1.16.04.1) ... Setting up juju (2.0~beta7-0ubuntu1.16.04.1) ... 安装完成后，我们可以使用 LXD 启动一个新的“控制器”。这意味着 Juju 不会修改你主机上的任何东西，它会在 LXD 容器中安装它的管理服务。现在我们创建一个“test”控制器：stgraber@dakara:~$ juju bootstrap localhost test Creating Juju controller "local.test" on localhost/localhost Bootstrapping model "admin" Starting new instance for initial controller Launching instance - juju-745d1be3-e93d-41a2-80d4-fbe8714230dd-machine-0 Installing Juju agent on bootstrap instance Preparing for Juju GUI 2.1.2 release installation Waiting for address Attempting to connect to 10.178.150.72:22 Logging to /var/log/cloud-init-output.log on remote host Running apt-get update Running apt-get upgrade Installing package: curl Installing package: cpu-checker Installing package: bridge-utils Installing package: cloud-utils Installing package: cloud-image-utils Installing package: tmux Fetching tools: curl -sSfw 'tools from %{url_effective} downloaded: HTTP %{http_code}; time %{time_total}s; size %{size_download} bytes; speed %{speed_download} bytes/s ' --retry 10 -o $bin/tools.tar.gz <[https://streams.canonical.com/juju/tools/agent/2.0-beta7/juju-2.0-beta7-xenial-amd64.tgz]> Bootstrapping Juju machine agent Starting Juju machine agent (jujud-machine-0) Bootstrap agent installed Waiting for API to become available: upgrade in progress (upgrade in progress) Waiting for API to become available: upgrade in progress (upgrade in progress) Waiting for API to become available: upgrade in progress (upgrade in progress) Bootstrap complete, local.test now available. 这会花费一点时间，这时你可以看到一个正在运行的一个新的 LXD 容器：stgraber@dakara:~$ lxc list juju- +-----------------------------------------------------+---------+----------------------+------+------------+-----------+ | NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | +-----------------------------------------------------+---------+----------------------+------+------------+-----------+ | juju-745d1be3-e93d-41a2-80d4-fbe8714230dd-machine-0 | RUNNING | 10.178.150.72 (eth0) | | PERSISTENT | 0 | +-----------------------------------------------------+---------+----------------------+------+------------+-----------+ 在 Juju 这边，你可以确认它是有响应的，并且还没有服务运行：stgraber@dakara:~$ juju status [Services] NAME STATUS EXPOSED CHARM [Units] ID WORKLOAD-STATUS JUJU-STATUS VERSION MACHINE PORTS PUBLIC-ADDRESS MESSAGE [Machines] ID STATE DNS INS-ID SERIES AZ 你也可以在浏览器中访问 Juju 的 GUI 界面：stgraber@dakara:~$ juju gui Opening the Juju GUI in your browser. If it does not open, open this URL: https://10.178.150.72:17070/gui/97fa390d-96ad-44df-8b59-e15fdcfc636b/ 不过我更倾向使用命令行，因此我会在接下来使用。部署一个 minecraft 服务让我们先来一个简单的，部署在一个容器中使用一个 Juju 单元的服务。stgraber@dakara:~$ juju deploy cs:trusty/minecraft Added charm "cs:trusty/minecraft-3" to the model. Deploying charm "cs:trusty/minecraft-3" with the charm series "trusty". 命令返回会很快，然而这不意味着服务已经启动并运行了。你应该使用 juju status 来查看：stgraber@dakara:~$ juju status [Services] NAME STATUS EXPOSED CHARM minecraft maintenance false cs:trusty/minecraft-3 [Units] ID WORKLOAD-STATUS JUJU-STATUS VERSION MACHINE PORTS PUBLIC-ADDRESS MESSAGE minecraft/1 maintenance executing 2.0-beta7 1 10.178.150.74 (install) Installing java [Machines] ID STATE DNS INS-ID SERIES AZ 1 started 10.178.150.74 juju-97fa390d-96ad-44df-8b59-e15fdcfc636b-machine-1 trusty 我们可以看到它正在忙于在刚刚创建的 LXD 容器中安装 java。stgraber@dakara:~$ lxc list juju- +-----------------------------------------------------+---------+----------------------+------+------------+-----------+ | NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | +-----------------------------------------------------+---------+----------------------+------+------------+-----------+ | juju-745d1be3-e93d-41a2-80d4-fbe8714230dd-machine-0 | RUNNING | 10.178.150.72 (eth0) | | PERSISTENT | 0 | +-----------------------------------------------------+---------+----------------------+------+------------+-----------+ | juju-97fa390d-96ad-44df-8b59-e15fdcfc636b-machine-1 | RUNNING | 10.178.150.74 (eth0) | | PERSISTENT | 0 | +-----------------------------------------------------+---------+----------------------+------+------------+-----------+ 过一会之后，如我们所见服务就部署完毕了：stgraber@dakara:~$ juju status [Services] NAME STATUS EXPOSED CHARM minecraft active false cs:trusty/minecraft-3 [Units] ID WORKLOAD-STATUS JUJU-STATUS VERSION MACHINE PORTS PUBLIC-ADDRESS MESSAGE minecraft/1 active idle 2.0-beta7 1 25565/tcp 10.178.150.74 Ready [Machines] ID STATE DNS INS-ID SERIES AZ 1 started 10.178.150.74 juju-97fa390d-96ad-44df-8b59-e15fdcfc636b-machine-1 trusty 这时你就可以启动你的 Minecraft 客户端了，将其指向 10.178.150.74，端口是 25565。现在可以在新的 minecraft 服务器上玩了！当你不再需要它，只需运行：stgraber@dakara:~$ juju destroy-service minecraft 只要等待几秒就好了。部署一个更复杂的 web 应用Juju 的主要工作是建模复杂的服务，并以可扩展的方式部署它们。为了更好地展示，让我们部署一个 Juju “组合”。 这个组合是由网站，API，数据库，静态 Web 服务器和反向代理组成的基本 Web 服务。所以这将扩展到 4 个互联的 LXD 容器。stgraber@dakara:~$ juju deploy cs:~charmers/bundle/web-infrastructure-in-a-box added charm cs:~hp-discover/trusty/node-app-1 service api deployed (charm cs:~hp-discover/trusty/node-app-1 with the series "trusty" defined by the bundle) annotations set for service api added charm cs:trusty/mongodb-3 service mongodb deployed (charm cs:trusty/mongodb-3 with the series "trusty" defined by the bundle) annotations set for service mongodb added charm cs:~hp-discover/trusty/nginx-4 service nginx deployed (charm cs:~hp-discover/trusty/nginx-4 with the series "trusty" defined by the bundle) annotations set for service nginx added charm cs:~hp-discover/trusty/nginx-proxy-3 service nginx-proxy deployed (charm cs:~hp-discover/trusty/nginx-proxy-3 with the series "trusty" defined by the bundle) annotations set for service nginx-proxy added charm cs:~hp-discover/trusty/website-3 service website deployed (charm cs:~hp-discover/trusty/website-3 with the series "trusty" defined by the bundle) annotations set for service website related mongodb:database and api:mongodb related website:nginx-engine and nginx:web-engine related api:website and nginx-proxy:website related nginx-proxy:website and website:website added api/0 unit to new machine added mongodb/0 unit to new machine added nginx/0 unit to new machine added nginx-proxy/0 unit to new machine deployment of bundle "cs:~charmers/bundle/web-infrastructure-in-a-box-10" completed 几秒后，你会看到 LXD 容器在运行了：stgraber@dakara:~$ lxc list juju- +-----------------------------------------------------+---------+-----------------------+------+------------+-----------+ | NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | +-----------------------------------------------------+---------+-----------------------+------+------------+-----------+ | juju-745d1be3-e93d-41a2-80d4-fbe8714230dd-machine-0 | RUNNING | 10.178.150.72 (eth0) | | PERSISTENT | 0 | +-----------------------------------------------------+---------+-----------------------+------+------------+-----------+ | juju-97fa390d-96ad-44df-8b59-e15fdcfc636b-machine-2 | RUNNING | 10.178.150.98 (eth0) | | PERSISTENT | 0 | +-----------------------------------------------------+---------+-----------------------+------+------------+-----------+ | juju-97fa390d-96ad-44df-8b59-e15fdcfc636b-machine-3 | RUNNING | 10.178.150.29 (eth0) | | PERSISTENT | 0 | +-----------------------------------------------------+---------+-----------------------+------+------------+-----------+ | juju-97fa390d-96ad-44df-8b59-e15fdcfc636b-machine-4 | RUNNING | 10.178.150.202 (eth0) | | PERSISTENT | 0 | +-----------------------------------------------------+---------+-----------------------+------+------------+-----------+ | juju-97fa390d-96ad-44df-8b59-e15fdcfc636b-machine-5 | RUNNING | 10.178.150.214 (eth0) | | PERSISTENT | 0 | +-----------------------------------------------------+---------+-----------------------+------+------------+-----------+ 几分钟后，所有的服务应该部署完毕并运行了：stgraber@dakara:~$ juju status [Services] NAME STATUS EXPOSED CHARM api unknown false cs:~hp-discover/trusty/node-app-1 mongodb unknown false cs:trusty/mongodb-3 nginx unknown false cs:~hp-discover/trusty/nginx-4 nginx-proxy unknown false cs:~hp-discover/trusty/nginx-proxy-3 website false cs:~hp-discover/trusty/website-3 [Relations] SERVICE1 SERVICE2 RELATION TYPE api mongodb database regular api nginx-proxy website regular mongodb mongodb replica-set peer nginx website nginx-engine subordinate nginx-proxy website website regular [Units] ID WORKLOAD-STATUS JUJU-STATUS VERSION MACHINE PORTS PUBLIC-ADDRESS MESSAGE api/0 unknown idle 2.0-beta7 2 8000/tcp 10.178.150.98 mongodb/0 unknown idle 2.0-beta7 3 27017/tcp,27019/tcp,27021/tcp,28017/tcp 10.178.150.29 nginx-proxy/0 unknown idle 2.0-beta7 5 80/tcp 10.178.150.214 nginx/0 unknown idle 2.0-beta7 4 10.178.150.202 website/0 unknown idle 2.0-beta7 10.178.150.202 [Machines] ID STATE DNS INS-ID SERIES AZ 2 started 10.178.150.98 juju-97fa390d-96ad-44df-8b59-e15fdcfc636b-machine-2 trusty 3 started 10.178.150.29 juju-97fa390d-96ad-44df-8b59-e15fdcfc636b-machine-3 trusty 4 started 10.178.150.202 juju-97fa390d-96ad-44df-8b59-e15fdcfc636b-machine-4 trusty 5 started 10.178.150.214 juju-97fa390d-96ad-44df-8b59-e15fdcfc636b-machine-5 trusty 这时你就可以在 80 端口访问 http://10.178.150.214，并且会看到一个 Juju 学院页面。清理所有东西如果你不需要 Juju 创建的容器并且不在乎下次需要再次启动，最简单的方法是：stgraber@dakara:~$ juju destroy-controller test --destroy-all-models WARNING! This command will destroy the "local.test" controller. This includes all machines, services, data and other resources. Continue [y/N]? y Destroying controller Waiting for hosted model resources to be reclaimed Waiting on 1 model, 4 machines, 5 services Waiting on 1 model, 4 machines, 5 services Waiting on 1 model, 4 machines, 5 services Waiting on 1 model, 4 machines, 5 services Waiting on 1 model, 4 machines, 5 services Waiting on 1 model, 4 machines, 5 services Waiting on 1 model, 4 machines Waiting on 1 model, 4 machines Waiting on 1 model, 4 machines Waiting on 1 model, 4 machines Waiting on 1 model, 4 machines Waiting on 1 model, 4 machines Waiting on 1 model, 2 machines Waiting on 1 model Waiting on 1 model All hosted models reclaimed, cleaning up controller machines 我们用下面的方式确认：stgraber@dakara:~$ lxc list juju- +------+-------+------+------+------+-----------+ | NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | +------+-------+------+------+------+-----------+ 总结Juju 2.0 内置的 LXD 支持使得可以用一种非常干净的方式来测试各种服务。在 Juju charm store 中有很多预制的“组合”可以用来部署，甚至可以用多个“charm”来组合你想要的架构。Juju 与 LXD 是一个完美的解决方案，从一个小的 Web 服务到大规模的基础设施都可以简单开发，这些都在你自己的机器上，并且不会在你的系统上造成混乱！额外信息Juju 网站： http://www.ubuntu.com/cloud/jujuJuju charm store ： https://jujucharms.comLXD 的主站在： https://linuxcontainers.org/lxdLXD 的 GitHub 仓库： https://github.com/lxc/lxdLXD 的邮件列表： https://lists.linuxcontainers.orgLXD 的 IRC 频道： #lxcontainers on irc.freenode.net如果你不想或者不能在你的机器上安装 LXD ，你可以在 web 上试试在线版的 LXD。作者简介：我是 Stéphane Graber。我是 LXC 和 LXD 项目的领导者，目前在加拿大魁北克蒙特利尔的家所在的Canonical 有限公司担任 LXD 的技术主管。via: https://www.stgraber.org/2016/06/06/lxd-2-0-lxd-and-juju-1012/作者：Stéphane Graber 译者：geekpi 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
这是 LXD 2.0 系列介绍文章的第九篇。LXD 入门安装与配置你的第一个 LXD 容器资源控制镜像管理远程主机及容器迁移LXD 中的 DockerLXD 中的 LXD实时迁移LXD 和 JujuLXD 和 OpenStack调试，及给 LXD 做贡献介绍LXD 2.0 中的有一个尽管是实验性质的但非常令人兴奋的功能，那就是支持容器检查点和恢复。简单地说，检查点/恢复意味着正在运行的容器状态可以被序列化到磁盘，要么可以作为同一主机上的有状态快照，要么放到另一主机上相当于实时迁移。要求要使用容器实时迁移和有状态快照，你需要以下条件：一个非常新的 Linux 内核，4.4 或更高版本。CRIU 2.0，可能需要一些 cherry-pick 的提交，具体取决于你确切的内核配置。直接在主机上运行 LXD。 不能在容器嵌套下使用这些功能。对于迁移，目标主机必须至少实现源主机的指令集，目标主机内核必须至少提供与源主机相同的系统调用，并且在源主机上挂载的任何内核文件系统也必须可挂载到目标主机上。Ubuntu 16.04 LTS 已经提供了所有需要的依赖，在这种情况下，您只需要安装 CRIU 本身：apt install criu 使用 CRIU有状态快照一个普通的快照看上去像这样：stgraber@dakara:~$ lxc snapshot c1 first stgraber@dakara:~$ lxc info c1 | grep first first (taken at 2016/04/25 19:35 UTC) (stateless) 一个有状态快照看上去像这样：stgraber@dakara:~$ lxc snapshot c1 second --stateful stgraber@dakara:~$ lxc info c1 | grep second second (taken at 2016/04/25 19:36 UTC) (stateful) 这意味着所有容器运行时状态都被序列化到磁盘并且作为了快照的一部分。可以像你还原无状态快照那样还原一个有状态快照：stgraber@dakara:~$ lxc restore c1 second stgraber@dakara:~$ 有状态快照的停止/启动比方说你由于升级内核或者其他类似的维护而需要重启机器。与其等待重启后启动所有的容器，你可以：stgraber@dakara:~$ lxc stop c1 --stateful 容器状态将会写入到磁盘，会在下次启动时读取。你甚至可以看到像下面那样的状态：root@dakara:~# tree /var/lib/lxd/containers/c1/rootfs/state/ /var/lib/lxd/containers/c1/rootfs/state/ ├── cgroup.img ├── core-101.img ├── core-102.img ├── core-107.img ├── core-108.img ├── core-109.img ├── core-113.img ├── core-114.img ├── core-122.img ├── core-125.img ├── core-126.img ├── core-127.img ├── core-183.img ├── core-1.img ├── core-245.img ├── core-246.img ├── core-50.img ├── core-52.img ├── core-95.img ├── core-96.img ├── core-97.img ├── core-98.img ├── dump.log ├── eventfd.img ├── eventpoll.img ├── fdinfo-10.img ├── fdinfo-11.img ├── fdinfo-12.img ├── fdinfo-13.img ├── fdinfo-14.img ├── fdinfo-2.img ├── fdinfo-3.img ├── fdinfo-4.img ├── fdinfo-5.img ├── fdinfo-6.img ├── fdinfo-7.img ├── fdinfo-8.img ├── fdinfo-9.img ├── fifo-data.img ├── fifo.img ├── filelocks.img ├── fs-101.img ├── fs-113.img ├── fs-122.img ├── fs-183.img ├── fs-1.img ├── fs-245.img ├── fs-246.img ├── fs-50.img ├── fs-52.img ├── fs-95.img ├── fs-96.img ├── fs-97.img ├── fs-98.img ├── ids-101.img ├── ids-113.img ├── ids-122.img ├── ids-183.img ├── ids-1.img ├── ids-245.img ├── ids-246.img ├── ids-50.img ├── ids-52.img ├── ids-95.img ├── ids-96.img ├── ids-97.img ├── ids-98.img ├── ifaddr-9.img ├── inetsk.img ├── inotify.img ├── inventory.img ├── ip6tables-9.img ├── ipcns-var-10.img ├── iptables-9.img ├── mm-101.img ├── mm-113.img ├── mm-122.img ├── mm-183.img ├── mm-1.img ├── mm-245.img ├── mm-246.img ├── mm-50.img ├── mm-52.img ├── mm-95.img ├── mm-96.img ├── mm-97.img ├── mm-98.img ├── mountpoints-12.img ├── netdev-9.img ├── netlinksk.img ├── netns-9.img ├── netns-ct-9.img ├── netns-exp-9.img ├── packetsk.img ├── pagemap-101.img ├── pagemap-113.img ├── pagemap-122.img ├── pagemap-183.img ├── pagemap-1.img ├── pagemap-245.img ├── pagemap-246.img ├── pagemap-50.img ├── pagemap-52.img ├── pagemap-95.img ├── pagemap-96.img ├── pagemap-97.img ├── pagemap-98.img ├── pages-10.img ├── pages-11.img ├── pages-12.img ├── pages-13.img ├── pages-1.img ├── pages-2.img ├── pages-3.img ├── pages-4.img ├── pages-5.img ├── pages-6.img ├── pages-7.img ├── pages-8.img ├── pages-9.img ├── pipes-data.img ├── pipes.img ├── pstree.img ├── reg-files.img ├── remap-fpath.img ├── route6-9.img ├── route-9.img ├── rule-9.img ├── seccomp.img ├── sigacts-101.img ├── sigacts-113.img ├── sigacts-122.img ├── sigacts-183.img ├── sigacts-1.img ├── sigacts-245.img ├── sigacts-246.img ├── sigacts-50.img ├── sigacts-52.img ├── sigacts-95.img ├── sigacts-96.img ├── sigacts-97.img ├── sigacts-98.img ├── signalfd.img ├── stats-dump ├── timerfd.img ├── tmpfs-dev-104.tar.gz.img ├── tmpfs-dev-109.tar.gz.img ├── tmpfs-dev-110.tar.gz.img ├── tmpfs-dev-112.tar.gz.img ├── tmpfs-dev-114.tar.gz.img ├── tty.info ├── unixsk.img ├── userns-13.img └── utsns-11.img 0 directories, 154 files 还原容器也很简单：stgraber@dakara:~$ lxc start c1 实时迁移实时迁移基本上与上面的有状态快照的停止/启动相同，除了容器目录和配置被移动到另一台机器上。stgraber@dakara:~$ lxc list c1 +------+---------+-----------------------+----------------------------------------------+------------+-----------+ | NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | +------+---------+-----------------------+----------------------------------------------+------------+-----------+ | c1 | RUNNING | 10.178.150.197 (eth0) | 2001:470:b368:4242:216:3eff:fe19:27b0 (eth0) | PERSISTENT | 2 | +------+---------+-----------------------+----------------------------------------------+------------+-----------+ stgraber@dakara:~$ lxc list s-tollana: +------+-------+------+------+------+-----------+ | NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | +------+-------+------+------+------+-----------+ stgraber@dakara:~$ lxc move c1 s-tollana: stgraber@dakara:~$ lxc list c1 +------+-------+------+------+------+-----------+ | NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | +------+-------+------+------+------+-----------+ stgraber@dakara:~$ lxc list s-tollana: +------+---------+-----------------------+----------------------------------------------+------------+-----------+ | NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | +------+---------+-----------------------+----------------------------------------------+------------+-----------+ | c1 | RUNNING | 10.178.150.197 (eth0) | 2001:470:b368:4242:216:3eff:fe19:27b0 (eth0) | PERSISTENT | 2 | +------+---------+-----------------------+----------------------------------------------+------------+-----------+ 限制正如我之前说的，容器的检查点/恢复还是非常新的功能，我们还在努力地开发这个功能、修复已知的问题。我们确实需要更多的人来尝试这个功能，并给我们反馈，但我不建议在生产中使用这个功能。我们跟踪的问题列表在 Launchpad上。我们估计在带有 CRIU 的 Ubuntu 16.04 上带有几个服务的基本的 Ubuntu 容器能够正常工作。然而在更复杂的容器、使用了设备直通、复杂的网络服务或特殊的存储配置下可能会失败。要是有问题，CRIU 会尽可能地在转储时失败，而不是在恢复时。在这种情况下，源容器将继续运行，快照或迁移将会失败，并生成一个日志文件用于调试。在极少数情况下，CRIU 无法恢复容器，在这种情况下，源容器仍然存在但将被停止，并且必须手动重新启动。发送 bug 报告我们正在跟踪 Launchpad 上关于 CRIU Ubuntu 软件包的检查点/恢复相关的错误。大多数修复 bug 工作是在上游的 CRIU 或 Linux 内核上进行，但是这种方式我们更容易跟踪。要提交新的 bug 报告，请看这里。请务必包括：你运行的命令和显示给你的错误消息lxc info 的输出（*）lxc info <container name>的输出lxc config show -expanded <container name> 的输出dmesg（*）的输出/proc/self/mountinfo 的输出（*）lxc exec <container name> - cat /proc/self/mountinfo 的输出uname -a（*）的输出/var/log/lxd.log（*）的内容/etc/default/lxd-bridge（*）的内容/var/log/lxd/<container name>/ 的 tarball（*）如果报告迁移错误，而不是状态快照或有状态停止的错误，请将上面所有含有（*）标记的源与目标主机的信息发来。额外信息CRIU 的网站在： https://criu.orgLXD 的主站在： https://linuxcontainers.org/lxdLXD 的 GitHub 仓库： https://github.com/lxc/lxdLXD 的邮件列表： https://lists.linuxcontainers.orgLXD 的 IRC 频道： #lxcontainers on irc.freenode.net作者简介：我是 Stéphane Graber。我是 LXC 和 LXD 项目的领导者，目前在加拿大魁北克蒙特利尔的家所在的Canonical 有限公司担任 LXD 的技术主管。via: https://stgraber.org/2016/04/25/lxd-2-0-live-migration-912/作者：Stéphane Graber 译者：geekpi 校对：wxy本文由 LCTT 组织翻译，Linux中国 荣誉推出
这是 LXD 2.0 系列介绍文章的第八篇。LXD 入门安装与配置你的第一个 LXD 容器资源控制镜像管理远程主机及容器迁移LXD 中的 DockerLXD 中的 LXD实时迁移LXD 和 JujuLXD 和 OpenStack调试，及给 LXD 做贡献介绍在上一篇文章中，我介绍了如何在 LXD 中运行 Docker，这是一个访问由 Docker 提供的应用程序组合的很好方式，同时 Docker 还运行在 LXD 提供的安全环境中。我提到的一个情况是为你的用户提供一个 LXD 容器，然后让他们使用他们的容器来运行 Docker。那么，如果他们自己想要在其容器中使用 LXD 运行其他 Linux 发行版，或者甚至允许另一组人来访问运行在他们的容器中的 Linux 系统呢？原来 LXD 使得用户运行嵌套容器变得非常简单。嵌套 LXD最简单的情况可以使用 Ubuntu 16.04 镜像来展示。 Ubuntu 16.04 云镜像预装了 LXD。守护进程本身没有运行，因为它是由套接字激活的，所以它不使用任何资源，直到你真正使用它。让我们启动一个启用了嵌套的 Ubuntu 16.04 容器：lxc launch ubuntu-daily:16.04 c1 -c security.nesting=true 你也可以在一个已有的容器上设置 security.nesting：lxc config set <container name> security.nesting true 或者对所有的容器使用一个指定的配置文件：lxc profile set <profile name> security.nesting true 容器启动后，你可以从容器内部得到一个 shell，配置 LXD 并生成一个容器：stgraber@dakara:~$ lxc launch ubuntu-daily:16.04 c1 -c security.nesting=true Creating c1 Starting c1 stgraber@dakara:~$ lxc exec c1 bash root@c1:~# lxd init Name of the storage backend to use (dir or zfs): dir We detected that you are running inside an unprivileged container. This means that unless you manually configured your host otherwise, you will not have enough uid and gid to allocate to your containers. LXD can re-use your container's own allocation to avoid the problem. Doing so makes your nested containers slightly less safe as they could in theory attack their parent container and gain more privileges than they otherwise would. Would you like to have your containers share their parent's allocation (yes/no)? yes Would you like LXD to be available over the network (yes/no)? no Do you want to configure the LXD bridge (yes/no)? yes Warning: Stopping lxd.service, but it can still be activated by: lxd.socket LXD has been successfully configured. root@c1:~# lxc launch ubuntu:14.04 trusty Generating a client certificate. This may take a minute... If this is your first time using LXD, you should also run: sudo lxd init Creating trusty Retrieving image: 100% Starting trusty root@c1:~# lxc list +--------+---------+-----------------------+----------------------------------------------+------------+-----------+ | NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | +--------+---------+-----------------------+----------------------------------------------+------------+-----------+ | trusty | RUNNING | 10.153.141.124 (eth0) | fd7:f15d:d1d6:da14:216:3eff:fef1:4002 (eth0) | PERSISTENT | 0 | +--------+---------+-----------------------+----------------------------------------------+------------+-----------+ root@c1:~# 就是这样简单。在线演示服务器因为这篇文章很短，我想我会花一点时间谈论我们运行中的演示服务器。我们今天早些时候刚刚达到了 10000 个会话！这个服务器基本上只是一个运行在一个相当强大的虚拟机上的正常的 LXD，一个小型的守护进程实现了我们的网站所使用的 REST API。当你接受服务条款时，将为你创建一个新的 LXD 容器，并启用 security.nesting，如上所述。接着你就像使用 lxc exec 时一样连接到了那个容器，除了我们使用 websockets 和 javascript 来做这些。你在此环境中创建的容器都是嵌套的 LXD 容器。如果你想，你可以进一步地嵌套。我们全范围地使用了 LXD 资源限制，以防止一个用户的行为影响其他用户，并仔细监控服务器的任何滥用迹象。如果你想运行自己的类似的服务器，你可以获取我们的网站和守护进程的代码：git clone https://github.com/lxc/linuxcontainers.org git clone https://github.com/lxc/lxd-demo-server 额外信息LXD 的主站在： https://linuxcontainers.org/lxdLXD 的 GitHub 仓库： https://github.com/lxc/lxdLXD 的邮件列表： https://lists.linuxcontainers.orgLXD 的 IRC 频道： #lxcontainers on irc.freenode.net作者简介：我是 Stéphane Graber。我是 LXC 和 LXD 项目的领导者，目前在加拿大魁北克蒙特利尔的家所在的Canonical 有限公司担任 LXD 的技术主管。via: https://www.stgraber.org/2016/04/14/lxd-2-0-lxd-in-lxd-812/作者：Stéphane Graber 译者：geekpi 校对：wxy本文由 LCTT 组织翻译，Linux中国 荣誉推出
这是 LXD 2.0 系列介绍文章的第七篇。LXD 入门安装与配置你的第一个 LXD 容器资源控制镜像管理远程主机及容器迁移LXD 中的 DockerLXD 中的 LXD实时迁移LXD 和 JujuLXD 和 OpenStack调试，及给 LXD 做贡献为什么在 LXD 中运行 Docker正如我在系列的第一篇中简要介绍的，LXD 的重点是系统容器，也就是我们在容器中运行一个完全未经修改的 Linux 发行版。LXD 的所有意图和目的并不在乎容器中的负载是什么。它只是设置容器命名空间和安全策略，然后运行 /sbin/init 来生成容器，接着等待容器停止。应用程序容器，例如由 Docker 或 Rkt 所实现的应用程序容器是非常不同的，因为它们用于分发应用程序，通常在它们内部运行单个主进程，并且比 LXD 容器生命期更短暂。这两种容器类型不是相互排斥的，我们的确看到使用 Docker 容器来分发应用程序的价值。这就是为什么我们在过去一年中努力工作以便让 LXD 中运行 Docker 成为可能。这意味着，使用 Ubuntu 16.04 和 LXD 2.0，您可以为用户创建容器，然后可以像正常的 Ubuntu 系统一样连接到这些容器，然后运行 Docker 来安装他们想要的服务和应用程序。要求要让它正常工作要做很多事情，Ubuntu 16.04 上已经包含了这些：支持 CGroup 命名空间的内核（4.4 Ubuntu 或 4.6 主线内核）使用 LXC 2.0 和 LXCFS 2.0 的 LXD 2.0一个自定义版本的 Docker（或一个用我们提交的所有补丁构建的）Docker 镜像，其受限于用户命名空间限制，或者使父 LXD 容器成为特权容器（security.privileged = true）运行一个基础的 Docker 载荷说完这些，让我们开始运行 Docker 容器！首先你可以用下面的命令得到一个 Ubuntu 16.04 的容器：lxc launch ubuntu-daily:16.04 docker -p default -p docker -p default -p docker 表示 LXD 将 default 和 docker 配置文件应用于容器。default 配置文件包含基本网络配置，而 docker 配置文件告诉 LXD 加载几个必需的内核模块并为容器设置一些挂载。 docker 配置文件还支持容器嵌套。现在让我们确保容器是最新的并安装 docker：lxc exec docker -- apt update lxc exec docker -- apt dist-upgrade -y lxc exec docker -- apt install docker.io -y 就是这样！你已经安装并运行了一个 Docker 容器。现在让我们用两个 Docker 容器开启一个基础的 web 服务：stgraber@dakara:~$ lxc exec docker -- docker run --detach --name app carinamarina/hello-world-app Unable to find image 'carinamarina/hello-world-app:latest' locally latest: Pulling from carinamarina/hello-world-app efd26ecc9548: Pull complete a3ed95caeb02: Pull complete d1784d73276e: Pull complete 72e581645fc3: Pull complete 9709ddcc4d24: Pull complete 2d600f0ec235: Pull complete c4cf94f61cbd: Pull complete c40f2ab60404: Pull complete e87185df6de7: Pull complete 62a11c66eb65: Pull complete 4c5eea9f676d: Pull complete 498df6a0d074: Pull complete Digest: sha256:6a159db50cb9c0fbe127fb038ed5a33bb5a443fcdd925ec74bf578142718f516 Status: Downloaded newer image for carinamarina/hello-world-app:latest c8318f0401fb1e119e6c5bb23d1e706e8ca080f8e44b42613856ccd0bf8bfb0d stgraber@dakara:~$ lxc exec docker -- docker run --detach --name web --link app:helloapp -p 80:5000 carinamarina/hello-world-web Unable to find image 'carinamarina/hello-world-web:latest' locally latest: Pulling from carinamarina/hello-world-web efd26ecc9548: Already exists a3ed95caeb02: Already exists d1784d73276e: Already exists 72e581645fc3: Already exists 9709ddcc4d24: Already exists 2d600f0ec235: Already exists c4cf94f61cbd: Already exists c40f2ab60404: Already exists e87185df6de7: Already exists f2d249ff479b: Pull complete 97cb83fe7a9a: Pull complete d7ce7c58a919: Pull complete Digest: sha256:c31cf04b1ab6a0dac40d0c5e3e64864f4f2e0527a8ba602971dab5a977a74f20 Status: Downloaded newer image for carinamarina/hello-world-web:latest d7b8963401482337329faf487d5274465536eebe76f5b33c89622b92477a670f 现在这两个 Docker 容器已经运行了，我们可以得到 LXD 容器的 IP 地址，并且访问它的服务了！stgraber@dakara:~$ lxc list +--------+---------+----------------------+----------------------------------------------+------------+-----------+ | NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | +--------+---------+----------------------+----------------------------------------------+------------+-----------+ | docker | RUNNING | 172.17.0.1 (docker0) | 2001:470:b368:4242:216:3eff:fe55:45f4 (eth0) | PERSISTENT | 0 | | | | 10.178.150.73 (eth0) | | | | +--------+---------+----------------------+----------------------------------------------+------------+-----------+ stgraber@dakara:~$ curl http://10.178.150.73 The linked container said... "Hello World!" 总结就是这样了！在 LXD 容器中运行 Docker 容器真的很简单。现在正如我前面提到的，并不是所有的 Docker 镜像都会像我的示例一样，这通常是因为 LXD 带来了额外的限制，特别是用户命名空间。在这种模式下只有 Docker 的 overlayfs 存储驱动可以工作。该存储驱动有一组自己的限制，这进一步限制了在该环境中可以有多少镜像工作。如果您的负载无法正常工作，并且您信任该 LXD 容器中的用户，你可以试下：lxc config set docker security.privileged true lxc restart docker 这将取消激活用户命名空间，并以特权模式运行容器。但是请注意，在这种模式下，容器内的 root 与主机上的 root 是相同的 uid。现在有许多已知的方法让用户脱离容器，并获得主机上的 root 权限，所以你应该只有在信任你的 LXD 容器中的用户可以具有主机上的 root 权限才这样做。额外信息LXD 的主站在： https://linuxcontainers.org/lxdLXD 的 GitHub 仓库： https://github.com/lxc/lxdLXD 的邮件列表： https://lists.linuxcontainers.orgLXD 的 IRC 频道： #lxcontainers on irc.freenode.net作者简介：我是 Stéphane Graber。我是 LXC 和 LXD 项目的领导者，目前在加拿大魁北克蒙特利尔的家所在的Canonical 有限公司担任 LXD 的技术主管。via: https://www.stgraber.org/2016/04/13/lxd-2-0-docker-in-lxd-712/作者：Stéphane Graber 译者：geekpi 校对：wxy本文由 LCTT 组织翻译，Linux中国 荣誉推出
这是 LXD 2.0 系列介绍文章的第六篇。LXD 入门安装与配置你的第一个 LXD 容器资源控制镜像管理远程主机及容器迁移LXD 中的 DockerLXD 中的 LXD实时迁移LXD 和 JujuLXD 和 OpenStack调试，及给 LXD 做贡献远程协议LXD 2.0 支持两种协议:LXD 1.0 API：这是在客户端和 LXD 守护进程之间使用的 REST API，以及在 LXD 守护进程间复制/移动镜像和容器时使用的 REST API。Simplestreams：Simplestreams 协议是 LXD 客户端和守护进程使用的只读、仅针对镜像的协议，用于客户端和 LXD 守护进程获取镜像信息以及从一些公共镜像服务器（如 Ubuntu 镜像）导入镜像。以下所有内容都将使用这两个协议中的第一个。安全LXD API 的验证是通过客户端证书在 TLS 1.2 上使用最近的密钥验证的。 当两个 LXD 守护进程必须直接交换信息时，源守护程序生成一个临时令牌，并通过客户端传输到目标守护程序。 此令牌仅可用于访问特定流，并且会被立即撤销，因此不能重新使用。为了避免中间人攻击，客户端工具还将源服务器的证书发送到目标服务器。这意味着对于特定的下载操作，目标服务器会被提供源服务器的 URL、所需资源的一次性访问令牌以及服务器应该使用的证书。 这可以防止中间人攻击，并且只允许临时访问所传输的对象。网络需求LXD 2.0 使用这样一种模型，某个操作的目标（接收端）直接连接到源以获取数据。这意味着你必须确保目标服务器可以直接连接到源、可以更新任何所需的防火墙。我们有个允许反向连接的计划，允许通过客户端代理本身以应对那些严格的防火墙阻止两台主机之间通信的罕见情况。与远程主机交互LXD 使用的是“远程”的概念，而不是让我们的用户总是提供主机名或 IP 地址，然后在他们想要与远程主机交互时验证证书信息。默认情况下，唯一真正的 LXD 远程配置是 local:，这也是默认的远程（所以你不必输入它的名称）。这个本地（local:）远程使用 LXD REST API 通过 unix 套接字与本地守护进程通信。添加一台远程主机假设你已经有两台装有 LXD 的机器：你的本机以及远程那台我们称为“foo”的主机。首先你需要确保“foo”正在监听网络，并设置了一个密码，以便得到一个远程 shell，运行：lxc config set core.https_address [::]:8443 lxc config set core.trust_password something-secure 在你本地 LXD 上，你需要使它对网络可见，这样我们可以从它传输容器和镜像：lxc config set core.https_address [::]:8443 现在已经在两端完成了守护进程的配置，你可以添加“foo”到你的本地客户端：lxc remote add foo 1.2.3.4 （将 1.2.3.4 替换成你的 IP 或者 FQDN）看上去像这样：stgraber@dakara:~$ lxc remote add foo 2607:f2c0:f00f:2770:216:3eff:fee1:bd67 Certificate fingerprint: fdb06d909b77a5311d7437cabb6c203374462b907f3923cefc91dd5fce8d7b60 ok (y/n)? y Admin password for foo: Client certificate stored at server: foo 你接着可以列出远端服务器，你可以在列表中看到“foo”：stgraber@dakara:~$ lxc remote list +-----------------+-------------------------------------------------------+---------------+--------+--------+ | NAME | URL | PROTOCOL | PUBLIC | STATIC | +-----------------+-------------------------------------------------------+---------------+--------+--------+ | foo | https://[2607:f2c0:f00f:2770:216:3eff:fee1:bd67]:8443 | lxd | NO | NO | +-----------------+-------------------------------------------------------+---------------+--------+--------+ | images | https://images.linuxcontainers.org:8443 | lxd | YES | NO | +-----------------+-------------------------------------------------------+---------------+--------+--------+ | local (default) | unix:// | lxd | NO | YES | +-----------------+-------------------------------------------------------+---------------+--------+--------+ | ubuntu | https://cloud-images.ubuntu.com/releases | simplestreams | YES | YES | +-----------------+-------------------------------------------------------+---------------+--------+--------+ | ubuntu-daily | https://cloud-images.ubuntu.com/daily | simplestreams | YES | YES | +-----------------+-------------------------------------------------------+---------------+--------+--------+ 与它交互好了，所以我们已经有了一台定义好的远程服务器，我们现在可以做些什么？现在，就如你看到的，唯一的不同是你必须告诉 LXD 要哪台主机运行。比如：lxc launch ubuntu:14.04 c1 它会在默认主机（lxc remote get-default），也就是你的本机上运行。lxc launch ubuntu:14.04 foo:c1 这个会在 foo 上运行。列出远程主机正在运行的容器可以这么做：stgraber@dakara:~$ lxc list foo: +------+---------+---------------------+-----------------------------------------------+------------+-----------+ | NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | +------+---------+---------------------+-----------------------------------------------+------------+-----------+ | c1 | RUNNING | 10.245.81.95 (eth0) | 2607:f2c0:f00f:2770:216:3eff:fe43:7994 (eth0) | PERSISTENT | 0 | +------+---------+---------------------+-----------------------------------------------+------------+-----------+ 你要记住的一件事是你需要在远程主机上同时指定镜像和容器。因此如果你在“foo”上有一个“my-image”的镜像，并且希望从它创建一个“c2”的容器，你需要运行：lxc launch foo:my-image foo:c2 最后，就如你希望的那样得到一个远程容器的 shell：lxc exec foo:c1 bash 复制容器在两台主机间复制容器就如它听上去那样简单：lxc copy foo:c1 c2 你会有一个新的从远程“c1”复制过来的本地“c2”容器。这需要停止“c1”容器，但是你可以在运行的时候只复制一个快照：lxc snapshot foo:c1 current lxc copy foo:c1/current c3 移动容器除非你在做实时迁移（将会在之后的文章中讲到），不然你需要在移动前先停止容器，接着就会如你预料的那样。lxc stop foo:c1 lxc move foo:c1 local: 这个例子等同于：lxc stop foo:c1 lxc move foo:c1 c1 是如何工作的正如你期望的那样, 与远程容器的交互时 LXD 使用的 REST API 并不是通过本地 Unix 套接字，而是通过 HTTPS 传输。当两个守护程序之间交互时会变得有些棘手，如复制和移动的情况。在这种情况下会发生：用户运行lxc move foo：c1 c1。客户端联系 local: 远程以检查是否现有“c1”容器。客户端从“foo”获取容器信息。客户端从源“foo”守护程序请求迁移令牌。客户端将迁移令牌以及源 URL 和“foo”的证书发送到本地 LXD 守护程序以及容器配置和周围设备。然后本地 LXD 守护程序使用提供的令牌直接连接到“foo” a) 它连接到第一个控制 websocket b) 它协商文件系统传输协议（zfs 发送/接收，btrfs 发送/接收或者纯 rsync） c) 如果在本地可用，它会解压用于创建源容器的镜像。这是为了避免不必要的数据传输。 d) 然后它会将容器及其任何快照作为增量传输。如果成功，客户端会命令“foo”删除源容器。在线尝试没有两台机器来尝试远端交互和复制/移动容器？没有问题，你可以使用我们的 demo 服务。这里甚至还包括了一步步的指导！额外信息LXD 的主站在： https://linuxcontainers.org/lxdLXD 的 GitHub 仓库： https://github.com/lxc/lxdLXD 的邮件列表： https://lists.linuxcontainers.orgLXD 的 IRC 频道： #lxcontainers on irc.freenode.net作者简介：我是 Stéphane Graber。我是 LXC 和 LXD 项目的领导者，目前在加拿大魁北克蒙特利尔的家所在的Canonical 有限公司担任 LXD 的技术主管。via: https://www.stgraber.org/2016/04/12/lxd-2-0-remote-hosts-and-container-migration-612/作者：Stéphane Graber 译者：geekpi 校对：wxy本文由 LCTT 组织翻译，Linux中国 荣誉推出
这是 LXD 2.0 系列介绍文章的第五篇。LXD 入门安装与配置你的第一个 LXD 容器资源控制镜像管理远程主机及容器迁移LXD 中的 DockerLXD 中的 LXD实时迁移LXD 和 JujuLXD 和 OpenStack调试，及给 LXD 做贡献因为 lxd 容器管理有很多命令，因此这篇文章会很长。 如果你想要快速地浏览这些相同的命令，你可以尝试下我们的在线演示！容器镜像如果你以前使用过 LXC，你可能还记得那些 LXC “模板”，基本上都是导出一个容器文件系统以及一点配置的 shell 脚本。大多数模板是通过在本机上执行一个完整的发行版自举来生成该文件系统。这可能需要相当长的时间，并且无法在所有的发行版上可用，另外可能需要大量的网络带宽。回到 LXC 1.0，我写了一个“下载”模板，它允许用户下载预先打包的容器镜像，用模板脚本在中央服务器上生成，接着高度压缩、签名并通过 https 分发。我们很多用户从旧版的容器生成方式切换到了使用这种新的、更快更可靠的创建容器的方式。使用 LXD，我们通过全面的基于镜像的工作流程向前迈进了一步。所有容器都是从镜像创建的，我们在 LXD 中具有高级镜像缓存和预加载支持，以使镜像存储保持最新。与 LXD 镜像交互在更深入了解镜像格式之前，让我们快速了解下 LXD 可以让你做些什么。透明地导入镜像所有的容器都是由镜像创建的。镜像可以来自一台远程服务器并使用它的完整 hash、短 hash 或者别名拉取下来，但是最终每个 LXD 容器都是创建自一个本地镜像。这有个例子：lxc launch ubuntu:14.04 c1 lxc launch ubuntu:75182b1241be475a64e68a518ce853e800e9b50397d2f152816c24f038c94d6e c2 lxc launch ubuntu:75182b1241be c3 所有这些引用相同的远程镜像（在写这篇文章时），在第一次运行这些命令其中之一时，远程镜像将作为缓存镜像导入本地 LXD 镜像存储，接着从其创建容器。下一次运行其中一个命令时，LXD 将只检查镜像是否仍然是最新的（当不是由指纹引用时），如果是，它将创建容器而不下载任何东西。现在镜像被缓存在本地镜像存储中，你也可以从那里启动它，甚至不检查它是否是最新的：lxc launch 75182b1241be c4 最后，如果你有个名为“myimage”的本地镜像，你可以：lxc launch my-image c5 如果你想要改变一些自动缓存或者过期行为，在本系列之前的文章中有一些命令。手动导入镜像从镜像服务器中复制如果你想复制远程的某个镜像到你本地镜像存储，但不立即从它创建一个容器，你可以使用lxc image copy命令。它可以让你调整一些镜像标志，比如：lxc image copy ubuntu:14.04 local: 这只是简单地复制一个远程镜像到本地存储。如果您想要通过比记住其指纹更容易的方式来记住你引用的镜像副本，则可以在复制时添加别名：lxc image copy ubuntu:12.04 local: --alias old-ubuntu lxc launch old-ubuntu c6 如果你想要使用源服务器上设置的别名，你可以要求 LXD 复制下来：lxc image copy ubuntu:15.10 local: --copy-aliases lxc launch 15.10 c7 上面的副本都是一次性拷贝，也就是复制远程镜像的当前版本到本地镜像存储中。如果你想要 LXD 保持镜像最新，就像它在缓存中存储的那样，你需要使用 –auto-update 标志：lxc image copy images:gentoo/current/amd64 local: --alias gentoo --auto-update 导入 tarball如果某人给你提供了一个单独的 tarball，你可以用下面的命令导入：lxc image import <tarball> 如果你想在导入时设置一个别名，你可以这么做：lxc image import <tarball> --alias random-image 现在如果你被给了两个 tarball，要识别哪个是含有 LXD 元数据的。通常可以通过 tarball 的名称来识别，如果不行就选择最小的那个，元数据 tarball 包是很小的。 然后将它们一起导入：lxc image import <metadata tarball> <rootfs tarball> 从 URL 中导入lxc image import 也可以与指定的 URL 一起使用。如果你的一台 https Web 服务器的某个路径中有 LXD-Image-URL 和 LXD-Image-Hash 的标头设置，那么 LXD 就会把这个镜像拉到镜像存储中。可以参照例子这么做：lxc image import https://dl.stgraber.org/lxd --alias busybox-amd64 当拉取镜像时，LXD 还会设置一些标头，远程服务器可以检查它们以返回适当的镜像。 它们是 LXD-Server-Architectures 和 LXD-Server-Version。这相当于一个简陋的镜像服务器。 它可以通过任何静态 Web 服务器提供一中用户友好的导入镜像的方式。管理本地镜像存储现在我们本地已经有一些镜像了，让我们瞧瞧可以做些什么。我们已经介绍了最主要的部分，可以从它们来创建容器，但是你还可以在本地镜像存储上做更多。列出镜像要列出所有的镜像，运行 lxc image list：stgraber@dakara:~$ lxc image list +---------------+--------------+--------+------------------------------------------------------+--------+----------+------------------------------+ | ALIAS | FINGERPRINT | PUBLIC | DESCRIPTION | ARCH | SIZE | UPLOAD DATE | +---------------+--------------+--------+------------------------------------------------------+--------+----------+------------------------------+ | alpine-32 | 6d9c131efab3 | yes | Alpine edge (i386) (20160329_23:52) | i686 | 2.50MB | Mar 30, 2016 at 4:36am (UTC) | +---------------+--------------+--------+------------------------------------------------------+--------+----------+------------------------------+ | busybox-amd64 | 74186c79ca2f | no | Busybox x86_64 | x86_64 | 0.79MB | Mar 30, 2016 at 4:33am (UTC) | +---------------+--------------+--------+------------------------------------------------------+--------+----------+------------------------------+ | gentoo | 1a134c5951e0 | no | Gentoo current (amd64) (20160329_14:12) | x86_64 | 232.50MB | Mar 30, 2016 at 4:34am (UTC) | +---------------+--------------+--------+------------------------------------------------------+--------+----------+------------------------------+ | my-image | c9b6e738fae7 | no | Scientific Linux 6 x86_64 (default) (20160215_02:36) | x86_64 | 625.34MB | Mar 2, 2016 at 4:56am (UTC) | +---------------+--------------+--------+------------------------------------------------------+--------+----------+------------------------------+ | old-ubuntu | 4d558b08f22f | no | ubuntu 12.04 LTS amd64 (release) (20160315) | x86_64 | 155.09MB | Mar 30, 2016 at 4:30am (UTC) | +---------------+--------------+--------+------------------------------------------------------+--------+----------+------------------------------+ | w (11 more) | d3703a994910 | no | ubuntu 15.10 amd64 (release) (20160315) | x86_64 | 153.35MB | Mar 30, 2016 at 4:31am (UTC) | +---------------+--------------+--------+------------------------------------------------------+--------+----------+------------------------------+ | | 75182b1241be | no | ubuntu 14.04 LTS amd64 (release) (20160314) | x86_64 | 118.17MB | Mar 30, 2016 at 4:27am (UTC) | +---------------+--------------+--------+------------------------------------------------------+--------+----------+------------------------------+ 你可以通过别名或者指纹来过滤：stgraber@dakara:~$ lxc image list amd64 +---------------+--------------+--------+-----------------------------------------+--------+----------+------------------------------+ | ALIAS | FINGERPRINT | PUBLIC | DESCRIPTION | ARCH | SIZE | UPLOAD DATE | +---------------+--------------+--------+-----------------------------------------+--------+----------+------------------------------+ | busybox-amd64 | 74186c79ca2f | no | Busybox x86_64 | x86_64 | 0.79MB | Mar 30, 2016 at 4:33am (UTC) | +---------------+--------------+--------+-----------------------------------------+--------+----------+------------------------------+ | w (11 more) | d3703a994910 | no | ubuntu 15.10 amd64 (release) (20160315) | x86_64 | 153.35MB | Mar 30, 2016 at 4:31am (UTC) | +---------------+--------------+--------+-----------------------------------------+--------+----------+------------------------------+ 或者指定一个镜像属性中的键值对来过滤：stgraber@dakara:~$ lxc image list os=ubuntu +-------------+--------------+--------+---------------------------------------------+--------+----------+------------------------------+ | ALIAS | FINGERPRINT | PUBLIC | DESCRIPTION | ARCH | SIZE | UPLOAD DATE | +-------------+--------------+--------+---------------------------------------------+--------+----------+------------------------------+ | old-ubuntu | 4d558b08f22f | no | ubuntu 12.04 LTS amd64 (release) (20160315) | x86_64 | 155.09MB | Mar 30, 2016 at 4:30am (UTC) | +-------------+--------------+--------+---------------------------------------------+--------+----------+------------------------------+ | w (11 more) | d3703a994910 | no | ubuntu 15.10 amd64 (release) (20160315) | x86_64 | 153.35MB | Mar 30, 2016 at 4:31am (UTC) | +-------------+--------------+--------+---------------------------------------------+--------+----------+------------------------------+ | | 75182b1241be | no | ubuntu 14.04 LTS amd64 (release) (20160314) | x86_64 | 118.17MB | Mar 30, 2016 at 4:27am (UTC) | +-------------+--------------+--------+---------------------------------------------+--------+----------+------------------------------+ 要了解镜像的所有信息，你可以使用lxc image info：stgraber@castiana:~$ lxc image info ubuntu Fingerprint: e8a33ec326ae7dd02331bd72f5d22181ba25401480b8e733c247da5950a7d084 Size: 139.43MB Architecture: i686 Public: no Timestamps: Created: 2016/03/15 00:00 UTC Uploaded: 2016/03/16 05:50 UTC Expires: 2017/04/26 00:00 UTC Properties: version: 12.04 aliases: 12.04,p,precise architecture: i386 description: ubuntu 12.04 LTS i386 (release) (20160315) label: release os: ubuntu release: precise serial: 20160315 Aliases: - ubuntu Auto update: enabled Source: Server: https://cloud-images.ubuntu.com/releases Protocol: simplestreams Alias: precise/i386 编辑镜像编辑镜像的属性和标志的简单方法是使用：lxc image edit <alias or fingerprint> 这会打开默认文本编辑器，内容像这样：autoupdate: true properties: aliases: 14.04,default,lts,t,trusty architecture: amd64 description: ubuntu 14.04 LTS amd64 (release) (20160314) label: release os: ubuntu release: trusty serial: "20160314" version: "14.04" public: false 你可以修改任何属性，打开或者关闭自动更新，或者标记一个镜像是公共的（后面详述）。删除镜像删除镜像只需要运行：lxc image delete <alias or fingerprint> 注意你不必移除缓存对象，它们会在过期后被 LXD 自动移除（默认上，在最后一次使用的 10 天后）。导出镜像如果你想得到目前镜像的 tarball，你可以使用lxc image export，像这样：stgraber@dakara:~$ lxc image export old-ubuntu . Output is in . stgraber@dakara:~$ ls -lh *.tar.xz -rw------- 1 stgraber domain admins 656 Mar 30 00:55 meta-ubuntu-12.04-server-cloudimg-amd64-lxd.tar.xz -rw------- 1 stgraber domain admins 156M Mar 30 00:55 ubuntu-12.04-server-cloudimg-amd64-lxd.tar.xz 镜像格式LXD 现在支持两种镜像布局，unified 或者 split。这两者都是有效的 LXD 格式，虽然后者在与其他容器或虚拟机一起运行时更容易重用其文件系统。LXD 专注于系统容器，不支持任何应用程序容器的“标准”镜像格式，我们也不打算这么做。我们的镜像很简单，它们是由容器文件系统，以及包含了镜像制作时间、到期时间、什么架构，以及可选的一堆文件模板的元数据文件组成。有关镜像格式的最新详细信息，请参阅此文档。unified 镜像（一个 tarball）unified 镜像格式是 LXD 在生成镜像时使用的格式。它们是一个单独的大型 tarball，包含 rootfs 目录下的容器文件系统，在 tarball 根目录下有 metadata.yaml 文件，任何模板都放到 templates 目录。tarball 可以用任何方式压缩（或者不压缩）。镜像散列是压缩后的 tarball 的 sha256 。Split 镜像（两个 tarball）这种格式最常用于滚动更新镜像并已经有了一个压缩文件系统 tarball 时。它们由两个不同的 tarball 组成，第一个只包含 LXD 使用的元数据， metadata.yaml 文件在根目录，任何模板都在 templates 目录。第二个 tarball 只包含直接位于其根目录下的容器文件系统。大多数发行版已经有这样的 tarball，因为它们常用于引导新机器。 此镜像格式允许不经修改就重用。两个 tarball 都可以压缩（或者不压缩），它们可以使用不同的压缩算法。 镜像散列是元数据的 tarball 和 rootfs 的 tarball 结合的 sha256。镜像元数据典型的 metadata.yaml 文件看起来像这样：architecture: "i686" creation_date: 1458040200 properties: architecture: "i686" description: "Ubuntu 12.04 LTS server (20160315)" os: "ubuntu" release: "precise" templates: /var/lib/cloud/seed/nocloud-net/meta-data: when: - start template: cloud-init-meta.tpl /var/lib/cloud/seed/nocloud-net/user-data: when: - start template: cloud-init-user.tpl properties: default: | #cloud-config {} /var/lib/cloud/seed/nocloud-net/vendor-data: when: - start template: cloud-init-vendor.tpl properties: default: | #cloud-config {} /etc/init/console.override: when: - create template: upstart-override.tpl /etc/init/tty1.override: when: - create template: upstart-override.tpl /etc/init/tty2.override: when: - create template: upstart-override.tpl /etc/init/tty3.override: when: - create template: upstart-override.tpl /etc/init/tty4.override: when: - create template: upstart-override.tpl 属性两个唯一的必填字段是 creation date（UNIX 纪元时间）和 architecture。 其他都可以保持未设置，镜像就可以正常地导入。额外的属性主要是帮助用户弄清楚镜像是什么。 例如 description 属性是在 lxc image list 中可见的。 用户可以使用其它属性的键/值对来搜索特定镜像。相反，这些属性用户可以通过 lxc image edit来编辑，creation date 和 architecture 字段是不可变的。模板模板机制允许在容器生命周期中的某一点生成或重新生成容器中的一些文件。我们使用 pongo2 模板引擎来做这些，我们将所有我们知道的容器信息都导出到模板。 这样，你可以使用用户定义的容器属性或常规 LXD 属性来自定义镜像，从而更改某些特定文件的内容。正如你在上面的例子中看到的，我们使用在 Ubuntu 中使用它们来进行 cloud-init 并关闭一些 init 脚本。创建你的镜像LXD 专注于运行完整的 Linux 系统，这意味着我们期望大多数用户只使用干净的发行版镜像，而不是只用自己的镜像。但是有一些情况下，你有自己的镜像是有必要的。 例如生产服务器上的预配置镜像，或者构建那些我们没有构建的发行版或者架构的镜像。将容器变成镜像目前使用 LXD 构造镜像最简单的方法是将容器变成镜像。可以这么做：lxc launch ubuntu:14.04 my-container lxc exec my-container bash <do whatever change you want> lxc publish my-container --alias my-new-image 你甚至可以将一个容器过去的快照变成镜像：lxc publish my-container/some-snapshot --alias some-image 手动构建镜像构建你自己的镜像也很简单。生成容器文件系统。这完全取决于你使用的发行版。对于 Ubuntu 和 Debian，它将用于启动。配置容器中该发行版正常工作所需的任何东西（如果需要任何东西）。制作该容器文件系统的 tarball，可选择压缩它。根据上面描述的内容写一个新的 metadata.yaml 文件。创建另一个包含 metadata.yaml 文件的 tarball。用下面的命令导入这两个 tarball 作为 LXD 镜像：lxc image import <metadata tarball> <rootfs tarball> --alias some-name在一切都正常工作前你可能需要经历几次这样的工作，调整这里或那里，可能会添加一些模板和属性。发布你的镜像所有 LXD 守护程序都充当镜像服务器。除非另有说明，否则加载到镜像存储中的所有镜像都会被标记为私有，因此只有受信任的客户端可以检索这些镜像，但是如果要创建公共镜像服务器，你需要做的是将一些镜像标记为公开，并确保你的 LXD 守护进程监听网络。只运行 LXD 公共服务器最简单的共享镜像的方式是运行一个公共的 LXD 守护进程。你只要运行：lxc config set core.https_address "[::]:8443" 远程用户就可以添加你的服务器作为公共服务器：lxc remote add <some name> <IP or DNS> --public 他们就可以像使用任何默认的镜像服务器一样使用它们。 由于远程服务器添加了 -public 选项，因此不需要身份验证，并且客户端仅限于使用已标记为 public 的镜像。要将镜像设置成公共的，只需使用 lxc image edit 编辑它们，并将 public 标志设置为 true。使用一台静态 web 服务器如上所述，lxc image import 支持从静态 https 服务器下载。 基本要求是：服务器必须支持具有有效证书的 HTTPS、TLS 1.2 和 EC 算法。当访问 lxc image import 提供的 URL 时，服务器必须返回一个包含 LXD-Image-Hash 和 LXD-Image-URL 的 HTTP 标头。如果你想使它动态化，你可以让你的服务器查找 LXD 在请求镜像时发送的 LXD-Server-Architectures 和 LXD-Server-Version 的 HTTP 标头，这可以让你返回符合该服务器架构的正确镜像。构建一个简单流服务器ubuntu: 和 ubuntu-daily: 远端服务器不使用 LXD 协议（images: 使用），而是使用称为简单流（simplestreams）的不同协议。简单流基本上是一个镜像服务器的描述格式，使用 JSON 来描述产品以及相关产品的文件列表。它被各种工具，如 OpenStack、Juju、MAAS 等用来查找、下载或者做镜像系统，LXD 将它作为用于镜像检索的原生协议。虽然这的确不是提供 LXD 镜像的最简单的方法，但是如果你的镜像也被其它一些工具使用，那这也许值得考虑一下。关于简单流的更多信息可以在这里找到。总结我希望这篇关于如何使用 LXD 管理镜像以及构建和发布镜像文章让你有所了解。对于以前的 LXC 而言，可以在一组全球分布式系统上得到完全相同的镜像是一个很大的进步，并且引导了更多可复制性的发展方向。额外信息LXD 的主站在： https://linuxcontainers.org/lxdLXD 的 GitHub 仓库： https://github.com/lxc/lxdLXD 的邮件列表： https://lists.linuxcontainers.orgLXD 的 IRC 频道： #lxcontainers on irc.freenode.net如果你不想或者不能在你的机器上安装 LXD ，你可以在 web 上试试在线版的 LXD 。作者简介：我是 Stéphane Graber。我是 LXC 和 LXD 项目的领导者，目前在加拿大魁北克蒙特利尔的家所在的Canonical 有限公司担任 LXD 的技术主管。via: https://www.stgraber.org/2016/03/30/lxd-2-0-image-management-512/作者：Stéphane Graber 译者：geekpi 校对：wxy本文由 LCTT 组织翻译，Linux中国 荣誉推出原文：https://www.stgraber.org/2016/03/30/lxd-2-0-image-management-512/