返回新闻概览

LXC 3.0.0 已发布

2018 年 3 月 27 日

简介

LXC 团队很高兴宣布发布 LXC 3.0.0!

这是自 LXC 2.1.0 版本发布以来 6 个多月密集工作的成果。
这是 LXC 项目的第三个 LTS 版本,将得到支持到 2023 年 6 月。

主要变更

所有命令都支持 lxc-start <container-name> 语法

LXC 工具现在支持在不使用 -n 命令行标志的情况下传递容器名称。例如,您现在可以运行

chb@conventiont|~
> lxc-start xenial

chb@conventiont|~
> lxc-info xenial
Name:           xenial
State:          RUNNING
PID:            12765
Memory use:     15.24 MiB
KMem use:       3.72 MiB
Link:           veth99VMO3
 TX bytes:      858 bytes
 RX bytes:      2.49 KiB
 Total bytes:   3.33 KiB

chb@conventiont|~
> lxc-attach xenial
root@xenial:/# exit

chb@conventiont|~
> lxc-stop xenial

asciicast

删除对所有旧版配置键的支持

从 LXC 3.0 开始,所有旧版配置键都不受支持。
可以从 LXC 2.1 版本发布公告 中获取已弃用键及其新对应项的完整列表。

可以使用 lxc-update-config 工具将旧的、现在无效的配置转换为新格式。

完全支持统一的 cgroup 层次结构,包括资源限制

我们很高兴地宣布,LXC 将完全支持新的统一 cgroup 层次结构(或 cgroup v2,cgroup2)。为此,我们还引入了一个新的配置键 lxc.cgroup2.[controller name] 来配置统一的 cgroup 层次结构上的 cgroup 限制。
有关详细信息,您可以阅读 这篇博文

删除旧版 cgmanager 和 cgfs cgroup 驱动程序

LXC 删除了 cgmanagercgfs 旧版 cgroup 驱动程序,在此过程中清理了许多代码。有关详细信息,您可以阅读 这篇博文

将模板和语言绑定拆分

LXC 删除了旧的基于模板的容器构建系统,转而支持新的项目 distrobuilder。有关详细信息,您可以阅读 这篇博文

请注意,为了简化迁移,旧的模板脚本和配置文件仍然可以在 单独的存储库 中使用,并与 LXC 3.0.0 一起作为 lxc-templates 发布。

以下模板保留在 LXC 3.0 树中,并得到完全支持
- lxc-busybox(主要用于测试)
这是一个非常小的模板,可用于设置 busybox 容器。只要找到 busybox 二进制文件,您就可以随时构建自己的最小特权或非特权系统或应用程序容器映像;不需要网络或任何其他依赖项。您需要做的就是
lxc-create c3 -t busybox

