0x01 前言

在测试软件的部署过程经常要安装新的虚拟机,虽然用虚拟机模板是一种不错的解决方案,但某些情况下使用全新安装的迅才是首选。

centos7无论是通过CLI还是GUI的方式安装都要进行一些列配置,部署次数少还可以接受,久而久之会觉得这东西装起来真的好繁琐,完全没心思去折腾。为了偷懒,我决定通过kickstart与tftp结合我家里的pfsense实现自动安装。

0x02 准备

需要准备的东西还不少,首先需要一个DHCP服务器,并且配置网络启动的相关信息。在这篇文章里不涉及自建DHCP服务器的内容,因为我使用pfsense作为路由器,因此会有相关的服务。

登入pfsense并定位到以下页面:

  • Services \ DHCP \ ServerLAN

在需要配置网络启动的网络中找到以下选项并配置即可,其中“Next Server”需要填写tftp服务器的地址:

然后需要安装tftp服务器,通过以下命令安装即可:

[root@web ~]# yum install -y tftp-server syslinux

实现网络启动需要用到文件,这些文件可以通过以下地址找到:

我写这篇文章的时候,centos最新版本是7.5.1804,当版本更新时,这个目录会被移至最新版本的目录下,所以这里放一个截图做记录:

在我的使用环境中会使用我自有的centos镜像:

  • http://mirror.t.com/centos/base/

最后,还需要一台安装有GUI界面的centos7,用于运行kickstart以便生成配置文件。

0x03 tftp

pfsense的部分比较简单,就是一个服务指向的配置。当客户机请求DHCP服务内容时会返还网络启动的相关信息,在这里是tftp的服务器地址与BIOS文件。

tftp是至关重要的一步,它会将BIOS文件发送给客户机用于引导,其中的配置文件会包含启动方式等内容。

安装完tftp后需要启用tftp服务,首先修改配置文件:

# 打开文件
[root@web ~]# vim /etc/xinetd.d/tftp

# 将disable的值设为no
service tftp
{
        socket_type             = dgram
        protocol                = udp
        wait                    = yes
        user                    = root
        server                  = /usr/sbin/in.tftpd
        server_args             = -s /var/lib/tftpboot
        disable                 = no
        per_source              = 11
        cps                     = 100 2
        flags                   = IPv4
}

然后进入以下目录并新建一个文件夹:

# 进入文件夹
[root@web ~]# cd /var/lib/tftpboot/

# 新建文件夹
[root@web tftpboot]# mkdir pxelinux.cfg

在这新建的文件夹下新建一个配置文件,内容如下:

# 新建文件
[root@web tftpboot]# vim pxelinux.cfg/default 

# 填入内容
default menu.c32
prompt 0
timeout 30

menu title ##### NGX Proj PXE Boot Menu #####

label 1
menu label ^1) Install CentOS 7 x64 - NGX Proj
kernel vmlinuz
append initrd=initrd.img inst.ks=http://mirror.t.com/centos/base/anaconda-ks.cfg

如果有多个选项,可以增加“label”块的内容,比如:

label 1
menu label ^1) Install CentOS 7 x64 - NGX Proj
kernel vmlinuz
append initrd=initrd.img inst.ks=http://mirror.t.com/centos/base/anaconda-ks.cfg

label 2
menu label ^2) Install CentOS 7 x86 - NGX Proj x86
kernel vmlinuz
append initrd=initrd.img inst.ks=http://mirror.t.com/centos/base/anaconda-ks_x86.cfg

因为我是通过网络进行自动安装,需要在“inst.ks”中指定kickstart生成的自动安装配置文件。因为我有自建的源镜像,所以地址如上。

当客户机启动时,会默认选择第一个选项为默认启动项。如果有多个启动项,你可能需要更长的等待时间,在上面配置文件中的“timeout”值为30,意为3秒,可以根据实际情况自行修改。

最后需要将启动需要的文件复制到tftpboot目录中:

[root@web tftpboot]# cp /usr/share/syslinux/pxelinux.0 /var/lib/tftpboot/
[root@web tftpboot]# cp /usr/share/syslinux/menu.c32 /var/lib/tftpboot/
[root@web tftpboot]# cp /usr/share/syslinux/memdisk /var/lib/tftpboot/
[root@web tftpboot]# cp /usr/share/syslinux/mboot.c32 /var/lib/tftpboot/
[root@web tftpboot]# cp /usr/share/syslinux/chain.c32 /var/lib/tftpboot/

还需要下载通过清华大学镜像站下载以下两个文件并放置到 /var/lib/tftpboot/ 目录中:

vmlinuz
initrd.img

该文件夹中所有的文件含义如下:

  • chain.c32:引导系统
  • mboot.c32:通过内存引导
  • memdisk:将内存模拟为磁盘
  • menu.c32:菜单文件
  • pxelinux.0:引导程序,用于加载kernel和initrd
  • vmlinuz:内核
  • initrd.img:虚拟根文件

完成后即可通过以下命令启动tftp服务:

# 设为开机启动
[root@web tftpboot]# systemctl enable tftp.socket

# 立即启动
[root@web tftpboot]# systemctl start tftp.socket

然后建立一个虚拟机进行测试,需要注意的是,虚拟机需要一个可以和DHCP服务器通讯的网卡,但不需要光驱:

因为还没配置kickstart与安装源,所以启动后会有错误,如果出现以下信息则说明tftp服务器运作正常:

0x04 kickstart

在进行kickstart配置文件的编写前需要准备centos的基础源“base”,这个安装源里包含一个很重要的文件:comps.xml。这个文件里包含group list,有了它才可以获取最小化安装、桌面化安装或者是服务器级别安装所需要的软件包。

在该xml文件同级别的目录下还有一个Packages文件夹,存放着基础的软件包;一个repodata文件夹,存放着Packages文件夹下软件包信息的数据库:

其他文件和文件夹一般用不上。

准备好之后在有桌面环境的centos7系统上打开软件管理器并安装kickstart,然后运行:

根据实际情况选择并填写相关信息。

其实每次安装完系统,在root目录下都会有一个名为“anaconda-ks.cfg”的文件,而这款软件到最后也是生成这个文件。

因此,不想折腾有GUI桌面的centos系统,通过最小化安装,然后将root目录下的“anaconda-ks.cfg”文件复制到本地进行修改。

以下是我的配置文件:

#platform=x86, AMD64, or Intel EM64T
#version=DEVEL
# Install OS instead of upgrade
install
# Keyboard layouts
keyboard 'us'
# Root password
rootpw --iscrypted $1$i5xGQgfg$Ar6fz7Xcv..KAPK3ISu/a.
# Use network installation
url --url="http://mirror.t.com/centos/base/"
# System language
lang en_US
# System authorization information
auth  --useshadow  --passalgo=sha512
# Use text mode install
text
# SELinux configuration
selinux --disabled
# Do not configure the X Window System
skipx

# Firewall configuration
firewall --disabled
# Network information
network  --bootproto=dhcp --device=eth0
# Reboot after installation
reboot
# System timezone
timezone Asia/Hong_Kong
# System bootloader configuration
bootloader --location=mbr
# Partition clearing information
clearpart --none --initlabel
# Disk partitioning information
part pv.253 --fstype="lvmpv" --ondisk=sda --size=1 --grow
part /boot --fstype="xfs" --ondisk=sda --size=1024
volgroup centos --pesize=4096 pv.253
logvol none  --fstype="None" --size=1 --grow --thinpool --metadatasize=16 --chunksize=65536 --name=pool00 --vgname=centos
logvol swap  --fstype="swap" --size=1024 --name=swap --vgname=centos
logvol /  --fstype="xfs" --size=1 --grow --thin --poolname=pool00 --name=root --vgname=centos

%packages
@core
perl
wget
bind-utils
net-tools
telnet

%end

%addon com_redhat_kdump --disable

%end

#%post --nochroot
#hostnamectl set-hostname $(cat /sys/class/net/ens192/address | sed 's/://g' | cut -c 7-12)
#
#%end

