Incus手册

什么是Incus

2023年7月5日,Canonical宣布全面接管LXD。原先的LXD工作组对此相当不满,因此Fork并重新制作了移除掉所有Canonical相关内容的Incus。

抛开这些历史问题不谈,Incus的本质是:

一套为容器和虚拟机提供统一的集群化管理接口的系统

架构

Incus由三个部分组成:

  • 服务端:即incusd,提供基于UNIX套接字或REST API的接口供客户端访问。
  • 客户端:即incus,提供访问服务端进行集群管理的功能。
  • 镜像仓库:提供容器镜像或虚拟机磁盘的下载仓库,即https://images.linuxcontainers.org

Incus管理的对象包括虚拟机和系统容器(相对于应用容器而言,即LXC),它们的区别如下表:

虚拟机 系统容器 应用容器
使用独立内核 使用宿主机内核 使用宿主机内核
使用了硬件虚拟化功能(Intel VT-X或AMD-V) 只使用内核功能 只使用内核功能
使用资源最多 使用资源较少 使用资源较少
有完整的引导过程,因此启动较慢 只有用户空间的Init过程,启动较快 完全没有引导和Init过程,启动最快
典型代表是Qemu-KVM 典型代表是LXC 典型代表是Docker

vm-sc-ac.jpeg

安装

目前Incus服务端仅在Debian 13+的官方源中进行提供:

1
apt install incus

客户端不限平台,二进制程序可以在Github页面直接下载。

安装Incus后,被添加到incus-admin附加组的用户有权对其进行管理。

初始化

执行:

1
incus admin init

命令进行初始化,初始化过程包括以下几个问题:

  1. 是否启用集群模式?默认为否。
    • 如果选择是,那么会询问使用的IP、以及是否加入已存在的集群。
    • 如果选择加入已存在的集群,那么会要求提供集群Token。
    • 如果选择不加入,那么会询问新建的集群名称。
  2. 是否创建一个存储池?默认为是。
    • 存储池默认名称为default。使用dir存储引擎(无法修改)。
  3. 是否自动创建网桥?默认为是。
    • 自动创建的网桥是一个三层交换机(路由器)。用户可以手动提供IPv4/IPv6使用的子网,也可以让Incus自动选择一个子网。
    • 如果选择是,还会要求提供网桥的设备名称,默认为lxcbr0
    • 如果选择否,那么会要求用户选择一个已存在的网桥或网络接口。可以创建一个二层网桥,或是使用一个物理接口,不过如果是物理接口的话,会被Incus占用,不能再被宿主机使用。
  4. 是否在网络上暴露该服务器(即启用REST API)?默认为否。
  5. 是否定期更新镜像?默认为是。
  6. 是否打印一个Preseed自动化YAML?默认为否。

除了手动初始化,还有三种自动初始化方式:

  • --minimal:全部使用默认选项,自动化初始化。
  • --auto:从命令行选项获取配置,自动化初始化。可用配置有:
    • --network-address:绑定的IP。
    • --network-port:绑定的端口号,默认为8443。
    • --storage-backend=dir|lvm|btrfs|zfs|ceph:使用的存储引擎。
    • --storage-create-device=/dev/设备:使用device引擎并指定设备存储。
    • --storage-create-loop=大小:使用loop引擎并指定大小。
    • --storage-pool='名称':指定存储池名称。
  • --preseed:从标准输入获取Preseed YAML配置文件,自动化初始化。

网络

检察网络

列出可用网络:

1
incus network list

检查网络:

1
incus network show|info 网络名

创建网络

Incus可以创建一个用于虚拟机/容器的网络:

1
incus network create 网络名称 [参数=值] --type=bridge|ovn|macvlan|sriov|physical [--target=节点]
  • bridge:使用一个三层交换机进行路由的网络。
  • ovn:使用OVN网络。
  • macvlan:在网卡上创建MACVLan设备。
  • sriov:使用支持SR-IOV的网卡。
  • physical:直接使用物理网卡,注意这会导致该网卡在宿主机上不可用。

bridgeovn常用参数有:

  • ipv4.address:子网的CIDR,默认为auto
  • ipv4.dhcp:是否启用DHCP,默认为true
  • ipv6.address:子网的CIDR,默认为auto
  • ipv6.dhcp:是否启用DHCP,默认为true

