使用 Incus 取代 LXD(Incus 的安装、设置和使用方法)

发表于 更新于

前言

Incus 是一个新的容器(LXC)/虚拟机管理器,它的目标是取代 LXD。由于 LXD 实际上已成为 Canonical 公司(Ubuntu 的开发公司)完全掌控的项目,LXC 创始人分叉出了 Incus,并将其纳入为 LXC 社区的一部分。现在的 Incus 的可用性已经很高了,无论对于个人还是企业都是极为有用的工具。

本文将告诉你怎么安装和使用。这是一篇新手向教程。过程十分简单!

Incus vs LXD

目前的 Incus 和 LXD 的功能高度相同,它们基本可以平替对方。考虑到 LXD 不再是一个社区形式的项目,如果能用 Incus 取代,那当然是一件令人舒心的事情。

当然,使用 Incus 不仅仅是考虑到开源精神。实际上 Incus 就是 LXD 的首席维护者兼创始人分叉出来的项目,所以我对 Incus 有非常高的信心。甚至我认为相比 XLD 它的未来会更好。

Incus vs Docker

可能有人会有疑问,同样是管理容器,我为什么不用 Docker?

这是一个好问题,但如果你是 Docker 用户就不算一个合格的提问。因为你足够认识 Docker 的话,就应该知道它是为封装单个应用程序而设计的,并没有模拟虚拟机。严格来说 Docker 容器是一种从泛用性的虚拟化技术中剪裁定制后的效果。在 Docker 容器中需要有一个前台进程存在,Docker 根据这个进程的状态判断是否存活并执行相应的失败恢复策略。

我们通常将一个 Docker 容器视为一个服务,而不是一个操作系统。但 LXC 容器的存在形式就类似于虚拟机,它提供一个轻量级的操作系统环境,不为任何场合定制和剪裁。默认情况下 LXC 取代不了 Docker,而 LXC 容器的泛用性也是 Docker 不适合替代的。

简单来说,你想快捷启动一个部署起来很麻烦的软件,那就用 Docker 容器。你需要一个虚拟机体验各种发行版或创建隔离环境,那就用 LXC 容器(即 Incus)。

安装

除非你总是使用 ArchLinux、NixOS 这类非常新的系统,否则我建议你通过第三方源安装 Incus。自带的源可能没有或版本太旧,尤其是一些老旧的服务器发行版。Incus 和 LXD 分家没有太久,它还在积极更新。我们应该尽量使用最新的版本。

对于 Debian 和 Ubuntu,你可以添加 Incus 创始人维护的源 zabbly/incus。一般来讲,建议使用 Stable repository 源,别忘了按步骤添加公钥

如果是 CentOS 这类过于陈旧的发行版,我建议你更换系统。

一键安装

从我的服务器助手脚本集合 Hentioe/server-helpers 中使用 debian-install-incus 一键安装!

初始化

此章节是正式使用 Incus 前的一系列步骤,主要是初始化存储、网络等设置。Incus 提供了便捷的命令帮我们完成。

添加组

如果你使用 root 用户,可以忽略这个小章节。如果是普通用户,请将自己添加到 incus-admin 组,不然操作 Incus 的命令会大量使用 sudo,很不方便。

YOUR-USERNAME 替换为你的用户名,分别执行以下两条命令:

sudo usermod -aG incus-admin $USER
newgrp incus-admin

不需要重新登录,newgrp 命令会刷新当前 shell 的组身份。

交互设置

执行以下命令进入交互式设置:

incus admin init

此命令有多个步骤,如下:

Would you like to use clustering? (yes/no) [default=no]:

是否使用集群,一般来说不需要。

Do you want to configure a new storage pool? (yes/no) [default=yes]:

是否配置新的存储池,一般来说需要。

Name of the new storage pool [default=default]:

新存储池的名称,默认即可。

Would you like to create a new local network bridge? (yes/no) [default=yes]:

是否创建新的本地网络桥,一般来说需要。

What should the new bridge be called? [default=incusbr0]:

网桥的接口名称,默认即可。

What IPv4 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]:
What IPv6 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]:

IPv4 和 IPv6 地址,一般来说都需要。如果你的服务器没有 IPv6,可以将 IPv6 设置为 none

Would you like the server to be available over the network? (yes/no) [default=no]:

是否允许通过网络访问 Incus 服务,随意。

Address to bind to (not including port) [default=all]:

绑定的地址,默认即可。

Port to bind to [default=8443]:

绑定的端口,默认即可。

Would you like stale cached images to be updated automatically? (yes/no) [default=yes]:

是否自动更新缓存的镜像,随意。

Would you like a YAML "init" preseed to be printed? (yes/no) [default=no]:

是否打印 YAML 预设,随意。

如果你未来足够熟悉 Incus,后续可直接使用 incus admin init --minimal 命令自动化基本设置。

查找镜像

在创建前,需了解 Incus 的镜像仓库,它被称作 remote(远程)。输入以下命令查看默认远程:

incus remote list

输出如下:

+-----------------+------------------------------------+---------------+-------------+--------+--------+--------+
|      NAME       |                URL                 |   PROTOCOL    |  AUTH TYPE  | PUBLIC | STATIC | GLOBAL |
+-----------------+------------------------------------+---------------+-------------+--------+--------+--------+
| images          | https://images.linuxcontainers.org | simplestreams | none        | YES    | NO     | NO     |
+-----------------+------------------------------------+---------------+-------------+--------+--------+--------+
| local (current) | unix://                            | incus         | file access | NO     | YES    | NO     |
+-----------------+------------------------------------+---------------+-------------+--------+--------+--------+

执行以下命令查看 images 远程镜像列表:

incus image list images:
注意,输出可能会非常大不易观察。建议用 grep <distro_name> 过滤为特定镜像列表。

使用浏览器访问 https://images.linuxcontainers.org 也可查看镜像列表。

容器/虚拟机

基本管理

我们创建一个 Alpine 容器:

incus launch images:alpine/3.20 first

再创建第二个容器:

incus launch images:alpine/3.20 second

基于第一个容器,复制出第三个容器:

incus copy first third

查看实例列表:

incus list

输出如下:

+--------+---------+----------------------+---------------------------------------------+-----------+-----------+
|  NAME  |  STATE  |         IPV4         |                    IPV6                     |   TYPE    | SNAPSHOTS |
+--------+---------+----------------------+---------------------------------------------+-----------+-----------+
| first  | RUNNING | 10.242.148.61 (eth0) | fd42:bd9:ebf7:9c2:216:3eff:febe:12c1 (eth0) | CONTAINER | 0         |
+--------+---------+----------------------+---------------------------------------------+-----------+-----------+
| second | RUNNING | 10.242.148.10 (eth0) | fd42:bd9:ebf7:9c2:216:3eff:feeb:e0d (eth0)  | CONTAINER | 0         |
+--------+---------+----------------------+---------------------------------------------+-----------+-----------+
| third  | STOPPED |                      |                                             | CONTAINER | 0         |
+--------+---------+----------------------+---------------------------------------------+-----------+-----------+

因为我们没有通过 launch 命令创建 third 容器,所以它并未启动。使用命令 incus start third 启动它。

停止一个容器,并删除它:

incus stop third
incus delete third

使用 --force 参数可以强制删除,即使容器正在运行:

incus delete --force first second

以上列举出了容器的基本管理操作。对于 Incus 而言,虚拟机和容器都被抽象为同一套管理模式。给 launch 命令传递 --vm 即是创建虚拟机:

incus launch images:ubuntu/24.04 ubuntu-24 --vm

上面我们创建了一个 Ubuntu 24.04 的虚拟机,使用 incus list 命令查看它会看到 TYPEVIRTUAL-MACHINE 而不是 CONTAINER

资源限制

我们还可以限制虚拟机或容器的资源,如 CPU 和内存。以下命令限制了 CPU 为 1 个核心,内存为 256MB:

incus launch images:ubuntu/24.04 limited-ubuntu-24 --config limits.cpu=1 --config limits.memory=256MiB --vm

使用 incus config show limited-ubuntu-24 查看详细,对比无限制容器/虚拟机可以看出其中的配置区别。

进入容器

使用以下命令进入容器:

incus exec ubuntu-24 -- bash

这里的 -- 表示在容器内执行的命令,此处是 bash 即进入命令行。当然你还可以执行其它命令,如:

incus exec ubuntu-24 -- free -h

上面的命令会输出 ubuntu-24 容器的内存使用情况而不进入容器。对比 limited-ubuntu-24 你会发现内存大小不一致(因为后者内存被限制)。

我们再执行 ping 命令,做一个简单的网络连通性测试:

incus exec ubuntu-24 -- ping bing.com

如果 ping 命令连 Bing 的服务器都无法连接,说明我们的系统中可能存在网络问题。请参考修复联网问题章节解决。

注意,虚拟机的启动速度会比较慢。在未完全启动前 exec 命令会执行失败。

修复联网问题

如果你的系统已安装 Docker,可能会发现刚刚创建的 Incus 容器/虚拟机没法联网。这跟 Docker 默认的路由规则有关。除非你要卸载 Docker,否则请启用系统的 IPv4 转发功能解决网络冲突:

echo "net.ipv4.conf.all.forwarding=1" | sudo tee /etc/sysctl.d/99-forwarding.conf
sudo systemctl restart systemd-sysctl

接下来,重启 Docker 和 Incus 服务:

sudo systemctl restart docker docker.socket incus incus.socket

再次进入容器或虚拟机检查网络。如果仍然无法联网,请尝试重启系统让它生效。

此处的具体问题本文不作详细描述了,如果有后续我会单独发文解释其中的原因和原理。如果你遇到的网络问题和 Docker 无关,那么请留意防火墙和容器 IP 分配。

创建代理

