- 后端
- 虚拟化
- 容器运行时
【免费下载链接】incus
Powerful system container and virtual machine manager
Incus 项目(project)是 Incus 用于组织实例、镜像、配置文件和网络的逻辑分组机制,它让一台 Incus 服务器可以在同一命名空间内承载互不干扰的多组工作负载。本文以 doc/explanation/projects.md 为主线,结合创建、使用、约束项目的实操文档与项目配置参考,系统讲解项目的隔离模型、受限项目(confined project)的多用户方案,以及features.*、limits.*、restricted.*等核心配置项的完整语义,读完即可动手搭建隔离的多租户环境并配置资源配额与安全限制。
为什么需要项目:场景与价值
你可以用项目让 Incus 服务器保持整洁,把相关的实例分组管理。除了彼此隔离的实例,每个项目还可以拥有自己独立的镜像、配置文件(profile)、网络和存储资源。以下两类场景最能体现项目的价值:
场景一:按业务线隔离大批实例如果你运行着大量用途不同的实例(例如为不同客户项目提供服务),希望让这些实例互相隔离,便于定位和维护;同时出于一致性考虑,还希望不同客户项目中的实例能复用相同的实例名。此时可以为每个客户项目创建一个 Incus 项目,并为各项目配置不同的配置文件、网络和存储——同一实例名(如web-01)可以安全地出现在多个项目中,互不冲突。
场景二:共享服务器上的多用户环境当一台 Incus 服务器被多个用户共享时,每个用户运行自己的实例、配置自己的配置文件。管理员希望把用户实例约束在各自的范围内,让每个用户只能操作自己的实例、看不到别人的实例,同时限制每个用户的资源用量,防止不同用户的实例相互干扰。这种情况下,可以使用受限项目(confined project)搭建多用户环境。
Incus 自带一个名为default的项目,所有未显式切换项目的操作都落在其中。创建新项目的具体步骤见 创建项目。
项目的隔离模型
实例的隔离是强制的
项目始终封装(encapsulate)其包含的实例:实例不能跨项目共享,同一实例名可以在多个项目中重复使用;当你在某个项目内操作时,只能看到属于该项目的实例。这一点在源码中也有直接体现——internal/server/project/project.go中Instance()函数会在项目名非default时,给实例名加上<project>_前缀(分隔符为_),DNS()函数则把项目名作为 DNS 后缀追加为<instance>.<project>,从底层保证实例与 DNS 名称在物理层面不会串扰;InstanceParts()则负责从带前缀的名称中反向解析出项目与实例名。镜像卷作为缓存资源则始终关联到default项目(见StorageVolumeProject的注释与实现)。
其他实体的隔离:feature 开关
镜像、配置文件、网络、存储这些实体则有两种归属方式:在项目中隔离,或者从default项目继承。由项目内的 feature 开关决定:
- feature 开启(
true)→ 对应实体在项目中隔离,项目使用自己的一套资源; - feature 关闭(
false)→ 对应实体从default项目继承。
例如:开启features.networks后,项目使用独立的一套网络,不再使用default项目中的网络;关闭features.images后,项目能访问default项目中的镜像,且在该项目中添加的任何镜像也会被写入default项目。
必须注意:feature 要在开始使用新项目之前选定。一旦项目包含实例,feature 即被锁定,要修改就必须先删除项目内所有实例。此外,升级带来的新增 feature 对既有项目默认是关闭的。
全部 feature 选项的列表见 项目配置参考 与 doc/config_options.txt(project-features配置组)。
多用户环境下的受限项目(Confined Projects)
当 Incus 服务器被多个用户共享(例如实验室环境)时,可以用项目约束每个用户的活动范围。除按上文隔离实例及其他实体外,受限项目还把用户限制在自己的空间内,防止其访问其他用户的实例或数据;任何影响服务器本身及其配置的变更(如添加或移除存储)都不被允许。
更关键的是,这一方案允许用户在不属于incus-admin组的情况下使用 Incus。incus-admin组成员拥有 Incus 的完全访问权,包括挂载宿主文件系统路径、调整实例安全特性——这些能力理论上可以被利用来获得宿主机的 root 权限(详见 安全文档)。受限项目限制了用户在 Incus 内能做的事,同时防止用户借此获取宿主机 root 权限,从而实现"能用、但越不了权"的安全边界。
项目的认证方式
将项目限定到特定用户,有两种认证方式可供选择:
客户端证书(Client certificates)在 TLS 证书认证 中,可以把客户端证书限制为只允许访问特定项目。要求被限制的项目必须先存在。使用受限证书连接的客户端只能看到被授予访问权的项目。
多用户 Incus 守护进程(Multi-user Incus daemon)守护进程支持按用户动态创建项目,通常用于属于incus组、但不在权限更高的incus-admin组的用户。当这类用户开始使用 Incus 时,Incus 会自动为该用户创建一个受限项目并切换过去。如果此时 Incus 尚未初始化,会自动以默认配置完成初始化。
两种认证方式的具体启用与配置步骤见 将项目限定到特定用户。
创建与配置项目
创建和配置项目的完整操作流程见 创建和配置项目,下面提炼核心命令。
创建项目
使用incus project create命令,例如创建名为my-project的项目:
incus project create my-project通过--config标志指定配置选项。例如创建隔离实例、但允许访问default项目镜像的项目:
incus project create my-project-shared-images --config features.images=false再如创建阻止安全敏感特性(如容器嵌套)、但允许备份的受限项目:
incus project create my-restricted-project --config restricted=true --config restricted.backups=allow一个常见的坑:创建项目时不指定任何配置选项时,features.profiles会被置为true(这是它的初始值),意味着配置文件在项目中隔离——新项目将无法访问default项目的default配置文件,从而缺少创建实例所需的配置(如 root 磁盘)。解决办法是给项目的default配置文件添加 root 磁盘设备:
incus profile device add default root disk path=/ pool=<pool_name>或在创建实例前把默认配置文件的磁盘配置补进去(见下文"跨项目复制配置文件")。
配置项目
配置项目可以只设置某个具体选项,也可以整体编辑项目。注意:部分配置选项只能在项目不含任何实例时设置。
设置单个配置选项:使用incus project set。例如把my-project中可创建的容器数量限制为 5:
incus project set my-project limits.containers=5取消配置选项:使用incus project unset。注意:unset 后选项会回到默认值,而这个默认值可能与项目创建时的初始值不同(典型例子就是features.profiles:初始值为true,默认值为false)。
整体编辑:使用incus project edit,会以交互式编辑器打开完整配置:
incus project edit my-project使用不同的项目
当项目不止default一个时,必须确保操作时使用或指向正确的项目。需要注意:如果存在限定到特定用户的项目,只有拥有 Incus 完全访问权的用户能看到全部项目;没有完全访问权的用户只能看到自己有权限的项目的信息。
列出项目
incus project list默认输出为表格形式,各列依次为:NAME(项目名)、IMAGES/PROFILES/STORAGE VOLUMES/STORAGE BUCKETS/NETWORKS/NETWORK ZONES(对应各 feature 是否开启,YES表示隔离)、DESCRIPTION(描述)、USED BY(被引用数)。当前所在项目会标注(current)。可以用--format标志切换输出格式(详见incus project list --help)。
切换项目
默认情况下,发出的所有命令都作用于当前所在项目,可用incus project list查看当前项目。切换项目:
incus project switch <project_name>指定项目(不切换)
许多 Incus 命令支持--project标志,在不切换当前项目的前提下把操作指向特定项目。注意:只能指向你有权限的项目。
列出某项目内的实例:
incus list --project my-project把实例移动到另一个项目:
incus move <instance_name> <new_instance_name> --project <source_project> --target-project <target_project>目标项目中不存在同名实例时,可以保留原实例名。例如把my-instance从default项目移动到my-project并保留名称:
incus move my-instance my-instance --project default --target-project my-project跨项目复制配置文件:如果项目按默认设置创建(features.profiles=true,配置文件隔离),项目访问不到default项目的默认配置文件,创建实例时会报错:
incus launch images:debian/12 my-instance Creating my-instance Error: Failed instance creation: Failed creating instance record: Failed initializing instance: Failed getting root disk: No root device could be found解决办法是把default项目默认配置文件的内容管道导入当前项目的默认配置文件:
incus profile show default --project default | incus profile edit default项目配置参考
项目的键/值配置采用命名空间组织,共四大类:features.*、limits.*、restricted.*以及项目专属配置(project-specific)。完整清单见 项目配置参考,以下逐类说明(选项含义与默认值均来自 doc/config_options.txt 的对应配置组)。
项目特性:features.*
features 决定哪些实体在项目中隔离、哪些从default项目继承。某个feature.*选项为true时,对应实体即在项目中隔离。创建时未显式配置的选项取初始值;但 unset 后回到默认值,且所有feature.*的默认值都是false。
| 配置项 | 初始值 | 默认值 | 说明 |
|---|---|---|---|
features.images | true | false | 是否使用独立的镜像与镜像别名集合 |
features.networks | false | false | 是否使用独立的网络集合(需要服务器配置 OVN) |
features.networks.zones | false | false | 是否使用独立的网络区域(network zones)集合 |
features.profiles | true | false | 是否使用独立的配置文件集合 |
features.storage.buckets | true | false | 是否使用独立的存储桶(storage buckets)集合 |
features.storage.volumes | true | false | 是否使用独立的存储卷(storage volumes)集合 |
项目限制:limits.*
limits.*为项目内容器与虚拟机使用的资源定义硬上限。一部分选项限制项目内允许的实体数量(如limits.containers、limits.networks),另一部分限制项目内所有实例的资源用量总和(如limits.cpu、limits.processes)。后者通常作用于每个实例上配置的实例资源限制(直接配置或经配置文件配置),而非实际占用的资源。
例如:把项目的limits.memory设为50GiB,则项目内所有实例的limits.memory配置值之和必须小于 50 GiB;把limits.cpu设为100,则所有实例的limits.cpu之和必须低于 100。
使用 limits 时必须满足以下条件:
- 设置了某个
limits.*且实例存在对应配置时,项目内所有实例都必须定义对应配置(直接或经配置文件); limits.cpu不能与limits.cpu.allowance(即 CPU 集合/范围形式的实例配置)同时使用——项目内每个实例的limits.cpu必须是一个数字,不能是集合或范围;limits.memory必须设置为绝对值,不能是百分比。
| 配置项 | 类型 | 说明 |
|---|---|---|
limits.containers | integer | 项目内最多可创建的容器数 |
limits.cpu | integer | 项目内使用的 CPU 总数上限(各实例limits.cpu之和) |
limits.disk | string | 项目内所有实例卷、自定义卷与镜像的磁盘空间总量上限 |
limits.disk.pool.<POOL_NAME> | string | 项目在指定存储池上的磁盘空间上限 |
limits.instances | integer | 项目内最多可创建的实例(容器+虚拟机)总数 |
limits.memory | string | 项目内存用量上限(各实例limits.memory之和,绝对值) |
limits.networks | integer | 项目可拥有的网络数上限 |
limits.processes | integer | 项目内进程数上限(各实例limits.processes之和) |
limits.virtual-machines | integer | 项目内最多可创建的虚拟机数 |
项目限制(安全向):restricted.*
要阻止项目内实例访问安全敏感特性(如容器嵌套、raw LXC 配置),把restricted设为true;随后可用各种restricted.*选项单独放行被整体屏蔽的个别特性。注意三点:
restricted必须为true,restricted.*选项才生效;restricted=false时修改restricted.*无效;- 把全部
restricted.*设为allow,等价于把restricted设为false; - 大多数
restricted.*是二元开关(block/allow),少数支持更细粒度的取值。
例如,限制项目屏蔽所有安全敏感特性、但放行容器嵌套:
incus project set <project_name> restricted=true incus project set <project_name> restricted.containers.nesting=allow主要选项一览(默认值标注于括号内):
| 配置项 | 默认值 | 取值与语义 |
|---|---|---|
restricted | false | 是否屏蔽安全敏感特性;restricted.*生效的前提 |
restricted.backups | block | 是否阻止创建实例/卷备份(allow/block) |
restricted.cluster.groups | — | 允许指定的集群组(字符串列表),未指定则全部可访问 |
restricted.cluster.target | block | 是否阻止指定集群成员(allow/block) |
restricted.containers.interception | block | 系统调用拦截选项(allow/block/full);allow放行通常安全的拦截项,文件系统挂载仍被阻止 |
restricted.containers.lowlevel | block | 低层容器选项如raw.lxc、raw.idmap、volatile.*(allow/block) |
restricted.containers.nesting | block | 是否允许嵌套运行 Incus(allow/block,放行security.nesting) |
restricted.containers.privilege | unprivileged | 特权容器管控:unprivileged禁止security.privileged=true;isolated额外禁止security.idmap.isolated=false;allow无限制 |
restricted.devices.disk | managed | 磁盘设备:block除 root 外禁用全部;managed仅允许带pool=的磁盘;allow无限制 |
restricted.devices.disk.paths | — | restricted.devices.disk=allow时,用逗号分隔的路径前缀列表限制 disk 设备source,留空则全部允许 |
restricted.devices.gpu | block | 是否禁用gpu类型设备(allow/block) |
restricted.devices.infiniband | block | 是否禁用infiniband类型设备 |
restricted.devices.nic | managed | 网络设备:block禁用全部;managed仅允许带network=的 NIC;allow无限制 |
restricted.devices.pci | block | 是否禁用pci类型设备 |
restricted.devices.proxy | block | 是否禁用proxy类型设备 |
restricted.devices.unix-block | block | 是否禁用unix-block类型设备 |
restricted.devices.unix-char | block | 是否禁用unix-char类型设备 |
restricted.devices.unix-hotplug | block | 是否禁用unix-hotplug类型设备 |
restricted.devices.usb | block | 是否禁用usb类型设备 |
restricted.idmap.gid/restricted.idmap.uid | — | 允许出现在实例raw.idmap中的宿主机 GID/UID 范围 |
restricted.images.servers | — | 允许使用的镜像服务器域名列表(逗号分隔),未设置则全部可访问 |
restricted.networks.access | — | 允许使用的网络名列表(逗号分隔),未设置则全部可访问;开启features.networks时,列出的 default 项目网络会被共享进本项目且其名称不可用于本项目自有网络;依赖restricted.devices.nic |
restricted.networks.integrations | — | 允许项目网络使用的网络集成(integrations)列表 |
restricted.networks.subnets | block | 允许分配的上联(uplink)网络子网列表,格式<uplink>:<subnet> |
restricted.networks.uplinks | — | 允许作为项目网络上联的网络名列表 |
restricted.networks.zones | block | 允许使用的网络区域(或区域下条目)列表 |
restricted.snapshots | block | 是否阻止创建实例/卷快照 |
restricted.storage-pools.access | — | 允许使用的存储池名列表,未设置则全部可访问 |
restricted.virtual-machines.lowlevel | block | 低层 VM 选项如raw.qemu、volatile.*(allow/block) |
restricted.virtual-machines.nesting | allow | 嵌套虚拟化(allow/block;block时所有 VM 必须security.nesting=false) |
项目专属配置(project-specific)
除上述三类外,部分服务器选项可在项目级覆盖,同时支持添加用户元数据:
| 配置项 | 说明 |
|---|---|
backups.compression_algorithm | 本项目备份使用的压缩算法(bzip2/gzip/lz4/lzma/xz/zstd/none) |
images.auto_update_cached | 是否自动更新项目内缓存的镜像 |
images.auto_update_interval | 缓存镜像更新检查间隔(小时),设为0禁用 |
images.compression_algorithm | 项目内新镜像的压缩算法(取值同上) |
images.default_architecture | 混合架构集群中的默认架构 |
images.remote_cache_expiry | 未使用的远程缓存镜像在项目中的过期天数 |
network.hwaddr_pattern | 集群内使用的 MAC 地址模板(如10:66:6a:xx:xx:xx,每个x由0–f随机字符替换;需自行评估碰撞概率) |
user.* | 用户提供的自由格式键/值对 |
深入源码:项目机制如何落地
阅读 internal/server/project/project.go 可以直观理解项目隔离的底层设计:
- 命名空间前缀机制:
Instance()用<project>_前缀区分不同项目的同名实例,DNS()用<instance>.<project>后缀区分 DNS 名,StorageVolume()则统一用<project>_前缀处理存储卷。实例名不允许包含_分隔符(而项目名可以),因此InstanceParts()从右侧做一次分割即可可靠解析项目与实例名。 - 卷归属的动态判定:
StorageVolumeProject()/StorageVolumeProjectFromRecord()展示了实体归属逻辑的复杂度——镜像卷作为缓存始终归default项目;自定义卷只有在项目开启features.storage.volumes时才归本项目,否则归default项目;其余卷类型总是归指定项目。这说明"继承 vs 隔离"不是简单的布尔判断,而是按实体类型精细化处理的。 - 测试佐证:internal/server/project/project_test.go 与权限相关测试覆盖了项目解析与权限校验逻辑,cmd/incus/project.go 则是
incus project系列子命令的 CLI 入口。
常见问题速查
| 问题 | 解决办法 |
|---|---|
新项目创建实例报No root device could be found | features.profiles初始为true,项目访问不到 default 项目的默认配置文件。执行incus profile show default --project default \| incus profile edit default复制配置,或用incus profile device add补 root 磁盘 |
| 项目内已创建实例,想改 feature | 必须先把项目内所有实例删除(feature 随实例存在而锁定) |
| unset 后配置与创建时不一致 | unset回到默认值而非初始值(如features.profiles默认false、初始true) |
| 想限制每个用户的资源但允许备份 | incus project create <name> --config restricted=true --config restricted.backups=allow,再配合limits.*设置配额 |
| 客户端只能看特定项目 | 证书添加时用--restricted --projects,或incus config trust edit <fingerprint>修改现有证书(restricted=true+ 指定projects) |
相关文档路径:项目总览 doc/projects.md、创建与配置 doc/howto/projects_create.md、使用多项目 doc/howto/projects_work.md、按用户约束 doc/howto/projects_confine.md、配置参考 doc/reference/projects.md、配置选项源文件 doc/config_options.txt。
- 后端
- 虚拟化
- 容器运行时
【免费下载链接】incus
Powerful system container and virtual machine manager
相关推荐
Apache DolphinScheduler多租户隔离:用户权限与资源配额管控实践指南 🚀
Apache DolphinScheduler多租户隔离:用户权限与资源配额管控实践指南 🚀 Apache DolphinScheduler作为现代化的数据编
任务调度大数据后端微服务WaveTerm:如何用零代码小部件系统重构你的终端工作流
WaveTerm:如何用零代码小部件系统重构你的终端工作流 在传统的终端工作流中,开发者需要在命令行界面、文件管理器、Web浏览器和系统监控工具之间频繁切换,这
开发工具桌面应用AI 应用CLIKubeOperator多租户隔离:项目级资源配额管理完全指南
KubeOperator多租户隔离:项目级资源配额管理完全指南 KubeOperator作为一款开源的轻量级Kubernetes发行版,为企业用户提供了强大的多
云原生容器编排运维后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考