macvlansriovphysical参数有:

  • parent:使用的父网卡。

在集群中创建网络时,需要在每个节点上都执行一次创建命令,最后再不带--target选项执行一次创建命令。

接入网络

执行以下命令将虚拟机/容器实例接入网络:

1
incus network attach 网络名 实例名 实例中的接口名

实际上这是这条命令的别名:

1
incus config device add 实例名 实例中的接口名 nic network=网络名

实际上,Incus也支持让实例直接接入接口:

1
incus config device add 实例名 实例中的接口名 nic nictype=bridged|macvlan|sriov|physical|routed parent=接口名

这种方式不是推荐方式,唯一实用的场景是手动创建一个二层网桥并使用bridged模式让实例接入。

编辑网络

要在创建后修改网络配置,执行:

1
incus network set 网络名 参数=值

或是执行:

1
incus network edit 网络名

直接编辑YAML配置。

ACL

Incus支持对网络创建ACL规则。首先创建一个ACL配置:

1
incus network acl create ACL配置名 [参数=值]

可用参数有:

  • description:描述。
  • ingress:入站规则列表。
  • egress:出站规则列表。

然后在ACL配置中添加规则:

1
incus network acl rule add ACL配置名 ingress|egress [参数=值]

可用参数有:

  • action=allow|reject|drop:必须参数,针对特定数据包的操作。
  • state=enabled|disabled|logged:规则状态。logged表示登记模式。
  • description:描述。
  • source:匹配源地址,可以是CIDR,也可以是IP范围。特殊的值@internal表示内网通信,@external表示外网通信。
  • destination:匹配目标地址,可以是CIDR,也可以是IP范围。
  • protocol=icmp4|icmp6|tcp|udp:网络协议。
  • source_port:仅支持tcp|udp协议,匹配源端口号。
  • destination_port:仅支持tcp|udp协议,匹配目标端口号。
  • icmp_type:仅支持icmp4|icmp6协议,匹配ICMP类型号。
  • icmp_code:仅支持icmp4|icmp6协议,匹配ICMP状态码。

要编辑一个ACL配置,执行:

1
incus network acl edit ACL配置名

要查看ACL日志,执行:

1
incus network acl show-log ACL配置名

要分配ACL,执行:

1
incus network set 网络名 security.acls=ACL配置名

或是仅对实例生效:

1
incus config device set 实例名 实例中的网络接口 security.acls=ACL配置名

配置了ACL的网络/接口的默认行为是阻止所有的出入站数据包,要修改这一行为,执行:

1
incus network set 网络名 security.acks.default.ingress|egress.action=allow

1
incus config device set 实例名 实例中的网络接口 security.acls.default.ingress|egress.action=allow

端口映射

Incus可以方便地配置端口映射,先添加监听的IP:

1
incus network forward create 网络名 监听IP [参数=值]

可用参数有:

  • description:描述。
  • ports:端口列表。

然后即可添加映射端口:

1
incus network forward port add 网络名 监听IP tcp|udp 监听端口 目标IP [参数=值]

其他参数如下:

  • target_port:目标端口,默认和监听端口一致。
  • description:描述。

编辑执行:

1
incus network forward edit 网络名 监听IP

存储

Incus将存储使用池式管理。支持以下几类存储引擎:

存储特性 目录 BTRFS LVM ZFS CEPH RBD CEPHFS CEPH OBJ
宿主机共享
独立磁盘
回环设备
远程存储
写入优化
克隆
快照
配额 是(Ext4或Xfs)

检察存储池

列出可用存储池:

1
incus storage list

检查存储池:

1
incus storage show|info 池名

创建存储池

执行:

1
incus storage create 池名 存储引擎 [参数=值]

dir引擎可用参数有:

  • source=目录位置:指定目录位置。

btrfs引擎可用参数有:

  • size=:大小。
  • source=/dev/sdX:指定建立了Btrfs文件系统的块设备。

lvm引擎可用参数有:

  • size=:大小。
  • source=/dev/sdX lvm.vg_name=VG名:在指定块设备上创建VG并使用。
  • source=VG名:指定使用的VG。
  • lvm.thinpool_name=:创建一个指定名称的Thinpool。

