这些年IDC装机的大法也在不断衍进,从最早的自己手动装一堆tftp/dhcp/syslinux/httpd,到cobbler的一统江湖,到现在的dnsmasq轻量级安装,一直在变化。

因为cobbler实在太重,不得不专门起一个kvm虚机来运行,不喜欢啊。所以一直也在研究终极安装服务器的大法

最近一个星期折腾服务器无数回,找出了个自己比较喜欢的方式。

首先介绍一下背景知识:

PXE
可以通过网络给计算机安装操作系统。 PXE协议大致上结合了DHCP和TFTP。

gPXE
gPXE是PXE的一个开源实现(更早的实现是Etherboot)。通过gPXE能让网卡直接支持网络启动,而不依赖于网卡自带的PXE固件。同时相比PXE,gPXE支持更多的协议。 传统的PXE只能通过TFTP进行传输,而gPXE支持HTTP,iSCSI和ATA over Ethernet(AoE),甚至支持wifi链接。

iPXE
iPXE表示 it doesn't PXE。iPXE是gPXE的原班人马写的(他们从Etherboot开始),作为官方的gPXE的替代品。 gPXE扩展的功能在iPXE中都得到支持。 之所以不再使用gPXE是由于存在版权纠纷,iPXE从2010年4月开始,基于同一个代码库开始开发。

PXELINUX
Syslinux是一个优秀的系统启动加载器(bootloader),可引导自硬盘、光盘、和通过PXE的网络启动。 PXELINUX派生自Syslinux,用来使支持PXE的网卡从网络引导启动Linux。PXELINUX程序不是烧在网卡里,而是存储在TFTP服务器上。

Chainloading iPXE
可以把iPXE当作固件刷进计算机网卡的ROM里替换掉自带的PXE,但更为常见的是通过chainloading的方式进入iPXE。

有点晕是吧,总结一下,pxe包含了gpxe/ipxe/pxelinux,ipxe是最新的,gpxe/ipxe/pxelinux可以通过chain的方式顶替网卡原生的pxe。

我们的方式:

客户机启动网卡自带的固件pxe,然后去服务器拉一个gpxe/ipxe,然后启动这个gpxe/ipxe,再去服务器拉具体的启动文件,这种就是chain方式了。我们就可以用到http/iscsi/wifi等各种先进方式启动了。

我们第一步先要准备一个gpxe的boot文件,之所以用gpxe是因为可以在网站直接生成boot文件,省的编译了。

在线生成gpxe文件的网站:

http://rom-o-matic.net/gpxe/gpxe-1.0.1/contrib/rom-o-matic/

我们选2个地方即可:

  1. Choose an output format:[PXE bootstrap loader keep [Keep PXE stack method 1] (.kpxe)]
  2. Choose a NIC type:[undionly]

然后下载文件即可。

解释一下,文件后缀有.pxe/.kpxe/.kkpxe,这是个递进关系,.pxe是最原生的网卡驱动,.kpxe包含了调用原生网卡的UNDI驱动(无网卡驱动),.kkpxe更进一步,包含了UNDI+PXE原生网卡的驱动。

当然选择.kpxe了,这就直接导致第二个选项选undionly即可,我们的目的就是通过这个gpxe调用原生网卡上的驱动即可,如果是你自己要烧网卡的bootroom片子,就得选择所有驱动了。

这样我们会得到一个 gpxe-1.0.1-undionly.kpxe 的文件,保存备用。

然后我们来设置Dnsmasq:

log-dhcp

dhcp-no-override

enable-tftp  
tftp-root = /tftpboot

dhcp-range=tftp,172.16.36.100,172.16.36.105

dhcp-match=gpxe,175  
dhcp-boot=net:#gpxe,gpxe-1.0.1-undionly.kpxe,gxe/bootserver,172.16.36.1  
dhcp-boot=http://172.16.8.1/ks/boot.txt  

注意:gpxe-1.0.1-undionly.kpxe是放在/tftpboot/gpxe/之下的

上面有很多玄机,dhcp-range是随便设一个地址池,因为最终实际是gpxe来决定地址,所以第一次dhcp得到的地址反而不重要了。dhcp-no-override一定要有,否则gpxe有bug,无法启动。

