一般来说,我们要搭建一个正式的pxe自动装机系统,需要装 dnsmasq 做 dhcp + tftp ,需要编译 ipxe 来获得 undionly.kpxe ,需要 http 服务器来提供资源下载,repo 同步服务来提供 repo。组件非常多,也比较麻烦。
当然,这么多也是有必要的,因为可以持续提供一个稳定的装机系统。
场景一换,如果我们在本地机房里,什么都没有,想搭一套环境的步骤就比较繁复了。
PyPXE 就是非常简单的一个程序,居然自己实现了用于 PXE 的 dhcp、tftp 和 http 全部的功能,而且支持 iPXE。
太牛逼了,前提啊,PyPXE 是基于 Python 2.7 的,Python 3.x是运行不了的。
想让它跑起来还必须做一定的修改,步骤如下:
一、下载PyPXE
1git clone https://github.com/pypxe/PyPXE.git
2cd PyPXE
下载就行了,不用安装。
二、手动生成config.json配置文件
1{
2 "DHCP_SERVER_IP": "192.168.85.27",
3 "DHCP_FILESERVER": "192.168.85.27",
4
5 "DHCP_OFFER_BEGIN": "192.168.85.200",
6 "DHCP_OFFER_END": "192.168.85.250",
7 "DHCP_SUBNET": "255.255.255.0",
8 "DHCP_ROUTER": "192.168.85.1",
9 "DHCP_DNS": "114.114.114.114",
10
11 "DHCP_SERVER_PORT": 67,
12 "DHCP_BROADCAST": "",
13 "DHCP_MODE_PROXY": false,
14 "DHCP_WHITELIST": false,
15 "HTTP_PORT": 80,
16 "LEASES_FILE": "",
17 "MODE_DEBUG": "dhcp",
18 "MODE_VERBOSE": "",
19 "NBD_BLOCK_DEVICE": "",
20 "NBD_COPY_TO_RAM": false,
21 "NBD_COW": true,
22 "NBD_COW_IN_MEM": false,
23 "NBD_PORT": 10809,
24 "NBD_SERVER_IP": "0.0.0.0",
25 "NBD_WRITE": false,
26 "NETBOOT_DIR": "netboot",
27 "NETBOOT_FILE": "boot.http.ipxe",
28 "STATIC_CONFIG": "",
29 "SYSLOG_PORT": 514,
30 "SYSLOG_SERVER": null,
31 "USE_DHCP": true,
32 "USE_HTTP": true,
33 "USE_IPXE": true,
34 "USE_TFTP": true
35}
上面json文件无法加注解,我们把它分三部分
-
本机配置,本机的地址都是 192.168.85.27
-
dhcp 的配置,开始192.168.85.200,结束192.68.85.250,掩码255.255.255.0,网关192.168.85.1,DNS114.114.114.114
-
第三部分不用动
三、下载ISO并修改ipxe脚本
1cd netboot
2wget http://mirrors.163.com/rocky/8/isos/x86_64/Rocky-8.4-x86_64-dvd1.iso
3mkdir rocky8.iso
4mount -o loop Rocky-8.4-x86_64-dvd1.iso rocky8.iso
5
6cat << EOF >> boot.http.ipxe
7#!ipxe
8
9:start
10menu PXE Boot Options
11item shell iPXE shell
12item Rocky8 Install rocky8
13item exit Exit to BIOS
14
15choose --default rocky8 --timeout 5000 option && goto ${option}
16:shell
17shell
18
19
20:rocky8
21set root http://192.168.85.27/rocky8.iso
22initrd ${root}/images/pxeboot/initrd.img
23kernel ${root}/images/pxeboot/vmlinuz inst.repo=${root}/ initrd=initrd.img ip=dhcp
24boot
25
26
27:exit
28exit
29EOF
三、修改源代码
运行一下:
1python -m pypxe.server --config config.json --debug all --verbose all
如果我们起一台机器或者虚机,会报第一个错:
UnicodeDecodeError: ‘ascii’ codec can’t decode byte 0xc0 in position 0: ordinal not in range(128)
这个是代码报错,我们需要修改一下
1vi pypxe/dhcp.py
2
3 def tlv_encode(self, tag, value):
4 '''Encode a TLV option.'''
5
6 # 注释掉下面的两行,我们不需要打印出我们一定能看懂的字符,都按bytes处理即可
7 #if type(value) is str:
8 # value = value.encode('ascii')
9 value = bytes(value)
10 return struct.pack('BB', tag, len(value)) + value
然后我们需要修改第二个地方,理由是这个 PyPXE 会判断 Client 发过来的 dhcp 请求,它只实现了针对PXE-Client的 Vendor-class:
所以我们也要屏蔽一下,否则按照正常过程
客户端dhcp –> PyPXE 后,PyPXE 送回客户 ipxe 脚本,然后客户安装,当加载了vmlinuz
和initrd
之后会进入anaconda-linux
进行系统安装,过程中会再次向DHCP服务器申请IP地址, 这个时候他向DHCP Server
发出的discover
申请是得不到回复的,因此安装过程将被打断。
1vi pypxe/dhcp.py
2
3 def validate_req(self, client_mac):
4 # client request is valid only if contains Vendor-Class = PXEClient
5 '''代码整个注释掉,直接返回 True
6 if self.whitelist and self.get_mac(client_mac) not in self.get_namespaced_static('dhcp.binding'):
7 self.logger.info('Non-whitelisted client request received from {0}'.format(self.get_mac(client_mac)))
8 return False
9 if 60 in self.options[client_mac] and 'PXEClient'.encode() in self.options[client_mac][60][0]:
10 self.logger.info('PXE client request received from {0}'.format(self.get_mac(client_mac)))
11 return True
12 self.logger.info('Non-PXE client request received from {0}'.format(self.get_mac(client_mac)))
13 return False
14 '''
15 return True
这样修改后,就可以正常安装了。
服务器启动:
客户端启动pxe开始安装,看下面,系统的ipxe dhcp一次,然后chainload.kpxe 又一次,anaconda 又一次,最少会发三次或更多的dhcp请求。
用 VNC 连进去可以看到安装画面,如果是 kickstart 就是全自动安装了。