Kickstart无人值守实战:从配置文件到PXE批量装机的完整指南

发布时间:2026/10/24 0:54:24
Kickstart无人值守实战:从配置文件到PXE批量装机的完整指南

干运维的人都有体会,批量装系统是最磨人的活之一。早期我经历过的场景是这样的:几十台裸机,一台一台塞光盘、点鼠标,选语言、分区、设密码、等进度条,重复几十遍。中间稍有分神,分区选错了、时区选差了,回头还得重来。后来接触到Kickstart,才算把这件脏活彻底解放出来——把安装过程中所有需要人工点击确认的步骤,全部预先写进一个配置文件里,安装程序自动读取、自动执行,全程无人值守,十几台机器的系统部署,基本就是从开机到重启,一气呵成。

这篇文章想跟大家聊的,正是Linux环境下的无人值守安装脚本Kickstart的完整玩法。内容不光讲配置文件长什么样,更会拆解它是怎么生效的、实际生产环境怎么配合网络装机来用、遇到问题怎么排查。不管你是刚入行的系统管理员,还是已经在维护几十台服务器的老运维,这套东西都能直接落地上手。

1. Kickstart解决的是什么问题——批量装机的痛点和核心原理

1.1 手工安装最大的成本不是等,而是“重复决策”

一台机器手动装Linux,单看时间不算长,半小时到四十分钟能跑完。但如果你一个月要装二十台、五十台呢?麻烦的不是等进度条,而是那十几分钟密集的交互决策:语言选什么、键盘布局用什么、磁盘怎么分、swap给多大、root密码设什么、软件包选哪批、时区是哪个、安装源从哪来。每一项都要人盯着,每一项出错都可能让你前功尽弃。

Kickstart的思路很直接:把安装程序需要问的所有问题,预先整理成一个纯文本配置文件。安装时通过引导参数告诉安装器“读这个文件”,安装器就不再用交互界面问你问题,而是直接从文件里拿答案。

这个思路其实跟日常生活中的“模板思维”一模一样。你填过在线表单吧?每次填同样的姓名、地址、联系方式,很烦对吧。Kickstart相当于做了一份自动填表脚本,表单来了,程序自己把答案填好、点提交。你唯一的任务是确保模板里的内容是准确的。

1.2 Kickstart工作机制:不是安装器的一部分,而是安装器的“答题卡”

很多初学者会误以为Kickstart是一个单独的工具或一个安装程序,其实不是。它本质上是一个配置文件,格式是纯文本的,服务对象是Red Hat系的安装器Anaconda。

流程是这样的:机器从启动介质引导,加载内核和初始initrd镜像,此时引导程序传参里带了类似ks=http://192.168.1.10/ks.cfg的选项。Anaconda在初始化阶段读到这个参数,就会通过网络拉取这个文件,然后把这个文件里的配置逐条解析成安装动作。文件里写了分区,它就自动分区;写了时区,它就自动设置时间;写了密码,它就直接写入。整个过程没有任何交互界面出现,装完自动重启。

所以Kickstart本身不参与安装,它更像一份说明书,Anaconda是照着说明书干活的工人。

1.3 Kickstart配置文件的结构:三段式的清晰布局

Kickstart文件的主干由三大部分组成,理解了这个结构,后面手写就不会乱。

命令段 %packages ... %end %pre ... %end %post ... %end

命令段是必选的,放的是最基础的系统配置指令,包括安装源、语言、键盘、网络、分区、引导装载程序、root密码等。%packages段指定要安装哪些软件包。%pre和%post段分别是在安装开始前和系统安装完成后执行的脚本,用于做定制化处理。

还有一类可选段,比如%addon com_redhat_kpatch、%anaconda,某些特定发行版或特定需求才会用到。日常使用中,绝大多数人只需要掌握命令段、%packages段和%post段就够了。

2. 配置文件从哪来——三种获取方式,各有适用场景

2.1 系统自带样板:root家目录的anaconda-ks.cfg

如果你手头已经有一台装好的Linux服务器,进到root用户家目录,会发现一个叫anaconda-ks.cfg的文件。这个文件就是当初安装这台系统时,Anaconda根据你手动选择的选项生成的Kickstart文件。

