news 2026/10/10 9:01:08

Incus `disk` 设备类型完全指南:存储卷挂载、主机路径共享与 VM 磁盘配置

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Incus `disk` 设备类型完全指南:存储卷挂载、主机路径共享与 VM 磁盘配置
  • 后端
  • 虚拟化
  • 容器运行时

【免费下载链接】incus

Powerful system container and virtual machine manager

项目地址:https://gitcode.com/gh_mirrors/inc/incus
点击查看免费下载

导读:disk是 Incus 中最核心的设备类型之一,负责为容器和虚拟机实例提供附加存储。本文以 doc/reference/devices_disk.md 为骨架,系统讲解其九类source来源(存储卷、主机路径、Ceph、ISO、tmpfs 等)的挂载方法、initial.*初始配置的三种应用场景,以及全部设备选项的取值与默认值,并结合 internal/server/device/disk.go 的实现细节说明底层行为。读完本文,你将能够熟练为实例配置根磁盘、自定义卷、VM 云配置盘与内存文件系统。

一、disk设备是什么

disk设备为实例提供附加存储。对于容器,它本质上是实例内部的一个挂载点——既可以是宿主机上已有文件或目录的 bind-mount,也可以是对块设备的常规挂载。对于虚拟机,文件系统类挂载通过9p或virtiofs(可用时)共享宿主侧的挂载点或目录,块设备则作为 VirtIO 磁盘挂载。

`disk` 设备类型同时支持容器和虚拟机,且两者都支持热插拔(hotplugging)。

这里有一个值得注意的细节:设备名会影响设备生成的序列号(serial)。当设备名超过 14 个字符(nvme和virtio-blk总线)或 30 个字符(virtio-scsi总线)时,Incus 会对设备值进行哈希,以保证生成的序列号不超过长度约束,而设备名本身保持不变。

从源码看,disk设备在 internal/server/device/disk.go 中定义为一个disk结构体,实现了设备接口的Start、Stop、Add、Remove、Update等全部生命周期方法,并通过 CanHotPlug() 决定是否支持热插拔。

二、disk设备的九种来源(source类型)

source选项的值决定了磁盘设备的类型。创建磁盘设备的基础命令是:

incus config device add <实例名> <设备名> disk [key=value...]

1. 存储卷(Storage volume)——最常用

指定存储卷的名称作为source:

incus config device add <实例名> <设备名> disk pool=<存储池名> source=<卷名> [path=<实例内路径>]
  • path对文件系统卷是必需的,对块卷则不需要。
  • 也可以使用incus storage volume attach命令挂载存储卷,两条命令底层使用同一机制。
  • 子路径挂载:source=<卷名>/<子路径>语法可以将自定义卷的某个子路径挂载进实例。如果该子路径在自定义卷中不存在,设备启动时会根据initial.XYZ配置键自动创建,并设置对应的所有权与权限。

2. 主机路径(Path on the host)

共享宿主机上的文件系统或块设备:

incus config device add <实例名> <设备名> disk source=<宿主机路径> [path=<实例内路径>]

同样,path对文件系统是必需的,对块设备则不需要。源码中通过 sourceIsLocalPath() 判断source是否为宿主机本地路径——空值、cloud-init:config、agent:config、tmpfs:、tmpfs-overlay:以及 Ceph/CephFS 前缀都不视为本地路径。

3. Ceph RBD

如果已有外部托管的 Ceph RBD 卷,可以这样挂载:

incus config device add <实例名> <设备名> disk source=ceph:<存储池名>/<卷名> ceph.user_name=<用户名> ceph.cluster_name=<集群名> [path=<实例内路径>]

path对文件系统必需,对块设备不需要。

4. CephFS

挂载外部托管的 Ceph 文件系统:

incus config device add <实例名> <设备名> disk source=cephfs:<fs_name>/<路径> ceph.user_name=<用户名> ceph.cluster_name=<集群名> path=<实例内路径>

注意 CephFS 是文件系统,因此path始终必需。源码中以strings.HasPrefix(d.config["source"], "cephfs:")识别该来源(sourceIsCephFs())。

5. ISO 文件(仅 VM)

将 ISO 文件作为虚拟机内部的 ROM 设备添加:

incus config device add <实例名> <设备名> disk source=<宿主机文件路径>

该来源仅适用于虚拟机,不需要path。

6. VMcloud-init配置盘(仅 VM)

由cloud-init.vendor-data与cloud-init.user-data配置键生成一个 cloud-init 配置 ISO,挂载到虚拟机上,VM 内的 cloud-init 在启动时会检测该驱动器并应用配置:

incus config device add <实例名> <设备名> disk source=cloud-init:config

源码中该值对应常量diskSourceCloudInit = "cloud-init:config"(disk.go),ISO 的生成实现在 generateVMConfigDrive()。

7. VMagent配置盘(仅 VM)

生成一个包含 agent 二进制、配置文件和安装脚本的 ISO。这在9p不受支持的环境中加载 agent 的替代方案:

incus config device add <实例名> <设备名> disk source=agent:config

对应常量diskSourceAgent = "agent:config",生成逻辑见 generateVMAgentDrive()。

8. Tmpfs(内存文件系统)

incus config device add <实例名> <设备名> disk source=tmpfs: path=<实例内路径> [size=<大小>] [initial.uid=<uid>] [initial.gid=<gid>] [initial.mode=<模式>]

source与path都必需。它会在实例内创建一个tmpfs挂载,支持通过可选属性设置大小、所有权与权限。注意源码中 CanHotPlug() 对tmpfs:与tmpfs-overlay:返回false,即这两种来源不支持热插拔。

9. Tmpfs with overlayfs(tmpfs-overlay:)

需要 tmpfs 行为又叠加 overlayfs 语义时使用:

incus config device add <实例名> <设备名> disk source=tmpfs-overlay: path=<实例内路径> [size=<大小>] [initial.uid=<uid>] [initial.gid=<gid>] [initial.mode=<模式>]

source与path都必需,且目标path必须已存在于容器内部。它提供带 overlayfs 处理的临时内存文件系统。

三、根磁盘设备的初始卷配置(initial.*)

initial.*前缀的配置用于新建实例的根磁盘设备,只在实例创建时生效,因此可以为实例创建独立于存储池默认设置的独特配置。

例如,给现有 profile 添加zfs.block_mode初始配置,此后使用该 profile 创建的每个新实例都会生效:

incus profile device set <profile_name> <设备名> initial.zfs.block_mode=true

也可以直接在创建实例时设置:

incus init <镜像> <实例名> --device <设备名>,initial.zfs.block_mode=true

注意:initial.*不能与自定义卷选项一起使用,也不能用于设置卷的大小。

四、initial.uid、initial.gid与initial.mode的三种应用场景

这三个配置项在三种不同场景下发挥作用:

  1. 根磁盘设备:传递给存储驱动,在创建时设置实例根卷的所有权与模式(受驱动支持性限制)。
  2. tmpfs:与tmpfs-overlay:设备:被转换为底层tmpfs挂载的uid=、gid=、mode=挂载选项。
  3. 带子路径的自定义卷(如source=myvol/sub/path):作为设备启动时自动创建子目录的所有权与模式。

在所有场景中,initial.uid和initial.gid默认值为0,initial.mode默认值为0711(八进制)。

五、initial.copy:首次启动时向空卷复制容器内容

initial.copy适用于挂载到容器的自定义卷磁盘。当设置为true时,容器启动时若卷(或其子路径)为空,会将容器自身文件系统中path处的现有内容复制进卷。这正好符合应用容器将数据随镜像打包在挂载目录中的使用习惯(典型场景如path=/var/lib/mysql的数据库目录)。

关键行为细节:

  • 每个卷只复制一次,是否已复制记录在卷的volatile.initial.copied键中。已有内容的卷不会被触碰,复制后又被清空的卷也不会再次复制。
  • 复制时保留所有权、权限、时间戳与扩展属性,并按容器的 ID map 转换;符号链接原样复制;设备节点会被跳过。
  • 复制只在启动已停止的容器时执行,因此不能把带initial.copy的设备添加到运行中的容器上。

完整示例:

incus storage volume create <存储池名> <卷名> incus config device add <实例名> <设备名> disk pool=<存储池名> source=<卷名> path=/var/lib/mysql initial.copy=true

