一、PXE简介 
PXE(Pre-boot Execution Environment,预启动执行环境)是由Intel公司开发的网络启动技术,工作于Client/Server的网络模式,支持工作站通过网络从远端服务器下载映像,并由此支持通过网络启动操作系统,在启动过程中,终端要求服务器分配IP地址,再用TFTP(trivialfile transfer protocol)或MTFTP(multicasttrivial file transfer protocol)协议下载一个启动软件包到本机内存中执行,由这个启动软件包完成终端基本软件设置,从而引导预先安装在服务器中的终端操作系统。
严格来说,PXE 并不是一种安装方式,而是一种引导方式
本文主要介绍在pxe模式下同时支持bios和UEFI的PXE安装部署方式,同时支持centos7、almalinux8、ubuntu,三层VLAN环境,PXE服务器架设完成后可以永不宕机使用,有问题可以私信。

二、PXE的工作历流程介绍及示意图

pxe boot nvmeusb bios 设置 pxe启动bios_linux

三、配置centos环境

#centos版本
[root@pxeserver ~]# cat /etc/centos-release
CentOS Linux release 7.9.2009 (Core)
[root@pxeserver ~]# 

#配置IP地址
[root@pxeserver ~]# cat /etc/sysconfig/network-scripts/ifcfg-ens33 
NAME=ens33
DEVICE=ens33
ONBOOT=yes
IPADDR=192.168.2.30
NETWORK=255.255.255.0
GATEWAY=192.168.2.254
DNS1=223.5.5.5

#关闭防火墙和selinux
systemctl stop firewalld && systemctl disable firewalld
sed -i '/^SELINUX=/c SELINUX=disabled' /etc/selinux/config
setenforce 0

#安装dnsmasq及其他软件
yum install -y dnsmasq wget
  
#创建pxe工作目录
mkdir -p /root/pxe/http/iso  /root/pxe/http/tftp

#下载iso镜像到/root/pxe/http/iso目录(centos7、Almalinux8和ubuntu-server 22.04)
wget -P /root/pxe/http/iso https://mirrors.aliyun.com/centos/7/isos/x86_64/CentOS-7-x86_64-Everything-2009.iso
wget -P /root/pxe/http/iso https://mirrors.aliyun.com/almalinux/8/isos/x86_64/AlmaLinux-8.6-x86_64-dvd.iso
wget -P /root/pxe/http/iso https://mirrors.aliyun.com/ubuntu-releases/22.04/ubuntu-22.04-live-server-amd64.iso

#查看下载情况
[root@pxeserver ~]# ll /root/pxe/http/iso/
总用量 22172436
-rwxr-xr-x. 1 root root 11037310976 6月  10 13:45 AlmaLinux-8.6-x86_64-dvd.iso
-rwxr-xr-x. 1 root root 10200547328 6月  10 13:45 CentOS-7-x86_64-Everything-2009.iso
-rwxr-xr-x. 1 root root  1466714112 6月  10 13:46 ubuntu-22.04-live-server-amd64.iso

#创建mount目录,用于iso镜像文件挂载
mkdir -p /root/pxe/http/iso/centos7 /root/pxe/http/iso/almalinux8 /root/pxe/http/iso/ubuntu

#mount光盘内容,方便后续http共享yum源
mount /root/pxe/http/iso/CentOS-7-x86_64-Everything-2009.iso /root/pxe/http/iso/centos7/
mount /root/pxe/http/iso/AlmaLinux-8.6-x86_64-dvd.iso /root/pxe/http/iso/almalinux8/
mount /root/pxe/http/iso/ubuntu-22.04-live-server-amd64.iso /root/pxe/http/iso/ubuntu/