这份文件完全可以拿来当作新机器的安装配置模板。优点是生成真实、跟当前系统状态完全一致,缺点是里面内容有冗余,而且如果你当初很多选项是默认的,生成的文件可能不符合你的预期。我在实际项目中,经常拿这台机器的配置做底子,改几处再上新机器,比如把IP改了、把分区调一下,速度比自己从头写快很多。

2.2 图形化工具配置:system-config-kickstart

部分发行版可以安装一个叫system-config-kickstart的图形化配置工具,界面是表单式的,基本配置、软件包、分区等都能用鼠标选。

我个人的评价是:适合完全不想手写文件、只想快速生成一个可用配置的人。但它的界面比较老旧,支持的选项跟Anaconda实际版本不一定完全对齐,高级选项也不全。我用过几次,最后还是回到了手写,因为手写虽然要记语法,但可控性强太多。

2.3 手写最小模板:生产环境最推荐的方式

生产环境我最推荐的方式,是自己维护一份精练的Kickstart模板,需要什么功能往里加。这个模板是经年在多台机器上验证过的,风险低、可控性高。

我给团队里带的模板,通常控制在五十行以内,涵盖了安装源、分区、网络、密码、软件包和一段post脚本。这样任何新机器拿过来,改几个参数就能用。

文件不追求大而全,核心是可读、可维护。这个思路跟“代码评审”一个道理——Kickstart也是运维的代码,一样要简洁、要写注释、要有版本记录。

3. 核心配置项逐段拆解——从命令段到%post的完整语法

3.1 命令段基础:安装源、语言时区、网络和root密码

命令段是Kickstart文件的骨架,每个指令一行。先看一段最基本的配置:

install url --url="http://mirror.example.com/centos/7/os/x86_64/" lang en_US.UTF-8 keyboard us timezone Asia/Shanghai --isUtc --nontp network --bootproto=dhcp --device=ens33 --onboot=yes --ipv6=auto rootpw --iscrypted $6$xxx...(加密后的哈希) selinux --enforcing firewall --enabled --service=ssh services --enabled=sshd reboot

逐行解释一下:

  • install:明确告诉安装器这是全新安装而不是升级。配合url、nfs、cdrom、harddrive等参数指定安装源。
  • url --url=:从HTTP服务器拉取安装树。生产环境最常用HTTP方式,因为配置简单、速度快。也支持nfs --server= --dir=的写法。
  • lang和keyboard:设置语言和键盘布局。注意这里的时区设置timezone Asia/Shanghai --isUtc,--isUtc表示硬件时钟按UTC处理,生产服务器一般建议这样配,避免BIOS时钟和系统时区相互干扰。
  • network:网络配置。--bootproto=dhcp表示用DHCP,也可以写成--bootproto=static --ip=192.168.1.10 --netmask=255.255.255.0 --gateway=192.168.1.1 --nameserver=8.8.8.8。--device=指定网卡名,如果你不确定机器的网卡名,可以省略这一项,安装器会自动判断。
  • rootpw:root密码。强烈建议用--iscrypted参数传加密后的哈希,不要明文写密码。明文密码在配置文件和安装日志里都会出现,风险太大。
  • selinux、firewall、services:系统安全基线。生产环境我一般开启SELinux(--enforcing),防火墙只放行ssh。如果你内部网络环境复杂,可以按需调整。
  • reboot:安装完成后自动重启。加上它会省去最后一步手动点重启的麻烦,但也要注意别在测试环境误用,容易导致重复装机。

可以这么理解,命令段就是“全局配置面板”,所有跟系统初始状态有关的选项,都在这里处理。

3.2 分区与引导装载程序:最容易写错,也最怕写错的部分

分区指令是Kickstart中出问题最多的地方,写错容易把磁盘数据搞没,需要格外谨慎。

常用写法分两种:

自动分区版:

clearpart --all --initlabel autopart --type=lvm

clearpart --all --initlabel表示清除磁盘上的所有分区表和已有分区,--initlabel是无交互地初始化磁盘标签。autopart让Anaconda自动创建分区方案,--type=lvm声明用LVM布局。默认情况下它会自动创建/、swap、/home等逻辑卷,省事但灵活性低。

手写分区版:

clearpart --all --initlabel part /boot --fstype=xfs --size=1024 part pv.01 --size=1 --grow volgroup vg_root pv.01 logvol / --fstype=xfs --name=lv_root --vgname=vg_root --size=20480 logvol swap --name=lv_swap --vgname=vg_root --size=4096

