Stéphane Graber 发布的文章

这是 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 中运行 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/lxd

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

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

LXD 的 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 系列介绍文章的第七篇。

  1. LXD 入门
  2. 安装与配置
  3. 你的第一个 LXD 容器
  4. 资源控制
  5. 镜像管理
  6. 远程主机及容器迁移
  7. LXD 中的 Docker
  8. LXD 中的 LXD
  9. 实时迁移
  10. LXD 和 Juju
  11. LXD 和 OpenStack
  12. 调试,及给 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 将 defaultdocker 配置文件应用于容器。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/lxd

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

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

LXD 的 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 系列介绍文章的第六篇。

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

当两个守护程序之间交互时会变得有些棘手,如复制和移动的情况。

在这种情况下会发生:

  1. 用户运行lxc move foo:c1 c1
  2. 客户端联系 local: 远程以检查是否现有“c1”容器。
  3. 客户端从“foo”获取容器信息。
  4. 客户端从源“foo”守护程序请求迁移令牌。
  5. 客户端将迁移令牌以及源 URL 和“foo”的证书发送到本地 LXD 守护程序以及容器配置和周围设备。
  6. 然后本地 LXD 守护程序使用提供的令牌直接连接到“foo” a) 它连接到第一个控制 websocket b) 它协商文件系统传输协议(zfs 发送/接收,btrfs 发送/接收或者纯 rsync) c) 如果在本地可用,它会解压用于创建源容器的镜像。这是为了避免不必要的数据传输。 d) 然后它会将容器及其任何快照作为增量传输。
  7. 如果成功,客户端会命令“foo”删除源容器。

在线尝试

没有两台机器来尝试远端交互和复制/移动容器?

没有问题,你可以使用我们的 demo 服务。这里甚至还包括了一步步的指导!

额外信息

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

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

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

LXD 的 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 系列介绍文章的第五篇。

  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中国 荣誉推出