zfs引擎可用参数有:

  • size=:大小。
  • source=/dev/sdX zfs.pool_name=ZPool名:在指定块设备上创建ZPool并使用。
  • source=ZPool名:指定使用的ZPool。
  • source=ZPool名/Dataset名 volume.zfs.block.mode=yes:指定使用的Dataset。

ceph(Ceph RBD)可用的参数有:

  • ceph.cluster_name=:使用的Ceph集群名。
  • ceph.osd.pool_name=:使用的Ceph池名。
  • source=OSD名:使用现有的Ceph OSD池。

cephfs引擎可用参数有:

  • source=文件系统名[/目录]:指定使用的CephFS文件系统。
  • cephfs.create_missing=true:自动创建不存在的目录。

cephobject引擎可用参数有:

  • cephobject.radosgw.endpoint=:指定Ceph Object的HTTP入点。

在集群中创建存储池时,需要在每个节点上都执行一次创建命令,最后再不带--target选项执行一次创建命令。

编辑存储池

要在创建后修改存储池配置,执行:

1
incus storage set 池名 参数=值

例如:

1
incus storage set 池名 size=大小

或是执行:

1
incus storage edit 池名

直接编辑YAML配置。

使用存储池

很简单,在启动实例时使用--storage=池名选项指定即可。如果没有指定,那么使用当前的Profile的配置。

要移动实例,执行:

1
incus move 实例名 --storage 池名

创建存储卷

Incus在创建实例时会自动创建所需的卷,在移除实例时会自动删除,因此一般情况下,存储卷不需要手动创建,创建的命令为:

1
incus storage volume create 池名 卷名 [--type=filesystem|block] [参数=值]

可用类型有两种:

  • filesystem:存储容器和容器镜像。不能挂载在虚拟机中。
  • block:存储虚拟机的虚拟磁盘。不能挂载在容器中。

可用参数有:

  • size:大小。

此外,还有一种特殊的类型iso,也就是可挂载的ISO镜像卷,不过这种卷的创建方式是:

1
incus storage volume import 池名 ISO路径 卷名 --type=iso

这种卷只能是只读的。

要设置卷的用途(备份或镜像),执行:

1
incus config set storage.backups_volme|storage.images_volume 池名/卷名

查看存储卷:

1
incus storage volume list 池名

设置存储卷

要在创建后修改存储卷配置,执行:

1
incus storage set 池名 卷名 参数=值

例如:

1
incus storage set 池名 卷名 size=大小

或是执行:

1
incus storage edit 池名 卷名

直接编辑YAML配置。

挂载存储卷

对于filesystem卷:

1
incus storage volume attach 池名 卷名 容器实例名 挂载路径

对于block卷:

1
incus storage volume attach 池名 卷名 虚拟机实例名

此外,也可以把卷作为一个设备挂载:

1
incus storage volume attach 池名 卷名 实例名 设备名 [挂载路径] [参数=值]

这条命令实际上是个别名:

1
incus config device add 实例名 设备名 disk pool=池名 source=卷名 [path=路径] [参数=值]

设置IO限制

在把卷作为设备挂载时,可以通过参数设置IO限制,在本质上,这是通过Cgroup实现的:

  • boot.priority:设备的引导优先级,用于虚拟机。
  • io.bus=nvme|virtio-blk|virtio-scsi:设备的接口。
  • io.cache=none|writeback|unsafe:设备的写入缓存。
  • limits.max:最大读写速度限制。
  • limits.read:最大读速度限制。
  • limits.write:最大写入速度限制。

拷贝/移动存储卷

也就是:

1
incus storage volume copy|move 池名/卷名 池名/卷名

不过,要在集群节点之间移动的话,还要加上:

1
incus storage volume copy|move 池名/卷名 池名/卷名 --target 节点 --destination-target 节点

还可以在Incus集群间移动:

1
incus storage volume copy|move 主机名:池名/卷名 主机名:池名/卷名

备份卷

创建一个快照:

1
incus storage volume snapshot 池名 卷名 [快照名]

检查快照:

1
incus storage volume show|info 池名 卷名

编辑快照:

1
incus storage volume edit 池名 卷名/快照名

删除快照:

1
incus storage volume delete 池名 卷名/快照名

还可以设置定时快照:

1
incus storage volume set 池名 卷名 snapshots.schedule '分 时 日 月 年'|@daily

恢复快照:

1
incus storage volume restore 池名 卷名 快照名

还可以从快照构建一个新的卷:

1
incus storage volume copy 池名/卷名/快照名 池名/卷名/快照名

导出备份:

1
incus storage volume export 池名 卷名 [文件路径]

可用选项如下:

  • --compression=gzip|bzip2|none:压缩方式。
  • --optimized-storage:仅用于zfsbtrfs,启用优化存储方式。
  • --volume-only:仅备份卷,不备份快照。

恢复备份:

1
incus storage volume import 池名 文件路径 [卷名]

REST API

Incus支持使用REST API的方式进行远程使用。在服务端上执行:

1
incus config set core.https_address :8443|IP|URL

以设置REST API监听地址。然后在客户端执行:

1
incus remote add 名称 IP|URL

以添加,再执行:

1
incus remote switch 名称

表明当前使用。

客户端和服务端之间可以使用证书认证:

1
incus config add-certificate 文件路径 --protocol=incus

还可以使用PKI模式,具体步骤为:

  1. 把CA证书上传到服务端的/var/lib/incus目录。
  2. 把CA证书上传到客户端的~/.config/incus目录。
  3. 把CA签名的证书上传到客户端和服务端。
  4. 重启服务端。

集群

Incus支持以集群方式运作,集群内部使用CowSQL分布式数据库存储元信息,并且使用木筏算法选举。集群最少需要三个节点以实现高可用。

查看集群信息:

1
incus query /1.0/cluster

原理

Incus集群中的每个节点要么是Voter节点,要么是Stand-by节点。Voter节点会参与分布式数据库选主,默认情况下,一个集群中只有三个节点是Voter,其他都是Standby节点。当有一个Voter节点离线时,一个Standby节点会立刻升级为Voter节点并顶替。这些相关配置项分别有:

  • cluster.max_voters:最大Voter节点数。
  • cluster.max_standby:最大Standby节点数。
  • cluster.offline_threshold:认为离线的响应延迟。

建立集群

在第一台Incus服务器上执行:

1
incus admin init

然后在:

1
Would you like to use Incus clustering?

选择yes,设置该服务器的IP即可。这个IP之后可以执行:

1
incus config set cluster.https_adddress=IP|URL

进行重设。

完成后执行:

1
incus cluster list

即可检查集群情况。

加入集群

在集群服务器上执行:

1
incus cluster add 成员名

然后在新的服务器上执行:

1
incus admin init

然后在:

1
Would you like to use Incus clustering?

选择yes,在:

1
Are you joining an existing cluster?

也选择yes,加入集群并输入Token即可。

执行:

1
incus cluster show|info 节点名

以查看信息。

节点证书位于/var/lib/incus/cluster.crt

节点组

一部分节点可以组成节点组方便管理,执行:

1
incus cluster group create 组名

以创建节点组。

设置节点所在组:

1
incus cluster group assign 节点名 default,组名

该命令会覆盖默认组,所以可以带上default防止覆盖。

将节点添加组:

1
incus cluster group add 节点名 组名

节点组可以在--target选项中使用@组名调用。

镜像存储

默认情况下Incus会在每个节点上保存一份镜像的拷贝,这一行为可以通过:

  • cluster.image_minimal_replica:镜像最小副本数,设为-1表示节点数。

修改。

实例存储

新建实例时,Incus默认会尝试保存在当前实例数量最少的那个节点上。不过,也可以使用--target选项指定节点。scheduler.instance可以修改默认操作。

  • 如果scheduler.instance=all,那么使用默认逻辑。
  • 如果scheduler.instance=manual,那么该节点必须被手动指定使用。
  • 如果scheduler.instance=group,那么该节点在用户选择同组节点时可能会被使用。

执行:

1
incus cluster set 节点名 scheduler.instance all|manual|group

以设置。

要移动实例,执行:

1
incus move 实例名 --target 节点名

排空/隔离

有时候我们会希望排空/隔离一个节点(比如升级),此时执行:

1
incus cluster evacuate 节点名

即可,要恢复,执行:

1
incus cluster restore 节点名

此外,对于离线节点还可以自动排空,只需要设置:

1
incus cluster set 节点名 cluster.healing_threshold=秒数

删除节点

1
incus cluster remove 节点名 [--force]
  • --force用于移除离线节点。