先说clearpart --all --initlabel,如果没有这一行,安装器在磁盘已有分区时会停下来问你要不要清理,无人值守模式下这就卡住了。所以要么写clearpart,要么写ignoredisk,总得给磁盘一个交代。

part和logvol的写法要重点注意:

  • part /boot --size=1024这段表示创建一块大小约1GB的/boot分区。--size单位是MB,不写--grow就表示固定大小。
  • part pv.01 --size=1 --grow表示创建一个LVM物理卷,起始给1MB大小并允许扩展到剩余所有空间。这是LVM配置的惯用写法,好处是分区大小能随磁盘容量自动调整,不需要在配置里写死总大小。
  • volgroup vg_root pv.01把物理卷卷进卷组。
  • logvol /系列为逻辑卷分配空间。

手写分区的价值,在于你能精确控制每个挂载点的大小。比如某些业务场景/srv或/data需要大空间独立挂载,自动分区就做不到。

还要注意bootloader指令:

bootloader --location=mbr --boot-drive=sda

--location=mbr表示把GRUB引导装载程序安装到主引导记录,传统BIOS启动的机器这么写。UEFI启动的机器写法不同,需要引导到EFI分区,这里不展开,实际部署前请务必确认目标机器的启动模式。

3.3 %packages段:软件包选择的两种组合思路

%packages段控制装完系统后都有哪些软件,里面有两种写法:

%packages @^minimal-environment @core vim lrzsz -unused-packages %end

以@开头的是包组,比如@^minimal-environment是环境组,@core是核心包组。^符号表示它是环境组定义,一个Kickstart文件里只能有一个环境组。不带@的是单个软件包名。减号-开头表示从包组里排除某个包。

团队里新的机器一般都用最小化环境(minimal-environment)加上core组,这样系统体积小、攻击面小、后续维护干净。需要什么工具再用yum手动装就行,避免装上大量用不到的软件包占用磁盘和内存。

如果你装的是图形化桌面用途,可以写成@^graphical-server-environment,但不建议生产服务器这么干。

3.4 %pre和%post脚本:安装前后的个性化处理窗口

%pre段在所有安装动作之前执行,脚本运行在一个极简环境里,可以做磁盘检测、网络探测,甚至动态生成分区配置。

我举一个实际场景:某次项目里,需要根据机器磁盘大小决定swap分区是4GB还是8GB,%pre里就用lsblk拿到磁盘容量,根据条件重新生成一个分区配置片段,写入/tmp/partition.cfg,然后命令段里用%include /tmp/partition.cfg引用。这种方式让一套Kickstart配置能同时适配不同配置的机器。

%post段是在系统安装完成、配置文件已经写入磁盘后执行的,运行环境是一个完整的系统,此时可以用yum装软件、改配置、创建用户、调整内核参数。这段是实际运用最多的地方,可以把本需要装完系统手工做的所有事后操作全部自动化。

%post --log=/root/kickstart-post.log echo "export LANG=en_US.UTF-8" >> /etc/profile.d/env.sh useradd deploy mkdir -p /data echo "192.168.1.20 monitor.example.com" >> /etc/hosts timedatectl set-timezone Asia/Shanghai %end

--log=参数会把post脚本的输出写到指定文件,排查问题的时候非常好用。如果没写这个参数,脚本报错信息会丢失,出问题很难定位。

post脚本默认在chroot环境里执行,多数情况下问题不大。如果脚本里有需要访问安装介质或宿主环境的路径,要用%post --nochroot,但这种情况较少见。

3.5 需要特别注意的语法细节

有几个Kickstart语法上的细节,我踩过坑,写出来告诉大家:

  • 所有命令大小写敏感。install和Install是不同的,写错会被当作未知命令跳过,或直接报错。
  • 时间、密码类参数格式不统一。rootpw密码必须写--iscrypted参数并传入加密哈希,格式为$6$salt$hash;但早期版本允许--plaintext直接传明文。每改一个版本前先查文档,不要依赖记忆。
  • %pre、%post、%packages段必须以%end结束,如果漏了,Anaconda解析时会报错并中止安装。
  • ks文件里的注释以#开头,但%pre、%post内部的脚本里,注释符按脚本语言的规则来,不能用#通吃。

