标签 LXC 下的文章

CGManager 是一个核心的特权守护进程,通过一个简单的 D-Bus API 管理你所有的 cgroup。它被设计用来处理嵌套的 LXC 容器以及接受无特权的请求,包括解析用户名称空间的 UID/GID。

组件

cgmanager

这个守护进程在宿主机上运行,​​将 cgroupfs 挂载到一个独立的挂载名称空间(所以它不能从宿主机上看到),绑定 /sys/fs/cgroup/cgmanager/sock 用于传入的 D-Bus 查询,并通常处理宿主机上直接运行的所有客户端。

cgmanager 既接受使用 D-Bus + SCM 凭证的身份验证请求,用于在命名空间之间转换 uid、gid 和 pid,也可以使用简单的 “unauthenticated”(只是初始的 ucred)D-Bus 来查询来自宿主机级别的查询。

cgproxy

你可能会在两种情况下看到这个守护进程运行。在宿主机上,如果你的内核老于 3.8(没有 pidns 连接支持)或处于容器中(只有 cgproxy 运行)。

cgproxy 本身并不做任何 cgroup 配置更改,而是如其名称所示,代理请求给主 cgmanager 进程。

这是必要的,所以一个进程可以直接使用 D-Bus(例如使用 dbus-send)与 /sys/fs/cgroup/cgmanager/sock 进行通信。

之后 cgproxy 将从该查询中得到 ucred,并对真正的 cgmanager 套接字进行身份验证的 SCM 查询,并通过 ucred 结构体传递参数,使它们能够正确地转换为 cgmanager 可以理解的宿主机命名空间 。

cgm

一个简单的命令行工具,与 D-Bus 服务通信,并允许你从命令行执行所有常见的 cgroup 操作。

通信协议

如上所述,cgmanager 和 cgproxy 使用 D-Bus。建议外部客户端(所以不要是 cgproxy)使用标准的 D-Bus API,不要试图实现 SCM creds 协议,因为它是不必要的,并且容易出错。

相反,只要简单假设与 /sys/fs/cgroup/cgmanager/sock 的通信总是正确的。

cgmanager API 仅在独立的 D-Bus 套接字上可用,cgmanager 本身不连接到系统总线,所以 cgmanager/cgproxy 不要求有运行中的 dbus 守护进程。

你可以在这里阅读更多关于 D-Bus API。

许可证

CGManager 是免费软件,大部分代码是根据 GNU LGPLv2.1+ 许可条款发布的,一些二进制文件是在 GNU GPLv2 许可下发布的。

该项目的默认许可证是 GNU LGPLv2.1+

支持

CGManager 的稳定版本支持依赖于 Linux 发行版以及它们自己承诺推出稳定修复和安全更新。

你可以从 Canonical Ltd 获得对 Ubuntu LTS 版本的 CGManager 的商业支持。


via: https://linuxcontainers.org/cgmanager/introduction/

作者:Canonical Ltd. 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

本文的主角是容器,一种类似虚拟机但更轻量级的构造。你可以轻易地在你的 Ubuntu 桌面系统中创建一堆容器!

虚拟机会虚拟出整个电脑让你来安装客户机操作系统。相比之下,容器复用了主机的 Linux 内核,只是简单地 包容 了我们选择的根文件系统(也就是运行时环境)。Linux 内核有很多功能可以将运行的 Linux 容器与我们的主机分割开(也就是我们的 Ubuntu 桌面)。

Linux 本身需要一些手工操作来直接管理他们。好在,有 LXD(读音为 Lex-deeh),这是一款为我们管理 Linux 容器的服务。

我们将会看到如何:

  1. 在我们的 Ubuntu 桌面上配置容器,
  2. 创建容器,
  3. 安装一台 web 服务器,
  4. 测试一下这台 web 服务器,以及
  5. 清理所有的东西。

设置 Ubuntu 容器

如果你安装的是 Ubuntu 16.04,那么你什么都不用做。只要安装下面所列出的一些额外的包就行了。若你安装的是 Ubuntu 14.04.x 或 Ubuntu 15.10,那么按照 LXD 2.0 系列(二):安装与配置 来进行一些操作,然后再回来。

确保已经更新了包列表:

sudo apt update
sudo apt upgrade

安装 lxd 包:

sudo apt install lxd

若你安装的是 Ubuntu 16.04,那么还可以让你的容器文件以 ZFS 文件系统的格式进行存储。Ubuntu 16.04 的 Linux kernel 包含了支持 ZFS 必要的内核模块。若要让 LXD 使用 ZFS 进行存储,我们只需要安装 ZFS 工具包。没有 ZFS,容器会在主机文件系统中以单独的文件形式进行存储。通过 ZFS,我们就有了写入时拷贝等功能,可以让任务完成更快一些。

安装 zfsutils-linux 包(若你安装的是 Ubuntu 16.04.x):

sudo apt install zfsutils-linux

安装好 LXD 后,包安装脚本应该会将你加入 lxd 组。该组成员可以使你无需通过 sudo 就能直接使用 LXD 管理容器。根据 Linux 的习惯,你需要先登出桌面会话然后再登录 才能应用 lxd 的组成员关系。(若你是高手,也可以通过在当前 shell 中执行 newgrp lxd 命令,就不用重登录了)。

在开始使用前,LXD 需要初始化存储和网络参数。

运行下面命令:

$ sudo lxd init
Name of the storage backend to use (dir or zfs): zfs
Create a new ZFS pool (yes/no)? yes
Name of the new ZFS pool: lxd-pool
Would you like to use an existing block device (yes/no)? no
Size in GB of the new loop device (1GB minimum): 30
Would you like LXD to be available over the network (yes/no)? no
Do you want to configure the LXD bridge (yes/no)? yes 
> You will be asked about the network bridge configuration. Accept all defaults and continue.
Warning: Stopping lxd.service, but it can still be activated by:
 lxd.socket
 LXD has been successfully configured.
$ _

我们在一个(单独)的文件而不是块设备(即分区)中构建了一个文件系统来作为 ZFS 池,因此我们无需进行额外的分区操作。在本例中我指定了 30GB 大小,这个空间取之于根(/) 文件系统中。这个文件就是 /var/lib/lxd/zfs.img

行了!最初的配置完成了。若有问题,或者想了解其他信息,请阅读 https://www.stgraber.org/2016/03/15/lxd-2-0-installing-and-configuring-lxd-212/

创建第一个容器