灾难恢复

有时候你会非常不幸的同时损失多个节点,此时Incus集群会处于不可用状态,要恢复,执行:

1
incus admin cluster list-database

查看可用的节点。连接一个节点,执行:

1
systemctl stop incus.service incus.socket

停止服务,然后执行:

1
incus admin cluster recover-from-quorum-loss

恢复数据。最后再启动服务。

重设IP

当集群中有节点IP发生变化时,必须执行:

1
incus admin cluster edit

重新设置。

镜像

配置镜像源

Incus会从https://images.linuxcontainers.org拉取镜像,不过这一配置可以进行修改:

1
incus remote set-url images https://xxx

或者添加:

1
incus remote add 名称 https://xxx --protocol=simplestreams --public

需要注意的是,Incus只接受HTTPS。

查看远程源中的镜像:

1
incus image list 源名称:[镜像名] [版本号] [architecture=架构] 

镜像默认会自动更新,要禁用这一行为,执行:

1
incus config set images.auto_update_interval=0

下载镜像

在Incus中,本地镜像实际上也是一个源,名为local:,因此要“下载镜像”实际上就是把镜像拷贝到local:源:

1
incus image copy 源名称:镜像名/版本 local: [--vm] [--alias=名称] [--copy-aliases] [--auto-update]

如果没有使用--alias或是--copy-alias选项,那么镜像将没有名称,只能使用ID(指纹)指定,很麻烦。

检查镜像信息:

1
incus image show|info 镜像名|ID

给镜像设置别名:

1
incus image alias list|create|delete 别名 镜像ID

重设别名:

1
incus image alias rename 别名 新别名

导出与导入镜像

导出镜像,即把镜像到出到文件中:

1
incus image export 源名:镜像名 [目录] [--vm]

从文件中导入镜像:

1
incus image import 镜像文件路径
1
incus image import 源数据Tar包路径 根文件系统Tar包路径

从实例或快照中发布镜像:

1
incus publish 实例名[/快照名] [源名:]

镜像Profile

从镜像创建实例时,会自动按顺序使用Profile,这些Profile可以执行:

1
incus image edit 镜像名

进行修改。

实例

实例,即一个VM或容器,是Incus的中心。Incus使用一致的接口创建VM和容器,但是底层技术是不一样的:

  • 容器使用liblxc实现。
  • 虚拟机使用qemu-kvm实现,附带一个incus-agent虚拟机代理辅助工作。

创建实例

创建一个实例的命令为:

1
incus create|launch|init [镜像服务器:]镜像名 实例名 [--vm] [其他选项]

如果没有指定镜像服务器,那么默认使用local:,即本地镜像。

可用的其他选项包括:

  • -e|--ephemeral:临时模式,停止容器时抹除任何操作。
  • -p|--profile 配置名:指定使用的配置组。
    • --no-profile:不适应任何配置组。这意味着必须手动配置容器。
  • -c|--config 配置=值:手动设置配置项。
  • -n|--network 网络名:指定使用的Incus网络。
  • -s|--storage 池名:指定使用的Incus存储池。
  • --target 节点:指定使用的集群节点。
  • -d|--device 设备,配置=值:指定一些虚拟设备配置。例如设置根文件系统大小--device root,size=
  • --empty:什么也不做,新建一个空实例。

要从ISO引导VM,首先需要创建一个ISO镜像的存储卷:

1
incus storage volume import 池名 ISO文件路径 卷名 --type=iso

然后在虚拟机中添加设备:

1
incus config device add 实例名 设备名 disk pool=池名 source=卷名 boot.priority=0

安装完成后移除卷:

1
incus storage volume detach 池名 卷名 实例名

控制与检查实例

所有的Incus实例接口都支持命令行和REST两种方式。

列出实例:

1
2
3
incus list [匹配词=值]

incus query /1.0/instances?filter=[匹配词+eq+值]

可用匹配词包括:

  • type=container|vm:类型。
  • status=running|stopped:状态。
  • location=节点名:所在节点。
  • 系统类型.*:系统类型。

查看实例信息:

1
2
3
incus info 实例名 [--show-log]

incus query /1.0/instances/实例名

启动实例:

1
2
3
incus start 实例名 [--console]

incus query --request PUT /1.0/instances/实例名/state --data '{"action": "start"}'