#查看iso镜像挂载情况
[root@pxeserver ks]# lsblk 
NAME              MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sda                 8:0    0   50G  0 disk 
├─sda1              8:1    0    1G  0 part 
├─sda2              8:2    0   19G  0 part 
│ ├─centos-swap   253:1    0    2G  0 lvm  
│ └─centos-root   253:3    0   17G  0 lvm  
├─sda3              8:3    0    1G  0 part /boot
├─sda4              8:4    0    1K  0 part 
└─sda5              8:5    0   29G  0 part 
  ├─centos00-root 253:0    0   26G  0 lvm  /
  └─centos00-swap 253:2    0    3G  0 lvm  [SWAP]
sr0                11:0    1 1024M  0 rom  
loop0               7:0    0  9.5G  0 loop /root/pxe/http/iso/centos7
loop1               7:1    0 10.3G  0 loop /root/pxe/http/iso/almalinux8
loop2               7:2    0  1.4G  0 loop /root/pxe/http/iso/ubuntu
[root@pxeserver ks]# 

#centos 基础环境配置完毕

四、基于dnsmasq配置DHCP和tftp服务

cd /root/pxe:
#生成dnsmasq文件,关键配合详见说明
cat << EOF >/root/pxe/dnsmasq.conf
# 关闭DNS解析
port=0

#Setup the server to be your authoritative DHCP server
dhcp-authoritative

#Set the DHCP server to hand addresses sequentially
dhcp-sequential-ip

# 设置DHCP分发IP端范围、地址掩码、IP地址有效时间
dhcp-range=subnet0,192.168.2.100,192.168.2.200,255.255.255.0,12h
dhcp-option=subnet0,option:router, 192.168.2.254
dhcp-option=subnet0,option:dns-server,223.5.5.5

###dhcp for subnet1,存在三层环境及dhcp relay的话需要配置,负责可以注销或者忽略
dhcp-range=subnet1,192.168.4.100,192.168.4.200,255.255.255.0,12h
dhcp-option=subnet1,option:router, 192.168.4.254
dhcp-option=subnet1,option:dns-server,192.168.4.1

# 设置引导程序相对tftp根目录的路径
dhcp-match=set:bios,option:client-arch,0
dhcp-match=set:ipxe,175
dhcp-boot=tag:!ipxe,tag:bios,undionly.kpxe
dhcp-boot=tag:!ipxe,tag:!bios,ipxe.efi
dhcp-boot=tag:ipxe,boot.ipxe

# 设置tftp服务
enable-tftp
tftp-lowercase
dhcp-no-override
tftp-root=/root/pxe/http/tftp  
EOF
                                  
#启动dhcp及tftp服务
/usr/sbin/dnsmasq --conf-file=/root/pxe/dnsmasq.conf  --user=root

五、基于第三方软件gohttpserver配置http服务

#为了简化配置,我们不采用apache或者nginx,直接采用第三方软件gohttpserver
cd /root/pxe && wget https://github.com/codeskyblue/gohttpserver/releases/download/1.1.0/gohttpserver_1.1.0_linux_amd64.tar.gz

#解压
tar vzxf gohttpserver_1.1.0_linux_amd64.tar.gz

#启动http服务
./gohttpserver -r /root/pxe/http --addr :80 --auth-type http --auth-http pxe:pxe

访问http://192.168.2.30,输入用户名pxe和密码pxe查看配置效果

pxe boot nvmeusb bios 设置 pxe启动bios_linux_02

由于gohttpserver无法直接后台运行,我们配置systemd服务来后台管理gohttpserver软件

#生成gohttpserver的systemd配置文件
cat << EOF >/etc/systemd/system/gohttpserver.service
[Unit]
Description=Systemd gohttpserver
After=network.target

[Service]
User=root
# Execute `systemctl daemon-reload` after ExecStart= is changed.
ExecStart=/root/pxe/gohttpserver -r /root/pxe/http --addr :80 --auth-type http --auth-http pxe:pxe
[Install]
WantedBy=multi-user.target
EOF

#刷新服务
systemctl daemon-reload

#启动gohttpserver
systemctl start gohttpserver.service

# 查看状态
systemctl status gohttpserver.service

