
1. 为什么我要啃 debian-installer 这块硬骨头第一次接触 debian-installer 是在给一台老旧的工控机装系统的时候。那台机器没有光驱USB 启动兼容性也差常规的桌面版安装镜像写进去要么卡在引导阶段要么识别不到硬盘。折腾了大半天我决定换个思路——直接用 debian-installer 的网络安装方式从最小化组件开始搭。结果这一试反而让我对这套安装器产生了浓厚的兴趣。debian-installer 是 Debian 系统官方使用的安装程序框架它负责从引导加载、硬件探测、分区、软件包选择到系统初始化的全流程。很多人平时用图形化安装镜像点几下就装完了根本感知不到它的存在。但一旦遇到特殊硬件、自动化部署、定制镜像这些场景图形界面那套就不够用了你必须回到 debian-installer 的底层逻辑里去解决问题。这篇文章适合几类人看一是经常需要批量装机的运维人员二是想自己定制安装镜像的开发者三是对 Linux 启动流程和系统安装机制好奇的技术爱好者。我会从整体架构讲到具体实操把我在实际使用中踩过的坑和总结的技巧都摊开来说。不管你是第一次听说 debian-installer还是已经用过但没深入理解相信都能从中拿到一些能直接用的东西。2. debian-installer 的整体架构与设计思路2.1 模块化设计为什么它不是一个大程序debian-installer 最核心的设计理念就是模块化。它不是一个单体程序而是由一系列 udebmicro-deb包组成的集合。每个 udeb 负责一个特定的功能比如硬件探测、分区、网络配置、软件包下载等。安装过程中主程序会根据当前阶段和硬件情况动态加载需要的 udeb。这种设计的好处非常明显。首先安装镜像可以做得非常小因为不需要把所有可能的驱动和工具都塞进去用到什么加载什么。其次扩展性强你想加一个自定义的安装步骤只需要写一个新的 udeb 并注册到流程里就行。最后它天然支持网络安装因为核心组件加载完后剩下的软件包可以直接从网络仓库拉取。我刚开始不理解为什么要搞得这么复杂后来自己尝试给一个特殊阵列卡做驱动注入时才明白如果安装器是铁板一块你根本没法在不重新编译整个程序的情况下加入新驱动。而 udeb 机制让你可以把驱动打包成一个独立模块在安装启动时通过参数加载进去整个流程非常干净。2.2 安装阶段的划分逻辑debian-installer 把整个安装过程划分成多个阶段每个阶段有明确的职责。大致可以分为引导阶段、早期用户空间初始化、硬件探测与配置、分区与文件系统创建、基础系统安装、软件包选择与安装、引导加载器安装、收尾配置。这些阶段不是随意划分的而是按照依赖关系来组织的。比如你必须先探测到硬盘才能进行分区必须先挂载好目标文件系统才能往里装基础系统。每个阶段完成后安装器会把状态记录下来这样即使中途重启也能从断点继续。这里有个细节值得注意debian-installer 的状态管理是通过/var/lib/dpkg和安装器自己的状态文件共同维护的。如果你在安装过程中手动干预了某些步骤可能会导致状态不一致后续阶段就会报错。我遇到过好几次因为手动挂载了分区但没更新状态文件导致安装器找不到目标根分区的情况。所以除非你很清楚自己在做什么否则尽量不要在安装过程中手动操作。2.3 网络安装与本地安装的取舍debian-installer 支持多种安装介质光盘、USB、网络引导、甚至硬盘上的 ISO 文件。不同介质对应的组件加载策略不同。网络安装镜像netinst只包含核心组件和基础系统其余软件包全部从网络仓库下载。完整安装镜像则包含了大量常用软件包适合没有网络的环境。我个人的经验是如果你网络条件稳定优先用 netinst。原因有三一是镜像小下载快写 USB 也快二是安装时拉取的软件包永远是最新版本不用装完再升级一大堆三是你可以精确控制装哪些包避免完整镜像里那些你可能永远用不到的东西。但网络安装也有坑。最典型的就是安装过程中网络中断导致某个软件包下载失败整个安装卡住。这时候你需要检查网络配置确认 DNS 和网关是否正确然后重新尝试。如果反复失败可以考虑换一个离你更近的软件源镜像。3. 核心组件拆解与关键机制解析3.1 udeb 包的加载与依赖处理udeb 是 debian-installer 的基本功能单元。它的格式和普通 deb 包类似但去掉了文档和非必要文件体积更小。安装器在启动时会根据预设的配方recipe决定加载哪些 udeb。这个配方通常定义在安装镜像的udeb.list文件中。每个 udeb 都有自己的依赖声明。安装器使用一个简化版的依赖解析器来处理这些依赖确保加载顺序正确。比如分区工具依赖于磁盘探测工具那么磁盘探测会先被加载。这里有一个实操中容易忽略的点如果你自己制作 udeb一定要把依赖写清楚。我见过有人打包了一个自定义的硬件检测脚本但没有声明对hw-detect的依赖结果安装时脚本执行了但硬件信息还没被收集导致检测失败。正确的做法是在 udeb 的 control 文件里加上Depends: hw-detect。3.2 预置文件preseed的工作机制预置文件是 debian-installer 实现自动化安装的核心手段。它本质上是一个文本文件里面用键值对的形式定义了安装过程中所有需要交互的问题的答案。安装器启动时如果检测到预置文件就会自动读取并应用这些答案从而实现无人值守安装。预置文件的加载方式有几种可以放在安装介质上可以通过网络下载也可以通过内核命令行参数指定。最常见的是放在 USB 启动盘的根目录命名为preseed.cfg然后在引导参数里加上autotrue prioritycritical preseed/file/cdrom/preseed.cfg。我刚开始用 preseed 的时候经常遇到某个问题没有被自动回答安装卡在交互界面。后来发现原因是预置文件里的键名写错了或者优先级设置不对。debian-installer 的问题有优先级之分critical级别的问题必须回答high级别的大多数情况下也需要回答。如果你把priority设得太低一些关键问题就不会被自动回答。我的建议是先用prioritycritical跑一遍看看哪些问题会冒出来然后针对性地补充预置项。3.3 硬件探测与驱动加载流程硬件探测是安装过程中最复杂也最容易出问题的环节。debian-installer 使用hw-detect组件来扫描系统总线识别已连接的硬件设备然后根据设备 ID 匹配对应的内核模块。对于常见硬件标准安装镜像里已经包含了足够的驱动。但对于一些较新的网卡、阵列卡或者特殊外设你可能需要手动提供驱动。方法是在安装启动时通过modules参数指定要加载的模块或者把驱动 udeb 放到安装介质的特定目录下。我印象最深的一次是给一台搭载了某款较新网卡的服务器装系统。标准镜像根本不识别那块网卡网络安装直接没法进行。解决办法是去硬件厂商的支持页面下载对应的驱动 udeb解压后把.udeb文件放到 USB 启动盘的firmware目录下然后在引导参数里加上firmware1。安装器启动时会自动扫描这个目录并加载里面的固件和驱动。注意驱动 udeb 的版本必须和安装器使用的内核版本匹配否则加载会失败。你可以在安装器启动后的 shell 里用uname -r查看当前内核版本。4. 从零开始定制一个自动化安装镜像4.1 准备工作工具与材料清单要定制一个自动化安装镜像你需要准备以下东西一台运行 Debian 或 Ubuntu 的构建机磁盘空间至少 10GBdebian-installer源码包或官方 netinst 镜像debootstrap工具用于构建基础系统xorriso或genisoimage用于重新打包 ISO一个文本编辑器用来写 preseed 文件如果涉及自定义 udeb还需要dpkg-dev和debhelper我建议直接在构建机上安装debian-installer的构建依赖apt-get install debian-installer build-essential dpkg-dev debhelper xorriso这些工具装好后你就可以开始动手了。4.2 提取并修改安装器组件第一步是把官方 netinst 镜像挂载或解压提取出里面的安装器组件。你可以直接下载 netinst ISO然后用xorriso提取mkdir -p /tmp/iso-extract xorriso -osirrox on -indev debian-netinst.iso -extract / /tmp/iso-extract提取完成后你会看到install目录下有一系列文件包括内核、initrd 和 udeb 包。如果你只是想加一个 preseed 文件不需要动这些组件直接在 ISO 根目录放一个preseed.cfg就行。但如果你想加入自定义驱动或修改安装流程就需要替换或添加 udeb。替换 udeb 时要注意新的 udeb 必须和原版使用相同的包名和版本号格式否则依赖解析会出问题。我一般会保留原文件备份确认新组件工作正常后再删除备份。4.3 编写 preseed 文件的关键参数preseed 文件的编写是自动化安装的核心。下面是一个我常用的最小化 preseed 模板适用于单盘、单网卡的服务器# 语言和地区 d-i debian-installer/locale string en_US.UTF-8 d-i keyboard-configuration/xkb-keymap select us # 网络配置 d-i netcfg/choose_interface select auto d-i netcfg/get_hostname string debian-auto d-i netcfg/get_domain string local # 软件源 d-i mirror/country string manual d-i mirror/http/hostname string deb.debian.org d-i mirror/http/directory string /debian d-i mirror/http/proxy string # 时钟 d-i clock-setup/utc boolean true d-i time/zone string Asia/Shanghai # 分区 d-i partman-auto/method string regular d-i partman-auto/choose_recipe select atomic d-i partman-partitioning/confirm_write_new_label boolean true d-i partman/choose_partition select finish d-i partman/confirm boolean true d-i partman/confirm_nooverwrite boolean true # 用户账户 d-i passwd/root-login boolean false d-i passwd/user-fullname string deploy d-i passwd/username string deploy d-i passwd/user-password password yourpassword d-i passwd/user-password-again password yourpassword # 软件包选择 tasksel tasksel/first multiselect standard, ssh-server # 引导加载器 d-i grub-installer/only_debian boolean true d-i grub-installer/with_other_os boolean true # 收尾 d-i finish-install/reboot_in_progress note这个模板里分区部分用的是atomic方案也就是把整个磁盘分成一个根分区加一个交换分区。如果你需要更复杂的分区布局可以自定义 recipe。比如要分/home和/var就需要写一个更详细的 partman recipe。提示preseed 文件里的密码是明文存储的。如果你在意安全性可以在安装完成后用chpasswd或者配置管理工具统一改密码。4.4 重新打包 ISO 并测试preseed 文件准备好后把它放到 ISO 提取目录的根目录下然后重新打包xorriso -as mkisofs -o custom-debian.iso \ -isohybrid-mbr /usr/lib/ISOLINUX/isohdpfx.bin \ -c isolinux/boot.cat \ -b isolinux/isolinux.bin \ -no-emul-boot -boot-load-size 4 -boot-info-table \ -eltorito-alt-boot \ -e boot/grub/efi.img \ -no-emul-boot -isohybrid-gpt-basdat \ /tmp/iso-extract打包完成后把 ISO 写入 USB 或者挂载到虚拟机上测试。测试时建议先用虚拟机跑一遍确认所有步骤都能自动完成。如果卡在某个交互界面就回到 preseed 文件里补充对应的键值。我自己的测试流程是先用 VirtualBox 建一个最小配置的虚拟机分配 1GB 内存和 20GB 磁盘然后挂载自定义 ISO 启动。观察安装日志看是否有报错或卡顿。确认无误后再在物理机上做最终验证。5. 实操中遇到的典型问题与排查方法5.1 安装卡在探测硬件阶段这是最常见的问题之一。表现是安装器启动后屏幕停在Detecting hardware不动或者反复扫描但找不到硬盘。排查思路首先确认硬盘是否被 BIOS/UEFI 正确识别。如果 BIOS 里都看不到硬盘那问题出在硬件连接或 BIOS 设置上跟安装器无关。如果 BIOS 能看到但安装器看不到那很可能是缺少对应的内核模块。解决办法在安装器启动时按Tab键编辑引导参数加上modules你的驱动模块名。如果不知道模块名可以先启动到安装器的 shell在引导参数里加breakinit然后用lspci和lsmod对比分析。我遇到过一块老式 IDE 硬盘安装器默认没有加载ide-generic模块。加上moduleside-generic后问题解决。5.2 网络安装时软件包下载失败网络安装过程中如果某个软件包下载失败安装器会提示重试或跳过。如果反复失败通常是网络配置或软件源的问题。排查步骤检查/etc/resolv.conf里的 DNS 配置是否正确用ping测试到软件源镜像的连通性检查软件源地址是否写错比如把deb.debian.org写成了deb.debian.com如果使用了代理确认代理配置是否正确我踩过的一个坑是preseed 文件里写了mirror/http/proxy但代理地址已经失效导致所有下载都超时。后来把代理配置去掉直接连官方源就正常了。5.3 分区方案不生效preseed 里定义了分区方案但安装时还是弹出了手动分区界面。这种情况通常是分区相关的 preseed 键值没有覆盖到所有必要的问题。debian-installer 的分区流程会问好几个问题分区方法、分区方案、是否写入新标签、是否确认分区操作等。你必须把这些问题都预置好才能完全跳过手动分区。我建议的做法是先用prioritycritical跑一遍把弹出的所有分区相关问题都记下来然后逐个在 preseed 里补充答案。常见的遗漏项包括partman-partitioning/confirm_write_new_label和partman/confirm_nooverwrite。5.4 引导加载器安装失败GRUB 安装失败通常有几个原因一是目标磁盘的 MBR 或 EFI 分区有问题二是安装器没有正确识别引导设备三是多系统环境下 GRUB 配置冲突。如果是 UEFI 启动确认 EFI 系统分区ESP已经正确挂载到/boot/efi并且格式为 FAT32。如果是 BIOS 启动确认目标磁盘的前 512 字节没有被其他引导程序占用。我遇到过一次 GRUB 安装失败原因是之前手动创建了一个 BIOS Boot 分区但没有设置正确的分区类型。后来用parted把分区类型改成bios_grub才解决。5.5 常见问题速查表问题现象可能原因解决方法卡在硬件探测缺少驱动模块引导参数加modulesxxx网络下载失败DNS 或源地址错误检查 resolv.conf 和 mirror 配置分区界面弹出preseed 键值不全补充 partman 相关预置项GRUB 安装失败引导分区配置错误检查 ESP 或 BIOS Boot 分区安装后无法启动引导顺序不对进 BIOS 调整启动顺序键盘布局不对未预置键盘配置加keyboard-configuration/xkb-keymap6. 进阶技巧把安装器玩出花来6.1 用 late_command 做安装后配置preseed 文件里的late_command允许你在安装完成后、重启之前执行自定义命令。这个功能非常实用可以用来做很多收尾工作比如安装额外的软件包修改 SSH 配置配置网络绑定写入自定义的 MOTD用法示例d-i preseed/late_command string \ in-target apt-get install -y vim htop; \ in-target sed -i s/#PermitRootLogin.*/PermitRootLogin no/ /etc/ssh/sshd_config; \ in-target systemctl enable ssh注意in-target前缀它表示命令在目标系统的 chroot 环境中执行。如果不加这个前缀命令会在安装器的临时环境中执行效果完全不同。6.2 自定义 udeb 实现特殊硬件支持如果你有一批特殊硬件需要支持而官方安装器没有包含对应驱动可以自己打包一个 udeb。步骤大致如下准备好驱动源码和编译环境编译出.ko内核模块创建一个 udeb 目录结构包含DEBIAN/control和模块文件用dpkg-deb --build打包成 udeb把 udeb 放到安装介质的firmware目录下control 文件的内容示例Package: my-driver-modules Version: 1.0 Architecture: amd64 Maintainer: yourname Depends: kernel-image, core-modules Description: Custom driver modules for special hardware打包完成后在引导参数里加上firmware1安装器会自动加载这个 udeb。6.3 利用 preseed 实现多机型差异化部署如果你需要给不同型号的机器装系统但希望用同一个安装镜像可以通过 preseed 文件里的条件判断来实现差异化配置。debian-installer 支持在 preseed 里使用if语句根据硬件信息或启动参数决定执行哪些配置。比如# 根据内存大小决定是否创建交换分区 d-i partman-auto/init_automatically_partition \ select 20some_device__________regular更复杂的逻辑可以通过early_command和late_command结合 shell 脚本来实现。你可以在early_command里检测硬件型号然后把结果写入一个临时文件后续的 preseed 配置再根据这个文件的内容做判断。我做过一个项目需要给三种不同配置的服务器装系统。用同一个 ISO通过 MAC 地址区分机型然后在early_command里根据 MAC 地址下载对应的 preseed 片段实现了完全自动化的差异化部署。6.4 调试安装器的实用技巧调试 debian-installer 最有效的方法是看日志。安装器会把详细日志写到/var/log/syslog和/var/log/installer/目录下。如果安装失败你可以切换到安装器的 shell按AltF2或AltF3查看这些日志。另外在引导参数里加上debug可以让安装器输出更详细的调试信息。如果问题出在某个特定组件还可以用DEBCONF_DEBUGdeveloper环境变量来开启 debconf 的调试输出。我常用的一个技巧是在early_command里启动一个 SSH 服务这样我就可以从另一台机器远程登录到安装器环境方便查看日志和执行调试命令。具体做法是在 preseed 里加d-i preseed/early_command string \ anna-install openssh-server-udeb; \ mkdir -p /etc/ssh; \ echo root:yourpassword | chpasswd这样安装器启动后你就可以用 SSH 连上去慢慢排查问题了。7. 我个人在实际操作中的几点体会debian-installer 这套东西刚上手的时候确实会觉得繁琐文档分散报错信息也不够友好。但一旦你理解了它的模块化设计和 preseed 机制就会发现它的灵活性和可控性是图形化安装器完全比不了的。我最大的体会是不要怕看日志。安装器把每一步都记录得很清楚遇到问题先去看/var/log/syslog十有八九能找到线索。另外preseed 文件不要一次写太多先写最小集跑通了再逐步加功能。每次只改一个地方这样出问题的时候容易定位。还有一个很实用的建议把你调试好的 preseed 文件和自定义 udeb 都放到版本控制里。下次需要给新机型做部署时直接基于之前的配置改效率会高很多。我现在维护着一个 preseed 仓库里面按机型分目录每个目录下有自己的 preseed 和驱动文件用起来非常顺手。最后分享一个小技巧如果你不确定某个 preseed 键名是否正确可以在安装器的 shell 里用debconf-show命令查看当前所有可用的配置项。这个命令会列出所有已经注册的 debconf 问题及其当前值是排查 preseed 键名错误的利器。