所有 LXD 的管理操作都可以通过 lxc 命令来进行。我们通过给 lxc 不同参数来管理容器。

lxc list

可以列出所有已经安装的容器。很明显,这个列表现在是空的,但这表示我们的安装是没问题的。

lxc image list

列出可以用来启动容器的(已经缓存的)镜像列表。很明显这个列表也是空的,但这也说明我们的安装是没问题的。

lxc image list ubuntu:

列出可以下载并启动容器的远程镜像。而且指定了显示 Ubuntu 镜像。

lxc image list images:

列出可以用来启动容器的(已经缓存的)各种发行版的镜像列表。这会列出各种发行版的镜像比如 Alpine、Debian、Gentoo、Opensuse 以及 Fedora。

让我们启动一个 Ubuntu 16.04 容器,并称之为 c1

$ lxc launch ubuntu:x c1
Creating c1
Starting c1
$ 

我们使用 launch 动作,然后选择镜像 ubuntu:xx 表示 Xenial/16.04 镜像),最后我们使用名字 c1 作为容器的名称。

让我们来看看安装好的首个容器,

$ lxc list

+---------|---------|----------------------|------|------------|-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+---------|---------|----------------------|------|------------|-----------+
| c1 | RUNNING | 10.173.82.158 (eth0) | | PERSISTENT | 0 |
+---------|---------|----------------------|------|------------|-----------+

我们的首个容器 c1 已经运行起来了,它还有自己的 IP 地址(可以本地访问)。我们可以开始用它了!

安装 web 服务器

我们可以在容器中运行命令。运行命令的动作为 exec

$ lxc exec c1 -- uptime
 11:47:25 up 2 min,0 users,load average:0.07,0.05,0.04
$ _

exec 后面,我们指定容器、最后输入要在容器中运行的命令。该容器的运行时间只有 2 分钟,这是个新出炉的容器:-)。

命令行中的 -- 跟我们 shell 的参数处理过程有关。若我们的命令没有任何参数,则完全可以省略 -

$ lxc exec c1 -- df -h

这是一个必须要 - 的例子,由于我们的命令使用了参数 -h。若省略了 -,会报错。

然后我们运行容器中的 shell 来更新包列表。

$ lxc exec c1 bash
root@c1:~# apt update
Ign http://archive.ubuntu.com trusty InRelease
Get:1 http://archive.ubuntu.com trusty-updates InRelease [65.9 kB]
Get:2 http://security.ubuntu.com trusty-security InRelease [65.9 kB]
...
Hit http://archive.ubuntu.com trusty/universe Translation-en 
Fetched 11.2 MB in 9s (1228 kB/s) 
Reading package lists... Done
root@c1:~# apt upgrade
Reading package lists... Done
Building dependency tree 
...
Processing triggers for man-db (2.6.7.1-1ubuntu1) ...
Setting up dpkg (1.17.5ubuntu5.7) ...
root@c1:~# _

我们使用 nginx 来做 web 服务器。nginx 在某些方面要比 Apache web 服务器更酷一些。

root@c1:~# apt install nginx
Reading package lists... Done
Building dependency tree
...
Setting up nginx-core (1.4.6-1ubuntu3.5) ...
Setting up nginx (1.4.6-1ubuntu3.5) ...
Processing triggers for libc-bin (2.19-0ubuntu6.9) ...
root@c1:~# _

让我们用浏览器访问一下这个 web 服务器。记住 IP 地址为 10.173.82.158,因此你需要在浏览器中输入这个 IP。

lxd-nginx

让我们对页面文字做一些小改动。回到容器中,进入默认 HTML 页面的目录中。

root@c1:~# cd /var/www/html/
root@c1:/var/www/html# ls -l
total 2
-rw-r--r-- 1 root root 612 Jun 25 12:15 index.nginx-debian.html
root@c1:/var/www/html#

使用 nano 编辑文件,然后保存:

lxd-nginx-nano

之后,再刷一下页面看看,

lxd-nginx-modified

清理

让我们清理一下这个容器,也就是删掉它。当需要的时候我们可以很方便地创建一个新容器出来。

$ lxc list
+---------+---------+----------------------+------+------------+-----------+
| NAME | STATE   | IPV4                 | IPV6 | TYPE       | SNAPSHOTS    |
+---------+---------+----------------------+------+------------+-----------+
| c1   | RUNNING | 10.173.82.169 (eth0) |      | PERSISTENT | 0            |
+---------+---------+----------------------+------+------------+-----------+
$ lxc stop c1
$ lxc delete c1
$ lxc list
+---------+---------+----------------------+------+------------+-----------+
| NAME | STATE   | IPV4                 | IPV6 | TYPE       | SNAPSHOTS    |
+---------+---------+----------------------+------+------------+-----------+
+---------+---------+----------------------+------+------------+-----------+

我们停止(关闭)这个容器,然后删掉它了。

本文至此就结束了。关于容器有很多玩法。而这只是配置 Ubuntu 并尝试使用容器的第一步而已。


via: https://blog.simos.info/trying-out-lxd-containers-on-our-ubuntu/

作者:Simos Xenitellis 译者:lujun9972 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

最近,我对 lxc exec 进行了几个改进。如果你不知道它的话我介绍一下,lxc execLXD 的客户端工具,使用 LXD 客户端 api 与 LXD 守护程序通信,并执行用户想要执行的各种程序,以下是你可以使用的一个例子:

我们的主要目标之一就是使 lxc execssh 类似,因为它是交互式或非交互式远程运行命令的标准。这使得 把 lxc exec 做得很好变得有点棘手。

1、 处理后台任务

一个长期存在的问题当然是如何正确处理后台任务。这是一个关于 LXD 2.7 实例的问题的例子:

你可以看到,在后台执行任务将导致 lxc exec 无法退出。许多命令可以触发此问题:

chb@conventiont|~
> lxc exec zest1 bash
root@zest1:~# yes &
y
y
y
.
.
.

现在没有什么能救你了。yes 将会永远直接写入stdout

问题的根源在于 stdout 是一直打开着的,但这是必要的,因为它用以确保用户所启动的进程写入的任何数据实际上都是通过我们建立的 websocket 连接读取并发回的。

假如你想这样,运行一个 shell 会话,然后在后台运行一个进程,并马上退出 shell。对不起,它并不能如预期那样。

第一种并且原始的方法是一旦你检测到前台程序(例如 shell)已经退出就直接关闭 stdout。但这不像想得那么好,当你运行快速执行程序时,这个问题会变得明显,比如:

lxc exec -- ls -al /usr/lib

这里 lxc exec 进程(和相关的 forkexec 进程。但现在不要考虑它,只要记住 Go + setns() 不相往来就行了……)会在 stdout 中所有的缓冲数据被读取之前退出。这种情况下将会导致截断输出,没有人想要这样。在尝试使用几个方法来解决问题之后,包括禁用 pty 缓冲(我告诉你,这不太漂亮,也没有如预期工作。)和其他奇怪的思路,我设法通过几个 poll() “技巧”(在某种意义上说一个“技巧”)解决了这个问题。现在你终于可以运行后台任务,并且可以完全退出了。如图:

2、 报告由信号引起的退出码

ssh 是一个很棒的工具。但有一件事,我一直以来不喜欢当 ssh 运行的命令接收到一个信号时, ssh 总是会报告 -1,也就是退出码 255。当你想要了解导致程序终止的信号时,这很烦人。这就是为什么我最近实施标准 shell 所使用的惯例 128 + n 来报告任何由信号导致的退出,其中 n 被定义为导致执行程序退出的信号量。例如,在 SIGKILL 信号上,你会看到 128 + SIGKILL = 137(计算其他致命信号的退出码作为读者的练习)。所以你可以这么做:

chb@conventiont|~
> lxc exec zest1 sleep 100

现在,将 SIGKILL 发送到执行程序(不是 lxc exec本身,因为 SIGKILL 不可转发)。

kill -KILL $(pidof sleep 100)

最后检查你程序的退出码:

chb@conventiont|~
> echo $?
137

瞧。这显然只有当 a) 退出码没有超过 8 位计算壁垒,b)当执行程序不使用 137 来表示成功(这可真……有趣?!)。这两个论点似乎对我来说都不太有说服力。前者因为致命信号量不应该超过这个范围。后者因为(i)这是用户问题,(ii)这些退出代码实际上是保留的(我是这样认为。),(iii)你在本地或其他上面运行程序时会遇到同样的问题。

我看到的主要优点是这能够回报执行程序细粒度的退出状态。注意,我们不会报告所有被信号杀死的程序实例。比如说,当你的程序能够处理 SIGTERM 并且完全退出时,LXD 没有简单的方法来检测到这个情况并报告说这个程序被信号杀死了。你只会简单地收到退出码 0

3、 转发信号

这可能不太有趣(或者也许不是,谁知道呢),但我发现它非常有用。正如你在 SIGKILL 案例中看到的那样,我明确地指出,必须将 SIGKILL 发送到执行程序,而不是 lxc exec命令本身。这是因为 SIGKILL 在程序中无法处理。程序可以做的唯一的事情就是去死,像现在这样……像这个例子……马上(你明白了了吧……)。但是程序可以处理很多其他信号 SIGTERMSIGHUP',当然也可以处理SIGUSR1SIGUSR2。因此,当你发送可以被lxc exec` 处理而不是被执行程序处理的信号时,较新版本的 LXD 会将信号转发到执行进程。这在脚本中非常方便。

无论如何,我希望你觉得这篇小小的 lxc exec 文章/胡言乱语有用。享受 LXD 吧,这是与一只疯狂的美丽的野兽玩耍。请试试在线实验:https://linuxcontainers.org/lxd/try-it/,对于开发人员看看这里:https://github.com/lxc/lxd 并给我们补丁。

我们不要求签署任何 CLA,我们遵循内核风格,只要其中有 “Signed-off-by” 这行就好。


via: https://cbrauner.wordpress.com/2017/01/20/lxc-exec-vs-ssh/

作者:brauner 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

这是 LXD 2.0 系列介绍文章的第五篇。

  1. LXD 入门
  2. 安装与配置
  3. 你的第一个 LXD 容器
  4. 资源控制
  5. 镜像管理
  6. 远程主机及容器迁移
  7. LXD 中的 Docker
  8. LXD 中的 LXD
  9. 实时迁移
  10. LXD 和 Juju
  11. LXD 和 OpenStack
  12. 调试,及给 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-URLLXD-Image-Hash 的标头设置,那么 LXD 就会把这个镜像拉到镜像存储中。

可以参照例子这么做:

lxc image import https://dl.stgraber.org/lxd --alias busybox-amd64

当拉取镜像时,LXD 还会设置一些标头,远程服务器可以检查它们以返回适当的镜像。 它们是 LXD-Server-ArchitecturesLXD-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 datearchitecture 字段是不可变的。

模板

模板机制允许在容器生命周期中的某一点生成或重新生成容器中的一些文件。

我们使用 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

手动构建镜像

构建你自己的镜像也很简单。

  1. 生成容器文件系统。这完全取决于你使用的发行版。对于 Ubuntu 和 Debian,它将用于启动。
  2. 配置容器中该发行版正常工作所需的任何东西(如果需要任何东西)。
  3. 制作该容器文件系统的 tarball,可选择压缩它。
  4. 根据上面描述的内容写一个新的 metadata.yaml 文件。
  5. 创建另一个包含 metadata.yaml 文件的 tarball。
  6. 用下面的命令导入这两个 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-HashLXD-Image-URL 的 HTTP 标头。

如果你想使它动态化,你可以让你的服务器查找 LXD 在请求镜像时发送的 LXD-Server-ArchitecturesLXD-Server-Version 的 HTTP 标头,这可以让你返回符合该服务器架构的正确镜像。

构建一个简单流服务器

ubuntu:ubuntu-daily: 远端服务器不使用 LXD 协议(images: 使用),而是使用称为简单流(simplestreams)的不同协议。

简单流基本上是一个镜像服务器的描述格式,使用 JSON 来描述产品以及相关产品的文件列表。

它被各种工具,如 OpenStack、Juju、MAAS 等用来查找、下载或者做镜像系统,LXD 将它作为用于镜像检索的原生协议。

虽然这的确不是提供 LXD 镜像的最简单的方法,但是如果你的镜像也被其它一些工具使用,那这也许值得考虑一下。

关于简单流的更多信息可以在这里找到。

总结

我希望这篇关于如何使用 LXD 管理镜像以及构建和发布镜像文章让你有所了解。对于以前的 LXC 而言,可以在一组全球分布式系统上得到完全相同的镜像是一个很大的进步,并且引导了更多可复制性的发展方向。

额外信息

LXD 的主站在: https://linuxcontainers.org/lxd