[<img src='https://discuss.linuxcontainers.org/uploads/default/original/1X/3719f8fe09f03069e3741d1ba64141f3d9540b54.png' alt='asciicast'>](https://asciinema.org/a/165788)
  • lxc-download(下载我们预先构建的映像)
    此模板允许您从我们的映像服务器下载预先构建的映像。这很可能是目前大多数用户用来创建非特权容器的方式。
  • lxc-local(导入本地构建的映像(例如,来自 distrobuilder))
    这是一个新模板,它使用标准的 LXCLXD 系统容器映像。可以使用以下命令创建容器:
    lxc-create c1 -t local -- --metadata /path/to/meta.tar.xz --fstree /path/to/rootfs.tar.xz

其中 --metadata 标志需要指向包含容器元数据的文件。这只是任何预先构建的 LXC 容器映像附带的标准 meta.tar.xz 文件。--fstree 标志需要指向一个文件系统树。然后创建容器只需

asciicast

  • lxc-oci(使用 OCI 应用程序容器映像)
    这是可以用来下载和运行 OCI 容器的模板。使用它很简单,只需
    lxc-create c2 -t oci -- --url docker://alpine
    这是另一个 asciicast

asciicast

将 cgroup PAM 模块移入 LXC 树

LXC 一直以来都支持完全非特权容器,即非特权用户运行的非特权容器。这样做的一个重要部分是一个 PAM 模块,该模块为非特权用户创建可写 cgroup。此模块已从 LXCFS 树移入 LXC 树本身,以便用户更容易运行完全非特权容器。
有关详细信息,您可以阅读 这篇博文

新的 OCI 模板

这增加了从 OCI 格式创建应用程序容器的支持。
示例
- 从 ../oci 中的本地 OCI 布局创建容器

    sudo lxc-create -t oci -n a1 -- -u oci:../oci:alpine
  • 从 docker hub 拉取创建容器。
    sudo lxc-create -t oci -n u1 -- -u docker://ubuntu
    

asciicast

URL 的指定格式与 skopeo copy 相同。

用于控制台日志记录的简单高效环形缓冲区

LXC 支持将容器的控制台日志记录到文件。这有一个不幸的副作用,即允许容器中的用户有效地写入他们想要的任何数据到主机,这可能会绕过容器中设置的配额。

此基本实现也很难查询,因为必须读取一个可能非常大的文件,而且该文件在容器重启时不会重置。

LXC 3.0 现在为控制台日志记录引入了环形缓冲区。此内存缓冲区的大小有限,可以通过 LXC API 中的新函数进行查询。它可以随时重置,并且可以在容器关闭时转储到磁盘。

允许 seccomp 根据参数过滤系统调用

为了支持根据参数过滤系统调用,seccomp 版本 2 规范扩展为以下形式

syscall_name action [index,value,op,valueTwo] [index,value,op]...

其中元组 [index,value,valueTwo,op] 的参数具有以下含义

  1. index (uint32_t)
    系统调用参数的索引。
  2. value (uint64_t)
    由 "index" 指定的系统调用参数的值。
  3. valueTwo (uint64_t,可选)
    由 "index" 指定的系统调用参数的值。此可选值
    仅在与 SCMP_CMP_MASKED_EQ 结合使用时有效。
  4. op (char *)
    系统调用参数的操作符。有效操作符是常量

    • SCMP_CMP_NE(!=)
    • SCMP_CMP_LE(<=)
    • SCMP_CMP_EQ(==)
    • SCMP_CMP_GE(>=)
    • SCMP_CMP_GT(>)
    • SCMP_CMP_MASKED_EQ(&=)

    libseccomp >= v2.3.2 定义。
    为了方便起见,liblxc 还理解 libseccomp 常量后面的括号中指示的标准操作符表示法作为等效表示法。
    请注意,为同一个系统调用指定多个条目是合法的。

扩展的 seccomp 版本 2 配置文件的示例如下:

2
blacklist allow
reject_force_umount  # comment this to allow umount -f;  not recommended
[all]
kexec_load errno 1 [0,1,SCMP_CMP_LE][3,1,==][5,1,SCMP_CMP_MASKED_EQ,1]
open_by_handle_at errno 1
init_module errno 1
finit_module errno 1
delete_module errno 1
unshare errno 9 [0,0x10000000,SCMP_CMP_EQ]
unshare errno 2 [0,0x20000000,SCMP_CMP_EQ]

支持守护进程化应用程序容器

自 2008 年首次发布以来,LXC 一直通过最小化的 init 系统运行应用程序容器。PID 命名空间需要一个能够处理信号和收集退出子进程的功能性 init 系统。这就是为什么应用程序容器始终将应用程序作为第二个进程运行。
新的 LXC 版本现在还允许您运行基本上任何守护进程化的进程
asciicast

lxc-* 工具中删除所有内部符号

lxc-* 工具现在完全依赖于公共 LXC API。

处理 /proc 使用 hidepid={1,2} 属性挂载

这使您能够在主机 /proc 文件系统使用 hidepid={1,2} 选项挂载时附加到容器,该选项限制了对 /proc/PID 目录的访问。

支持挂载的挂载传播

这增加了对挂载传播(privatesharedslaveunbindablerprivatersharedrslaverunbindable)的支持,以通过 lxc.mount.entrylxc.mount.fstab 指定的挂载条目。

强化线程安全

  • 删除了所有互斥锁
  • _exit() 替换了子进程中的所有 exit() 调用
  • F_OFD_SETLKF_OFD_SETLKWF_OFD_GETLK 打开文件描述符锁替换了所有 F_SETLKF_SETLKWF_GETLK
    如果打开文件描述符锁不可用,LXC 将回退到 BSD flock()

更多详细信息可以从 这篇博文 中获取。

删除 aufs 存储驱动程序

aufs 存储驱动程序已从 LXC 2.1 开始弃用,现在正式删除。

编码风格和代码清理

代码库已根据我们的书面 编码风格 进行了广泛的清理。

新的配置键

lxc.cgroup2.[控制器名称]

指定要设置在统一 cgroup 层次结构上的控制组值。控制器名称是控制组的字面名称。允许的名称及其值的语法不受 LXC 限制,而是取决于启动容器时运行的 Linux 内核的功能,例如,lxc.cgroup2.memory.high

lxc.hook.version

通过环境变量将参数以新样式传递,设置为 1,否则设置为 0 以将它们作为参数传递。此设置影响所有传统上作为脚本参数传递的挂钩参数。具体来说,它影响容器名称、部分(例如“lxc”、“net”)和挂钩类型(例如“clone”、“mount”、“pre-mount”)参数。如果使用新样式挂钩,则参数将作为环境变量可用。容器名称将设置为 LXC_NAME。(这独立于用于此配置项的值进行设置。)部分将设置为 LXC_HOOK_SECTION,挂钩类型将设置为 LXC_HOOK_TYPE。它还影响如何传递指向容器命名空间的文件描述符的路径。如果设置为 1,则每个命名空间将设置一个单独的环境变量 LXC_[命名空间标识符]_NS。如果设置为 0,则路径将作为参数传递给停止挂钩。

lxc.execute.cmd

从容器根文件系统到默认运行的二进制文件的绝对路径。此配置选项可以设置为指定通过 execute() API 调用启动的应用程序容器的默认二进制文件,并伴随基于系统容器的 lxc.init.cmd 配置键。

lxc.init.cwd

容器内部用作工作目录的绝对路径。

lxc.proc.[proc 文件名]

指定要设置的 proc 文件名。可用的文件名是在 /proc/PID/ 目录下列出的文件名。例如,lxc.proc.oom_score_adj = 10

lxc.console.buffer.size

设置此选项指示 LXC 分配一个内存中的环形缓冲区。容器的控制台输出将写入环形缓冲区。请注意,环形缓冲区必须至少与标准页大小一样大。当传递小于单个页大小的值时,LXC 将分配一个大小为单个页大小的环形缓冲区。页大小通常为 4kB。关键字 auto 将导致 LXC 分配一个大小为 128kB 的环形缓冲区。当手动指定环形缓冲区的大小,值应在转换为字节时为 2 的幂。有效的尺寸前缀为 kBMBGB。(请注意,所有转换都基于 1024 的倍数。这意味着 kb == KiBMB == MiBGB == GiB。)

lxc.console.size

设置此选项指示 LXC 对 lxc.console.logfile 中指定的控制台日志文件的大小设置限制。请注意,日志文件的大小必须至少与标准页大小一样大。当传递小于单个页大小的值时,LXC 将将日志文件的大小设置为单个页大小。页大小通常为 4kB。关键字 auto 将导致 LXC 对日志文件设置 128kB 的限制。当手动指定日志文件的大小,值应在转换为字节时为 2 的幂。有效的尺寸前缀为 kBMBGB。(请注意,所有转换都基于 1024 的倍数。这意味着 kb == KiBMB == MiBGB == GiB。)如果用户希望将控制台环形缓冲区镜像到磁盘,则应将 lxc.console.size 设置为等于 lxc.console.buffer.size

lxc.console.rotate

是否旋转 lxc.console.logfile 中指定的控制台日志文件。

lxc.mount.entryrelative 选项

使用 relative 属性设置的挂载点将被视为相对于挂载的容器根目录。例如,

lxc.mount.entry = /dev/null proc/kcore none bind,relative 0 0

将扩展 dev/null${LXC_ROOTFS_MOUNT}/dev/null,并将它挂载到容器内的 proc/kcore

通过 lxc.mount.auto 指定的 cgroup 挂载的 force 属性

cgroup:mixed:force:

force 选项将导致 LXC 在任何情况下都执行容器的 cgroup 挂载。否则,它类似于 cgroup:mixed。这在 cgroup 命名空间启用时特别有用,在该情况下,LXC 通常会将 cgroup 挂载留给容器的 init 二进制文件,因为这样做是完全安全的。

cgroup:ro:force:

force 选项将导致 LXC 在任何情况下都执行容器的 cgroup 挂载。否则,它类似于 cgroup:ro。这在 cgroup 命名空间启用时特别有用,在该情况下,LXC 通常会将 cgroup 挂载留给容器的 init 二进制文件,因为这样做是完全安全的。

cgroup:rw:force:

force 选项将导致 LXC 在任何情况下都执行容器的 cgroup 挂载。否则,它类似于 cgroup:rw。这在 cgroup 命名空间启用时特别有用,在该情况下,LXC 通常会将 cgroup 挂载留给容器的 init 二进制文件,因为这样做是完全安全的。

cgroup-full:mixed:force:

force 选项将导致 LXC 在任何情况下都执行容器的 cgroup 挂载。否则,它类似于 cgroup-full:mixed。这在 cgroup 命名空间启用时特别有用,在该情况下,LXC 通常会将 cgroup 挂载留给容器的 init 二进制文件,因为这样做是完全安全的。

cgroup-full:ro:force:

force 选项将导致 LXC 在任何情况下都执行容器的 cgroup 挂载。否则,它类似于 cgroup-full:ro。这在 cgroup 命名空间启用时特别有用,在该情况下,LXC 通常会将 cgroup 挂载留给容器的 init 二进制文件,因为这样做是完全安全的。

cgroup-full:rw:force:

force 选项将导致 LXC 在任何情况下都执行容器的 cgroup 挂载。否则,它类似于 cgroup-full:rw。这在 cgroup 命名空间启用时特别有用,在该情况下,LXC 通常会将 cgroup 挂载留给容器的 init 二进制文件,因为这样做是完全安全的。

lxc.namespace.clone

指定容器应该创建的命名空间。要创建的命名空间指定为用空格分隔的列表。每个命名空间都必须对应于 /proc/PID/ns 目录中看到的标准命名空间标识符之一。当 lxc.namespace.clone 未明确设置时,将使用内核和当前配置支持的所有命名空间。

要创建一个新的 mountnetipc 命名空间,请设置 lxc.namespace.clone = mount net ipc

lxc.namespace.keep

指定容器应该从创建它的进程中继承的命名空间。要保留的命名空间指定为用空格分隔的列表。每个命名空间都必须对应于 /proc/PID/ns 目录中看到的标准命名空间标识符之一。lxc.namespace.keep 是一个黑名单选项,即在强制容器必须保留特定命名空间集时很有用。

要保留 networkuseripc 命名空间,请设置 lxc.namespace.keep = user net ipc

请注意,共享 pid 命名空间可能不适用于大多数 init 系统。

请注意,如果容器请求一个新的用户命名空间,并且容器想要继承网络命名空间,它也需要继承用户命名空间。

lxc.namespace.share.[命名空间标识符]

指定要从另一个容器或进程继承的命名空间。[命名空间标识符] 后缀需要替换为 /proc/PID/ns 目录中出现的命名空间之一。

要从另一个进程继承命名空间,请将 lxc.namespace.share.[命名空间标识符] 设置为进程的 PID,例如 lxc.namespace.share.net = 42

要从另一个容器继承命名空间,请将 lxc.namespace.share.[命名空间标识符] 设置为容器的名称,例如 lxc.namespace.share.pid = c3

要从另一个容器继承命名空间,该容器位于与标准 LXC 路径不同的路径中,请将 lxc.namespace.share.[命名空间标识符] 设置为容器的完整路径,例如 lxc.namespace.share.user = /opt/c3

为了继承命名空间,调用者需要对进程或容器具有足够的权限。

请注意,在系统容器之间共享 pid 命名空间可能不适用于大多数 init 系统。

请注意,如果两个进程位于不同的用户命名空间,而其中一个进程想要继承另一个进程的网络命名空间,通常也需要继承用户命名空间。

lxc.sysctl.[内核参数名称]

指定要设置的内核参数。可用的参数是在/proc/sys/下列出的参数。请注意,并非所有 sysctl 都是命名空间的。更改非命名空间 sysctl 会导致系统范围的设置被修改。sysctl(8)。如果未使用任何值,LXC 将清除在此之前指定的参数。

lxc.hook.start-host

在容器设置完成后,并在启动容器 init 之前,在主机命名空间中运行的钩子。

这应该满足几种用例。一个示例是 CNI 的支持。
例如,用以下内容替换根拥有容器中的网络配置

lxc.net.0.type = empty
lxc.hook.start-host = /bin/lxc-start-netns

where /bin/lxc-start-netns contains:

=================================

echo "starting" > /tmp/debug
ip link add host1 type veth peer name peer1
ip link set host1 master lxcbr0
ip link set host1 up
ip link set peer1 netns "${LXC_PID}"
=================================

nic 'peer1' 如预期那样被放置到容器中。
为了使其正常工作,我们将容器 init 的 pid 作为 LXC_PID 传递给环境变量,因为 lxc-info 在那时无法工作。

API 扩展

console_log()

一个新的 API 扩展

    int console_log(struct lxc_container *c, struct lxc_console_log *log);

已添加,它支持与容器新添加的内存中环形缓冲区进行交互。以下结构包含可用的参数和返回值

struct lxc_console_log {
    /* Clear the console log. */
    bool clear;

    /* Retrieve the console log. */
    bool read;

    /* This specifies the maximum size to read from the ringbuffer. Setting
     * it to 0 means that the a read can be as big as the whole ringbuffer.
     * On return callers can check how many bytes were actually read.
     * If "read" and "clear" are set to false and a non-zero value is
     * specified then up to "read_max" bytes of data will be discarded from
     * the ringbuffer.
     */
    uint64_t *read_max;

    /* Data that was read from the ringbuffer. If "read_max" is 0 on return
     * "data" is invalid.
     */
    char *data;
};

reboot2()

这添加了reboot2()作为新的 API 扩展。此函数会正确等待直到重新引导成功。它接受一个超时参数。当设置为> 0时,reboot2()将阻塞,直到达到超时时间,如果将超时时间设置为零,reboot2()将不会阻塞,如果设置为-1reboot2()将无限期地阻塞。

CRIU `migrate() API 调用中的MIGRATE_FEATURE_CHECK

对于像预复制或后复制迁移这样的迁移优化功能,无法通过简单地查看CRIU版本来确定其支持情况。这样的功能依赖于架构/内核/criu 组合,并且CRIU提供了一个功能检查接口来查询它是否受支持。

这添加了一个 LXC 接口,通过migrate() API 调用来查询 CRIU 的这些功能。对于 LXD 中最近的预复制迁移支持,这可用于自动检测是否应该使用预复制迁移。

除了现有的migrate() API 命令之外,它还添加了一个新命令:MIGRATE_FEATURE_CHECK

struct migrate_opts通过成员features_to_check扩展,该成员是一个位掩码,定义了要查询哪些CRIU功能。

目前只支持查询FEATURE_MEM_TRACKFEATURE_LAZY_PAGES功能。

LXC_ATTACH_TERMINAL添加到attach() API 调用中

分配新的终端已移至 API 本身。调用者现在可以设置LXC_ATTACH_TERMINAL以请求附加到从主机的devpts挂载点分配的新的终端,然后附加到容器。

支持和升级

LXC 3.0.0 将得到支持,直到 2023 年 6 月,以及我们目前的 LTS 版本。
LXC 2.0 现在将加入 LXC 1.0,只获得关键的错误修复和安全更新。

我们强烈建议所有 LXC 用户计划升级到 3.0 分支。
由于 libpam-cgfs 向 LXC 的过渡,这应该在升级到 LXCFS 3.0 的同时完成,以避免潜在的冲突。

下载

贡献者

LXC 3.0.0 版本是由总共 42 位贡献者为您带来的。