虽然我们可以轻易创建大量容器/虚拟机,但无法将它们作为真正意义上的“服务”器使用。因为从外部(指宿主机之外)无法访问容器/虚拟机内的服务。这时候我们可以创建代理设备,将宿主机流量转发到容器中。

代理设备达成的效果很像端口转发,但比传统的端口转发更强大。

容器代理

我们再次创建一个轻便的 Alpine 容器:

incus launch images:alpine/3.20 alpine

进入容器安装 Nginx:

incus exec alpine -- ash
apk add nginx

在容器中输入 nginx 命令启动 Nginx,然后退出容器。在宿主机系统上使用命令访问容器内的 Web 服务:

curl http://<CONTAINER_IP>

确保看到 Nginx 有所响应(内容不用在意,404 也是正常的)。接下来我们要给容器创建代理设备,将容器内的 HTTP 服务暴露到外部。

使用以下命令添加代理设备:

incus config device add alpine http-proxy proxy listen=tcp:0.0.0.0:8080 connect=tcp:127.0.0.1:80

上述命令向 alpine 容器添加了名为 http-proxy 的类型为 proxy 的设备。将宿主机的 8080 端口的 TCP 流量转发到容器的 80 端口。

现在,尝试外部访问你的服务器宿主机 IP 以及 8080 端口的 HTTP 服务,你应该会看到 Nginx 的响应。

虚拟机代理

容器代理是简单方便的,虚拟机则麻烦一些,因为虚拟机仅支持 NAT 模式的代理。在 NAT 模式下,虚拟机的 NIC(网卡接口)必须指定静态地址。

我们先盲目的实验一次,给 ubuntu-24 这个虚拟机直接添加代理设备:

incus config device add ubuntu-24 ssh-proxy proxy listen=tcp:0.0.0.0:2222 connect=tcp:127.0.0.1:22

命令会响应错误:

Error: Invalid devices: Device validation failed for "ssh-proxy": Only NAT mode is supported for proxies on VM instances

错误在我们的意料之中,毕竟我提前说明过了虚拟机只支持 NAT 模式下的代理。我们进一步尝试用 NAT 模式创建代理设备:

incus config device add ubuntu-24 ssh-proxy proxy listen=tcp:0.0.0.0:2222 connect=tcp:127.0.0.1:22 nat=true

得到新的错误:

Error: Invalid devices: Device validation failed for "ssh-proxy": Cannot listen on wildcard address "0.0.0.0" when in nat mode

这是因为 NAT 模式不允许监听 0.0.0.0 这样的通配符地址,我们需要使用宿主机的外部 IP。假设我们的服务器 IP 是 8.8.8.8,那么我们再次执行:

incus config device add ubuntu-24 ssh-proxy proxy listen=tcp:8.8.8.8:2222 connect=tcp:127.0.0.1:22 nat=true

坏消息是仍然返回错误,好消息是这是最后一个错误:

Error: Failed to start device "ssh-proxy": Failed to start device "ssh-proxy": Connect IP "127.0.0.1" must be one of the instance's static IPv4 addresses

此消息指出我们的实例没有静态 IP,也就是我在此小章节开头提到的给 NIC 赋予静态 IP 这个前提。

先记住当前虚拟机的 IP 地址,我们就固定这个地址即可(或者自定义一个网段中的合法地址)。假设虚拟机 IP 是 10.242.148.181,我们将 eth0 这个设备的 IPv4 地址重写一次:

incus config device override ubuntu-24 eth0 ipv4.address=10.242.148.181

这次,重新添加 ssh-proxy 这个代理设备:

incus config device add ubuntu-24 ssh-proxy proxy listen=tcp:8.8.8.8:2222 connect=tcp:0.0.0.0:22 nat=true

从这里开始,我将 127.0.0.1 换成了 0.0.0.0,这是因为 NAT 模式下 connect 到的地址不允许 127.0.0.1。你要么使用容器的固定地址,要么使用通配符地址。

切记将 8.8.8.8 替换为服务器的真实公网 IP 地址。

不出意外你会看到 Device ssh-proxy added to ubuntu-24 这个正确的命令响应。接着你就可以用服务器 IP 以及 2222 端口,从公网 ssh 进入虚拟机。

有些虚拟机镜像默认可能不带 openssh-server,请自行安装并进行必要的基本设置。

删除代理

按照以上步骤,我们创建了 eth0 这个 NIC,以及 http-proxyssh-proxy 这两个代理。通过 incus config device rm 命令删除设备:

incus config device rm alpine http-proxy
incus config device rm ubuntu-24 eth0 ssh-proxy

以上命令会将我们在两个实例中创建的设备全部删掉,代理效果也会消失。

结束语

这些就是 Incus 的基本使用方法了。无论是个人电脑还是服务器,它都非常有用。在某些受限的发行版上(如 OpenWrt)可以发挥相当大的作用,因为我们从此可以轻易创建并使用各种发行版,而不用在意宿主机系统的生态有多糟糕。

作者头像 一点点入门知识 打赏作者
本文由作者按照 CC BY 4.0 进行授权
分享:

相关文章