LXD 的 GitHub 仓库: https://github.com/lxc/lxd

LXD 的邮件列表: https://lists.linuxcontainers.org

LXD 的 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/

这是 LXD 2.0 系列介绍文章的第四篇。

  1. LXD 入门
  2. 安装与配置
  3. 你的第一个 LXD 容器
  4. 资源控制
  5. 镜像管理
  6. 远程主机及容器迁移
  7. LXD 中的 Docker
  8. LXD 中的 LXD
  9. 实时迁移
  10. LXD 和 Juju
  11. LXD 和 OpenStack
  12. 调试,及给 LXD 做贡献

因为 LXD 容器管理有很多命令,因此这篇文章会很长。 如果你想要快速地浏览这些相同的命令,你可以尝试下我们的在线演示

可用资源限制

LXD 提供了各种资源限制。其中一些与容器本身相关,如内存配额、CPU 限制和 I/O 优先级。而另外一些则与特定设备相关,如 I/O 带宽或磁盘用量限制。

与所有 LXD 配置一样,资源限制可以在容器运行时动态更改。某些可能无法启用,例如,如果设置的内存值小于当前内存用量,但 LXD 将会试着设置并且报告失败。

所有的限制也可以通过配置文件继承,在这种情况下每个受影响的容器将受到该限制的约束。也就是说,如果在默认配置文件中设置 limits.memory=256MB,则使用默认配置文件(通常是全都使用)的每个容器的内存限制为 256MB。

我们不支持资源限制池,将其中的限制由一组容器共享,因为我们没有什么好的方法通过现有的内核 API 实现这些功能。

磁盘

这或许是最需要和最明显的需求。只需设置容器文件系统的大小限制,并对容器强制执行。

LXD 确实可以让你这样做!

不幸的是,这比它听起来复杂得多。 Linux 没有基于路径的配额,而大多数文件系统只有基于用户和组的配额,这对容器没有什么用处。

如果你正在使用 ZFS 或 btrfs 存储后端,这意味着现在 LXD 只能支持磁盘限制。也有可能为 LVM 实现此功能,但这取决于与它一起使用的文件系统,并且如果结合实时更新那会变得棘手起来,因为并不是所有的文件系统都允许在线增长,而几乎没有一个允许在线收缩。

CPU

当涉及到 CPU 的限制,我们支持 4 种不同的东西:

  • 只给我 X 个 CPU 核心

在这种模式下,你让 LXD 为你选择一组核心,然后为更多的容器和 CPU 的上线/下线提供负载均衡。

容器只看到这个数量的 CPU 核心。

  • 给我一组特定的 CPU 核心(例如,核心1、3 和 5)

类似于第一种模式,但是不会做负载均衡,你会被限制在那些核心上,无论它们有多忙。

  • 给我你拥有的 20% 处理能力

在这种模式下,你可以看到所有的 CPU,但调度程序将限制你使用 20% 的 CPU 时间,但这只有在负载状态才会这样!所以如果系统不忙,你的容器可以跑得很欢。而当其他的容器也开始使用 CPU 时,它会被限制用量。

  • 每测量 200ms,给我 50ms(并且不超过)

此模式与上一个模式类似,你可以看到所有的 CPU,但这一次,无论系统可能是多么空闲,你只能使用你设置的极限时间下的尽可能多的 CPU 时间。在没有过量使用的系统上,这可使你可以非常整齐地分割 CPU,并确保这些容器的持续性能。

另外还可以将前两个中的一个与最后两个之一相结合,即请求一组 CPU,然后进一步限制这些 CPU 的 CPU 时间。

除此之外,我们还有一个通用的优先级调节方式,可以告诉调度器当你处于负载状态时,两个争夺资源的容器谁会取得胜利。

内存

内存听起来很简单,就是给我多少 MB 的内存!

它绝对可以那么简单。 我们支持这种限制以及基于百分比的请求,比如给我 10% 的主机内存!

另外我们在上层支持一些额外的东西。 例如,你可以选择在每个容器上打开或者关闭 swap,如果打开,还可以设置优先级,以便你可以选择哪些容器先将内存交换到磁盘!

内存限制默认是“hard”。 也就是说,当内存耗尽时,内核将会开始杀掉你的那些进程。

或者,你可以将强制策略设置为“soft”,在这种情况下,只要没有别的进程的情况下,你将被允许使用尽可能多的内存。一旦别的进程想要这块内存,你将无法分配任何内存,直到你低于你的限制或者主机内存再次有空余。

网络 I/O

网络 I/O 可能是我们看起来最简单的限制,但是相信我,实现真的不简单!

我们支持两种限制。 第一个是对网络接口的速率限制。你可以设置入口和出口的限制,或者只是设置“最大”限制然后应用到出口和入口。这个只支持“桥接”和“p2p”类型接口。

第二种是全局网络 I/O 优先级,仅当你的网络接口趋于饱和的时候再使用。

块 I/O

我把最古怪的放在最后。对于用户看起来它可能简单,但有一些情况下,它的结果并不会和你的预期一样。

我们在这里支持的基本上与我在网络 I/O 中描述的相同。

你可以直接设置磁盘的读写 IO 的频率和速率,并且有一个全局的块 I/O 优先级,它会通知 I/O 调度程序更倾向哪个。

古怪的是如何设置以及在哪里应用这些限制。不幸的是,我们用于实现这些功能的底层使用的是完整的块设备。这意味着我们不能为每个路径设置每个分区的 I/O 限制。

这也意味着当使用可以支持多个块设备映射到指定的路径(带或者不带 RAID)的 ZFS 或 btrfs 时,我们并不知道这个路径是哪个块设备提供的。

这意味着,完全有可能,实际上确实有可能,容器使用的多个磁盘挂载点(绑定挂载或直接挂载)可能来自于同一个物理磁盘。

这就使限制变得很奇怪。为了使限制生效,LXD 具有猜测给定路径所对应块设备的逻辑,这其中包括询问 ZFS 和 btrfs 工具,甚至可以在发现一个文件系统中循环挂载的文件时递归地找出它们。

这个逻辑虽然不完美,但通常会找到一组应该应用限制的块设备。LXD 接着记录并移动到下一个路径。当遍历完所有的路径,然后到了非常奇怪的部分。它会平均你为相应块设备设置的限制,然后应用这些。

这意味着你将在容器中“平均”地获得正确的速度,但这也意味着你不能对来自同一个物理磁盘的“/fast”和一个“/slow”目录应用不同的速度限制。 LXD 允许你设置它,但最后,它会给你这两个值的平均值。