从实现看,disk_initial_copy.go 中的initialCopy()会先通过OperationLockName("InitialCopy", ...)与其他挂载同一卷的实例做串行化,再检查volatile.initial.copied,确认卷为空(跳过lost+found)后才执行复制,最后写入volatile.initial.copied = "true"。这也印证了"仅复制一次"的语义是有数据库层面保证的。

六、disk设备全部选项参考

以下选项由 doc/config_options.txt 的devices-disk配置组生成,同时可以在 validateConfig() 的校验规则中找到对应实现。

选项类型默认值必填说明
sourcestring—是文件系统或块设备的来源(见上文的类型说明)
pathstring—是(仅文件系统设备)实例内挂载路径。容器支持挂载文件系统设备、其中路径与单个文件;VM 支持文件系统设备与其中路径,不支持单个文件
poolstring—否磁盘设备所属的存储池(仅适用于 Incus 托管的存储卷)
readonlyboolfalse否是否只读挂载
recursiveboolfalse否是否递归挂载源路径
requiredbooltrue否源不存在时是否报错(optional为已废弃的旧名)
shiftboolfalse否建立 shifting overlay,将源 UID/GID 转换为与实例匹配(仅容器)
propagationstringprivate否bind-mount 在实例与宿主机之间的共享方式,取值:private、shared、slave、unbindable、rshared、rslave、runbindable、rprivate
raw.mount.optionsstring—否文件系统特定的挂载选项
sizestring—否磁盘大小(支持多种后缀,见单位说明),仅支持根文件系统/
size.statestring—否同size,但用于 VM 保存运行状态的文件系统卷
ceph.cluster_namestringceph否Ceph 集群名(Ceph/CephFS 来源必需)
ceph.user_namestringadmin否Ceph 集群用户名(Ceph/CephFS 来源必需)
attachedbooltrue否仅 VM:磁盘是挂载状态还是弹出状态
boot.prioritystring0否仅 VM:启动优先级(数值越大越先启动),skip表示跳过
dependentboolfalse否磁盘是否依赖实例
initial.*string—否根磁盘设备的初始卷配置,覆盖存储池默认卷配置
io.busstring块:virtio-scsi;文件系统:auto否仅 VM:覆盖设备总线。块设备可选nvme、virtio-blk、virtio-scsi(默认)、usb;文件系统可选9p、auto(默认,优先virtiofs否则9p)、virtiofs
io.cachestringnone否仅 VM:覆盖缓存模式。块设备可选none(默认)、writeback、unsafe;文件系统可选none(默认)、metadata、unsafe
wwnbool空否仅 VM:设置磁盘 World Wide Name(仅在virtio-scsi总线上支持)
limits.maxstring—否读写双向 I/O 限额(byte/s 和/或 IOPS),等价于同时设置limits.read与limits.write
limits.readstring—否读 I/O 限额(byte/s 与/或iops后缀),两者同时指定用逗号分隔
limits.writestring—否写 I/O 限额,规则同limits.read
limits.max.burststring—否读写双向的突发上限
limits.max.burst.lengthstring1s否突发上限可持续的时间
limits.read.burststring—否读突发上限(仅 VM),可持续limits.read.burst.length
limits.read.burst.lengthstring1s否读突发在回落到持续限额前可维持的时间
limits.write.burststring—否写突发上限(仅 VM)
limits.write.burst.lengthstring1s否写突发可维持的时间

关于io.bus的注意点:9p不支持热插拔,virtiofs不支持在线迁移(live migration)。auto模式会在migration.stateful未设为true且宿主机支持virtiofsd时优先使用virtiofs,否则回退到9p。

在实现层面,I/O 限额配置被解析为diskBlockLimit结构(disk.go),并通过limits.*系列键(diskLimitsConfigKeys)检测设备是否配置了限额;容器场景下最终会转换为 cgroup 的块设备读写限制。

七、迁移与热插拔行为

disk设备的迁移能力由 CanMigrate() 决定:

  • 根磁盘(path=/)总是可迁移的;
  • 远程存储池上的磁盘(如 Ceph)可迁移;
  • 特殊来源(cloud-init、agent、tmpfs 等)可迁移;
  • 其余本地路径磁盘不可迁移。