#配置开机自启动
systemctl enable gohttpserver.service

六、在tftp服务上配置ipxe

#下载ipxe相关文件
wget -P /root/pxe/http/tftp http://boot.ipxe.org/ipxe.pxe
wget -P /root/pxe/http/tftp http://boot.ipxe.org/undionly.kpxe
wget -P /root/pxe/http/tftp http://boot.ipxe.org/ipxe.efi

#手动生成boot.pxe配置文件
[root@pxeserver ks]# cat /root/pxe/http/tftp/boot.ipxe 
#!ipxe

set boot-url http://pxe:pxe@${next-server}

# Setup some basic convenience variables
set menu-timeout 5000

# Ensure we have menu-default set to something
isset ${menu-default} || set menu-default exit

######## MAIN MENU ###################
:start
menu Welcome to iPXE's Boot Menu
item
item --gap -- ------------------------- Utilities ------------------------------
item centos7       Install Centos7
item AlmaLinux8    Install AlmaLinux8
item ubuntu-server Install ubuntu server
item --gap -- ------------------------------ Advanced ---------------------------------
item --key r reboot     [R] Reboot the Computer
item --key x exit       [X] Exit (boot local disk)
choose --default exit --timeout 30000 target && goto ${target}

########## UTILITY ITEMS ####################
:failed
echo Booting failed, dropping to shell
goto start

:reboot
reboot

:exit
exit

:centos7
initrd ${boot-url}/iso/centos7/images/pxeboot/initrd.img
kernel ${boot-url}/iso/centos7/images/pxeboot/vmlinuz 
imgargs vmlinuz initrd=initrd.img inst.ks=${boot-url}/ks/ks-c7.cfg inst.repo=${boot-url}/iso/centos7
boot || goto failed
goto start

:AlmaLinux8
initrd ${boot-url}/iso/almalinux8/isolinux/initrd.img
kernel ${boot-url}/iso/almalinux8/isolinux/vmlinuz
imgargs vmlinuz initrd=initrd.img inst.ks=${boot-url}/ks/ks-alma8.cfg inst.repo=${boot-url}/iso/almalinux8/ inst.nompath
boot || goto failed
goto start

:ubuntu-server
kernel ${boot-url}/iso/ubuntu/casper/vmlinuz
initrd ${boot-url}/iso/ubuntu/casper/initrd
imgargs vmlinuz initrd=initrd url=${boot-url}/iso/ubuntu-22.04-live-server-amd64.iso autoinstall ds=nocloud-net;s=${boot-url}/autoinstall/ cloud-config-url=/dev/null ip=dhcp fsck.mode=skip --
boot || goto failed
goto start

找一台要装机的机器启动测试,已经可以正常看到启动菜单了,但是由于还未配置kickstart文件,因此无法正常pxe装机

pxe boot nvmeusb bios 设置 pxe启动bios_网络_03

七、配置Centos7的kickstart文件

#创建ks目录,注意该目录和boot.pxe配置文件相关,因此无法改变,如果自定义请一并修改boot.ipxe
mkdir /root/pxe/http/ks /root/pxe/http/init

#生成cento7的kickstart装机配置文件,文件路径和名称为/root/pxe/http/ks/ks-c7.cfg,因和boot.ipxe相关,勿动

[root@pxeserver ks]# cat /root/pxe/http/ks/ks-c7.cfg 
#platform=x86, AMD64, 或 Intel EM64T
#version=DEVEL
# Install OS instead of upgrade
install
# Keyboard layouts
keyboard 'cn'

network --bootproto=dhcp

# Root password xxxxxx
rootpw --iscrypted $6$1Jo.WEZA8zK27XL8$MbK7nk0adloNuN3xlOPx/RmQSRI/Z1hFvIhNblp3xm7z7Jt8F7APzWSyX9sAn.dPPlE0oPOzHYSkMqe9Rss84.


# System language
lang zh_CN.UTF-8
# System authorization information
auth  --useshadow  --passalgo=sha512