它怎么工作?

除了网络限制是通过较旧但是良好的“tc”实现的,上述大多数限制是通过 Linux 内核的 cgroup API 来实现的。

LXD 在启动时会检测你在内核中启用了哪些 cgroup,并且将只应用你的内核支持的限制。如果你缺少一些 cgroup,守护进程会输出警告,接着你的 init 系统将会记录这些。

在 Ubuntu 16.04 上,默认情况下除了内存交换审计外将会启用所有限制,内存交换审计需要你通过swapaccount = 1这个内核引导参数来启用。

应用这些限制

上述所有限制都能够直接或者用某个配置文件应用于容器。容器范围的限制可以使用:

lxc config set CONTAINER KEY VALUE

或对于配置文件设置:

lxc profile set PROFILE KEY VALUE

当指定特定设备时:

lxc config device set CONTAINER DEVICE KEY VALUE

或对于配置文件设置:

lxc profile device set PROFILE DEVICE KEY VALUE

有效配置键、设备类型和设备键的完整列表可以看这里

CPU

要限制使用任意两个 CPU 核心可以这么做:

lxc config set my-container limits.cpu 2

要指定特定的 CPU 核心,比如说第二和第四个:

lxc config set my-container limits.cpu 1,3

更加复杂的情况还可以设置范围:

lxc config set my-container limits.cpu 0-3,7-11

限制实时生效,你可以看下面的例子:

stgraber@dakara:~$ lxc exec zerotier -- cat /proc/cpuinfo | grep ^proces
processor : 0
processor : 1
processor : 2
processor : 3
stgraber@dakara:~$ lxc config set zerotier limits.cpu 2
stgraber@dakara:~$ lxc exec zerotier -- cat /proc/cpuinfo | grep ^proces
processor : 0
processor : 1

注意,为了避免完全混淆用户空间,lxcfs 会重排 /proc/cpuinfo 中的条目,以便没有错误。

就像 LXD 中的一切,这些设置也可以应用在配置文件中:

stgraber@dakara:~$ lxc exec snappy -- cat /proc/cpuinfo | grep ^proces
processor : 0
processor : 1
processor : 2
processor : 3
stgraber@dakara:~$ lxc profile set default limits.cpu 3
stgraber@dakara:~$ lxc exec snappy -- cat /proc/cpuinfo | grep ^proces
processor : 0
processor : 1
processor : 2

要限制容器使用 10% 的 CPU 时间,要设置下 CPU allowance:

lxc config set my-container limits.cpu.allowance 10%

或者给它一个固定的 CPU 时间切片:

lxc config set my-container limits.cpu.allowance 25ms/200ms

最后,要将容器的 CPU 优先级调到最低:

lxc config set my-container limits.cpu.priority 0

内存

要直接应用内存限制运行下面的命令:

lxc config set my-container limits.memory 256MB

(支持的后缀是 KB、MB、GB、TB、PB、EB)

要关闭容器的内存交换(默认启用):

lxc config set my-container limits.memory.swap false

告诉内核首先交换指定容器的内存:

lxc config set my-container limits.memory.swap.priority 0

如果你不想要强制的内存限制:

lxc config set my-container limits.memory.enforce soft

磁盘和块 I/O

不像 CPU 和内存,磁盘和 I/O 限制是直接作用在实际的设备上的,因此你需要编辑原始设备或者屏蔽某个具体的设备。

要设置磁盘限制(需要 btrfs 或者 ZFS):

lxc config device set my-container root size 20GB

比如:

stgraber@dakara:~$ lxc exec zerotier -- df -h /
Filesystem                        Size Used Avail Use% Mounted on
encrypted/lxd/containers/zerotier 179G 542M  178G   1% /
stgraber@dakara:~$ lxc config device set zerotier root size 20GB
stgraber@dakara:~$ lxc exec zerotier -- df -h /
Filesystem                       Size  Used Avail Use% Mounted on
encrypted/lxd/containers/zerotier 20G  542M   20G   3% /

要限制速度,你可以:

lxc config device set my-container root limits.read 30MB
lxc config device set my-container root.limits.write 10MB

或者限制 IO 频率:

lxc config device set my-container root limits.read 20Iops
lxc config device set my-container root limits.write 10Iops

最后你在一个过量使用的繁忙系统上,你或许想要:

lxc config set my-container limits.disk.priority 10

将那个容器的 I/O 优先级调到最高。

网络 I/O

只要机制可用,网络 I/O 基本等同于块 I/O。

比如:

stgraber@dakara:~$ lxc exec zerotier -- wget http://speedtest.newark.linode.com/100MB-newark.bin -O /dev/null
--2016-03-26 22:17:34-- http://speedtest.newark.linode.com/100MB-newark.bin
Resolving speedtest.newark.linode.com (speedtest.newark.linode.com)... 50.116.57.237, 2600:3c03::4b
Connecting to speedtest.newark.linode.com (speedtest.newark.linode.com)|50.116.57.237|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 104857600 (100M) [application/octet-stream]
Saving to: '/dev/null'

/dev/null 100%[===================>] 100.00M 58.7MB/s in 1.7s 

2016-03-26 22:17:36 (58.7 MB/s) - '/dev/null' saved [104857600/104857600]

stgraber@dakara:~$ lxc profile device set default eth0 limits.ingress 100Mbit
stgraber@dakara:~$ lxc profile device set default eth0 limits.egress 100Mbit
stgraber@dakara:~$ lxc exec zerotier -- wget http://speedtest.newark.linode.com/100MB-newark.bin -O /dev/null
--2016-03-26 22:17:47-- http://speedtest.newark.linode.com/100MB-newark.bin
Resolving speedtest.newark.linode.com (speedtest.newark.linode.com)... 50.116.57.237, 2600:3c03::4b
Connecting to speedtest.newark.linode.com (speedtest.newark.linode.com)|50.116.57.237|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 104857600 (100M) [application/octet-stream]
Saving to: '/dev/null'

/dev/null 100%[===================>] 100.00M 11.4MB/s in 8.8s 

2016-03-26 22:17:56 (11.4 MB/s) - '/dev/null' saved [104857600/104857600]

这就是如何将一个千兆网的连接速度限制到仅仅 100Mbit/s 的!

和块 I/O 一样,你可以设置一个总体的网络优先级:

lxc config set my-container limits.network.priority 5

