机房里最让人头疼的时刻,往往不是设备上架、也不是布线,而是那几十台刚从箱子里拆出来的裸机——它们连操作系统都没有,你要一台一台插U盘、点下一步、选分区、填密码。十台还能忍,五十台就是纯体力活,而且每一台的配置还会因为手抖产生细微差异。Cobbler 就是冲着这个场景来的:把装机这件事从"手工操作"变成"服务端定义 + 客户端开机自动完成"。它本身不发明任何新协议,而是把 PXE 引导、DHCP 分配、TFTP 传文件、Kickstart 自动应答这几件原本需要手工拼接的事,收拢到一套配置模型里统一管理。
这篇内容适合两类人:一类是刚接手批量装机任务、听过 Cobbler 但没真正部署过的运维或实验室管理员;另一类是用过一阵子、但每次cobbler check出来一堆报错就靠搜索引擎硬凑、始终没搞清各组件之间关系的同学。我会从一个可复现的单网段实验环境出发,把安装、配置、发行版导入、Kickstart 模板、DHCP 与 TFTP 接管、以及上线后最容易踩的坑完整走一遍,重点解释每一步"为什么这么做"。
1. Cobbler 在装机链路里到底站在哪个位置
1.1 从插 U 盘到开机自动装完,中间少了哪几步人工
先把你手工装一台机器的流程拆开看。第一步是让机器"知道去哪儿拿引导文件",这一步靠的是网卡的 PXE 固件:开机自检后网卡向广播域发 DHCP 请求,同时在请求里带上PXEClient这个标识。第二步是"拿到引导程序并执行",这是 TFTP 的活,客户端从 DHCP 回复里的next-server和filename两个字段得到服务器地址和引导文件名,然后下载pxelinux.0并运行。第三步是"按谁的定义装",也就是引导程序去拉一份自动应答文件(Kickstart 或 Autoinstall),里面写死了分区、语言、时区、root 密码、软件包列表。
手工模式下,这三步分别对应你手动搭一个 DHCP、手动配一个 TFTP、手动写一份 ks 文件,然后把它们之间的路径关系对上去。Cobbler 做的事情是:用一套 YAML/INI 配置统一描述这几层的参数,再用模板引擎把配置渲染成 DHCP 的dhcpd.conf、TFTP 目录下的引导配置、以及每个发行版对应的 ks 文件,最后通过一条cobbler sync命令把渲染结果落盘并重启相关服务。也就是说,Cobbler 本质是一个"配置到产物"的编译器,而不是一个新的引导协议。
理解这一点很关键。很多人第一次部署失败,都是因为把 Cobbler 当成了一个黑盒服务,改完配置不sync,或者改了/etc/dhcp/dhcpd.conf却发现下次同步被覆盖,方向就错了。
1.2 Cobbler 与 PXE、DHCP、TFTP、Kickstart 的分工关系
用一张表把这几个概念的分工说清楚,这是我带新人时最常用的一张对照表:
| 组件 | 在装机里负责什么 | Cobbler 是接管还是协同 |
|---|---|---|
| PXE | 网卡固件发起网络引导请求 | 不接管,客户端固件行为,无法改 |
| DHCP | 分配 IP,并告知引导服务器地址与文件名 | 可选择接管(渲染 dhcpd.conf)或对接现有 DHCP |
| TFTP | 传输引导程序与引导配置 | 基本全接管,产物落在 TFTP 根目录 |
| HTTP | 传输安装源和 ks 文件 | 一般由 Cobbler 自带的 Web 服务承担 |
| Kickstart | 自动应答安装过程 | 由 Cobbler 用模板渲染后托管 |
| Cobbler | 把上面这些串成一套可版本化的配置 | 居中调度 |
这里有个容易被忽略的点:Cobbler 对 DHCP 的接管是"可选"的。生产环境里通常已经有一台统一 DHCP 服务器,Cobbler 所在机器不一定有权限去动它。这时候要么通过 OMAPI 让 Cobbler 把条目写进远端 DHCP,要么干脆manage_dhcp: 0,由人工在现有 DHCP 里加next-server指向 Cobbler 机器。后一种做法最土,但在跨网段、跨权限的复杂环境里反而是最稳的。
1.3 值得上 Cobbler 的三类场景与两类劝退场景
说实话,Cobbler 并不适合所有人。我总结下来,值得折腾的是这三类:
- 一次要交付 10 台以上同构物理机,且后续还会有反复重装需求,比如实验室集群、测试环境池、教学机房。
- 装机规格需要严格一致,不允许"这台多分了 5G、那台忘了关 SELinux"这类差异。
- 需要把装机过程接入更大的自动化流程,比如 CMDB 里登记一台机器后自动生成装机配置。
劝退的场景也很明确:只装一两台、且一年到头装不了几次,直接做个 U 盘或者用虚拟化平台挂 ISO 更省事;另外如果全部是云主机,那这套东西完全用不上,云厂商的镜像机制已经解决完了。
还有一个中间地带值得提一句:如果你只有几台虚拟机要反复重装,Cobbler 的收益不明显,但要学的东西一样多。这时候可以先在虚拟机里把整套流程跑通,当作技术储备,不要一上来就在生产网段里搞。
2. 服务端落地前的环境盘点
2.1 一张表定下网络规划
Cobbler 部署失败有一大半是网络规划没做对。我建议在动手装包之前,先把下面这张表填满,填不满就先别装:
| 项目 | 示例值 | 说明 |
|---|---|---|
| Cobbler 服务器 IP | 192.168.100.10 | 必须固定,不能是 DHCP 获取 |
| 待装机网段 | 192.168.100.0/24 | 与服务器同网段最省事,跨网段需要 DHCP 中继 |
| DHCP 可分配范围 | 192.168.100.200-250 | 避开服务器和已分配地址 |
| 网关 | 192.168.100.1 | 装机时客户端要用 |
| TFTP 根目录 | /var/lib/tftpboot | Cobbler 默认,不要改 |
| 安装源 HTTP 端口 | 80 | 由 httpd 提供 |
| cobblerd 端口 | 25151 | 服务端自身通信,防火墙要放 |
| 引导方式 | BIOS / UEFI / 混用 | 直接决定后面引导器怎么配 |
规划里最容易出错的是"待装机网段和服务器不同网段"。PXE 的 DHCP 请求是广播,路由器默认不转发,跨网段必须配 DHCP 中继(ip helper-address)。很多人在这里卡了半天,最后发现是网络设备没配中继。所以第一次部署,强烈建议把服务器和待装机机器放在同一台交换机、同一个二层广播域里,把变量降到最少。
2.2 系统版本与 SELinux、防火墙的取舍
服务端系统我一般选企业级发行版的稳定小版本,比如 Rocky Linux 9 或者对应的商业版本。原因很简单:Cobbler 依赖 Python 运行环境和一堆系统服务,滚动更新的发行版容易在某些小版本上出现依赖冲突。
SELinux 和防火墙这两件事,我的建议是"不要一上来就关"。关掉确实能瞬间消灭一大堆报错,但你在生产环境里迟早要面对它们。更合理的做法是先把策略配好:
# SELinux 相关布尔值,让 httpd 和 cobblerd 能正常工作 setsebool -P httpd_can_network_connect true setsebool -P cobblerd_manage_tftp true # 防火墙放行端口(以 firewalld 为例) firewall-cmd --permanent --add-service=dhcp firewall-cmd --permanent --add-service=tftp firewall-cmd --permanent --add-service=http firewall-cmd --permanent --add-service=https firewall-cmd --permanent --add-port=25151/tcp firewall-cmd --reload如果你确实处在封闭实验环境里,临时把 SELinux 设成 permissive 观察一轮也可以,但一定要在日志里确认没有 AVC 拒绝之后,再决定是彻底关掉还是补策略。顺手看一眼ausearch -m avc -ts recent是个好习惯。
时间同步也别漏。TFTP 和引导文件本身对时间不敏感,但 Cobbler 的 Web 接口和部分认证机制依赖系统时间,时间偏差过大时会出现一些莫名其妙的登录失败。装个时间同步服务,成本极低。
2.3 软件源与依赖准备
Cobbler 依赖 EPEL 源里的若干包,所以先把 EPEL 配好再装。离线环境的话,需要提前把cobbler、cobbler-web、dhcp-server、tftp-server、pykickstart、httpd、syslinux、xinetd(老版本系统需要)这些包和它们的依赖全部下载下来,做一个本地源。
# 配置 EPEL 源后安装 dnf install -y epel-release dnf install -y cobbler cobbler-web dhcp-server tftp-server pykickstart httpd syslinux # 启动并设置开机自启 systemctl enable --now httpd systemctl enable --now cobblerd装完之后先别急着改配置,跑一次cobbler version确认二进制可用。这里有个顺序问题:cobbler check需要 cobblerd 处于运行状态才能连上,所以一定要先systemctl start cobblerd再执行检查命令,否则你会看到连接被拒绝的报错,然后把注意力错误地引到别处去。
3. 安装后的首次自检:cobbler check 报错逐条拆解
3.1 组件清单与配置文件分布
装完之后,先花两分钟认一下目录,这能在后面省掉大量"我改的文件到底生效没有"的困惑:
/etc/cobbler/settings或/etc/cobbler/settings.yaml:主配置,决定 Cobbler 管不管 DHCP、TFTP、DNS。/etc/cobbler/dhcp.template:DHCP 配置的渲染模板。/etc/cobbler/pxe/:引导菜单和引导配置的模板目录。/var/lib/cobbler/kickstarts/:Kickstart 应答文件存放处。/var/lib/cobbler/loaders/:引导程序二进制,比如pxelinux.0。/var/lib/cobbler/distro_mirror/:导入的安装源镜像。/var/lib/tftpboot/:TFTP 根目录,cobbler sync渲染产物落在这里。/var/www/cobbler/:Web 对外提供的安装源和 ks 文件。
配置文件的格式在两代版本之间有断代:较早版本用 INI 风格的/etc/cobbler/settings,较新版本(3.2 以后)换成了 YAML 的/etc/cobbler/settings.yaml。参数名基本一致,但书写格式不同,直接照抄网上的老教程很容易写出一个格式不对的文件导致服务起不来。判断方法很简单:ls /etc/cobbler/看一眼有没有settings.yaml。
3.2 settings 里必须先动的几个开关
下面这几个参数无论哪个版本都必须确认,我列成表格对照:
| 参数 | 建议值 | 为什么必须改 |
|---|---|---|
| server | Cobbler 服务器 IP | 默认是 localhost,客户端拿不到正确的源地址 |
| next_server | 同上,通常与 server 一致 | 告诉客户端去哪儿下引导程序 |
| manage_dhcp | 1 或 0 | 决定 Cobbler 是否渲染并重启 DHCP |
| manage_tftpd | 1 | 一般保持开启 |
| manage_dns | 0 | 除非确实要用它管 DNS,否则关掉减少干扰 |
| default_password_crypted | 加密后的密码 | 默认值等于明文弱密码,必须换 |
| pxe_just_once | 1 | 防止装完重启又进装机流程,形成死循环 |
| allow_duplicate_macs | 0 | 保持严格,重复 MAC 会带来难查的地址漂移 |
生成密码密文有几种方式,选一种即可:
# MD5 方式,老版本兼容性好 openssl passwd -1 -salt "$(openssl rand -hex 4)" 'YourStrongPass' # SHA-512 方式,新系统更推荐 openssl passwd -6 -salt "$(openssl rand -hex 8)" 'YourStrongPass'把输出结果整段贴到default_password_crypted的值里,注意引号和特殊字符要处理好。这里有个小坑:密文里包含$符号,在 YAML 里用双引号包裹时$是安全的,但如果用单引号又会遇到转义差异,所以最稳妥的做法是改完之后立刻systemctl restart cobblerd,然后用cobbler check看它是否还抱怨密码问题。
提示:改完 settings 文件后,一定要重启 cobblerd 再执行
cobbler sync。Cobbler 是读配置启动的守护进程,只改文件不重启,渲染出来的产物仍然是旧值,这个坑我踩过不止一次。
3.3 把 cobbler check 的输出当成待办清单
cobbler check的价值在于它把常见问题列成了一张清单。典型输出大概长这样,我按处理优先级排序说明:
The 'server' field in /etc/cobbler/settings must be set to something other than localhost For PXE to be functional, the 'next_server' field must be set to something other than 127.0.0.1 change 'disable' to 'no' in the tftp service configuration Some network boot-loaders are missing from /var/lib/cobbler/loaders SELinux is enabled. Please review the following wiki page for details Since iptables may be running, ensure 69, 80, and 25151 are unblocked The default password used by the sample templates is still set to 'cobbler' you need to set 'manage_dhcp' to 1处理顺序建议是:先修server和next_server,因为这两个错了后面全白搭;再修 TFTP 服务状态;然后补 loaders;最后处理 SELinux 和防火墙。DHCP 相关的提示可以放到下一章一起做,因为那需要配合模板改。
关于 TFTP 服务,不同系统版本有两种形态。老系统用 xinetd 托管,需要把/etc/xinetd.d/tftp里的disable改成no然后重启 xinetd;较新的系统直接提供 systemd socket:
# 新系统 systemctl enable --now tftp.socket systemctl status tftp.socket看到 socket 处于 active (listening) 状态就对了。
3.4 补齐引导程序:get-loaders 与离线兜底
"Some network boot-loaders are missing" 这条几乎每次都会出现。原因是 Cobbler 需要把pxelinux.0、菜单程序、以及 UEFI 用的grubx64.efi、shimx64.efi放进/var/lib/cobbler/loaders/,而安装包出于体积考虑并不全带。
# 联网环境 cobbler get-loaders # 执行完确认目录内容 ls -l /var/lib/cobbler/loaders/如果服务器不能上外网,cobbler get-loaders会失败。这时候有两个办法:一是从同在 EPEL 源里的 syslinux 包中把pxelinux.0、menu.c32、ldlinux.c32、libutil.c32等复制过去;二是从其他能联网的同版本机器上把整个 loaders 目录打包搬过来。注意ldlinux.c32这类依赖模块经常被漏掉,缺了它引导菜单会直接黑屏或者报找不到模块,这个现象很难往缺文件的方向想。
UEFI 环境还要额外关注grubx64.efi和shimx64.efi是否存在。这两个文件名在不同版本里可能带版本号后缀,Cobbler 的模板会按约定名去找,找不到就生成不出可用的 grub 配置。
3.5 用一次最小 sync 验证闭环
配置改到这一步,可以跑第一次同步了。这一步的意义是验证"Cobbler 能不能把配置渲染成文件":
cobbler sync正常输出会逐项告诉你它更新了哪些内容,包括 DHCP 配置、TFTP 目录、DNS 配置等。如果这一步就报错,先别往下走,把错误信息逐字看完,八成是模板路径不对或者目标目录没权限。同步完成后可以去/var/lib/tftpboot/下面看看有没有生成pxelinux.cfg/、grub/这些目录,有就说明渲染链路是通的。
4. 让 DHCP 与 TFTP 真正接管引导流程
4.1 manage_dhcp 的两种模式该怎么选
manage_dhcp设成 1,Cobbler 会渲染/etc/dhcp/dhcpd.conf并负责重启 dhcpd。这个模式的优势是配置集中、客户端条目自动生成;缺点是它会完全接管这台机器上的 DHCP 服务,如果这台机器同时还给别的网段提供 DHCP,就要小心冲突。
设成 0 则需要你自己维护 DHCP 配置,至少保证两件事:next-server指向 Cobbler 服务器,filename指向引导程序。这种模式适合公司已有统一 DHCP 的场景。还有一种折中方案是通过 OMAPI 让 Cobbler 把主机条目写进远端 DHCP 服务器,但配置复杂度明显上一个台阶,除非有硬性要求,我不建议第一次部署就上。
我一般这么选:实验环境和独立机房里用托管模式,一步到位;接入公司现网时用非托管模式,只改next-server和filename两个字段,改动面最小,出了问题也最容易回退。
4.2 dhcp.template 里必须动的那几个变量
托管模式下,你要改的是/etc/cobbler/dhcp.template。它本质是一个带占位符的模板,Cobbler 渲染时会替换成实际值。核心结构大致是这样的:
subnet $subnet netmask $netmask { option routers $gateway; option subnet-mask $netmask; option domain-name-servers $name_server; range dynamic-bootp $range; default-lease-time 21600; max-lease-time 43200; next-server $next_server; filename "pxelinux.0"; allow booting; allow bootp; }需要你确认的变量有$subnet、$netmask、$gateway、$range 这几个。它们的值不一定来自这个文件本身,很多时候是通过命令行参数传给渲染过程的,或者写在模板里直接写死。最省事的做法是把模板里的占位符替换成你实际的网段值:
subnet 192.168.100.0 netmask 255.255.255.0 { option routers 192.168.100.1; option subnet-mask 255.255.255.0; option domain-name-servers 192.168.100.10; range dynamic-bootp 192.168.100.200 192.168.100.250; default-lease-time 600; max-lease-time 7200; next-server 192.168.100.10; filename "pxelinux.0"; allow booting; allow bootp; }改完cobbler sync,然后去/etc/dhcp/dhcpd.conf确认渲染结果符合预期,再systemctl restart dhcpd(有些版本 Cobbler 会自己重启,但手动确认一次不亏)。
4.3 绑定 MAC 的机器条目是自动生成的
这是 Cobbler 一个很舒服的设计:只要你在 system 对象里绑定了 MAC,并保持 netboot 开启状态,同步时 Cobbler 会自动生成对应的 host 段追加到 DHCP 配置里。也就是说你不需要在模板里为每台机器手写固定地址。
但这也带来一个经典困惑:有些人在/etc/dhcp/dhcpd.conf里手写了 host 段,结果下次同步全被覆盖。记住原则就行——手改 dhcpd.conf 是无效操作,所有变更都应该通过 Cobbler 的对象模型来做。
注意:如果你的环境里想让装机机器拿到固定 IP,正确做法是
cobbler system add时带上--ip-address,而不是去改 dhcpd.conf。固定 IP 和 netboot 是两件独立的事,绑定 MAC 不等于绑定 IP。
4.4 TFTP 根目录与端口放行的验证方法
TFTP 环节出问题时的排查顺序我固定成三步:先确认服务在听 69 端口,再确认文件在根目录里存在,最后确认防火墙没拦。
# 1. 端口是否在监听(udp) ss -lunp | grep ':69' # 2. TFTP 根目录 ls -l /var/lib/tftpboot/ # 应该能看到 pxelinux.0、pxelinux.cfg/、grub/、images/ 等 # 3. 本地自测 tftp 127.0.0.1 -c get pxelinux.0 ls -l pxelinux.0本地能取到文件,说明服务端没问题,接下来才需要怀疑网络和防火墙。这个顺序能帮你快速把问题范围从"整个引导链路"缩小到"某一个端口"。
4.5 UEFI 与 BIOS 混用时的处理思路
现在新采购的机器基本都是 UEFI 引导,但老设备还在跑 BIOS,混用环境非常常见。两者的差别在于引导程序:BIOS 走pxelinux.0,UEFI 走grubx64.efi(前面通常还有shimx64.efi)。Cobbler 在同步时会在 TFTP 根目录下同时生成pxelinux.cfg/和grub/两套配置目录,具体走哪套由客户端固件决定。
实际做的时候,我倾向于把同一个安装源导入两次,做成两个 distro:一个给 BIOS 用,一个给 UEFI 用,各自指定对应的引导器属性,再各自建 profile。虽然看起来有点冗余,但比在同一个 profile 里来回切换引导器参数要稳得多,出了问题也能一眼看出是哪条线的问题。混用环境下最怕的就是"改了一处影响另一处",拆开就没有这个烦恼。
5. 镜像导入与三层对象模型:把装机做成模板工程
5.1 import 命令的完整流程
Cobbler 的 import 会做三件事:把安装源复制到/var/lib/cobbler/distro_mirror/下、解析出内核和 initrd 路径、自动创建一个同名 distro 和一个同名 profile。整个流程可以一次跑完:
# 挂载安装镜像 mkdir -p /mnt/iso mount -o loop /path/to/Rocky-9.iso /mnt/iso # 导入,name 是自定义标识,arch 要和镜像匹配 cobbler import --path=/mnt/iso --name=rocky9 --arch=x86_64 # 查看结果 cobbler distro list cobbler profile list导入时间取决于镜像大小和磁盘速度,一张 DVD 镜像通常要几分钟。这里有个很实际的注意点:Cobbler 是把整个安装源复制过去,不是建立软链接。所以规划磁盘时,要按"每个发行版一份完整镜像"来算容量,三个发行版就是三份空间。我见过有人用一块 50G 的盘导入四五个发行版,同步到一半写满,报错信息还不是"磁盘空间不足",排查起来很绕。
如果已经挂了 HTTP 上的安装源,也可以用cobbler import的变体只导入内核和 initrd,然后通过--tree之类的参数让安装过程从远端拉包。这种方式省磁盘,但要求装机时网络稳定,客户端和源之间带宽要够。我的选择是:小规模、网络一般的环境老老实实复制全量;大规模、内网带宽充足的环境才用远端源。
5.2 distro、profile、system 三层模型
理解这三层是写好 Cobbler 配置的关键。我用一张表说明:
| 层级 | 代表什么 | 典型内容 | 复用关系 |
|---|---|---|---|
| distro | 一个发行版的安装源 | 内核、initrd、架构、引导器 | 最底层,一个发行版一份 |
| profile | 一套装机规格 | Kickstart 文件、内核参数、repo | 从 distro 派生,可多个 |
| system | 一台具体机器 | MAC、IP、主机名、绑定的 profile | 从 profile 派生,一机一份 |
举个实际例子:同一份 Rocky 9 安装源,我可能建三个 profile——"通用服务器"、"计算节点"、"数据库节点",它们的区别只在于 Kickstart 里的分区方案和软件包列表不同。然后每台具体机器作为一个 system,绑定到对应 profile 上。这样一来,改一次"计算节点"的分区方案,所有绑定到该 profile 的机器重装时都会生效,不需要逐台修改。
这个模型也解释了为什么不该把 MAC 直接写进 profile。Profile 是规格,System 是实例,把实例信息写进规格里,模型就废了。
# 创建带 MAC 绑定的 system cobbler system add --name=node01 \ --profile=rocky9-compute \ --mac=00:11:22:33:44:55 \ --ip-address=192.168.100.101 \ --hostname=node01 \ --gateway=192.168.100.1 \ --name-servers=192.168.100.10 cobbler sync5.3 Kickstart 模板的编写要点
Kickstart 文件里最容易被忽略的是它是被模板引擎渲染过的。Cobbler 里放的是模板,同步时才渲染成最终的 ks 文件。这意味着你可以用占位符:
#platform=x86_64 install url --url=$tree text lang en_US.UTF-8 keyboard us network --bootproto=dhcp --device=link --activate rootpw --iscrypted $default_password_crypted firewall --enabled --service=ssh selinux --enforcing timezone Asia/Shanghai --utc bootloader --location=mbr --append="net.ifnames=0 biosdevname=0" zerombr clearpart --all --initlabel part /boot --fstype=xfs --size=1024 part pv.01 --grow --size=1 volgroup vg0 pv.01 logvol swap --name=lv_swap --vgname=vg0 --size=4096 logvol / --fstype=xfs --name=lv_root --vgname=vg0 --size=20480 --grow %packages @^minimal-environment @standard %end %post echo "provisioned at $(date)" >> /root/provision.log %end几个必须注意的点:$tree会被替换成安装源的地址,这是 Cobbler 提供的一个便捷变量;$default_password_crypted直接复用 settings 里的加密密码,这样一处改全局生效;bootloader那行加上net.ifnames=0 biosdevname=0能让网卡名回到eth0这种传统命名,对后续脚本化配置非常友好,但这个参数在不同发行版上的支持度有差异,需要实测。
最坑的是%post段里的$。因为整个文件要过一遍模板引擎,shell 里的$PATH、$(hostname)这类写法可能被引擎当成变量去解析,轻则渲染出错误内容,重则直接报错导致同步失败。两种规避方式我都用过:短脚本用反斜杠转义,比如echo \$PATH;长脚本干脆放到 HTTP 服务器上,在%post里用curl拉下来执行。后者更省心,也方便版本管理。
%post curl -s -o /tmp/post.sh http://192.168.100.10/cblr/scripts/post.sh bash /tmp/post.sh %end5.4 防止装完重装:pxe_just_once 与 nopxe 机制
装完第一遍后,机器会重启。如果 DHCP 和引导配置还在,它会再次进入装机流程,把刚装好的系统又覆盖一遍。这个死循环在新手环境里出现频率极高。
Cobbler 提供的解法是pxe_just_once: 1配合 ks 文件里的一个回调。原理是:装机完成前,客户端向 Cobbler 的服务接口发一个请求,把对应 system 的 netboot 标记关掉,下次这台机器再开机时 DHCP 就不再给它引导文件名了。这个请求需要在 ks 的%post里显式发起:
%post curl -s "http://192.168.100.10/cblr/svc/op/nopxe/system/node01" > /dev/null %end注意这里要写具体的 system 名字,如果机器很多,可以用 Cobbler 提供的变量让它自动填充,避免逐台维护。如果你的版本支持自动获取,优先用自动方式,因为手工写 name 意味着每加一台机器都要改一次模板。
如果pxe_just_once因为某些原因不生效,兜底办法是装完后手动执行cobbler system edit --name=node01 --netboot-enabled=false再同步。这个方法原始但绝对可靠,我一般会在交付文档里写上,作为应急手段。
6. 客户端引导失败的排查链路
6.1 从屏幕报错反推环节
排查 PXE 问题最有效的方法是看懂客户端屏幕上那几行字。它们其实直接告诉你是哪个环节断了:
| 屏幕提示 | 出问题的环节 | 优先检查项 |
|---|---|---|
| PXE-E51: No DHCP or proxyDHCP offers | DHCP | 网段、中继、dhcpd 是否运行 |
| PXE-E53: No boot filename received | DHCP 回复字段 | next-server 与 filename 是否下发 |
| PXE-E32: TFTP open timeout | TFTP | 69 端口、防火墙、tftp.socket 状态 |
| 卡在 Loading pxelinux.cfg/default | 引导配置 | TFTP 目录下配置是否生成 |
| 引导菜单出现但回车无反应 | 内核/initrd | images 目录文件是否完整 |
| 进入安装但报 ks 下载失败 | HTTP | ks 路径、httpd 状态、防火墙 80 |
我习惯按"DHCP → TFTP → HTTP"三段式排查,因为这是数据传输的先后顺序,按顺序查不会漏,也不会在 DHCP 还没通的时候去纠结 grub 配置。
6.2 拿不到地址时的排查动作
客户端报 E51,先在服务端确认 DHCP 服务状态和配置是否真的被渲染:
systemctl status dhcpd grep -n "next-server\|filename\|range" /etc/dhcp/dhcpd.conf tail -f /var/log/messages | grep dhcpd然后在客户端侧确认它确实发出了请求。如果你有一台同网段的机器,可以直接dhclient -v看交互过程。对于跨网段的场景,重点确认网络设备的 DHCP 中继配置。
这里有个隐蔽的坑:如果同网段里已经有一台路由器或防火墙在跑 DHCP,它可能先于 Cobbler 回复,客户端拿到的是别人的地址,自然拿不到引导文件名。这种"两个 DHCP 打架"的情况表现就是时而成功时而失败,排查时容易被忽略。解决办法是关掉其中一个,或者在交换机上做端口隔离。
6.3 TFTP 超时的处理步骤
E32 出现说明 DHCP 已经通了,问题在文件传输。按顺序做这几件事:
# 服务状态 systemctl status tftp.socket # 端口监听 ss -lunp | grep ':69' # 文件是否存在 ls -l /var/lib/tftpboot/pxelinux.0 # 放行防火墙 firewall-cmd --permanent --add-service=tftp && firewall-cmd --reload如果这些都对还不通,用另一台机器做一次真实 TFTP 拉取测试,比如tftp 192.168.100.10 -c get pxelinux.0。这一步能帮你区分"服务端问题"和"网络路径问题"。做到这里基本就能定位了。
6.4 cobbler sync 报错对照表
服务端渲染失败的报错通常不直观,我整理了几类最常见的:
| 报错关键词 | 常见原因 | 处理方式 |
|---|---|---|
| template error / cheetah | 模板语法错误或变量名写错 | 检查占位符拼写,确认变量在上下文中存在 |
| permission denied | 目标目录权限或 SELinux | 检查 /var/lib/tftpboot 属主,看 AVC 日志 |
| failed to connect to cobblerd | 守护进程没起来 | systemctl restart cobblerd 后重试 |
| dhcpd.conf syntax error | 模板渲染结果语法不对 | 直接看 /etc/dhcp/dhcpd.conf 渲染结果 |
| no space left | 磁盘满 | 查看 distro_mirror 占用,清理无用发行版 |
我个人的经验是,cobbler sync报错时不要急着改模板,先看它渲染出来的目标文件长什么样。很多时候是模板里的某个变量没被赋值,渲染出了一个空值或者字面量,导致下游服务解析失败。直接看产物比盯着模板猜要快得多。
7. 上线之后的维护与自动化接入
7.1 需要纳入备份的目录
Cobbler 的可迁移性其实相当好,因为它的状态集中在几个目录里。做好备份意味着换机器时几乎可以整体搬过去:
/etc/cobbler/:所有配置和模板,最关键。/var/lib/cobbler/kickstarts/:应答文件。/var/lib/cobbler/config/:对象数据(distro、profile、system 的定义)。/var/lib/cobbler/loaders/:引导程序,可以重新获取,但离线环境必须留着。
安装源镜像/var/lib/cobbler/distro_mirror/通常太大,可以按需备份或者事后重新导入。恢复时的顺序是:装包、回拷配置文件、回拷对象数据、cobbler sync、重启相关服务。我实际操作过一次整机迁移,全程不到半小时,比重新配一遍省事太多。
7.2 用命令行和接口把装机接进自动化流程
Cobbler 提供两层接口:命令行工具和基于 XML-RPC 的远程接口。命令行适合脚本化,远程接口适合和外部平台集成。
一个很常见的用法是:平台上新增一台机器时,脚本自动调用命令创建 system 对象并同步,机器上架通电后就自动装好了。
#!/bin/bash # 简化示例:登记一台机器 NAME=$1 MAC=$2 IP=$3 cobbler system add --name="$NAME" \ --profile=rocky9-compute \ --mac="$MAC" \ --ip-address="$IP" \ --hostname="$NAME" \ --netboot-enabled=true cobbler sync如果要用远程接口,Cobbler 的 Web 服务暴露了一套 XML-RPC 接口,需要先配置认证方式。默认的认证配置里有一个内置账号,密码是公开的弱口令,上线前必须改掉:
# 修改 Web 登录密码 htdigest /etc/cobbler/users.digest "Cobbler" cobbler改完重启 httpd 和 cobblerd。这一步经常被跳过,因为实验环境里大家都能登录就觉得没问题,但这台机器一旦接入办公网,就是一个敞开的管理入口。
7.3 三个让我印象最深的坑
第一个坑是改完配置忘了重启 cobblerd。我花了两个小时在怀疑 TFTP 模板有问题,最后发现是守护进程还拿着旧配置。后来我给自己定了个规矩:任何 settings 相关改动,第一件事就是重启服务再同步。
第二个坑是**ldlinux.c32这类依赖模块缺失**。现象是引导菜单能出现但选完就黑屏,或者提示找不到某个模块。因为pxelinux.0本身在,很容易认为引导程序是完整的。后来我养成习惯,一边补齐 loaders 目录,一边用ls核对文件清单,而不是只看报错提示里提到的那一个文件。
第三个坑是UEFI 和 BIOS 共用一套 profile。有一次一批新机器到了,装机死活进不去,老机器一切正常。原因是新机器走 UEFI,而 profile 的引导器配置是给 BIOS 用的。拆成两个 profile 之后问题消失。这件事之后,我在网络规划阶段就会把引导方式作为一个必填项确认下来。
7.4 版本升级时要注意的东西
跨大版本升级 Cobbler 时,最大的变化是配置文件格式从 INI 换到 YAML。升级前务必备份/etc/cobbler/,然后在测试环境里把旧配置迁移一遍,确认所有参数都被正确识别。有些参数在新版本里被重命名或废弃,cobbler check会给提示,但不会自动帮你改。
升级后的验证流程建议是:cobbler check清空所有非预期告警、cobbler sync无报错、找一台虚拟机走一次完整装机流程。走通这三步再碰生产环境。
另外,Cobbler 的对象模型在外置数据库和内置配置之间有过变化,如果你之前直接改过/var/lib/cobbler/config/下的文件,升级时要注意有没有被新格式覆盖。最稳妥的方式是升级前把对象清单导出成可读文本,比如cobbler report,出问题时能对照着重建。
我个人在实际操作中的体会是,Cobbler 的难点从来不在安装包本身,而在于它站在 DHCP、TFTP、HTTP、模板引擎、客户端固件的交叉路口上。任何一端出问题,现象都指向同一个结果——装机失败。所以真正省时间的做法,不是背命令,而是把每一层都单独验证一遍:服务在不在跑、端口通不通、文件在不在、配置渲染出来对不对。这四步做完,绝大多数问题都会自己现形。