一文讲透EVE-NG镜像识别的底层机制
<h1>1 同样是qcow2,为什么有的能跑有的不能?一文讲透EVE-NG镜像识别的底层机制</h1><hr />
<h2>1.1 现象引入</h2>
<p>把一个qcow2文件扔进EVE-NG目录,重启服务——设备列表里没有它。换个目录名再试,出现了,但启动后黑屏。再改一下文件名,终于能跑。同样的文件,三个结果。</p>
<p>这不是玄学。EVE-NG识别镜像从来不是"有文件就行",背后是一套严格的链式校验:目录前缀匹配模板→磁盘文件名映射控制器→启动时创建差分盘→权限校验通过。任何一环断开,镜像要么不可见,要么启动失败。</p>
<p>这篇文章把这条链路拆干净。</p>
<hr />
<h2>1.2 概念拆解:三层识别机制</h2>
<h3>1.2.1 第一层:目录前缀匹配(门牌号)</h3>
<p>快递送到楼栋,门牌号对不上就不送。EVE-NG的模板扫描逻辑同理。</p>
<p>镜像目录必须放在 <code>/opt/unetlab/addons/qemu/</code> 下,且目录名格式严格为 <code>{模板前缀}-{版本}</code>。前缀必须与 <code>/opt/unetlab/html/templates/intel/</code>(或 <code>amd/</code>)下的YML模板文件名一致。</p>
<p>举个例子:</p>
<ul>
<li>模板文件 <code>asav.yml</code> → 镜像目录名必须以 <code>asav-</code> 开头,如 <code>asav-9.20.1</code></li>
<li>模板文件 <code>fortinet-FGT.yml</code> → 镜像目录名必须以 <code>fortinet-FGT-</code> 开头,如 <code>fortinet-FGT-7.0.5</code></li>
</ul>
<p>如果目录前缀与任何模板文件名不匹配,EVE-NG会根据 <code>config.php</code> 中的 <code>TEMPLATE_DISABLED</code> 常量决定行为:</p>
<ul>
<li><code>.hided</code>(默认):设备下拉列表中直接隐藏该模板</li>
<li><code>.missing</code>:显示在列表中,但名称后追加 <code>.missing</code> 标记</li>
</ul>
<p>源码层面,<code>init.php:148-152</code> 在页面加载时扫描 addon 目录,将匹配结果与模板名前缀比对,不匹配的直接标记为不可用。</p>
<p>注意:模板YML文件并不预设磁盘控制器类型——控制器完全由镜像目录中的磁盘文件名在运行时自动检测决定(见第二层)。</p>
<h3>1.2.2 第二层:磁盘文件名→控制器映射(钥匙)</h3>
<p>同一把锁,钥匙型号不对插不进去。磁盘文件名不是随便取的——它决定了QEMU用哪种控制器加载这块盘。</p>
<p>EVE-NG支持8种磁盘控制器格式,每种文件名前缀对应不同的QEMU启动参数:</p>
<table>
<thead>
<tr>
<th>磁盘文件名</th>
<th>控制器类型</th>
<th>典型设备</th>
</tr>
</thead>
<tbody>
<tr>
<td>virtioa.qcow2</td>
<td>VirtIO(半虚拟化)</td>
<td>华为/思科/F5/Juniper等主流设备</td>
</tr>
<tr>
<td>hda.qcow2</td>
<td>IDE</td>
<td>旧型号设备(ACS、CheckPoint等)</td>
</tr>
<tr>
<td>scsia.qcow2</td>
<td>SCSI</td>
<td>部分存储类设备</td>
</tr>
<tr>
<td>sataa.qcow2</td>
<td>SATA</td>
<td>VMware vCenter等</td>
</tr>
<tr>
<td>lsia.qcow2</td>
<td>LSI SCSI</td>
<td>部分老旧镜像</td>
</tr>
<tr>
<td>megasasa.qcow2</td>
<td>MegaSAS</td>
<td>Cisco vWLC等</td>
</tr>
<tr>
<td>virtidea.qcow2</td>
<td>VirtIO-IDE</td>
<td>Nokia Timos 19等</td>
</tr>
<tr>
<td>cdrom.iso</td>
<td>CDROM</td>
<td>Arista vEOS启动盘等</td>
</tr>
</tbody>
</table>
<p>多盘场景下,文件名末尾字母按字母序递增:virtioa、virtiob、virtioc...</p>
<p>关键点:控制器类型由磁盘文件名自动检测(而非模板预设),用户在Web界面添加节点时不可更改。运行时 <code>__node.php:990-1069</code> 扫描镜像目录,通过正则匹配文件名前缀来决定QEMU启动参数——<code>virtioa.qcow2</code> 对应 <code>-drive if=virtio</code>,<code>hda.qcow2</code> 对应 <code>-hd</code>(IDE),以此类推。如果镜像文件名与设备预期的控制器类型不匹配(例如设备需要VirtIO但文件名为 <code>hda.qcow2</code>),QEMU启动命令会传入错误的 <code>-drive if=xxx</code> 或 <code>-hd</code> 参数,虚拟机内核无法识别启动盘——表现就是黑屏。</p>
<p>源码证据:<code>__node.php:990-1069</code> 通过正则匹配8种控制器前缀 + <code>.qcow2$</code> 后缀来识别磁盘文件。</p>
<h3>1.2.3 第三层:差分盘与权限(引擎点火)</h3>
<p>模板是蓝图,差分盘是施工图,权限是施工许可证。三层缺一不可。</p>
<p><strong>差分盘机制</strong>:当你在实验中启动一个QEMU节点时,EVE-NG不会直接读写模板镜像。而是通过 <code>qemu-img create -b {模板路径}/virtioa.qcow2 -f qcow2 {运行路径}/virtioa.qcow2</code> 创建一个Copy-On-Write(COW)叠加层。所有运行时变更写入差分盘,模板文件保持不变。</p>
<p>差分盘生成位置:<code>/opt/unetlab/tmp/{tenant_id}/{lab_id}/{node_id}/virtioa.qcow2</code></p>
<p>这意味着:</p>
<ul>
<li>同一个模板镜像可以被多个节点同时使用,互不干扰</li>
<li>Wipe节点 = 删除差分盘,下次启动时重新从模板创建干净的叠加层</li>
<li>差分盘损坏不影响模板,Wipe即可恢复</li>
</ul>
<p><strong>权限校验</strong>:上传镜像后必须执行 <code>/opt/unetlab/wrappers/unl_wrapper -a fixpermissions</code> 修复权限。这条命令不只是简单的chown——它按照EVE-NG内部的权限规范设置文件属主、属组和特殊权限位。手动执行 <code>chown</code> 会破坏特殊权限,导致QEMU无法读取磁盘文件,节点启动失败。</p>
<p>源码证据:<code>cli.php:755-830</code> 的 <code>prepareNode()</code> 函数依次执行创建运行目录、复制skeleton、创建差分盘、创建TAP接口、桥接虚拟网络。整个流程无原子性保证——任何一步失败,已创建的差分盘、TAP接口、目录不会自动回滚,需手动清理或Wipe。</p>
<hr />
<h2>1.3 原理图解:镜像从上传到启动的完整链路</h2>
<pre><code class="language-text">用户上传qcow2文件
|
v
放入 /opt/unetlab/addons/qemu/{前缀}-{版本}/
|
v
init.php 扫描:目录前缀 <--> YML模板文件名 匹配?
|-- 不匹配 --> TEMPLATE_DISABLED 控制显示:
| .hided = 隐藏/.missing = 标记缺失
|
v 匹配
设备下拉列表显示该镜像,用户拖拽节点到画布
|
v 点击启动
apiStartLabNode()
|
v
unl_wrapper -a start
|
v
prepareNode() 执行:
1. MkHomeDir() --> 创建 /opt/unetlab/tmp/{tid}/{lid}/{nid}/
2. cp -ar skeleton/* --> 复制skeleton到jail/
3. mount -B -o ro --> 只读挂载jail和qemu目录
4. qemu-img create -b ...--> 从模板创建差分盘(已存在则跳过)
5. addTap() --> 创建TAP接口 vunl{t}_{id}_{if}
6. connectInterface() --> TAP接入bridge(vnet{t}_{net_id})
|
v
getCommand() 生成 QEMU 启动命令行:
/opt/qemu/bin/qemu-system-x86_64
-drive file=/opt/unetlab/tmp/.../virtioa.qcow2,if=virtio <-- 控制器类型由文件名决定
-m {RAM} -smp {CPU}
-serial telnet:...:{console_port}
...
|
v
QEMU进程启动 --> 加载差分盘 --> 虚拟机识别磁盘 --> 启动OS
</code></pre>
<p><strong>四个常见卡点</strong>:</p>
<table>
<thead>
<tr>
<th>卡点</th>
<th>阶段</th>
<th>现象</th>
<th>根因</th>
</tr>
</thead>
<tbody>
<tr>
<td>目录前缀不匹配</td>
<td>模板扫描</td>
<td>设备列表不可见</td>
<td>目录名前缀与YML模板文件名不一致</td>
</tr>
<tr>
<td>磁盘控制器选错</td>
<td>QEMU参数生成</td>
<td>启动后黑屏</td>
<td>文件名与设备预期控制器不匹配(如设备需VirtIO但文件名为hda.qcow2),QEMU传了错误的if=参数</td>
</tr>
<tr>
<td>权限未修复</td>
<td>磁盘加载</td>
<td>启动失败/报IO错误</td>
<td>未执行fixpermissions,QEMU无法读取磁盘</td>
</tr>
<tr>
<td>差分盘残留损坏</td>
<td>差分盘创建</td>
<td>启动异常/配置残留</td>
<td>之前prepareNode()失败未回滚,差分盘存在但损坏</td>
</tr>
</tbody>
</table>
<hr />
<h2>1.4 实战案例:三个"同样qcow2"不同命运的真实场景</h2>
<h3>1.4.1 场景A:文件名对了,目录名错了</h3>
<p>操作:假如把FortiGate镜像放进 <code>/opt/unetlab/addons/qemu/fortigate-7.0.5/</code></p>
<p>结果:设备列表不显示。如果EVE-NG的模板文件名是 <code>fortinet-FGT.yml</code>,而目录前缀 <code>fortigate-</code> 与之不匹配,init.php扫描时直接跳过。</p>
<p>修正:</p>
<pre><code class="language-bash">mv /opt/unetlab/addons/qemu/fortigate-7.0.5 /opt/unetlab/addons/qemu/fortinet-FGT-7.0.5
</code></pre>
<p>规律:不确定前缀时,去 <code>/opt/unetlab/html/templates/intel/</code> 目录ls一下,YML文件名(去掉.yml)就是你要的前缀。</p>
<h3>1.4.2 场景B:目录名对了,磁盘控制器选错了</h3>
<p>操作:把华为AR1000v的qcow2文件命名为 <code>hda.qcow2</code></p>
<p>结果:设备列表显示了,但启动后黑屏。因为华为AR1000v的设备内核期望VirtIO控制器,而文件名 <code>hda.qcow2</code> 使 <code>__node.php</code> 自动匹配为IDE控制器,QEMU按 <code>-hd</code> 加载,华为VRP系统无法识别IDE盘。</p>
<p>修正:</p>
<pre><code class="language-bash">mv /opt/unetlab/addons/qemu/huawei-AR1000v-5.170/hda.qcow2 /opt/unetlab/addons/qemu/huawei-AR1000v-5.170/virtioa.qcow2
/opt/unetlab/wrappers/unl_wrapper -a fixpermissions
</code></pre>
<p>规律:不确定该用哪种控制器时,参考官方Qemu image namings文档的映射表,确认设备内核期望的控制器类型后,用对应的文件名命名。或者自行尝试几个不同控制器类型,注意wipe后启动</p>
<h3>1.4.3 场景C:一切正确,但忘了修权限</h3>
<p>操作:通过SFTP上传完镜像,直接去Web界面添加节点</p>
<p>结果:节点启动失败,QEMU无法读写磁盘文件。</p>
<p>修正:</p>
<pre><code class="language-bash">/opt/unetlab/wrappers/unl_wrapper -a fixpermissions
</code></pre>
<p>规律:每次上传镜像后,这条命令必须执行。不是可选项。而且绝对不能用 <code>chown -R</code> 替代——手动chown会破坏EVE-NG依赖的特殊权限位。</p>
<hr />
<h2>1.5 延伸思考</h2>
<p>理解了三层识别机制,你就拥有了"不查教程也能自己造镜像"的能力。流程就四步:</p>
<ol>
<li>对照 <code>/opt/unetlab/html/templates/intel/</code> 找到对应模板,确认目录前缀</li>
<li>查官方Qemu image namings映射表,确认设备期望的磁盘控制器类型,用对应文件名命名(virtioa 还是 hda)</li>
<li>上传qcow2到正确目录,命名为正确的磁盘文件名</li>
<li>执行 fixpermissions</li>
</ol>
<p>任何KVM镜像,只要宿主机CPU支持且资源足够,这套流程都能跑通。</p>
<hr />
<blockquote>
<p>本文为AI辅助生成草稿,技术细节请以实际环境验证为准。</p>
</blockquote>
<hr />
<h2>1.6 内容来源</h2>
<p>本文素材来自以下EVE-NG知识库文档,按章节索引:</p>
<h3>1.6.1 现象引入</h3>
<ul>
<li><strong>镜像文件导入后无法使用?——EVE-NG亚太代理中文区独家合作伙伴</strong>:7项检查要点,印证"同样的文件不同结果"的常见现象</li>
</ul>
<h3>1.6.2 第一层:目录前缀匹配</h3>
<ul>
<li><strong>04.3 <a href="http://xn--o1q717f4gf0y2a.md">镜像管理.md</a></strong> 1.1节:目录命名规则 <code>{前缀}-{版本}</code>;2.2节:模板文件名与镜像文件夹前缀的对应关系;3.2节:TEMPLATE_DISABLED机制(.hided/.missing)</li>
<li><strong>04.3 <a href="http://xn--o1q717f4gf0y2a.md">镜像管理.md</a></strong> R1校验记录:init.php:121-123 扫描 /opt/unetlab/addons/ 目录;init.php:148-152 模板名前缀匹配逻辑</li>
</ul>
<h3>1.6.3 第二层:磁盘文件名→控制器映射</h3>
<ul>
<li><strong>Qemu image namings——EVE-NG官方文档</strong>:完整的前缀-厂商-磁盘文件名映射表,8种HDD格式说明</li>
<li><strong>04.3 <a href="http://xn--o1q717f4gf0y2a.md">镜像管理.md</a></strong> 1.3节:源码验证的8种磁盘控制器格式(含megasas和cdrom.iso,旧文档遗漏的2种已修正);Step 5陷阱扫描:扩展名错误(.qcow应为.qcow2)、缺失格式(megasas/cdrom.iso)</li>
<li><strong>03.3 <a href="http://xn--jhqz85fiwjmnawex2qsn5a.md">节点与网络配置.md</a></strong> 3.1-3.2节:QEMU镜像目录命名前缀表、磁盘文件名与控制器对应表</li>
</ul>
<h3>1.6.4 第三层:差分盘与权限</h3>
<ul>
<li><strong>T-STARTNODE <a href="http://xn--6fr78aj04dliq.md">启动节点.md</a></strong> 维度5:prepareNode()完整流程(MkHomeDir→skeleton→mount→qemu-img create→addTap→connectInterface);差分盘路径映射;幂等性(已存在则跳过);无回滚机制</li>
<li><strong>T-STARTNODE <a href="http://xn--6fr78aj04dliq.md">启动节点.md</a></strong> 关键发现:差分盘"跳过已存在"特性、Wipe=删除差分盘、prepareNode失败残留不自动清理</li>
<li><strong>EVE-NG制作镜像常用命令</strong>:fixpermissions命令、qemu-img commit提交、qemu-img convert压缩</li>
<li><strong>镜像文件导入后无法使用?</strong> 第5项:fixpermissions修复权限;第6项:节点资源配置</li>
</ul>
<h3>1.6.5 原理图解</h3>
<ul>
<li><strong>T-STARTNODE <a href="http://xn--6fr78aj04dliq.md">启动节点.md</a></strong> 维度5:从apiStartLabNode到QEMU进程启动的完整调用链(api→unl_wrapper→start()→prepareNode→getCommand→qemu_wrapper)</li>
<li><strong>T-STARTNODE <a href="http://xn--6fr78aj04dliq.md">启动节点.md</a></strong> 维度4:控制台端口计算公式、skeleton目录结构</li>
</ul>
<h3>1.6.6 实战案例</h3>
<ul>
<li><strong>镜像文件导入后无法使用?</strong> 第1项:扩展名检查(Dynamips=.image / IOL=.bin / QEMU=.qcow2);第2项:目录及文件名规范</li>
<li><strong>Qemu image namings</strong>:fortinet-FGT前缀、huawei前缀、virtioa/hda映射关系</li>
<li><strong>Huawei AR1000v 镜像导入教程</strong>:重命名为virtioa.qcow2 + fixpermissions的完整步骤</li>
</ul>
<h3>1.6.7 延伸思考</h3>
<ul>
<li><strong>EVE-NG制作镜像常用命令</strong>:qemu-img create创建系统盘、qemu-img commit提交镜像、qemu-img convert -c压缩</li>
<li><strong>03.3 <a href="http://xn--jhqz85fiwjmnawex2qsn5a.md">节点与网络配置.md</a></strong> 4.1-4.2节:镜像格式(QCOW2/RAW/VMDK)及转换命令</li>
</ul>
页:
[1]