获取当前资源使用率

LXD API 可以导出目前容器资源使用情况的一点信息,你可以得到:

  • 内存:当前、峰值、目前内存交换和峰值内存交换
  • 磁盘:当前磁盘使用率
  • 网络:每个接口传输的字节和包数。

另外如果你使用的是非常新的 LXD(在写这篇文章时的 git 版本),你还可以在lxc info中得到这些信息:

stgraber@dakara:~$ lxc info zerotier
Name: zerotier
Architecture: x86_64
Created: 2016/02/20 20:01 UTC
Status: Running
Type: persistent
Profiles: default
Pid: 29258
Ips:
 eth0: inet 172.17.0.101
 eth0: inet6 2607:f2c0:f00f:2700:216:3eff:feec:65a8
 eth0: inet6 fe80::216:3eff:feec:65a8
 lo: inet 127.0.0.1
 lo: inet6 ::1
 lxcbr0: inet 10.0.3.1
 lxcbr0: inet6 fe80::f0bd:55ff:feee:97a2
 zt0: inet 29.17.181.59
 zt0: inet6 fd80:56c2:e21c:0:199:9379:e711:b3e1
 zt0: inet6 fe80::79:e7ff:fe0d:5123
Resources:
 Processes: 33
 Disk usage:
  root: 808.07MB
 Memory usage:
  Memory (current): 106.79MB
  Memory (peak): 195.51MB
  Swap (current): 124.00kB
  Swap (peak): 124.00kB
 Network usage:
  lxcbr0:
   Bytes received: 0 bytes
   Bytes sent: 570 bytes
   Packets received: 0
   Packets sent: 0
  zt0:
   Bytes received: 1.10MB
   Bytes sent: 806 bytes
   Packets received: 10957
   Packets sent: 10957
  eth0:
   Bytes received: 99.35MB
   Bytes sent: 5.88MB
   Packets received: 64481
   Packets sent: 64481
  lo:
   Bytes received: 9.57kB
   Bytes sent: 9.57kB
   Packets received: 81
   Packets sent: 81
Snapshots:
 zerotier/blah (taken at 2016/03/08 23:55 UTC) (stateless)

总结

LXD 团队花费了几个月的时间来迭代我们使用的这些限制的语言。 它是为了在保持强大和功能明确的基础上同时保持简单。

实时地应用这些限制和通过配置文件继承,使其成为一种非常强大的工具,可以在不影响正在运行的服务的情况下实时管理服务器上的负载。

更多信息

LXD 的主站在: https://linuxcontainers.org/lxd

LXD 的 GitHub 仓库: https://github.com/lxc/lxd

LXD 的邮件列表:https://lists.linuxcontainers.org

LXD 的 IRC 频道:#lxcontainers on irc.freenode.net

如果你不想在你的机器上安装LXD,你可以在线尝试下


作者简介:我是 Stéphane Graber。我是 LXC 和 LXD 项目的领导者,目前在加拿大魁北克蒙特利尔的家所在的Canonical 有限公司担任 LXD 的技术主管。


via: https://www.stgraber.org/2016/03/26/lxd-2-0-resource-control-412/

作者:Stéphane Graber 译者:geekpi 校对:wxy

本文由 LCTT 组织翻译,Linux中国 荣誉推出

这是 LXD 2.0 系列介绍文章的第三篇博客。

  1. LXD 入门
  2. 安装与配置
  3. 你的第一个 LXD 容器
  4. 资源控制
  5. 镜像管理
  6. 远程主机及容器迁移
  7. LXD 中的 Docker
  8. LXD 中的 LXD
  9. 实时迁移
  10. LXD 和 Juju
  11. LXD 和 OpenStack
  12. 调试,及给 LXD 做贡献

由于在管理 LXD 容器时涉及到大量的命令,所以这篇文章的篇幅是比较长的,如果你更喜欢使用同样的命令来快速的一步步实现整个过程,你可以尝试我们的在线示例

创建并启动一个新的容器

正如我在先前的文章中提到的一样,LXD 命令行客户端预配置了几个镜像源。Ubuntu 的所有发行版和架构平台全都提供了官方镜像,但是对于其他的发行版也有大量的非官方镜像,那些镜像都是由社区制作并且被 LXC 上游贡献者所维护。

Ubuntu

如果你想要支持最为完善的 Ubuntu 版本,你可以按照下面的去做:

lxc launch ubuntu:

注意,这里意味着会随着 Ubuntu LTS 的发布而变化。因此,如果用于脚本,你需要指明你具体安装的版本(参见下面)。

Ubuntu14.04 LTS

得到最新更新的、已经测试过的、稳定的 Ubuntu 14.04 LTS 镜像,你可以简单的执行:

lxc launch ubuntu:14.04

在该模式下,会指定一个随机的容器名。

如果你更喜欢指定一个你自己的名字,你可以这样做:

lxc launch ubuntu:14.04 c1

如果你想要指定一个特定的体系架构(非主流平台),比如 32 位 Intel 镜像,你可以这样做:

lxc launch ubuntu:14.04/i386 c2

当前的 Ubuntu 开发版本

上面使用的“ubuntu:”远程仓库只会给你提供官方的并经过测试的 Ubuntu 镜像。但是如果你想要未经测试过的日常构建版本,开发版可能对你来说是合适的,你需要使用“ubuntu-daily:”远程仓库。

lxc launch ubuntu-daily:devel c3

在这个例子中,将会自动选中最新的 Ubuntu 开发版本。

你也可以更加精确,比如你可以使用代号名:

lxc launch ubuntu-daily:xenial c4

最新的Alpine Linux

Alpine 镜像可以在“Images:”远程仓库中找到,通过如下命令执行:

lxc launch images:alpine/3.3/amd64 c5

其他

全部的 Ubuntu 镜像列表可以这样获得:

lxc image list ubuntu:
lxc image list ubuntu-daily:

全部的非官方镜像:

lxc image list images:

某个给定的原程仓库的全部别名(易记名称)可以这样获得(比如对于“ubuntu:”远程仓库):

lxc image alias list ubuntu:

创建但不启动一个容器

如果你想创建一个容器或者一批容器,但是你不想马上启动它们,你可以使用lxc init替换掉lxc launch。所有的选项都是相同的,唯一的不同就是它并不会在你创建完成之后启动容器。

lxc init ubuntu:

关于你的容器的信息

列出所有的容器

要列出你的所有容器,你可以这样这做:

lxc list

有大量的选项供你选择来改变被显示出来的列。在一个拥有大量容器的系统上,默认显示的列可能会有点慢(因为必须获取容器中的网络信息),你可以这样做来避免这种情况:

lxc list --fast

上面的命令显示了另外一套列的组合,这个组合在服务器端需要处理的信息更少。

你也可以基于名字或者属性来过滤掉一些东西:

stgraber@dakara:~$ lxc list security.privileged=true
+------+---------+---------------------+-----------------------------------------------+------------+-----------+
| NAME |  STATE  |        IPV4         |                       IPV6                    |    TYPE    | SNAPSHOTS |
+------+---------+---------------------+-----------------------------------------------+------------+-----------+
| suse | RUNNING | 172.17.0.105 (eth0) | 2607:f2c0:f00f:2700:216:3eff:fef2:aff4 (eth0) | PERSISTENT | 0         |
+------+---------+---------------------+-----------------------------------------------+------------+-----------+

在这个例子中,只有那些特权容器(禁用了用户命名空间)才会被列出来。

stgraber@dakara:~$ lxc list --fast alpine
+-------------+---------+--------------+----------------------+----------+------------+
|    NAME     |  STATE  | ARCHITECTURE |      CREATED AT      | PROFILES |    TYPE    |
+-------------+---------+--------------+----------------------+----------+------------+
| alpine      | RUNNING | x86_64       | 2016/03/20 02:11 UTC | default  | PERSISTENT |
+-------------+---------+--------------+----------------------+----------+------------+
| alpine-edge | RUNNING | x86_64       | 2016/03/20 02:19 UTC | default  | PERSISTENT |
+-------------+---------+--------------+----------------------+----------+------------+

在这个例子中,只有在名字中带有“alpine”的容器才会被列出来(也支持复杂的正则表达式)。

获取容器的详细信息

由于 list 命令显然不能以一种友好的可读方式显示容器的所有信息,因此你可以使用如下方式来查询单个容器的信息:

lxc info <container>

例如:

stgraber@dakara:~$ lxc info zerotier
Name: zerotier
Architecture: x86_64
Created: 2016/02/20 20:01 UTC
Status: Running
Type: persistent
Profiles: default
Pid: 31715
Processes: 32
Ips:
 eth0: inet 172.17.0.101
 eth0: inet6 2607:f2c0:f00f:2700:216:3eff:feec:65a8
 eth0: inet6 fe80::216:3eff:feec:65a8
 lo: inet 127.0.0.1
 lo: inet6 ::1
 lxcbr0: inet 10.0.3.1
 lxcbr0: inet6 fe80::c0a4:ceff:fe52:4d51
 zt0: inet 29.17.181.59
 zt0: inet6 fd80:56c2:e21c:0:199:9379:e711:b3e1
 zt0: inet6 fe80::79:e7ff:fe0d:5123
Snapshots:
 zerotier/blah (taken at 2016/03/08 23:55 UTC) (stateless)

生命周期管理命令

这些命令对于任何容器或者虚拟机管理器或许都是最普通的命令,但是它们仍然需要讲到。

所有的这些命令在批量操作时都能接受多个容器名。

启动

启动一个容器就向下面一样简单:

lxc start <container>

停止

停止一个容器可以这样来完成:

lxc stop <container>

如果容器不合作(即没有对发出的 SIGPWR 信号产生回应),这时候,你可以使用下面的方式强制执行:

lxc stop <container> --force

重启

通过下面的命令来重启一个容器:

lxc restart <container>

如果容器不合作(即没有对发出的 SIGINT 信号产生回应),你可以使用下面的方式强制执行:

lxc restart <container> --force

暂停

你也可以“暂停”一个容器,在这种模式下,所有的容器任务将会被发送相同的 SIGSTOP 信号,这也意味着它们将仍然是可见的,并且仍然会占用内存,但是它们不会从调度程序中得到任何的 CPU 时间片。

如果你有一个很占用 CPU 的容器,而这个容器需要一点时间来启动,但是你却并不会经常用到它。这时候,你可以先启动它,然后将它暂停,并在你需要它的时候再启动它。

lxc pause <container>

删除

最后,如果你不需要这个容器了,你可以用下面的命令删除它:

lxc delete <container>

注意,如果容器还处于运行状态时你将必须使用“-force”。

容器的配置

LXD 拥有大量的容器配置设定,包括资源限制,容器启动控制以及对各种设备是否允许访问的配置选项。完整的清单因为太长所以并没有在本文中列出,但是,你可以从[这里]获取它。

就设备而言,LXD 当前支持下面列出的这些设备类型:

  • 磁盘 既可以是一块物理磁盘,也可以只是一个被挂挂载到容器上的分区,还可以是一个来自主机的绑定挂载路径。
  • 网络接口卡 一块网卡。它可以是一块桥接的虚拟网卡,或者是一块点对点设备,还可以是一块以太局域网设备或者一块已经被连接到容器的真实物理接口。
  • unix 块设备 一个 UNIX 块设备,比如 /dev/sda
  • unix 字符设备 一个 UNIX 字符设备,比如 /dev/kvm
  • none 这种特殊类型被用来隐藏那种可以通过配置文件被继承的设备。

配置 profile 文件

所有可用的配置文件列表可以这样获取:

lxc profile list

为了看到给定配置文件的内容,最简单的方式是这样做:

lxc profile show <profile>

你可能想要改变文件里面的内容,可以这样做:

lxc profile edit <profile>

你可以使用如下命令来改变应用到给定容器的配置文件列表:

lxc profile apply <container> <profile1>,<profile2>,<profile3>,...

本地配置

有些配置是某个容器特定的,你并不想将它放到配置文件中,你可直接对容器设置它们:

lxc config edit <container>

上面的命令做的和“profile edit”命令是一样。

如果不想在文本编辑器中打开整个文件的内容,你也可以像这样修改单独的配置:

lxc config set <container> <key> <value>

或者添加设备,例如:

lxc config device add my-container kvm unix-char path=/dev/kvm

上面的命令将会为名为“my-container”的容器设置一个 /dev/kvm 项。

对一个配置文件使用lxc profile setlxc profile device add命令也能实现上面的功能。

读取配置

你可以使用如下命令来读取容器的本地配置:

lxc config show <container>

或者得到已经被展开了的配置(包含了所有的配置值):

lxc config show --expanded <container>

例如:

stgraber@dakara:~$ lxc config show --expanded zerotier
name: zerotier
profiles:
- default
config:
 security.nesting: "true"
 user.a: b
 volatile.base_image: a49d26ce5808075f5175bf31f5cb90561f5023dcd408da8ac5e834096d46b2d8
 volatile.eth0.hwaddr: 00:16:3e:ec:65:a8
 volatile.last_state.idmap: '[{"Isuid":true,"Isgid":false,"Hostid":100000,"Nsid":0,"Maprange":65536},{"Isuid":false,"Isgid":true,"Hostid":100000,"Nsid":0,"Maprange":65536}]'
devices:
 eth0:
  name: eth0
  nictype: macvlan
  parent: eth0
  type: nic
  limits.ingress: 10Mbit
  limits.egress: 10Mbit
 root:
  path: /
  size: 30GB
  type: disk
 tun:
  path: /dev/net/tun
  type: unix-char
ephemeral: false

这样做可以很方便的检查有哪些配置属性被应用到了给定的容器。

实时配置更新

注意,除非在文档中已经被明确指出,否则所有的配置值和设备项的设置都会对容器实时发生影响。这意味着在不重启正在运行的容器的情况下,你可以添加和移除某些设备或者修改安全配置文件。

获得一个 shell

LXD 允许你直接在容器中执行任务。最常用的做法是在容器中得到一个 shell 或者执行一些管理员任务。

和 SSH 相比,这样做的好处是你不需要容器是网络可达的,也不需要任何软件和特定的配置。

执行环境

与 LXD 在容器内执行命令的方式相比,有一点是不同的,那就是 shell 并不是在容器中运行。这也意味着容器不知道使用的是什么样的 shell,以及设置了什么样的环境变量和你的家目录在哪里。

通过 LXD 来执行命令总是使用最小的路径环境变量设置,并且 HOME 环境变量必定为 /root,以容器的超级用户身份来执行(即 uid 为 0,gid 为 0)。

其他的环境变量可以通过命令行来设置,或者在“environment.”配置中设置成永久环境变量。

执行命令

在容器中获得一个 shell 可以简单的执行下列命令得到:

lxc exec <container> bash

当然,这样做的前提是容器内已经安装了 bash。

更复杂的命令要求使用分隔符来合理分隔参数。

lxc exec <container> -- ls -lh /

如果想要设置或者重写变量,你可以使用“-env”参数,例如:

stgraber@dakara:~$ lxc exec zerotier --env mykey=myvalue env | grep mykey
mykey=myvalue

管理文件

因为 LXD 可以直接访问容器的文件系统,因此,它可以直接读取和写入容器中的任意文件。当我们需要提取日志文件或者与容器传递文件时,这个特性是很有用的。

从容器中取回一个文件

想要从容器中获得一个文件,简单的执行下列命令:

lxc file pull <container>/<path> <dest>

例如:

stgraber@dakara:~$ lxc file pull zerotier/etc/hosts hosts

或者将它读取到标准输出:

stgraber@dakara:~$ lxc file pull zerotier/etc/hosts -
127.0.0.1 localhost

# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts

向容器发送一个文件

发送以另一种简单的方式完成:

lxc file push <source> <container>/<path>

直接编辑一个文件

编辑是一个方便的功能,其实就是简单的提取一个给定的路径,在你的默认文本编辑器中打开它,在你关闭编辑器时会自动将编辑的内容保存到容器。

lxc file edit <container>/<path>

快照管理

LXD 允许你对容器执行快照功能并恢复它。快照包括了容器在某一时刻的完整状态(如果-stateful被使用的话将会包括运行状态),这也意味着所有的容器配置,容器设备和容器文件系统也会被保存。

创建一个快照

你可以使用下面的命令来执行快照功能:

lxc snapshot <container>

命令执行完成之后将会生成名为snapX(X 为一个自动增长的数)的记录。

除此之外,你还可以使用如下命令命名你的快照:

lxc snapshot <container> <snapshot name>

列出所有的快照

一个容器的所有快照的数量可以使用lxc list来得到,但是具体的快照列表只能执行lxc info命令才能看到。

lxc info <container>

恢复快照

为了恢复快照,你可以简单的执行下面的命令:

lxc restore <container> <snapshot name>

给快照重命名

可以使用如下命令来给快照重命名:

lxc move <container>/<snapshot name> <container>/<new snapshot name>

从快照中创建一个新的容器

你可以使用快照来创建一个新的容器,而这个新的容器除了一些可变的信息将会被重置之外(例如 MAC 地址)其余所有信息都将和快照完全相同。

lxc copy <source container>/<snapshot name> <destination container>

删除一个快照

最后,你可以执行下面的命令来删除一个快照:

lxc delete <container>/<snapshot name>

克隆并重命名

得到一个纯净的发行版镜像总是让人感到愉悦,但是,有时候你想要安装一系列的软件到你的容器中,这时,你需要配置它然后将它分支成多个其他的容器。

复制一个容器

为了复制一个容器并有效的将它克隆到一个新的容器中,你可以执行下面的命令:

lxc copy <source container> <destination container>

目标容器在所有方面将会完全和源容器等同。除了新的容器没有任何源容器的快照以及一些可变值将会被重置之外(例如 MAC 地址)。

移动一个快照

LXD 允许你复制容器并在主机之间移动它。但是,关于这一点将在后面的文章中介绍。

现在,“move”命令将会被用作给容器重命名。

lxc move <old name> <new name>

唯一的要求就是当容器应该被停止,容器内的任何事情都会被保存成它本来的样子,包括可变化的信息(类似 MAC 地址等)。

结论

这篇如此长的文章介绍了大多数你可能会在日常操作中使用到的命令。

很显然,这些如此之多的命令都会有不少选项,可以让你的命令更加有效率,或者可以让你指定你的 LXD 容器的某个具体方面。最好的学习这些命令的方式就是深入学习它们的帮助文档( -help)。

更多信息

如果你不想或者不能在你的机器上安装 LXD,你可以试试在线版本!


作者简介:我是 Stéphane Graber。我是 LXC 和 LXD 项目的领导者,目前在加拿大魁北克蒙特利尔的家所在的Canonical 有限公司担任 LXD 的技术主管。


来自于:https://www.stgraber.org/2016/03/19/lxd-2-0-your-first-lxd-container-312/ 作者:Stéphane Graber 译者:kylepeng93 校对:wxy

本文由 LCTT 原创翻译,Linux中国 荣誉推出