# Use graphical install
graphical

firstboot --disable
# SELinux configuration
selinux --disabled

# Firewall configuration
firewall --disabled
# Halt after installation
#poweroff
reboot

# System timezone
timezone Asia/Shanghai


%include /tmp/part-include
%pre

disk=$(while read line;do awk 'BEGIN{} {if ($2 == "0" && $4 == "nvme0n1") print $4} END{}';done < /proc/partitions)
size=$(while read line;do awk 'BEGIN{} {if ($2 == "0" && $4 == "nvme0n1") print $3} END{}';done < /proc/partitions)
if [ -z "$disk" ];then
    disk=$(while read line;do awk 'BEGIN{} {if ($2 == "0" && $4 == "sda") print $4} END{}';done < /proc/partitions)
    size=$(while read line;do awk 'BEGIN{} {if ($2 == "0" && $4 == "sda") print $3} END{}';done < /proc/partitions)
    if [ -z "$disk" ];then
        disk=$(while read line;do awk 'BEGIN{} {if ($2 == "0" && $4 == "vda") print $4} END{}';done < /proc/partitions)
        size=$(while read line;do awk 'BEGIN{} {if ($2 == "0" && $4 == "vda") print $3} END{}';done < /proc/partitions)
    fi
fi

#set swap size
if [ $size -lt 200000000 ];then
swap=4096
else
swap=32768
fi
echo $disk $size $swap

# partition
if [ $size -lt 2000000000 ];then
cat > /tmp/part-include << EOF
# System bootloader configuration
bootloader --location=mbr --boot-drive=$disk

# Partition clearing information
clearpart --all --initlabel --drives=$disk

# Disk partitioning information
#part /boot/efi --fstype="efi" --ondisk=$disk --size=600 --fsoptions="umask=0077,shortname=winnt"
#part biosboot --fstype="biosboot" --ondisk=$disk --size=1
part /boot --fstype="ext4" --ondisk=$disk --size=2048
part swap  --fstype="swap" --ondisk=$disk --size=$swap
part /     --fstype="xfs" --grow --ondisk=$disk --size=1
EOF
else
cat > /tmp/part-include << EOF
# System bootloader configuration
bootloader --location=mbr --boot-drive=$disk

# Partition clearing information
clearpart --all --initlabel --drives=$disk

# Disk partitioning information
#part /boot/efi --fstype="efi" --ondisk=$disk --size=600 --fsoptions="umask=0077,shortname=winnt"
part biosboot --fstype="biosboot" --ondisk=$disk --size=1
part /boot    --fstype="ext4" --ondisk=$disk --size=2048
part swap     --fstype="swap" --ondisk=$disk --size=$swap
part /        --fstype="xfs" --grow --ondisk=$disk --size=1
EOF
fi

if [ -d /sys/firmware/efi ] ; then
        sed -i 's/^[^#].*biosboot*/#&/g' -i /tmp/part-include
        sed -i '/^#.*efi/s/^#//g'   -i /tmp/part-include
fi

%end

%packages
@^minimal
@core
chrony
kexec-tools

%end

#%post   #脚本段,可以放脚本或命令,需要根据实际位置修改脚本
%post --log=/var/log/kickstart-post.log
wget http://pxe:pxe@192.168.2.30/init/init.sh
sh init.sh
rm init.sh -f
%end

选择centos7选项分别用bios及UEFI电脑通过pxe装机测试,centos7应该已经正常,由于bios和uefi硬盘分区格式有差异,为了同时兼容bios和uefi装机,ks文件里面对硬盘分区部分做了判断,如果修改ks文件,硬盘分区那坨代码勿动

八、配置Almalinux8的kickstart文件

#生成almalinux8的kickstart装机配置文件,文件路径和名称为/root/pxe/http/ks/ks-alma8.cfg,因和boot.ipxe相关,勿动

[root@pxeserver ks]# cat /root/pxe/http/ks/ks-alma8.cfg 
#platform=x86, AMD64, 或 Intel EM64T
#version=RHEL8