其次匹配gpxe,凡是175的都是gpxe,为什么要匹配呢?看下面,有两个dhcp-boot启动选项,#pxe,#表示不是,这行意思是:不是gpxe启动的话用/tftp/gpxe/gpxe-1.0.1-undionly.kpxe来启动,dhcp的server是172.16.36.1。第二行如果是gpxe启动的话,就chain到[http://172.16.8.1/ks/boot.txt]去启动

呵呵,如果不这么设置,就用一行的话:

dhcp-boot=gpxe-1.0.1-undionly.kpxe,gxe/bootserver,172.16.36.1  

就会陷入死循环,首先pxe启动,抓了个gpxe,gpxe又启动抓了个gpxe,又抓gpxe,循环往复没玩没了。所以必须标记gpxe,并跳出这个循环。

我们在172.16.8.1上面建立/centos7的yum安装源,同时建立/ks目录。准备boot.txt,boot2.php,boot3.php,centos7.ks四个文件,下面一个一个解释:

首先是boot.txt

cat boot.txt  
#!gpxe

chain http://172.16.8.1/ks/boot2.php?uuid=\${uuid}&mac=\${mac}&busid=\${busid}&ip=\${ip}&hostname=\${hostname:uristring}&serial=\${serial:uristring}&asset=\${asset:uristring}&manufacturer=\${manufacturer:uristring}&product=\${product:uristring}

大家看上面,boot.txt是个gpxe脚本,实际是让gpxe向boot2.php传输了一些数据过来,有机器的uuid/mac/busid/hostname/serial/product等等。给个具体的例子,从access.log看到实际发过来的请求如下:

"GET /ks/boot2.php?uuid=%5C44454c4c-5600-1054-8042-b1c04f433532&mac=%5Cec%3Af4%3Abb%3Ad9%3A96%3A40&busid=%5C01%3A80%3A86%3A15%3A21&ip=%5C172.16.36.100&hostname=%5C&serial=%5C1VTBC52&asset=%5C&manufacturer=%5CDell%20Inc.&product=%5CPowerEdge%20R730 HTTP/1.0" 200 46 "-" "gPXE/1.0.1" "-"

看上面,有很多信息,网卡的物理地址,还有serial,对应dell就是svc tag号,还有机器类别PowerEdge R730,这是个好东西啊。

我们购买机器的时候肯定有序列号,上架到机房的时候,可以让idc抄下来机柜和序列号,然后我们让网管规划好ip,再这里就可以利用serial来确定机器应该按哪个模板安装。

再看模板文件,centos7.ks

#version=DEVEL
auth --enableshadow --passalgo=sha512  
url --url=http://172.16.8.1/centos7  
text  
firstboot --enable  
ignoredisk --only-use=sda  
keyboard --vckeymap=us --xlayouts='us'  
lang en_US.UTF-8  
selinux --disabled

services --enabled="chronyd"

network  --bootproto=static --device=em1 --noipv6 --nodns --onboot=yes --gateway={gateway} --ip={ip} --nameserver=172.16.8.1 --netmask={netmask}  
network  --bootproto=dhcp --device=em2 --noipv6 --nodns --onboot=no  
network  --bootproto=dhcp --device=em3 --noipv6 --nodns --onboot=no  
network  --bootproto=dhcp --device=em4 --noipv6 --nodns --onboot=no  
network  --hostname={hostname}

#grub-crypt --sha-512
# root  pass is password
rootpw --iscrypted $6$0Y.SH935/jvt0X.D$lCte9H/KiFeGbTGrrcuXGZXmnq3Rk4Crz8nAvCgH.Pf2SNmoUdh.g5WGWJqtVO4QTyqvnXc/6FyVjptWIaM4w1  
timezone Asia/Shanghai --isUtc

bootloader --append=" crashkernel=auto" --location=mbr --boot-drive=sda  
clearpart --all --drives=sda

part /boot --fstype ext4 --size=500 --ondisk=/dev/sda  
part swap --size=8192 --ondisk=/dev/sda  
part / --fstype ext4 --size=1024 --grow --ondisk=/dev/sda

%packages
@compat-libraries
@core
wget  
net-tools  
chrony  
bridge-utils  
%end