%post
#echo "net.ipv6.conf.all.disable_ipv6 = 1" >> /etc/sysctl.conf
#echo "net.ipv6.conf.default.disable_ipv6 = 1" >> /etc/sysctl.conf
echo "curl -fsSL "http://mirror.t.com/ngx-shell/ngx-pxe-setting-script.sh " | /bin/sh" >> /etc/rc.local
chmod +x /etc/rc.d/rc.local
rm -f /etc/yum.repos.d/*
curl mirror.t.com/home.repo > /etc/yum.repos.d/home.repo
yum clean all && yum install epel-release -y && rm -f /etc/yum.repos.d/epel* &&  yum update -y
rm -f /etc/yum.repos.d/CentOS-* && rm -f /etc/yum.repos.d/epel*
mkdir /tmp/vmtools \
  && wget http://mirror.t.com/vmtools/latest.tar.gz -O /tmp/vmtools/latest.tar.gz \
  && tar zxvf /tmp/vmtools/latest.tar.gz -C /tmp/vmtools \
  && /tmp/vmtools/vmware-tools-distrib/vmware-install.pl -d \
  && rm -rf /tmp/vmtools

%end

我对其中的某些内容做了修改,以适配我的使用环境:

  • url:值为我自有的镜像地址
  • text:使用text模式安装而不加载GUI
  • 分区:分为3个区
    • swap:1GB
    • boot:1GB
    • /:剩下的空间
  • packages:最小化安装(core)并安装wget与curl
  • addon:关闭kdump
  • post:
    • 用私有的repo替换官方的repo文件
    • 升级系统
    • 安装vmtools

因为系统更新需要很长的时间,所以部署起来需要3到4分钟,所有流程完成后会自动重启。

编写完kickstart的配置文件后将其放置在pxelinux.cfg目录下default文件中“inst.ks”值所对应的目录中即可,该步骤不需要重启tftp,但需要确认能通过http服务访问。

0x05 结语

经过多次调试才找到适用于我环境的自动部署kickstart配置文件,因此不能照搬,要根据实际情况进行编写。

以下为安装完成后的配置脚本:

#!/bin/sh

######## hostname ########
LOCAL_IPADDR=$(hostname -I | cut -d ' ' -f 1)
PTR_ANSWER=$(dig +short -x "$LOCAL_IPADDR")

if [ -z "$PTR_ANSWER" ] ; then
    hostname=$(sed 's/://g' < /sys/class/net/ens192/address | cut -c 7-12)
else
    hostname=$(echo "$PTR_ANSWER" | cut -d '.' -f 1)
fi

hostnamectl set-hostname "$hostname"

######## IPv6 ########
cp /usr/share/doc/glibc-common-2.17/gai.conf /etc/ && echo 'precedence ::ffff:0:0/96 100' >> /etc/gai.conf

######## sysctl ########
cat <<EOF >> /etc/sysctl.conf

# 允许内核分配所有可用的物理内存
vm.overcommit_memory = 1

# 单个进程允许的最大 fd 数量
fs.file-max = 10245760000

# linux 内核允许的最大 fd 数量
fs.nr_open = 10245760000

# 数据包转发
net.ipv4.ip_forward = 1

# 防范syn攻击
net.ipv4.tcp_syncookies = 1

# 禁用重用time_wait的tcp端口
net.ipv4.tcp_tw_reuse = 0

# fin_wait超时时间
net.ipv4.tcp_fin_timeout = 15

# 动态分配端口的范围
net.ipv4.ip_local_port_range = 20000 65535

# 套接字最大数量
net.ipv4.tcp_max_tw_buckets = 65535

# syn队列长度
net.ipv4.tcp_max_syn_backlog = 10240

# 最大设备队列长度
net.core.netdev_max_backlog = 10240

# listen()等待请求的最大数量
net.core.somaxconn = 10240

# tcp 连接丢包重传次数,达到此值将刷新路由缓存
net.ipv4.tcp_retries1 = 2

# tcp 连接丢包重传次数,达到此值将断开 TCP 连接
net.ipv4.tcp_retries2 = 4

# 放弃建立连接前内核发送syn包的数量
net.ipv4.tcp_syn_retries = 2

# 放弃连接前内核发送syn+ack包的数量
net.ipv4.tcp_synack_retries = 2

# keepalive idle空闲时间
net.ipv4.tcp_keepalive_time = 30

# keepalive intvl间隔时间
net.ipv4.tcp_keepalive_intvl = 2

# keepalive probes最大探测次数
net.ipv4.tcp_keepalive_probes = 3

# 内核允许的最大孤立socket数量
net.ipv4.tcp_max_orphans = 4096

# socket默认读buffer大小
net.core.rmem_default = 655350

# socket默认写buffer大小
net.core.wmem_default = 655350

# socket最大读buffer大小
net.core.rmem_max = 65535000

# socket最大写buffer大小
net.core.wmem_max = 65535000

# socket最大内存buffer大小
net.core.optmem_max = 65535000

# tcp_socket读buffer大小. min/default/max
net.ipv4.tcp_rmem = 16384 1048576 12582912

# tcp_socket写buffer大小. min/default/max
net.ipv4.tcp_wmem = 16384 1048576 12582912

# 开启tcp_fastopen
net.ipv4.tcp_fastopen = 3

# 在路由中缓存 TCP 连接的各项指标
net.ipv4.tcp_no_metrics_save = 0

# 在连接空闲期间保持拥塞窗口的大小
net.ipv4.tcp_slow_start_after_idle = 0

# 磁盘
vm.dirty_background_ratio = 20
vm.dirty_ratio = 30
vm.dirty_expire_centisecs = 1000
vm.dirty_writeback_centisecs =  = 300
vm.min_free_kbytes = 65536
vm.swappiness = 1

EOF

######## limits ########
cat <<EOF >> /etc/security/limits.conf

*    soft    nofile          65535
*    hard    nofile          65535
*    soft    memlock         unlimited
*    hard    memlock         unlimited

EOF

######## PAM ########
echo "session required /usr/lib64/security/pam_limits.so" >> /etc/pam.d/login

######## repo ########
rm -f /etc/yum.repos.d/Centos*.repo
rm -f /etc/yum.repos.d/epel*.repo
yum clean all
yum install -y iftop htop python-pip python36-pip python36-devel python36 bash-completion bash-completion-extras vim zabbix40-agent wget gcc \
  ntpdate
pip3.6 install -i https://pypi.tuna.tsinghua.edu.cn/simple pip -U
pip3.6 config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
pip3.6 install glances

######## crontab ########
echo "*/5 * * * * root ntpdate 10.1.1.1" >> /etc/crontab