停止实例:

1
2
3
incus stop 实例名

incus query --request PUT /1.0/instances/实例名/state --data '{"action": "stop"}'

删除实例:

1
2
3
incus delete 实例名

incus query --request DELETE /1.0/instances/实例名

重建实例(保留配置,删除并重新创建):

1
2
3
4
5
incus rebuild [镜像名] 实例名 [--empty]

incus query --request POST /1.0/instances/实例名/rebuild --data '{"source": {"alias":"镜像名","server":"服务器URL", protocol:"simplestreams"}}'

incus query --request POST /1.0/instances/实例名/rebuild --data '{"source": {"type":"none"}}'

无特权容器

Incus默认以无特权方式运行容器,因此必须确保Root用户有合适的SubUID和SubGID。SubUID和SubGID的范围中至少要有65536个值,否则启动容器时会立刻失败

默认情况下,在安装Incus时,会给Root用户创建1000000-1000000000的SubUID和SubGID,这在一般情况下足够了。

在创建容器时,Incus会随机抽取一段UID(以下文字中省略GID)并对容器中的UID进行映射,因为抽取的UID是随机的,所以不同的容器映射的目标UID有可能重合,这在一般情况下没什么问题,不过如果用户对安全性很敏感,可以给容器设置配置项security.idmap.isolated=true,这可以确保容器映射的目标UID是唯一的。

此外,用户也可以手动设置UID映射,只需要给容器设置以下两个配置项:

  • security.idmap.base:映射的目标UID起点。
  • security.idmap.size:映射的UID数量。

如果用户不希望使用无特权容器(例如嵌套容器环境下),那么可以通过给容器或Profile中设置该配置项关闭:

  • security.privileged=true

配置实例

查看实例配置,执行:

1
2
3
incus config show 实例名 [--expanded]

incus query /1.0/instances/实例名

配置实例的命令为:

1
2
3
incus config set 实例名 配置项=值

incus query --request PATCH /1.0/instances/实例名 --data '{"config": {"配置项":"值"}}'

可用的配置项包括:

  • boot.autostart:随incusd守护进程启动。
  • limits.cpu:可用的CPU。
  • limits.cpu.priority:CPU占用优先级。
  • limits.disk.priority:IO优先级。
  • limits.memory:内存使用数,默认为1GiB。
  • limits.memory.enforce=soft|hard:内存限制性质。
  • limits.memory.swap:可用的交换空间数。
  • security.protection.delete:禁止实例被删除。默认禁用。
  • security.guestapi:是否在实例内提供/dev/incus接口。默认启用。

用于容器的增强功能:

  • security.privileged:是否运行特权容器,默认禁用。
  • security.idmap.base:用于非特权容器,ID映射的起点。默认为0,表示随机选择。
  • security.idmap.size:用于非特权容器,ID映射的数量。
  • security.idmap.isolated:用于非特权容器,ID映射是否严格独立。默认禁用。
  • security.nesting:是否允许容器嵌套,默认禁用。
  • security.protection.shift:禁止容器ID映射被调整,默认禁用。

用于虚拟机的增强功能:

  • security.agent.metrics:是否启用指标采集。默认启用。
  • security.csm:是否启用Legacy BIOS支持,默认禁用。
  • security.secureboot:是否启用安全引导,默认启用。
  • security.sev:是否启用AMD安全虚拟化技术。默认禁用。
  • security.sev.policy.es:是否启用AMD安全虚拟化加密。默认禁用。

要给实例添加设备,执行:

1
2
3
incus config device add 实例名 设备名 设备类型 [设备选项=值]

incus query --request PATCH /1.0/instances/实例名 --data '{"devices": {"设备名": {"type":"设备类型","设备选项":"值"}}}'