%post
yum -y erase NetworkManager  
cat <<EOF >/etc/sysconfig/network-scripts/ifcfg-br0  
DEVICE=br0  
TYPE=Bridge  
BOOTPROTO=static  
ONBOOT=yes  
IPADDR={ip}  
NETMASK={netmask}  
GATEWAY={gateway}  
EOF  
cat <<EOF > /etc/modprobe.d/bonding.conf  
alias bond0 bonding  
BONDING_OPTS="miimon=100 mode=1 primary=em1"  
EOF  
cat <<EOF > /etc/sysconfig/network-scripts/ifcfg-bond0  
DEVICE=bond0  
ONBOOT=yes  
USERCTL=no  
BRIDGE=br0  
EOF  
cat <<EOF > /etc/sysconfig/network-scripts/ifcfg-em1  
DEVICE=em1  
USERCTL=no  
ONBOOT=yes  
MASTER=bond0  
SLAVE=yes  
BRIDGE="br0"  
EOF  
cat <<EOF > /etc/sysconfig/network-scripts/ifcfg-em2  
DEVICE=em2  
USERCTL=no  
ONBOOT=yes  
MASTER=bond0  
SLAVE=yes  
BRIDGE="br0"  
EOF  
%end

reboot  

注意上面,{ip}、{netmask}、{gateway}、{hostname}是待定的。

然后看看boot2.php:

<?php  
$dns='172.16.8.1';
$base7='http://172.16.8.1/centos7';
$ks7='http://172.16.8.1/ks/boot3.php';

echo "#!gpxe\n";


switch ($_REQUEST['mac']) {

  case '\ec:f4:bb:d9:96:40':
    $ip="172.16.36.2:172.16.37.254:255.255.254.0:myhost-16-36-2";
    $ipa=explode(':',$ip);
    echo "ifopen net0\n";
    echo "set net0/ip $ipa[0]\n";
    echo "set net0/netmask $ipa[2]\n";
    echo "set net0/gateway $ipa[1]\n";
    echo "set net0/dns $dns\n";
    echo "set base-url $base7\n";
    echo "kernel \${base-url}/images/pxeboot/vmlinuz\n";
    echo "imgargs vmlinuz inst.ks=$ks7?file=centos7.ks&ip=$ipa[0]&mask=$ipa[2]&gateway=$ipa[1]&hostname=$ipa[3] ksdevice=bootif inst.vnc inst.vncpassword=password inst.sshd\n";
    echo "initrd \${base-url}/images/pxeboot/initrd.img\n"; 
    echo "boot\n";
    break;

  case '00:11:22:33:44:55':
    # boot from iSCSI
    echo "set initiator-iqn iqn.2007-08.com.example.initiator:initiator\n";
    # see http://ipxe.org/sanuri for the syntax
    echo "sanboot iscsi:san.example.com:6:3260:0:iqn.2007-08.com.example.san:sometarget\n";
    break;

  case '\00:77:21:ab:cd:ee':
    # boot boot.salstar.sk's super cool boot menu      
    echo "chain http://boot.salstar.sk\n";
    break;

  default:
    # exit iPXE and let machine go on with BIOS boot sequence
    echo "exit\n";
    break;
}

?> 

注意上面,我们是通过网卡的物理地址来判断模板的,用svc tag号更好。

第四个文件:boot3.php

<?php

$origin_str = file_get_contents($_REQUEST['file']);

$update_str = str_replace("{ip}", $_REQUEST['ip'], $origin_str);
$update_str = str_replace('{netmask}', $_REQUEST['mask'], $update_str);
$update_str = str_replace('{gateway}', $_REQUEST['gateway'], $update_str);
$update_str = str_replace('{hostname}', $_REQUEST['hostname'], $update_str);

echo $update_str;  
?>

boot3.php的功能就是替换掉模板文件里的ip等,然后返回一个正确的ks文件。

这下就完美了,我们基本就需要修改ks下的boot2.php和centos7.ks,就可以控制远程安装了。

dhcp和tftp,就用dnsmasq和那一个gxpe就够了,甚至dhcp都是胡乱的。省了大事了。

四个文件的打包下载: http://www.rendoumi.com/soft/pxe.tar.gz

还有更好玩的: 网卡原生pxe-->gpxe-->pxelinux.0

大家尽情想象吧。

comments powered by Disqus