4. 真正跑起来——搭配PXE和HTTP实现从网络引导到全自动安装

4.1 整体架构是什么样子的

Kickstart文件和安装源都有了,但没人想一台台去插光盘引导。生产环境的标准做法是:配合PXE进行网络引导。

机器开机时,网卡里的PXE固件会向DHCP服务器要一个IP地址,同时向DHCP要一个“下一阶段引导文件”的位置(通常是TFTP服务器上的pxelinux.0)。机器接着从TFTP下载引导程序,然后按引导配置文件里的指令加载内核和initrd,内核启动后Anaconda开始运行,此时再传给它ks文件的位置,安装程序拉取ks文件,全自动往下跑。

整个链路:

PXE引导(DHCP + TFTP + pxelinux.0) ↓ 内核和initrd加载 ↓ Anaconda启动,读取ks=参数 ↓ 从HTTP拉取ks文件 + 安装树 ↓ 无人值守安装完成

用一句话概括:DHCP决定机器从哪里引导,TFTP提供引导文件,HTTP提供安装内容,而Kickstart文件决定整个安装过程如何展开。

4.2 DHCP、TFTP、HTTP三个服务的具体配置

DHCP服务配置

以dnsmasq或dhcpd为例,核心要配置的地方是:

next-server 192.168.1.10; # TFTP服务器地址 filename "pxelinux.0"; # PXE引导程序文件名

如果用的是dnsmasq,则:

dhcp-range=192.168.1.100,192.168.1.200,12h dhcp-option=66,192.168.1.10 # next-server dhcp-option=67,pxelinux.0 # filename
TFTP服务器配置

安装tftp-server后,将/usr/share/syslinux/pxelinux.0、ldlinux.c32、vmlinuz、initrd.img等文件放到TFTP根目录下。pxelinux.0是PXE引导程序,vmlinuz和initrd.img从安装树镜像的isolinux目录里拿。

在TFTP根目录创建pxelinux.cfg/default文件:

default linux label linux kernel vmlinuz append initrd=initrd.img inst.ks=http://192.168.1.10/ks/ks.cfg inst.repo=http://192.168.1.10/centos/7/os/x86_64/ net.ifnames=0 biosdevname=0 ksdevice=eth0

default文件名表示所有未单独配置的机器都使用这个引导配置。如果你希望不同MAC地址的机器执行不同的ks文件,在pxelinux.cfg目录下放01-aa-bb-cc-dd-ee-ff这样命名的主机配置文件即可。

HTTP服务器配置

HTTP服务的角色是存放安装树和Kickstart文件。把ISO镜像挂载到/var/www/html/centos/7/os/x86_64/下,再把ks文件放到/var/www/html/ks/目录下,一个静态文件服务就完成了。

生产环境我一般用Nginx,纯静态文件服务,简单稳定。如果对性能没有特殊要求,用系统自带的httpd也没问题。

4.3 引导参数的取舍:ks=、ksdevice、ip=这些参数到底是干什么的

引导配置里的append行是打包所有启动参数的地方,其中跟Kickstart直接相关的有:

  • ks=http://192.168.1.10/ks/ks.cfg:告诉Anaconda从哪里读取Kickstart文件。这是无人值守安装的关键参数,没有它,Anaconda就回到交互模式。
  • ksdevice=eth0:指定安装过程中使用哪块网卡来访问网络。机器有多块网卡时,如果不指定,安装器可能选错网口,导致后续找不到安装源。
  • ip=192.168.1.10 netmask=255.255.255.0 gateway=192.168.1.1:如果你不希望安装过程依赖DHCP分配IP,可以直接在引导参数里把IP配死,这样ks文件的网络配置和引导参数网络配置要保持一致。
  • net.ifnames=0 biosdevname=0:关闭网卡的命名规则,让网卡按传统的eth0、eth1命名。这个参数看个人习惯,如果你已经习惯了新命名规则,可以不写。
  • vnc:如果机器没有显示器和键盘,需要远程图形化安装时,可以加vnc vncpassword=xxx参数,不过既然用Kickstart,通常都用不到了。

还有一个容易忽略的点:如果引导参数里不写ks=,Anaconda会在交互界面里以菜单形式问你ks文件在哪,那就不叫无人值守了。所以引导参数里ks=必须写上。

