
说个刚干完的活儿一批测试机器要批量装Debian一台台塞光盘选语言、分区、设密码实在太磨人。花了一个下午把官方Debian ISO重新打包往镜像里塞了一份preseed.cfg做成了UEFI和BIOS两种固件环境下都能跑的无人应答安装。整个过程不算复杂但引导参数、initrd注入、打包命令这些细节确实踩了几个坑。今天把这套完整做法和排查记录整理出来给同样被重复安装折磨的人一个能直接抄的作业。先说明白我说的“无人应答安装”不是用PXE、不是搞DHCPTFTP那一套而是把配置写进ISO让安装器从头到尾不弹对话框。这个方案最大的好处是交付简单——一个ISO文件扔给谁都能装对方不需要懂参数、不需要配网络。适合小批量设备部署、离线环境装系统、或者交付一台预配置好的“黄金镜像”。1. 动手之前先把整个思路理清楚1.1 安装器也是程序配置同样可以预置Debian的安装流程看起来很“交互式”但本质上是一段脚本化的流程语言选择、网络探测、磁盘分区、软件包安装、引导加载器写入每一步背后都是一次函数调用。安装器里有个机制叫preseed运行时去读取一份预设答案文件只要文件里的配置覆盖到安装器提问的每个环节安装就能完全无人值守地跑完。preseed.cfg就是这份“答案文件”。它的格式是一行一个d-i开头的键值对键名对应安装器内部的配置项值就是要填入的答案。比如安装器问“时区选哪个”对应的键是time/zone你写d-i time/zone string Asia/Shanghai安装器就直接用不再提问。关键点在于配置覆盖要全、顺序要对。安装器是线性推进的分区没配置好就不会往下走到软件包选择软件源没配置好装到一半就卡住。所以写preseed文件不是随便堆参数得跟着安装流程走一遍逐项确认没漏。1.2 为什么非要同时处理UEFI和BIOS早些年做这种镜像只需要处理BIOS引导ISO里一个isolinux配置就搞定了。但现在不行新机器默认UEFI老机器还在跑传统BIOS两套是完全不同的引导链路。项目BIOSLegacyUEFI分区表MBRGPT兼容MBR但不是最佳引导文件isolinux.bin txt.cfgGRUB efi.img安装器磁盘目标/dev/sda/dev/sda但必须有EFI分区ISO打包方式El Torito启动项El Torito alt-boot GPT混合镜像验证方式SeaBIOS虚拟机OVMF固件虚拟机不兼容的后果很具体同一份ISO在旧机器上能启动、新机器直接黑屏或进不了安装器。所以打包的时候必须做一个“混合ISO”让同一份.iso文件同时携带两种启动方式的引导记录。后面打包命令里的-isohybrid-mbr和-eltorito-alt-boot就是在干这件事。2. 环境准备文件结构不摸透后面全是坑2.1 准备工具推荐直接在一台Debian/Ubuntu机器上进行打包操作需要装这几个工具apt update apt install -y xorriso isolinux syslinux-utils cpio gzipxorriso负责把整合后的目录重新做成可引导ISO支持UEFIBIOS混合镜像isolinux提供BIOS引导所需的isohdpfx.bincpiogzip解包、重打包initrd时用装好之后先确认/usr/lib/ISOLINUX/isohdpfx.bin存在这个文件稍后打包时要用。如果你的发行版路径不同用dpkg -L isolinux查一下实际位置。2.2 挂载原版ISO并拷贝到工作区mkdir -p /data/debian-iso /data/debian-work mount -o loop,ro /tmp/debian-12.x-netinst.iso /mnt cp -a /mnt/* /data/debian-work/ umount /mnt cd /data/debian-work chmod -R uw .为什么不用7z x解压ISO因为要保留文件权限和目录结构尤其是引导文件cp -a直接保留一切属性最可靠。如果你在Windows上操作建议用.img写入工具先提取不要在Windows里用普通解压软件折腾文件属性和长文件名容易出问题。2.3 认识Debian ISO的目录结构解压后的目录长这样不同版本略有差异但核心结构一致debian-work/ ├── boot/ │ └── grub/ │ ├── grub.cfg # UEFI引导菜单 │ └── efi.img # UEFI启动镜像 ├── install.amd/ │ ├── initrd.gz # 安装器内存文件系统 │ └── vmlinuz # 安装器内核 ├── isolinux/ │ ├── boot.cat │ ├── isolinux.bin │ └── txt.cfg # BIOS引导菜单 └── md5sum.txt # ISO完整性校验文件install.amd/里的vmlinuz和initrd.gz是安装器的内核和文件系统整个无人应答的关键就是给这两个文件传递preseed配置。isolinux/和boot/grub/分别管BIOS和UEFI的启动菜单我们要改的就是txt.cfg和grub.cfg。3. preseed.cfg整个流程的灵魂这样写最稳先给一个可以直接用的完整文件路径/data/preseed.cfg。下面分块讲这样你看得懂每一行在干什么后期改起来也方便。3.1 基础配置语言、键盘、网络、时区# 语言与地区 d-i debian-installer/locale string en_US.UTF-8 d-i debian-installer/country string CN d-i debian-installer/language string en d-i keyboard-configuration/xkb-keymap select us # 网络配置 d-i netcfg/choose_interface select auto d-i netcfg/disable_dhcp boolean false d-i netcfg/get_hostname string deploy-node d-i netcfg/get_domain string local # 时区与NTP d-i time/zone string Asia/Shanghai d-i clock-setup/utc boolean true d-i clock-setup/ntp boolean true # 软件源 d-i mirror/country string manual d-i mirror/http/hostname string mirror.example.org d-i mirror/http/directory string /debian d-i mirror/http/proxy string d-i apt-setup/contrib boolean true d-i apt-setup/non-free boolean true注意几点debian-installer/country string CN和locale string en_US.UTF-8组合很重要很多教程只设locale结果安装器因为地区影响键盘布局和镜像站选择弹一个额外对话框出来。设成en_US.UTF-8CN可以保证语言是英文界面、地区是中国避免中文环境下安装器字体和选择框出问题。网络这里默认走DHCP如果你要装静态IP的机器把netcfg/disable_dhcp boolean false改成d-i netcfg/disable_dhcp boolean true d-i netcfg/confirm_static boolean true d-i netcfg/get_ipaddress string 192.168.1.100 d-i netcfg/get_netmask string 255.255.255.0 d-i netcfg/get_gateway string 192.168.1.1 d-i netcfg/get_nameservers string 192.168.1.1软件源这一节要特别注意mirror/http/hostname在无人应答安装里决定了装系统时从哪里拉软件包。如果你在公司内网这里直接写内部镜像站域名即可如果装完后系统还要继续更新记得在late_command里把apt源也写成同一个内网源否则装完第一次apt update又会去连公网。3.2 磁盘分区UEFI和BIOS的差异在这里体现分区别乱写这里是最容易卡住人的地方。先给两个版本简单版和LVM版。简单版适合整盘安装、默认分区布局d-i partman-auto/method string regular d-i partman-auto/choose_recipe select atomic d-i partman/confirm_write_new_label boolean true d-i partman/confirm boolean true d-i partman/confirm_nooverwrite boolean true d-i partman-partitioning/confirm_write_new_label boolean true d-i partman/choose_partition select finish这里的关键是partman-auto/choose_recipe select atomic。Debian的partman在UEFI模式下会自动检测到EFI系统分区约512MB的FAT32分区然后在剩余空间建根分区。BIOS模式下则会创建一个约50MB的/boot分区再加根分区。这个行为是partman内置的所以用atomic最简单、最不容易写错。LVM版适合需要统一磁盘空间管理的场景d-i partman-auto/method string lvm d-i partman-auto-lvm/guided_size string 60% d-i partman-auto/choose_recipe select atomic d-i partman-lvm/deviceize_vgname string vg0 d-i partman/confirm_write_new_label boolean true d-i partman/confirm boolean true d-i partman/confirm_nooverwrite boolean true d-i partman/choose_partition select finishguided_size string 60%的意思是LVM卷组使用磁盘60%的空间剩余空间留给后续手工扩容或其它用途。这个值按实际需求调。强烈建议在分区前加一条清空旧分区表的命令。装机的机器大多是旧机器磁盘上往往有残留的Windows、旧Linux分区表partman遇到这种情况经常弹确认框或者直接报错d-i preseed/early_command string wipefs -a /dev/sdaearly_command在安装器启动早期就执行此时分区还没开始直接物理抹掉旧分区表最干净。注意设备名如果U盘启动系统里可能是/dev/sdb要按实际环境调整或者用通配方式检测第一个非系统盘。3.3 用户账户、软件包与安装收尾# root密码 d-i passwd/root-login boolean true d-i passwd/root-password password Root123 d-i passwd/root-password-again password Root123 # 普通用户 d-i passwd/user-fullname string ops d-i passwd/username string ops d-i passwd/user-password password Ops123 d-i passwd/user-password-again password Ops123 d-i passwd/user-default-groups string sudo # 软件包选择 tasksel tasksel/first multiselect standard d-i pkgsel/include string openssh-server vim htop d-i pkgsel/upgrade select none # GRUB引导安装 d-i grub-installer/only_debian boolean true d-i grub-installer/bootdev string /dev/sda # 安装收尾 d-i finish-install/reboot_in_password string d-i preseed/late_command string \ in-target apt update; \ in-target apt install -y curl parted; \ in-target systemctl enable ssh密码这里直接写明文只是示例生产环境更稳妥的做法是安装后用SSH key登录、密码登录直接禁用或者用preseed的加密密码哈希。生成哈希的方法mkpasswd -m sha-512然后把输出替换到对应位置d-i passwd/root-password-crypted password $6$...tasksel/tasksel/first multiselect standard这一行是选安装什么软件集合。standard是最常用的基础工具箱如果没有它装完的系统可能连man、vim都没有。pkgsel/include是自己追加的额外软件包列表多个包用空格隔开。finish-install/reboot_in_password这个配置其实是Debian安装器让用户输入一个“突发断电保护密码”正常情况下回车跳过即可。在preseed里它可以留空直接跳过这行建议保留否则安装收尾时可能弹一个“是否要重启”对话框。late_command是整个preseed里最值钱的一段安装器在系统落盘、但还没重启的时候执行你给的命令。in-target的意思是在刚装好的系统里执行不是在自己当前环境执行。这里是往新系统里预装包、开服务的好机会。4. 修改引导配置让安装器加载preseedpreseed.cfg写好了但安装器还不会主动读它。需要同时改BIOS和UEFI两边的启动参数。4.1 BIOS路径修改isolinux/txt.cfg打开/data/debian-work/isolinux/txt.cfg不同版本叫法略有不同有的直接叫isolinux.cfg核心原理一致原始内容类似default install label install menu label ^Install kernel /install.amd/vmlinuz append vga788 --- quiet把append那一行改掉加上preseed参数append vga788 autotrue prioritycritical preseed/file/cdrom/preseed.cfg --- quietautotrue prioritycritical是Debian安装器的“自动模式开关”它告诉安装器“尽量用预设值不要问问题”。preseed/file/cdrom/preseed.cfg指向配置文件的路径。---之前的参数传给安装器内核---之后的参数给initrd里的用户态进程。如果你打算把ISO写到U盘安装路径可能不是/cdrom而是/media/或其它挂载点。最稳妥的办法是直接把preseed.cfg打进initrd里这个我在4.3讲。4.2 UEFI路径修改boot/grub/grub.cfg打开/data/debian-work/boot/grub/grub.cfg里面有几个menuentry找到默认的Install项改linux行menuentry Install { set background_colorblack linux /install.amd/vmlinuz autotrue prioritycritical preseed/file/cdrom/preseed.cfg --- quiet initrd /install.amd/initrd.gz }其它Advanced options、Graphical install等子菜单要么全改要么干脆删掉。我建议保留一个Install就够了减少安装过程中出现选择菜单的概率。无人应答嘛就是不能让人做选择题。4.3 兜底方案把preseed塞进initrd只靠preseed/file/cdrom/preseed.cfg有个致命漏洞如果ISO被U盘引导、或者挂载点顺序变化路径不对preseed就直接不生效安装器开始老老实实问答交互。所以更可靠的做法是把preseed文件直接打包进initrd。Debian 12的initrd.gz是gzip压缩的cpio归档操作方式mkdir -p /data/initrd-ed cd /data/initrd-ed zcat /data/debian-work/install.amd/initrd.gz | cpio -id cp /data/preseed.cfg . find . | cpio -o -H newc | gzip -1 /data/debian-work/install.amd/initrd.gz说明一下每个环节在干嘛zcat ... | cpio -id把gzip压缩的cpio归档解压到当前目录cp /data/preseed.cfg .把preseed文件放到initrd根目录find . | cpio -o -H newc把整个文件系统重新打包成cpio归档-H newc是新格式必须用它老格式Debian安装器不认识gzip -1压缩级别设成1安装器加载initrd时解压快一点镜像大小也差不了多少打包进去之后preseed文件路径就变成/preseed.cfg引导参数可以改成append vga788 autotrue prioritycritical file/preseed.cfg --- quiet注意这里用file而不是preseed/file因为此时preseed文件已经被initrd自带了安装器直接就能读不会再去找CDROM路径。这个做法还有一个额外收益安装器不会再因为找不到CDROM上的配置文件而报错。我强烈建议两种方式都做initrd里打一份CDROM根目录也放一份双保险。5. 重新打包ISO并更新校验5.1 xorriso打包命令逐参数拆解所有文件都放到位、改到位之后开始打包。先回到工作区更新md5sum然后执行xorrisocd /data/debian-work md5sum $(find . -type f) md5sum.txt cd /data xorriso -as mkisofs \ -V Debian-12.x-AutoInstall \ -o /data/debian-12.x-autoinstall.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 \ /data/debian-work参数逐个说-as mkisofs用兼容mkisofs的模式工作熟悉老命令的人更容易理解-V卷标设成一个有意义的名字方便安装后识别介质-isohybrid-mbr写入MBR引导代码这是让ISO可以“像U盘一样启动”的关键BIOS机器能识别-c isolinux/boot.catboot catalog文件BIOS El Torito启动需要-b isolinux/isolinux.bin指定BIOS启动引导文件-no-emul-boot告诉固件这个启动镜像不是软盘仿真-boot-load-size 4启动加载的扇区数4是isolinux的标准值-boot-info-table让isolinux把启动信息表写入引导文件尾部-eltorito-alt-boot添加第二个El Torito启动项这行开始是UEFI的配置-e boot/grub/efi.imgUEFI启动镜像文件-isohybrid-gpt-basdat在混合ISO的GPT分区表里写入正确的启动扇区位置UEFI从U盘启动时靠它打包完可以用xorriso -indev验证一下引导记录xorriso -indev /data/debian-12.x-autoinstall.iso -report_el_torito能看到Boot image里同时有isolinux.bin和efi.img两行说明双引导结构已经写进去了。5.2 为什么更新md5sum很重要Debian安装器在从光盘或U盘读取文件时会用md5sum.txt校验文件完整性。如果你改了任何文件却没更新这个校验文件安装器会在某个瞬间报“文件校验失败”然后中止安装非常坑。有的教程说“不更新也能装”那是因为U盘引导时部分固件不校验。但光盘安装时校验逻辑比较严格。我建议无论如何都重新生成一次成本极低cd /data/debian-work md5sum $(find . -type f) md5sum.txt注意find . -type f会把所有文件都算进去包括你新增的preseed.cfg这样安装器在读取preseed时也能正常通过校验。6. 实测验证UEFI和BIOS两套环境各跑一遍打包完不能直接拿去部署先在虚拟机里验证一遍。6.1 UEFI验证需要一个带OVMF固件的QEMU环境apt install -y qemu-system-x86 ovmf # 创建空白测试磁盘 qemu-img create -f qcow2 /data/uefi-disk.qcow2 20G # 启动UEFI模式安装 qemu-system-x86_64 \ -machine q35 \ -bios /usr/share/OVMF/OVMF_CODE.fd \ -m 2048 -smp 2 \ -cdrom /data/debian-12.x-autoinstall.iso \ -drive file/data/uefi-disk.qcow2,ifvirtio,formatqcow2 \ -boot d启动后观察是否直接进入GRUB菜单、是否在安装界面有选择对话框弹出。正常情况下应该一路自动走完中间不需要任何键盘输入。安装完成后磁盘里应该有EFI分区lsblk -f /dev/vda # 输出里应该有一个 vda1 类型为 vfat 的EFI分区6.2 BIOS验证qemu-img create -f qcow2 /data/bios-disk.qcow2 20G qemu-system-x86_64 \ -m 2048 -smp 2 \ -cdrom /data/debian-12.x-autoinstall.iso \ -drive file/data/bios-disk.qcow2,ifide,formatqcow2 \ -boot d这次不用-bios参数QEMU默认走SeaBIOS模拟传统BIOS引导。观察是否跳过isolinux菜单直接进入安装流程。6.3 《安装成功》的判定标准成功的标志不是“装完了”而是这三条同时满足全程无人工键盘输入自动完成语言、分区、软件包安装、grub写入系统重启后能正常进入grub菜单并进入新装的Debian系统登录后能确认preseed的参数真实生效grep -i preseed /var/log/installer/syslog这个命令会列出安装器读取preseed时的日志能看到你配置的每一项被接收的记录。如果这一项没有输出说明preseed根本没被加载前面的工作全部白做。7. 常见问题排查实录踩坑记录我把实践中遇到的高频问题整理成了一张速查表后面挑几个细讲。现象根本原因解决办法UEFI机器启动后黑屏不进安装器ISO缺少UEFI启动项确认打包命令里-eltorito-alt-boot -e boot/grub/efi.img正确BIOS机器启动后找不到引导缺少isohybrid MBR确认-isohybrid-mbr参数指向isohdpfx.bin卡在“分区”步骤弹确认框磁盘有旧分区表加preseed/early_command执行wipefspreseed完全没生效引导参数没写对或路径不对用file/preseed.cfginitrd注入替代/cdrom路径安装到一半软件源下载慢/卡住镜像源不可达换内网镜像源把hostname写对装完重启黑屏GRUB写入错误设备检查grub-installer/bootdev是否指向正确磁盘USB启动报文件校验失败md5sum.txt是旧的重新生成md5sum.txt安装器把网卡识别成多个名字无不用管按auto模式选第一个物理网卡分几个细讲都是真踩过的坑坑一preseed不生效排查思路很重要遇到preseed不生效第一反应不是怀疑配置而是先确认安装器到底有没有读到参数。在引导菜单界面按F6或按e编辑启动项看内核命令行里有没有autotrue和preseed/file。有的话看安装过程中按CtrlAltF4切到控制台在/dev/console日志里找preseed相关报错。最隐蔽的问题是大小写和路径preseed/file/cdrom/preseed.cfg中preseed/file的大小写不能错/cdrom不能写作/CDROM配置文件就叫preseed.cfg不能改名为preseed.CFG。Linux文件名大小写敏感这一点能排查出很多“明明写了却不生效”的诡异问题。坑二UEFI启动直接GRUB命令行有时UEFI启动不进菜单直接掉到GRUB的grub提示符。原因通常是grub.cfg里引用的字体文件或efi.img路径不对。检查/boot/grub/下有没有font.pf2和efi.img以及grub.cfg里的search --file语句指向的卷标是否和打包时的-V一致。# grub.cfg里常见的一段 if loadfont /boot/grub/font.pf2 ; then set gfxmodeauto insmod efi_gop insmod efi_uga insmod gfxterm terminal_output gfxterm fi如果改了卷标GRUB的search --label也要同步改或者干脆用search --file按文件路径找。坑三安装固件文件缺失导致装完没有无线网卡/特殊驱动Debian非自由固件不默认打包在netinst ISO里某些机器装完缺少网卡固件。解决办法是下载firmware-*.deb放到ISO的/firmware目录下Debian 12的安装器会自动扫描这个目录cd /data/debian-work wget http://mirror.example.org/debian/pool/non-free-firmware/f/firmware-nonfree/firmware-...deb mkdir -p firmware cp firmware-*.deb firmware/打包后再装安装器会提示“检测到非自由固件”自动加载。坑四使用固态盘或NVMe时设备名变化preseed里写的/dev/sda在NVMe机器上不成立设备可能是/dev/nvme0n1。处理方式有两种一是写一个脚本在early_command阶段探测第一个非系统盘把设备名传给partman二是直接用/dev/disk/by-path/这种稳定路径。对于小批量部署简单方式是在early_command里做软链接d-i preseed/early_command string \ if [ -b /dev/nvme0n1 ]; then ln -s /dev/nvme0n1 /dev/sda; fi让partman始终挂到/dev/sda上省去后面一堆判断逻辑。最后的经验说点实在的批量装机的活儿我前后干过不少这轮重新打包ISO的心得是看似绕了一大圈实际上比PXE方案稳定得多。PXE要考虑DHCP地址分配、TFTP性能、HTTP并发配置链路长、排查点分散而一个做好的无人应答ISO离线也能跑交付出去别人也不会用错。尤其是给不在同一网络的机器装系统U盘插上就走这是PXE替代不了的优势。另外还有一个可以顺手做的事在late_command里把SSH公钥、apt内网源配置、内部证书都塞进去这样“无人应答安装”的终点不是“装完一个裸系统”而是“装完就能直接投入使用”。更进阶一点可以把打包过程整个写成一个shell脚本留好preseed.cfg和版本号变量以后Debian出了新版本改一下版本号重新执行一遍就能在几分钟内产出一个新的自动化安装镜像。这活儿做到这个份上才算是真正省下了后续每一次重复安装的时间。