可用的设备类型及其选项有:

  • nic:网络接口。
    • network=:实际使用的Incus网络。
    • nictype=:直接接入的网络接口类型,不太常用。
  • disk:磁盘。
    • pool=:使用的存储池。使用source=指定池名。
    • source=:宿主机上的目录映射。
    • source=ceph:池名/卷名 ceph.user_name=用户名 ceph.cluster_name=集群名:使用Ceph RBD。
    • source=cephfs:FS名/路径 ceph.user_name=用户名 ceph.cluster_name=集群名:使用Ceph FS。
    • source=ISO文件路径:使用ISO镜像。
    • boot.priority:设备的引导优先级,用于虚拟机。
    • io.bus=nvme|virtio-blk|virtio-scsi:设备的接口。
    • io.cache=none|writeback|unsafe:设备的写入缓存。
    • limits.max:最大读写速度限制。
    • limits.read:最大读速度限制。
    • limits.write:最大写入速度限制。
  • unix-char:字符设备。
    • source:设备路径。
    • path:容器内的位置。
    • uid:所属的用户。
    • gid:所属的用户组。
    • mode:权限位,默认为0660
    • required:是否必要。
  • unix-block:块设备。
    • source:设备路径。
    • path:容器内的位置。
    • uid:所属的用户。
    • gid:所属的用户组。
    • mode:权限位,默认为0660
    • required:是否必要。
  • unix-hotplug:热插拔设备。
    • uid:所属的用户。
    • gid:所属的用户组。
    • mode:权限位,默认为0660
    • required:是否必要。
    • productid:设备ID。
    • vendorid:设备制造商ID。
  • usb:USB设备。
    • uid:所属的用户。
    • gid:所属的用户组。
    • mode:权限位,默认为0660
    • required:是否必要。
    • productid:USB设备ID。
    • vendorid:USB制造商ID。
  • gpu:GPU设备。
    • gputype=physical:物理显卡直通,可用于容器。
      • id:设备DRM ID。
      • pci:设备PCI地址。
      • productid:设备ID。
      • vendorid:设备制造商ID。
      • uid:所属的用户。
      • gid:所属的用户组。
      • mode:权限位,默认为0660
    • gputype=mdev:虚拟机的虚拟显卡。
      • id:设备DRM ID。
      • mdev:使用的虚拟显卡类型。
      • pci:设备PCI地址。
      • productid:设备ID。
      • vendorid:设备制造商ID。
    • gputype=mig:用于容器的NVIDIA MIG单元。
      • id:设备DRM ID。
      • mig.ci:MIG的计算实例ID。
      • mig.gi:MIG的GPU实例ID。
      • mig.uuid:MIG的设备UUID。
      • pci:设备PCI地址。
      • productid:设备ID。
      • vendorid:设备制造商ID。
    • gputype=sriov:用于虚拟机的SRIOV。
      • id:设备DRM ID。
      • pci:设备PCI地址。
      • productid:设备ID。
      • vendorid:设备制造商ID。
  • infiniband:IB网卡。
    • nictype=physical:物理的IB网卡。
      • parent:父网卡。
      • hwaddr:MAC地址。
      • mtu:MTU。
      • name:名称。
    • nictype=sriov:SRIOV分配的网卡。
      • parent:父网卡。
      • hwaddr:MAC地址。
      • mtu:MTU。
      • name:名称。
  • tpm:TPM。
    • path:路径,一般为/dev/tpmX
    • pathrm:资源管理器路径,一般为/dev/tpmrmX
  • pci:PCI设备。
    • address:PCI地址。

编辑实例配置:

1
2
3
incus config edit 实例名

incus query --request PUT /1.0/instances/实例名 --data '配置'

快照与备份

快照的实际实现原理随着存储引擎而变化,有些支持CoW的存储引擎支持优化的快照实现方式:

1
incus snapshot create 实例名 [快照名] [--reuse]
  • --reuse:覆盖原先已存在的快照。
  • --stateful:保存VM当前的运行状态。

删除快照:

1
incus delete 实例名/快照名

查看快照信息:

1
incus info 实例名/快照名

查看快照配置:

1
incus config show 实例名/快照名

编辑快照配置:

1
incus config edit 实例名/快照名

还可以设置定时快照:

1
incus snapshot set 实例名 snapshots.schedule 'Cron日程'|@daily

恢复快照:

1
incus snapshot restore 实例名 快照名

导出实例:

1
incus export 实例名 [文件路径]
  • --compression=gzip|bzip2|none:压缩方式。
  • --optimized-storage:仅用于zfsbtrfs,启用优化存储方式。
  • --instance-only:仅备份实例,不备份快照。

从文件恢复实例:

1
incus import 文件路径 [实例名]

配置组(Profile)