热插拔则由 CanHotPlug() 控制:tmpfs:/tmpfs-overlay:不可热插拔;io.bus为空或auto时,会读取 volatile 键中记录的io.bus是否为9p来判断——因为auto在启动时无法预知挂载类型,选择尽早失败更安全。

八、典型实战组合

以下命令组合覆盖了最常见的磁盘使用场景:

场景一:挂载自定义存储卷并自动填充容器数据(数据库目录)

incus storage volume create default mydb incus config device add c1 data disk pool=default source=mydb path=/var/lib/mysql initial.copy=true

场景二:共享宿主目录(只读)

incus config device add c1 share disk source=/srv/data path=/mnt/data readonly=true

场景三:给 VM 添加 ISO 安装镜像并设置启动优先级

incus config device add vm1 iso disk source=/home/iso/installer.iso boot.priority=10

场景四:临时内存盘

incus config device add c1 scratch disk source=tmpfs: path=/mnt/scratch size=1GiB

结合 doc/reference/devices_disk.md、配置组 doc/config_options.txt 与设备实现 internal/server/device/disk.go 一同查阅,可以覆盖从命令行挂载到内核级挂载语义的完整链路。

  • 后端
  • 虚拟化
  • 容器运行时

【免费下载链接】incus

Powerful system container and virtual machine manager

项目地址:https://gitcode.com/gh_mirrors/inc/incus
点击查看免费下载

相关推荐

上一篇:APK Installer 内置关于文档解析:开源合规、数据安全与第三方依赖库全景(以匈牙利语本地化版 About.hu-HU.md 为例)
下一篇:AIRI 接入 Replicate 云图像生成:从 API Token 配置到源码级原理解析

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/10 8:54:27

水流量示意图制作全指南:类型划分、工具选型与模板复用

干过给排水、环保、水利项目的人都有体会&#xff1a;方案汇报时&#xff0c;一张干净的水流量示意图&#xff0c;比满屏数据表格更能说服人。无论是污水厂提标改造的工艺流程图&#xff0c;还是城市供水管网的水量分配图&#xff0c;甚至是科研论文里的测流时序曲线&#xff0…

作者头像 李华
网站建设 2026/10/10 8:54:19

Steve Brunton | Reinforcement Learning | 笔记 | Lecture 3 | 深度强化学习在流体动力学与控制中的应用

目录前言1. 引言2. 强化学习框架回顾3. 流体动力学中的应用分类4. 鱼群游动研究5. 强化学习加速计算6. 综述与常用算法7. 流动控制8. 非稳态流体环境中的飞行控制9. 总结结语参考前言 学习 Steven Brunton 讲授的强化学习入门概述视频&#xff0c;本篇文章记录第三讲&#xff1…

作者头像 李华
网站建设 2026/10/10 8:51:54

Python餐厅菜品推荐系统:爬虫+协同过滤+Flask全栈实现

简介&#xff1a;本资源是一套基于Python开发的餐厅菜品推荐系统完整实现&#xff0c;面向人工智能初学者、数据科学爱好者及Web应用开发者&#xff0c;解决餐饮场景中个性化菜品推荐与用户行为分析的实际问题。项目涵盖从数据采集&#xff08;含爬虫模块spider-main&#xff0…

作者头像 李华
网站建设 2026/10/10 8:51:19

Valhalla /status 服务 API 详解:健康检查端点与 Tileset 状态查询实战

后端 【免费下载链接】valhalla Open Source Routing Engine for OpenStreetMap 项目地址&#xff1a; https://gitcode.com/gh_mirrors/va/valhalla 点击查看 免费下载 导读 /status 是 Valhalla 路由引擎暴露的一个轻量级状态服务端点&#xff1a;默认返回 HTTP 200 以及 v…

作者头像 李华
网站建设 2026/10/10 8:48:11

TypeScript到C#:OpenAI Codex SDK的.NET原生移植全攻略

从TypeScript到C#&#xff1a;手把手把OpenAI Codex SDK完整移植成.NET原生SDK我是在一条Windows构建流水线上被逼着走上这条路的。当时团队要在.NET后端里集成OpenAI Codex的编码智能体能力&#xff0c;按照官方文档&#xff0c;标准做法是npm install一个TypeScript SDK包。可…

作者头像 李华