# Shutdown after installation
#shutdown
reboot

# Use graphical install
graphical


# Keyboard layouts
# old format: keyboard cn
# new format:
keyboard --vckeymap=cn --xlayouts='cn'

# System language
lang zh_CN.UTF-8

# Network information
network  --bootproto=dhcp --device=link --activate

# Root password xxxxxx
rootpw --iscrypted $6$zmkAqn3jfgiQroZA$6GyV4fogxSE4MQnlxbgqKyUTVZSHy3Urkkwk8/fN8jRxdvQcQB1jzAx/23a8ngY0OH6fJSiG1QsfuSSzT7HB61
# System authorization information
auth --useshadow --passalgo=sha512

# SELinux configuration
selinux --disabled

# disable firewalld
firewall --disabled

# Run the Setup Agent on first boot
firstboot --disabled

# Do not configure the X Window System
skipx

# System services
services --enabled="chronyd"
# System timezone
timezone Asia/Shanghai

####################################################pre script
%pre
disk=$(while read line;do awk 'BEGIN{} {if ($2 == "0" && $4 == "nvme0n1") print $4} END{}';done < /proc/partitions)
size=$(while read line;do awk 'BEGIN{} {if ($2 == "0" && $4 == "nvme0n1") print $3} END{}';done < /proc/partitions)
if [ -z "$disk" ];then
    disk=$(while read line;do awk 'BEGIN{} {if ($2 == "0" && $4 == "sda") print $4} END{}';done < /proc/partitions)
    size=$(while read line;do awk 'BEGIN{} {if ($2 == "0" && $4 == "sda") print $3} END{}';done < /proc/partitions)
    if [ -z "$disk" ];then
        disk=$(while read line;do awk 'BEGIN{} {if ($2 == "0" && $4 == "vda") print $4} END{}';done < /proc/partitions)
        size=$(while read line;do awk 'BEGIN{} {if ($2 == "0" && $4 == "vda") print $3} END{}';done < /proc/partitions)
    fi
fi

#set swap size
if [ $size -lt 200000000 ];then
swap=4096
else
swap=32768
fi
echo $disk $size $swap


#[ -d /sys/firmware/efi ] && echo UEFI || echo BIOS

if [ $size -lt 2000000000 ];then
cat > /tmp/part-include << EOF

# Partition clearing information
clearpart --all --initlabel --drives=$disk

ignoredisk --only-use=$disk

# System bootloader configuration
bootloader --append="crashkernel=auto" --location=mbr --boot-drive=$disk

# Disk partitioning information
#part /boot/efi --fstype="efi" --ondisk=$disk --size=600 --fsoptions="umask=0077,shortname=winnt"
part /boot --fstype="xfs" --ondisk=$disk --size=2048
part swap  --fstype="swap" --ondisk=$disk --size=$swap
part /     --fstype="xfs" --grow --ondisk=$disk --size=1
EOF
else
cat > /tmp/part-include << EOF
ignoredisk --only-use=$disk

# System bootloader configuration
bootloader --append="crashkernel=auto" --location=mbr --boot-drive=$disk

# Partition clearing information
clearpart --all --initlabel --drives=$disk

# Disk partitioning information
#part /boot/efi --fstype="efi" --ondisk=$disk --size=600 --fsoptions="umask=0077,shortname=winnt"
part biosboot --fstype="biosboot" --ondisk=$disk --size=1
part /boot    --fstype="ext4" --ondisk=$disk --size=2048
part swap     --fstype="swap" --ondisk=$disk --size=$swap
part /        --fstype="xfs" --grow --ondisk=$disk --size=1
EOF
fi

if [ -d /sys/firmware/efi ] ; then
        sed -i 's/^[^#].*biosboot*/#&/g' -i /tmp/part-include
        sed -i '/^#.*efi/s/^#//g'   -i /tmp/part-include
fi

%end