5. 常见问题与排查技巧实录——那些装了几十台机器才会踩到的坑

5.1 问题一:安装卡在“No root password”或“You must set a root password”

现象:Kickstart文件里明明写了root密码,但安装仍然卡在原地不动。

原因:最常见的是rootpw指令本身写挂了。比如明文密码在旧版本允许,在新版本中默认被拒绝,或者--iscrypted参数写错了位置。

排查方式:看http服务器上的访问日志,确认ks文件确实被请求到了;再看文件内容,rootpw那一行的参数顺序对不对。我遇到过一次问题是加密哈希里的$符号在KoKickstart文件传输过程中被转义,导致哈希错误。

建议:手写加密哈希时,务必要复制完整的$6$...字符串。以HTTP方式拉取ks文件时,文件内容必须是原样文本,不要加上任何编码转换。

5.2 问题二:%post脚本没执行,或执行了但没生效

现象:系统装完,用户没创建成功,配置文件没改到位,日志也没留下。

原因:很多情况下是%post段的脚本语法错误,或者某个命令依赖网络,而当时网络还没就绪。

排查方式:给%post加--log=/root/ks-post.log参数,让脚本输出落盘;脚本里也要自己加set -e,让报错及时暴露。

%post --log=/root/ks-post.log set -e ... %end

我个人习惯是post脚本内先加sleep 10,等网络服务稳定后再执行网络相关命令。很多问题是网络设备刚初始化,马上拉包容易超时。

5.3 问题三:PXE引导正常,但Anaconda说“Unable to download the kickstart file”

现象:机器能从PXE启动,内核加载后没多久,报错说拉不到ks文件。

原因:HTTP路径写错了、防火墙没放行、或者ks=参数里的IP写错了。

排查方式:

  • 先看web服务器访问日志,确认有没有收到来自目标机器IP的请求。如果连请求都没有,说明网络路径有问题。
  • 用浏览器或curl在局域网内试一下,确认http://192.168.1.10/ks/ks.cfg能直接访问到。
  • 检查HTTP服务器有没有放行端口,如果开着firewalld,要记得添加规则。
  • 确认ks文件权限是644,Nginx/apache用户有足够权限读取。

5.4 问题四:分区相关错误,安装器一直提示磁盘信息无法识别

现象:安装过程在分区步骤中止,出现You have not defined a root partition (/), which is required for installation to continue之类的报错。

原因:分区配置里没有给根分区分配足够空间,尤其是手写逻辑卷时,/逻辑卷没写--size,或者写的是--size=1但没写--grow,导致创建失败。

排查方式:检查part pv.01、volgroup vg_root、logvol /这三条指令的归属关系。LVM链路缺一环就不生效。再检查clearpart --all后的磁盘是否被正确识别。

我踩过最深的一个坑是服务器磁盘是RAID卡直通,系统看到的磁盘设备名不是sda而是sdb,但配置文件里--drive=sda把这个盘忽略了,导致分区全部落空。排查了很久才发现是磁盘设备名的问题。

5.5 问题五:ks文件下载到了但语法报错,看不到具体原因

现象:Anaconda报错说ks配置文件有错误,但屏幕上只显示一行笼统信息,细节看不到。

排查方式:Kickstart文件的报错日志在Anaconda的日志目录里,具体文件位置因发行版不同略有差异,一般在/var/log/anaconda.log或/tmp/anaconda.log。用SSH或串口拿到日志后再看具体是哪条指令报错。

在用HTTP方式部署时,我建议先在本地用yum install pykickstart把ksvalidator装上,用它检查语法:

ksvalidator /var/www/html/ks/ks.cfg

这个命令会直接报出哪一行有问题,省得装机时反复试错。我把ksvalidator作为所有ks文件上线前的强制检查步骤,没有通过不允许进生产流程。

5.6 常规排错思路总结:从网络、文件、语法三个层面入手

我排Kickstart问题的经验大概可以框成三步:

  1. 网络层:先确认引导阶段机器能拿到IP、能访问HTTP服务器。用tcpdump在服务器端抓包看有没有来自目标机器的HTTP请求,最快。
  2. 文件层:确认ks文件路径、权限、内容正确。直接看HTTP访问日志,或者用curl模拟拉取。
  3. 语法层:用ksvalidator做静态检查。如果过了静态检查仍报错,就去翻Anaconda的详细日志。

