Jetson Orin Nano离线安装Ubuntu到NVMe SSD:摆脱SD卡的完整指南
给Jetson Orin Nano装Ubuntu最省事的姿势是插张SD卡直接刷但凡是把板子用在正经项目上的人很快就会受不了SD卡的速度和稳定性。我是在连续碰到SD卡写入校验失败之后下定决心换NVMe的把系统放到M.2 SSD上整机体验完全不是一个级别。可是Orin Nano又没有板载eMMC官方推荐烧录工具又绕不开联网下载JetPack组件想要“完全离线”手搓一个NVMe系统盘就得靠硬盘盒加上JetPack自带的磁盘镜像生成工具。这篇文章就把这条离线烧录路线从头到尾过一遍我踩过的坑和排查思路都会标出来确保你看完能直接复现。这个方案最大的价值在于不再需要SD卡中转不需要连接Jetson板子也不需要网络下载任何在线组件。你只要在一台普通Ubuntu电脑上把一个提前下载好的JetPack离线包解压用官方脚本生成一份完整的NVMe系统镜像再通过硬盘盒把镜像写入SSD最后把SSD插回Orin Nano通电开机就是Ubuntu。1. 为什么要把Orin Nano的系统装在NVMe上1.1 SD卡方案的三个痛点第一个痛点是速度。SD卡顺序读通常只有几十MB每秒随机读写性能更惨Ubuntu桌面点个应用要转圈Docker拉镜像、apt安装软件包、加载AI模型权重这些密集IO操作会被严重拖慢。开发板上的M.2插槽走PCIe总线NVMe SSD顺序读能轻松上千MB每秒随机读写性能也高出两三个数量级体验差距会在日常开发中反复被感知。第二个痛点是稳定性。嵌入式平台的供电和卡本身的质量参差不齐频繁掉电、长时间高负载写日志都可能让SD卡出现分区损坏或文件系统错误。我还遇到过系统跑到一半卡死在日志写入上的情况排查到最后才发现是SD卡过热降速导致IO超时。NVMe盘在这方面的可靠性要高得多散热条件也比插在卡槽里的SD卡好处理。第三个痛点是开发效率。传统SD卡刷机流程要反复拔卡、用读卡器写卡、再插回板子而且每次刷写都是一次全量写入。改用NVMe系统盘之后刷系统变成了“换一块SSD插上去”的操作准备两到三块盘就能在多个项目环境之间快速切换连系统备份都变得简单。1.2 离线烧录到底“离线”在哪传统用SDK Manager烧录Jetson设备需要联网登录NVIDIA账户、在线下载JetPack组件、再把组件安装到目标板。这个过程在网络不好、主机没有公网环境、或者需要批量部署多块板子的时候非常痛苦。本文走的是另一条路JetPack离线发布包Linux for Tegra的tar.gz包里面已经打包好了完整的rootfs、内核、bootloader和模块驱动。这些离线包配合Jetson Linux 36.3及以上版本自带的jetson-disk-image-creator.sh脚本可以在不连接任何Jetson设备的情况下直接生成一个完整的、可启动的磁盘镜像。这个镜像通过USB硬盘盒写入NVMe SSD后插回Orin Nano就能引导进入Ubuntu。整个过程不依赖SD卡不依赖网络甚至不需要把板子通过USB线连到主机上。主机只要满足基本的依赖安装条件剩下的流程全部在本地完成。1.3 这套方案更适合谁如果你的使用场景符合下面任意一条这套方案基本就是为你准备的手头有Jetson Orin Nano开发套件想把系统迁移到NVMe上让开发体验更流畅主机处于内网环境或网络不稳定下载在线组件困难需要完全离线安装需要一次给多块Orin Nano板子准备系统不想一块一块地插SD卡刷想保留多份独立的系统镜像比如一份用来跑深度学习训练验证一份用来做容器化应用测试随时插拔切换想熟悉Jetson启动链路和镜像生成原理后面遇到救砖、定制系统也不会慌。2. 准备工作硬件、镜像包与主机环境2.1 硬件清单与选型建议先列一张清单照着准备就行项目建议补充说明Jetson Orin Nano Developer Kit带板载M.2 Key M插槽的版本确认你的套件是NVMe版板子上有2280规格M.2接口NVMe SSDM.2 2280建议256GB起步JetPack 6.2桌面版加常用软件很容易吃掉60GB以上空间128GB会比较紧张NVMe硬盘盒USB 3.1/3.2主控优先JMS583或RTL9210B选兼容性好的主控避免写入过程中USB断开Ubuntu主机x86_64架构Ubuntu 20.04或22.04生成镜像时要chroot进ARM64 rootfsWindows和macOS不适合走完整流程显示器、键鼠HDMI显示器和USB键鼠首次启动配置Ubuntu用户时需要原装电源使用开发套件自带或官方推荐的USB-C电源供电不足可能导致NVMe掉盘或启动失败USB-C数据线备用少数情况下需要进Recovery模式刷bootloaderSSD选购有个容易被忽视的点优先选TLC颗粒的盘别只看价格买QLC。Jetson上跑的很多负载是高频小文件读写QLC盘在缓存耗尽后写入速度会跌到让人崩溃长期稳定性也不如TLC。品牌上选主流大厂就行不用追求顶级PCIe 4.0盘Orin Nano的M.2接口走PCIe通道顺序读取跑满千兆以上很容易瓶颈不在盘本身。硬盘盒方面我试过几个不同芯片的盒子JMS583和RTL9210B这两个主控的兼容性最稳在Ubuntu下即插即认dd写入过程中也不会出现USB断开重连的问题。便宜盒子偶尔会碰到写入大文件时掉线一旦掉线就要重新识别设备排查起来很烦。2.2 JetPack离线包的下载和识别JetPack离线包需要提前在能上网的电脑上下载然后拷贝到Ubuntu主机。具体位置在NVIDIA官网的JetPack下载页面选择对应Jetson Orin Nano Developer Kit的“Linux for Jetson Orin Nano Developer Kit”下载项拿到的是一个类似JetPack_6.2_Linux_JETSON_ORIN_NANO_TARGETS.tar.gz的文件。重点提醒一下下载的是“Target tarball”不是SDK Manager安装器。这个tar包里面已经包含了全部离线刷机所需的组件体积比较大大概有几个GB下载时留意磁盘空间。拷到Ubuntu主机后用以下命令解压mkdir ~/jetson_offline cd ~/jetson_offline tar xzf JetPack_6.2_Linux_JETSON_ORIN_NANO_TARGETS.tar.gz cd Linux_for_Tegra ls如果解压后没有出现Linux_for_Tegra目录检查一下是不是下载了不匹配的安装包版本。目录内部会有bootloader、rootfs、tools、flash.sh等关键内容有这些就说明包完整。2.3 主机环境与依赖安装生成镜像脚本依赖几个系统工具Ubuntu主机上需要提前装好。联网状态下执行sudo apt update sudo apt install -y qemu-user-static parted mtools dosfstools util-linux e2fsprogs如果主机完全离线可以在另一台同版本Ubuntu机器上执行apt download qemu-user-static parted ...把deb包下载下来拷到离线主机后用sudo dpkg -i *.deb安装。特别注意qemu-user-static一定要装因为脚本要chroot进ARM64的rootfs里执行一些二进制命令没有qemu模拟器这一步会直接报错。内存建议16GB以上8GB也能跑但生成镜像过程中会明显吃力。磁盘空间至少留出100GB空闲因为解压rootfs和生成镜像文件会同时占用大量空间。2.4 硬盘盒接入后的设备识别把NVMe SSD装进硬盘盒用USB线连接到Ubuntu主机然后执行lsblk -o NAME,SIZE,MODEL,SERIAL,TRAN一般会看到类似/dev/sdb的设备容量和硬盘盒里的SSD容量一致。如果硬盘盒是NVMe直通的某些情况下也可能出现/dev/nvme0n1但通过USB接入时大部分都是sdX形式。这一步要非常小心因为后续的dd命令会直接覆盖目标磁盘数据写错盘会把主机上的数据清空。我习惯用三个方法交叉确认先看容量是否匹配再对比插拔硬盘盒前后lsblk输出的差异最后看一眼lsblk里的MODEL和SERIAL信息是不是和硬盘盒里的硬件一致。3. 核心实操用官方脚本生成NVMe引导镜像3.1 解压后的目录与rootfs初始化进入Linux_for_Tegra目录后不要急着跑生成脚本先手动执行一次rootfs初始化把可能的环境问题提前暴露出来cd Linux_for_Tegra sudo ./apply_binaries.sh这步会把rootfs中原本需要目标板上执行的二进制适配工作放到主机上完成同时也会检查qemu-user-static是否正常。一切顺利的话输出末尾会显示完成提示。如果这步报错后面生成镜像基本也会挂问题越早暴露越容易定位。3.2 生成镜像的命令和参数解释jetson-disk-image-creator.sh位于Linux_for_Tegra/tools目录下核心命令长这样sudo tools/jetson-disk-image-creator.sh \ -o orin_nano_nvme.img \ -b jetson-orin-nano-devkit \ -e nvme0n1 \ -s 64G拆开解释一下每个参数的含义-o指定输出的镜像文件名名字随意但建议写到容易记住的位置-b目标板卡型号Orin Nano Developer Kit写jetson-orin-nano-devkit-e目标外部存储设备这一项是关键。写nvme0n1表示按NVMe硬盘的方式布局镜像生成的根文件系统参数、引导配置都会指向NVMe如果误写成mmcblk0生成的就是SD卡启动镜像插到NVMe上大概率起不来-s生成镜像的大小默认32GB。我习惯按SSD实际容量设置大一点比如256GB的SSD就设64GB或128GB避免后续装软件空间不够。生成过程根据机器性能和镜像大小一般需要十到二十分钟。日志输出非常冗长中间会看到大量文件拷贝、rootfs清理、分区写入的信息。看到类似Successfully created disk image的提示就说明成功了退出码为0。3.3 生成过程中到底发生了什么理解脚本内部做了四件核心事情能帮你在报错时快速定位问题第一准备rootfs。脚本会把Linux_for_Tegra/rootfs复制到临时工作目录然后chroot进去执行一些系统初始化、清理、安装依赖的操作这部分因为要跨架构模拟通常是耗时最长的阶段。第二生成分区表。脚本会根据-e参数确定设备类型然后按照Jetson的标准分区布局创建分区包括bootloader相关分区、boot分区、rootfs分区等。NVMe和SD卡的分区表结构在某些部分有差异所以-e参数必须匹配实际使用的硬件。第三写入bootloader。Jetson的启动依赖QSPI flash中的一级引导程序但磁盘镜像中也包含备用引导区域和完整的bootloader数据。这一步会把镜像支持从NVMe引导的必要文件放到正确位置。第四建立文件系统并打标。rootfs会写入ext4文件系统分区卷标、UUID、引导参数都会在此时确定。生成结束后你拿到的orin_nano_nvme.img就是一份和实际SSD容量无关的“半成品”写入目标盘后就能直接用。3.4 镜像文件与空间规划生成完成后查看一下镜像文件大小ls -lh orin_nano_nvme.img如果设了-s 64G文件大小会显示为64GB左右。生成这个文件会消耗等量磁盘空间主机上要留够。如果磁盘紧张可以用更小的-s 32G先顶着后期系统分区满了再扩容。有一个容易被忽略的点生成的镜像文件是按“固定大小”写的dd写入SSD时镜像末尾会留下部分未使用的空间这些剩余空间不会被自动合并到根分区。也就是说256GB的SSD写入64GB镜像后系统里df -h看到的可用空间可能还是60GB左右剩余空间在“未分区”区域。使用前或使用后做一次分区扩容即可操作方法写在后面。4. 写盘、装机与首次启动4.1 dd写盘命令与风险控制镜像生成好了接下来把NVMe SSD通过硬盘盒连到Ubuntu主机确认设备名后执行写入sudo dd iforin_nano_nvme.img of/dev/sdX bs64M statusprogress convfsync把/dev/sdX替换成你确认的设备名比如/dev/sdb。参数解释如下bs64M每次读写64MB块大小避免默认512字节块导致写入过慢statusprogress实时显示已写入的数据量和速度方便判断进度convfsync写入完成后把数据真正落盘避免拔线时数据还在缓存里。64GB镜像通过USB 3.1硬盘盒写入速度通常能到每秒几百MB几分钟就能完成。写盘完成后再执行一次sudo sync然后可以拔掉USB线。这里必须再次强调确认设备名的重要性。dd命令没有“你确定吗”的确认流程设备写错就是灾难。写入前建议把lsblk输出截图或抄下来反复比对容量和序列号。4.2 把SSD装回Jetson开发板从硬盘盒里取出NVMe SSD关掉Orin Nano电源把板载M.2插槽的散热片和固定螺丝打开SSD插到底后再拧回固定螺丝。插槽是M.2 Key M2280规格方向不对插不进去不用蛮力。装机时注意两点不要插入SD卡这个方案的意义就是验证纯NVMe启动如果插着SD卡板子可能会优先从SD卡引导干扰判断M.2位置确认压平很多启动不稳定是SSD没有完全插到位或者固定螺丝没拧紧导致的接触不良。接好HDMI显示器、USB键鼠插上原装电源适配器上电开机。4.3 首次上电进入Ubuntu上电后如果一切正常几秒钟内显示器就会出现NVIDIA Logo和Ubuntu启动画面。首次启动时会进入系统初始化向导按提示设置用户名、密码、时区、键盘布局等信息。这一步和普通Ubuntu安装后的初始化几乎一样。进入桌面后先验证系统确实在NVMe上lsblk df -h正常情况下lsblk里会看到nvme0n1设备根分区挂载在nvme0n1p1之类的分区上而不是mmcblk0。如果看到的是mmcblk0或启动卡住说明镜像生成或启动链路出了问题去第5节排查。5. 常见问题与排查技巧实录5.1 镜像生成阶段的典型报错报错现象可能原因解决办法qemu-aarch64-static: not found主机没装qemu-user-static安装后再重跑脚本parted: command not found缺parted工具sudo apt install partedFailed to generate disk image日志停在rootfs处理rootfs初始化不完整或目录权限损坏重新解压离线包先跑apply_binaries.sh磁盘空间不足导致生成中断镜像文件占满主机磁盘临时清理空间或减小-s容量脚本提示必须以root运行权限不足所有相关命令加sudo执行还有一个容易忽略的坑如果之前跑过别的版本的JetPackLinux_for_Tegra目录里残留了旧rootfs文件生成镜像时可能报一些莫名其妙的文件冲突。稳妥做法是每次重新解压一份干净的离线包不要混用。5.2 首次启动失败或找不到系统如果上电后显示器提示找不到启动设备或者卡在NVIDIA Logo界面不动优先排查以下几个方面。第一种情况SSD插入后完全没反应。先看电源是否够用Orin Nano对供电要求不算低劣质USB-C电源可能导致外设初始化失败。再检查M.2插槽接触是否良好把SSD拔下来重新插一遍确认固定螺丝拧紧。第二种情况提示找不到可启动设备。这通常意味着QSPI flash里的bootloader版本太老或配置里没启用NVMe引导。这时候需要把Orin Nano进入USB Recovery模式按住Recovery按键再上电用USB-C线连接到Ubuntu主机然后在Linux_for_Tegra目录下执行sudo ./flash.sh jetson-orin-nano-devkit这一步会把bootloader完整刷写到QSPI flash全程不联网所需的文件都在离线包里。刷完后断开USB线重新上电启动NVMe盘通常就能被识别了。注意这个操作是“烧写bootloader”而非“烧写整个系统”不会覆盖你SSD里的用户数据但执行前还是建议备份重要文件。第三种情况卡在内核启动阶段。多半是镜像生成时-e参数写错导致根文件系统指向了SD卡设备。重新检查-e nvme0n1重新生成镜像后再写盘。5.3 系统盘空间扩容与性能验证镜像写入后如果SSD容量大于镜像设定的-s大小剩余空间会处于未分区状态。要在系统运行后把未分配空间合并进根分区先确认根分区号lsblk一般来说nvme0n1下第一个分区nvme0n1p1就是根分区。然后执行sudo growpart /dev/nvme0n1 1 sudo resize2fs /dev/nvme0n1p1第一条命令扩大分区表第二条命令把文件系统扩展到整个分区执行完后df -h就会显示完整磁盘容量。不同JetPack版本的分区布局可能略有差异检查好分区号再操作。性能验证可以这样测sudo hdparm -Tt /dev/nvme0n1或者直接读盘测速sudo dd if/dev/nvme0n1 of/dev/null bs1M count1024跑出来的顺序读取速度和原来的SD卡方案相比基本能感觉到质的差别。实际开发中Docker镜像加载、pip安装大型依赖、PyTorch加载模型权重这些操作都能明显提速。5.4 几个提高效率的独家小技巧第一保留生成好的镜像文件。以后想重装系统只需要再次dd写入SSD几分钟搞定不需要重新跑生成流程。重装一次系统等于换一块盘开发效率提升非常明显。第二多备几块NVMe盘不同项目用不同系统盘。比如一块盘装纯命令行环境跑训练任务一块盘装完整桌面环境做调试切换项目就切换盘不用反复装环境。第三在镜像基础上把Docker和常用镜像预先装好然后再用jetson-disk-image-creator.sh重新生成一次镜像这样等于做了一个“带完整开发环境”的黄金系统盘新板子到手直接插盘就用。第四生成镜像前如果手头有其他JetPack版本尽量保持和实际部署场景一致。不同版本的kernel和bootloader之间存在差异混用容易踩到莫名其妙的坑。这套方案我前后跑了不下几十次从最早的SD卡转NVMe折腾到现在的全离线镜像生成稳定性已经非常可靠。硬盘盒加NVMe盘这套组合现在基本是我接触Jetson设备时的标准配置。你只要严格按照上面的步骤走大概率一次就能成功。真要是遇到问题多数情况都集中在bootloader版本和-e参数这两个点上耐心排查就行。