配置组保存了一系列的实例配置,它可以用于创建实例时复用。不过在创建实例时,命令行选项总是会覆盖配置组中的选项。Incus初始化时,会创建一个默认Profile,名为default

列出可用配置组:

1
incus profile list

查看配置组:

1
incus profile show 配置名

创建一个配置组:

1
incus profile create 配置名

编辑配置组可以使用配置实例一样的方式:

1
2
3
incus profile set 配置名 配置=值

incus profile device add 配置名 设备名 配置=名

不过,更合理的方法是直接编辑:

1
incus profile edit 配置名

在创建实例时可以使用一个配置组:

1
incus launch 镜像名 实例名 --profile 配置名

也可以给已创建的实例添加配置:

1
incus profile add|remove 实例名 配置名

执行命令

在实例中执行一条命令:

1
incus exec 实例名 -- 命令
  • 和Docker不一样,Incus不需要手动分配伪终端,它有能力在需要的情况下(运行Shell)自动分配。不过,如果希望强制使用非交互模式,也是可以的,只需要添加--force-noninteractive选项。
  • 默认情况下,Incus使用root用户和组运行命令,工作目录为/root,这些行为可以通过--user--group--cwd选项修改。
  • 使用--env ENV=VALUE选项设置环境变量。

要持久化设置环境变量,执行:

1
incus config set 实例名 environment.ENV=VALUE

进入终端

Incus可以连接到实例的/dev/console设备上以获取交互式终端:

1
incus console 实例名 [--show-log]

对于虚拟机,尤其是没有安装incus-agent的虚拟机,可以使用对Agent没有要求的SPICE协议进行连接,不过,这需要系统上安装virt-viewer

1
incus console 实例名 --type=vga

创建实例时可以自动进入终端:

1
incus init 镜像名 实例名 --console

获取实例中文件

Incus通过一些手段,实现了直接编辑实例中的文件:

1
incus file edit 实例名/文件路径

注意,你不能用这种方式创建文件。

也可以删除实例中的文件:

1
incus file delete 实例名/文件路径

还可以获取实例中的文件到宿主机:

1
incus file pull [-r] 实例名/文件路径 宿主机文件路径
  • 如果宿主机文件路径是-,那么会直接打印文件内容(拷贝到标准输出)。
  • -r表示递归拷贝。

上传宿主机文件:

1
incus file push [-r] 宿主机文件路径 实例名/文件路径

映射宿主机目录:

1
incus file mount 实例名/路径 宿主机路径

配置SFTP服务器:

1
incus file mount 实例名 [--listen 地址:端口]
  • 默认情况下,地址为127.0.0.1,端口号随机生成。

这样就可以使用sshfs连接了:

1
sshfs 用户名@地址:/路径 本地路径 -p 端口号

迁移

实例可以在节点间迁移。执行:

1
incus copy 实例名 目标主机:实例名

项目(Project)

项目是一组相互关联的实例的合集。项目可以包含一系列的镜像、网络和存储。

Incus初始化时会附带一个default项目。

创建项目

1
incus project create 项目名 --config 配置项=值

查看项目:

1
incus project list

配置项目

1
incus project set 项目名 配置项=值

可用配置项有:

  • features.images:是否允许存储镜像。
  • features.networks:是否允许存储网络。
  • features.storage.buckets:是否允许Bucket存储。
  • features.storage.volumes:是否允许存储卷。
  • features.profiles:是否允许存储Profile。
  • limits.containers:容器数量。
  • limits.virtual-machines:VM数量。
  • limits.instances:实例数量。
  • limits.cpu:CPU限制。
  • limits.memory:内存限制。
  • limits.disk:存储限制。
  • limits.networks:网络限制。
  • limits.processes:进程限制。
  • restricted:是否启用限制。
  • restricted.backups:限制使用备份。
  • restricted.containers.privilege:限制使用特权容器。
  • restricted.containers.nesting:限制容器嵌套。

编辑项目配置:

1
incus project edit 项目名

切换项目

配置完成后,可以切换项目:

1
incus project switch 项目名

或者,可以在Project间移动实例:

1
incus move 实例名 新实例名 --project 项目名 --target-project 项目名

用户特定项目

要将项目绑定到一个用户,执行:

1
incus config trust add --projects 项目名 --restricted
1
incus config trust add-certificate 证书路径 --projects 项目名 --restricted