######## zabbix-agent ########
sed -i 's/^Server=127\.0\.0\.1/Server=10\.1\.3\.0\/24/g' /etc/zabbix/zabbix_agentd.conf
sed -i 's/^ServerActive=127\.0\.0\.1/ServerActive=web\.dev\.t\.com/g' /etc/zabbix/zabbix_agentd.conf
sed -i "s/^Hostname=Zabbix server/Hostname=$hostname/g" /etc/zabbix/zabbix_agentd.conf

wget -r -np -R "index.html*" -P /var/tmp --cut-dirs=99 http://mirror.t.com/ngx-shell/zabbix_agent_shell/
cp /var/tmp/mirror.t.com/*.conf /etc/zabbix/zabbix_agentd.d/
chown -R zabbix:zabbix /etc/zabbix/zabbix_agentd.d/

rm -f /etc/zabbix_agentd.conf && ln -s /etc/zabbix/zabbix_agentd.conf /etc/zabbix_agentd.conf

systemctl enable zabbix-agent.service
systemctl restart zabbix-agent.service

######## 删除预设的脚本 ########
sed -i '/.*ngx-pxe-setting-script\.sh.*/d' /etc/rc.d/rc.local
rm -rf /etc/rc.local && ln -s /etc/rc.d/rc.local /etc/rc.local
rm -f /root/*.cfg