%include /tmp/part-include
#########################package
%packages
@^graphical-server-environment
#@^minimal-environment
chrony
kexec-tools

%end

########脚本段,可以放脚本或命令,需要根据实际位置修改脚本
%post --logfile=/var/log/kickstart-post.log
wget http://pxe:pxe@192.168.2.30/init/init.sh
sh init.sh
rm init.sh -f
%end

选择almalinux8选项分别用bios及UEFI电脑通过pxe装机测试,almalinux8应该已经正常,由于bios和uefi硬盘分区格式有差异,为了同时兼容bios和uefi装机,ks文件里面对硬盘分区部分做了判断,如果修改ks文件,硬盘分区那坨代码勿动

pxe boot nvmeusb bios 设置 pxe启动bios_centos_04


pxe boot nvmeusb bios 设置 pxe启动bios_ubuntu_05

九、配置ubuntu的data文件

#创建目录
mkdir -p /root/pxe/http/autoinstall

#在autoinstall目录分别创建meta-data、user-data、vendor-data文件,内容可以一样
[root@alima-pxe autoinstall]# cat meta-data 
#cloud-config
autoinstall:
  apt:
    disable_components: []
    geoip: true
    preserve_sources_list: false
    primary:
    - arches:
      - amd64
      - i386
      uri: http://cn.archive.ubuntu.com/ubuntu
    - arches:
      - default
      uri: http://ports.ubuntu.com/ubuntu-ports
  drivers:
    install: false
  identity:
    hostname: haiger
    password: $6$AHyhNY01uts3TKMl$3vQn.ZkTUcM1evQXLVYnS28xLMIBRt9SQqSSS3hBzcbdBVmwJA5m4pKxoIygwD8voWoyx.ILlswAyi/.sTzx00
    realname: haiger
    username: haiger
  kernel:
    package: linux-generic
  keyboard:
    layout: cn
    toggle: null
    variant: ''
  locale: en_US.UTF-8
  network:
    ethernets:
      ens33:
        dhcp4: true
    version: 2
  ssh:
    allow-pw: true
    authorized-keys: []
    install-server: true
  storage:
    config:
    - ptable: gpt
      serial: VMware_Virtual_NVMe_Disk_VMware_NVME_0000
      wwn: eui.8e39c51f7fdc13c3000c296361f5c2df
      path: /dev/nvme0n1
      wipe: superblock
      preserve: false
      name: ''
      grub_device: true
      type: disk
      id: disk-nvme0n1
    - device: disk-nvme0n1
      size: 1048576
      flag: bios_grub
      number: 1
      preserve: false
      grub_device: false
      type: partition
      id: partition-0
    - device: disk-nvme0n1
      size: 1902116864
      wipe: superblock
      flag: ''
      number: 2
      preserve: false
      grub_device: false
      type: partition
      id: partition-1
    - fstype: ext4
      volume: partition-1
      preserve: false
      type: format
      id: format-0
    - device: disk-nvme0n1
      size: 19569573888
      wipe: superblock
      flag: ''
      number: 3
      preserve: false
      grub_device: false
      type: partition
      id: partition-2
    - name: ubuntu-vg
      devices:
      - partition-2
      preserve: false
      type: lvm_volgroup
      id: lvm_volgroup-0
    - name: ubuntu-lv
      volgroup: lvm_volgroup-0
      size: 10737418240B
      wipe: superblock
      preserve: false
      type: lvm_partition
      id: lvm_partition-0
    - fstype: ext4
      volume: lvm_partition-0
      preserve: false
      type: format
      id: format-1
    - path: /
      device: format-1
      type: mount
      id: mount-1
    - path: /boot
      device: format-0
      type: mount
      id: mount-0
  updates: security
  version: 1

选择ubuntu通过pxe装机测试,ubuntu server应该已经可以正常通过pxe装机,有问题先手动装机生成meta-data、user-data、vendor-data装机模板,然后更新autoinstall目录后批量装机即可