只要按照这个顺序排查,百分之八十的问题都能在十分钟内定位。

6. 扩展玩法——Kickstart的更多生产实践

6.1 搭配正则脚本实现不同机器不同配置

在实际项目中,几十台机器可能不完全是同配置的。有的内存大,swap就多分点;有的业务角色不同,软件包组也不一样。这些差异可以通过在%pre阶段写识别逻辑来解决。

例如根据MAC地址前缀或主板序列号判断机器角色,再向/tmp/role.cfg写入不同配置片段,用%include引入:

%pre if [[ "$(dmidecode -s system-serial-number)" == "VMware-1234" ]]; then echo "part /data --fstype=xfs --size=20480" > /tmp/extra-part.cfg echo "nginx" >> /tmp/extra-packages.cfg else echo "part /data --fstype=xfs --size=10240" > /tmp/extra-part.cfg fi %end %include /tmp/extra-part.cfg %packages %include /tmp/extra-packages.cfg %end

这种动态生成的思路,让一套Kickstart模板可以覆盖多种规格的机器,非常实用。

6.2 Kickstart的最佳实践:配置文件也需要版本管理

Kickstart文件本质上是运维代码,那么就应该像代码一样管理。我自己维护了一套目录结构,每类系统、每个版本单独建目录,变更历史用版本管理工具记录。每次上线新机器前,先把ks文件跑一遍ksvalidator,再检查文件里的密码哈希、分区大小这些高风险项。

另外建议在ks文件头部写清注释:这套配置最初的适用场景是什么、改过哪些内容、最后更新时间是谁。团队协作时,这份注释能节省大量沟通成本。

6.3 和其他自动化工具的关系:Kickstart并不是唯一的答案

Kickstart天然跟PXE搭配用于裸机安装,但如果你已有虚拟化平台,或者需要更复杂的编排(比如装完系统后还要执行配置管理、应用部署),那可以把Kickstart当成“基础系统安装阶段”的解决方案,后续再接配置管理工具。

我所在的团队就是这样做的:Kickstart负责把操作系统装好、网络调通、基础工具就位;接着由配置管理工具(比如Ansible或者Salt,任意选一个即可)接管,负责业务软件部署与持续配置同步。两段分工明确,各管一段,流程非常清晰。

Kickstart负责“生”系统,配置管理工具负责“养”系统,这个分层思想,要比试图用一套工具搞定所有事情靠谱得多。

6.4 安全关注点:别在Kickstart里留明文敏感信息

最后想专门提醒一下安全。Kickstart文件里可能涉及root密码、网络配置、分区信息等敏感内容,这些文件如果放在HTTP服务器上,要注意访问权限。

我在生产环境里会这样做:

  • HTTP服务器上ks文件目录设置访问控制,只允许部署网段的IP访问;
  • 密码一律使用加密哈希,禁止明文;
  • 如果ks文件通过外部网络传输,建议用更安全的传输通道(不过一般内网部署没有这个顾虑);
  • 定期排查HTTP访问日志,看看有没有陌生IP在拉取ks文件。

毕竟ks文件一旦泄露,等于别人拿到了你所有服务器的初始登录凭据,还是要谨慎对待。

写在最后:我的一些体会

Kickstart这套东西,定位其实很小,它只解决“把操作系统装好”这一件事。但就是这一件事,被它理顺之后,整个运维的地基就稳了。从最初一台台手动装机,到后来一份ks文件配合PXE几十台机器批量搞定,这种前后对比带来的震撼,是我一直想把这些细节整理成文的原因。

如果看完这篇,你想从零开始搭建一套无人值守安装环境,建议按这个顺序走:先手写一份最小Kickstart文件,用KVM或VMware虚拟机试装,确认它能在纯文本非交互模式下跑通;然后再搭DHCP+TFTP+HTTP,把PXE引导弄通;最后再慢慢加%post段,把个性化定制加进去。每走一步都用ksvalidator和虚拟机测试去验证,别直接拿生产机试错。

Kickstart的语法并不复杂,但它藏了很多细枝末节。你踩过的每一个坑,最后都会变成部署脚本里的最佳实践。希望这篇文章能让你少走一些弯路。