系统基础优化

# 系统版本
Centos8

#关闭防火墙
systemctl stop firewalld.service
systemctl disable firewalld.service

#关闭selinux
sed -i 's/^SELINUX=.*/SELINUX=disabled/g' /etc/selinux/config
setenforce 0

#无盘服务器网络环境
192.168.50.0/24

必要软件包安装

# Centos7.9必要软件包
yum install -y tftp-server xinetd dhcp syslinux-tftpboot dracut-network nfs-utils

# Centos8.3必要软件包
yum install -y tftp-server dhcp-server syslinux-tftpboot dracut-network nfs-utils

# 配置dracut-network
echo 'add_dracutmodules+="nfs"' >> /etc/dracut.conf

配置tftp服务

cat << EOF | tee /etc/xinetd.d/tftp
service tftp
{ 
        socket_type = dgram
        protocol = udp
        wait = yes
        user = root
        server = /usr/bin/in.tftpd
        server_args = -s /var/lib/tftpboot  # -s 表示用/var/lib/tftpboot作为tftp服务的根目录
        disable = no  # 要修改成no,以便启动
        per_source = 11
        cps = 100 2
        flags = IPv4
}
EOF

开启 tftp 服务

systemctl enable --now tftp.service

配置DHCP

cat << EOF | tee /etc/dhcp/dhcpd.conf 
# 全局配置
ddns-update-style none;
ignore client-updates;
default-lease-time -1;
max-lease-time -1;
authritative;

# 设置地址范围
subnet 192.168.50.0 netmask 255.255.255.0 {
	range 192.168.50.50 192.168.50.100;         # 地址池范围
	option routers 192.168.50.2;                # 网关
	option domain-name-servers 114.114.114.114; # DNS地址
	option broadcast-address 192.168.50.255;    # 广播地址
}

# 配置 tftp 启动文件
allow booting;
allow bootp;
class "pxeclients" {
	match if substring(option vendor-class-identifier, 0, 9) = "PXEClient";
	next-server 192.168.50.128;
	filename "pxelinux.0";
}

# 设定客户端计算机名称并且绑定 IP 地址
host dlclient1{
	option host-name "dlclient1";
	hardware ethernet 00:0C:29:AF:52:BD;
	fixed-address 192.168.50.51;
}
EOF

启动服务

systemctl enable --now dhcpd.service

为无盘客户端配置文件系统

配置 NFS 存储

mkdir -p /var/lib/tftpboot/centos8/root 

vim /etc/exports
/var/lib/tftpboot/centos8/root 192.168.50.0/24(rw,no_root_squash)

systemctl enable --now nfs-server.service
exportfs -ra

构建无盘文件系统

yum groups -y install "Minimal Install" --releasever=8 --installroot=/var/lib/tftpboot/centos8/root/

yum -y install "lvm2" --releasever=8 --installroot=/var/lib/tftpboot/centos8/root/

yum -y install "kernel-modules-4.18.0-305.3.1.el8.x86_64" --releasever=8 --installroot=/var/lib/tftpboot/centos8/root/

cat << EOF | tee /var/lib/tftpboot/centos8/root/usr/lib/modules-load.d/boot.conf
xfs
ext4
EOF

示例

#客户节点系统添加软件
yum install -y software_name --installroot=/var/lib/tftpboot/centos8/root/ --releasever=8

#客户节点系统删除软件
yum erase -y software_name --installroot=/var/lib/tftpboot/centos8/root/ --releasever=8

为无盘客户端配置启动内核

cp /boot/vmlinuz-4.18.0-305.3.1.el8.x86_64 /var/lib/tftpboot/centos8/

# 这里也可以从 CentOS 官网下载内核 http://mirror.centos.org/centos/8/BaseOS/x86_64/os/images/pxeboot/

dracut -f  --add "nfs" --add-drivers "xfs" /var/lib/tftpboot/centos8/initrd.img 4.18.0-305.3.1.el8.x86_64

chmod 644 /var/lib/tftpboot/centos8/initrd.img

为无盘客户端配置启动引导文件

mkdir /var/lib/tftpboot/pxelinux.cfg 
cp /usr/share/syslinux/pxelinux.0 /var/lib/tftpboot/ 

vim /var/lib/tftpboot/pxelinux.cfg/default
default CentOS8
label CentOS8
    kernel centos8/vmlinuz-4.18.0-305.3.1.el8.x86_64
    append initrd=centos8/initrd.img root=nfs:192.168.50.128:/var/lib/tftpboot/centos8/root rw selinux=0

配置无盘系统密码

# 切换文件系统到无盘
chroot /var/lib/tftpboot/centos8/root/

# 设置无盘系统root密码
passwd root

#解决无盘命令提示符显示-bash-4.2
cp /etc/root/.bashrc /var/lib/tftpboot/centos8/root
cp /etc/root/.bash_profile  /var/lib/tftpboot/centos8/root

# 退出无盘系统
exit

实际应用

Centos 7 LVM xfs文件系统修复

ls -l /dev/mapper
xfs_repair /dev/mapper/centos-root
xfs_repair -L /dev/mapper/centos-root
reboot

centos 8 救援模式挂载lvm出错

vgchange -ay
  2 logical volume(s) in volume group "cl" now active

lvdisplay 
  --- Logical volume ---
  LV Path                /dev/cl/swap
  LV Name                swap
  VG Name                cl
  LV UUID                VXpVHT-E2B0-0rhV-dqCn-zhRd-NGtr-t5zqLe
  LV Write Access        read/write
  LV Creation host, time dlclient1, 2021-09-24 04:29:25 +0000
  LV Status              available
  # open                 0
  LV Size                2.00 GiB
  Current LE             512
  Segments               1
  Allocation             inherit
  Read ahead sectors     auto
  - currently set to     8192
  Block device           253:0
   
  --- Logical volume ---
  LV Path                /dev/cl/root
  LV Name                root
  VG Name                cl
  LV UUID                41BUbP-QIs1-DVOw-OCxD-9kt5-7GTC-yjA9M5
  LV Write Access        read/write
  LV Creation host, time dlclient1, 2021-09-24 04:29:25 +0000
  LV Status              available
  # open                 1
  LV Size                <17.00 GiB
  Current LE             4351
  Segments               1
  Allocation             inherit
  Read ahead sectors     auto
  - currently set to     8192
  Block device           253:1

lsblk 
NAME        MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda           8:0    0  20G  0 disk 
|-sda1        8:1    0   1G  0 part 
`-sda2        8:2    0  19G  0 part 
  |-cl-swap 253:0    0   2G  0 lvm  
  `-cl-root 253:1    0  17G  0 lvm  /mnt
sr0          11:0    1   7G  0 rom  

xfs_repair -L /dev/cl/root   # 修复xfs文件系统逻辑卷
mount /dev/cl/root /mnt

然后可以备份文件等操作

心得

# 查看 grouplist
yum grouplist
Available Environment Groups:
   Server with GUI
   Minimal Install
   Workstation
   KDE Plasma Workspaces
   Virtualization Host
   Custom Operating System
Installed Environment Groups:
   Server
Installed Groups:
   Container Management
   Headless Management
Available Groups:
   .NET Core Development
   RPM Development Tools
   Development Tools
   Graphical Administration Tools
   Legacy UNIX Compatibility
   Network Servers
   Scientific Support
   Security Tools
   Smart Card Support
   System Tools
   Fedora Packager
   Xfce

# 查看内核模块
lsmod 

# 查看 mod 具体信息
modinfo

# 动态加载模块
modprobe ext4
modprobe xfs
modprobe --show-depends ext4  # 显示ext4内核模块的依赖关系
--remove  移除模块

# 内核模块开机自动加载
cat > /etc/sysconfig/modules/ipvs.modules <<EOF
modinfo modulename >& dev/null
if [[ $? == 0 ]];then
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack
fi
EOF

chmod 755 /etc/sysconfig/modules/ipvs.modules

# Systemd 内核模块自动加载
/etc/modules-load.d/*.conf 中每行写入加载的内核模块
/usr/lib/modules-load.d/*.conf  中每行写入加载的内核模块
上面两个目录下 *.conf文件的的加载需要依靠systemd-modules-load.service,内核模块需要执行权限

# 查看加载的内核模块
cut -f1 -d' ' /proc/modules | grep -e ip_vs -e nf_conntrack

# pxe diskless 系统缺少 xfs 和 ext4 文件系统支持
yum -y install "kernel-modules-4.18.0-305.3.1.el8.x86_64" --releasever=8 --installroot=/var/lib/tftpboot/centos8/root/
modporbe xfs
modprobe ext4

从无盘启动看Linux启动原理

  • BIOS启动,完成自检,选择启动硬件
  • 如果是磁盘系统读取MBR
  • 从MBR指示,找到GRUB所在分区,加载GRUB显示菜单
  • 加载Linux内核到内存中
  • 执行INIT程序
  • 进入用户界面

由于我需要从网络启动,过程会变得复杂一些,主要变化如下

  • 在MBR引导前,需要执行一系列的PXE流程,目的是挂载iscsi磁盘。
  • 在加载linux内核后,由于之前iPXE固件已经退出,还需要再次挂载iscsi磁盘。

image.png

无盘启动并不是说完全没有磁盘,只是客户端本身没有磁盘,我们需要在远端给机器提供一种文件存储和磁盘共享的方案。 我这里选择的是iscsi共享,相比于NFS和samba共享,它更底层,对系统的兼容性更好。

iSCSI利用了TCP/IP作为沟通的渠道。透过两部计算机之间利用iSCSI的协议来交换SCSI命令,让计算机可以透过高速的局域网集线来把SAN模拟成为本地的储存设备。

关于iscsi的配置不是本文重点,这里就不详细描述了,要完成iscsi磁盘的挂载需要接信息。

iscsi服务器地址:我这里是nas服务的地址192.168.3.5

target名称:这个是服务端用来区分目标的,通常一个target服务一个客户端,并关联一块共享存储,例如:iqn.2005-10.org.freenas.ctl:yong-pc.volumio

initiator名称:这个是客户端名称,用来告诉服务端谁来请求了。

BIOS和UEFI

准备工作做完,我们先来了解一下计算机的启动原理,这里就要说到BIOS和UEFI,他们是计算机按下电源后最先被执行的程序。

BIOS (Basic Input/Output System)

上个世纪70年代初,"只读内存"(read-only memory,缩写为ROM)发明,开机程序被刷入ROM芯片,计算机通电后,第一件事就是读取它。这块芯片里的程序叫做"基本输入输出系统"(Basic Input/Output System),简称为BIOS。

image.png

BIOS程序首先检查,计算机硬件能否满足运行的基本条件,这叫做"硬件自检"(Power-On Self-Test),缩写为POST。 硬件自检完成后,BIOS把控制权转交给下一阶段的启动程序。

这时,BIOS需要知道,"下一阶段的启动程序"具体存放在哪一个设备。也就是说,BIOS需要有一个外部储存设备的排序,排在前面的设备就是优先转交控制权的设备。这种排序叫做"启动顺序"(Boot Sequence)。

UEFI (Unified Extensible Firmware Interface)

不知道大家是否发现,这些年已经很难看到BIOS的身影了。

ROM的存储能力有限,BIOS能驱动的硬件类型和数量大大受限。导致大量新硬件无法在PC启动时被加载。最明显就是你无法在BIOS时使用鼠标。此外BIOS的代码历史悠久难以维护。

在2005年年中时候,包括BIOS供应商、OS供应商、系统制造商以及芯片生产公司在内的行业参与者统一建立了统一的EFI联盟(UEFI,Unified Extensible Firmware Interface)并在2006年一月发行了UEFI规范2.0。

从此你可以愉快的在PC启动初期使用鼠标,甚至像苹果一样加载网络,实现联网下载并安装操作系统。

UEFI的启动流程和BIOS的启动流程不同

  • 系统开机 - 上电自检(Power On Self Test 或 POST)。
  • UEFI 固件被加载,并由它初始化启动要用的硬件。
  • 固件读取其引导管理器以确定从何处(比如,从哪个硬盘及分区)加载哪个 UEFI 应用。
  • 固件按照引导管理器中的启动项目,加载UEFI应用。
  • 已启动的 UEFI 应用还可以启动其他应用(对应于 UEFI shell 或 rEFInd之类的引导管理器的情况)或者启动内核及initramfs(对应于GRUB之类引导器的情况),这取决于 UEFI 应用的配置

PXE

上电自检完成后BIOS按照设置的启动顺序应该交棒磁盘,但是这个机器没有硬盘,也没有插入U盘,找不到任何启动设备的BIOS将控制权交给了网卡,BIOS光荣退场进入了PXE阶段。

预启动执行环境(Preboot eXecution Environment,PXE,也被称为预执行环境) 提供了一种使用网络接口启动计算机的机制。这种机制让计算机的启动可以不依赖本地数据存储设备(如硬盘)或本地已安装的操作系统。

image.png

PXE原理

  1. Client向 DHCP 发送IP地址请求消息,DHCP返回Client的IP地址,同时将启动文件(如:pxelinux.0)的位置信息(通常是TFTP路径)一并传送给Client
  2. Client向TFTP发送获取启动文件请求消息,TFTP接收到消息之后再向Client发送启动文件大小信息,试探Client是否满意,当TFTP收到Client发回的同意大小信息之后,正式向Client发送启动文件Client执行接收文件
  3. Client向TFTP发送针对本机的配置信息文件请求,TFTP将配置文件发回Client,继而Client根据配置文件执行后续操作。
  4. Client会加载启动文件,之后根据配置执行动作。这里有多重方案进行下一步操作。

可以直接通过Http协议获取Linux kernel和ramdisk然后启动

或者加载一块iscsi磁盘,将linux kernel和ramdisk等信息放在iscsi磁盘中,走正常磁盘引导。我用的是这种方案

image.png

iPXE

上面说到了启动文件,普通的pxe启动文件功能有限,通常只能从tftp服务器上获取文件,不支持HTTP协议和其他共享协议,更别说我们要支持的iscsi磁盘挂载了。这里推荐一个高端开源pxe启动文件:iPXE(https://ipxe.org/)。 它支持从HTTP、iscsi SAN、 Fibre Channel SAN、AoE SAN等多种方式启动,甚至还支持无线网卡。此外它还可以定制一个启动脚本和菜单。

iPXE需要根据自己硬件对应的平台进行编译,编译前需要搞清楚几个要点:

  • 启动方式:BIOS或者EFI前面已经说了。
  • 平台:X86或ARM,如果用树莓派等产品就是ARM,PC是x86
  • CPU位:32或64,32位机器只支持32位固件,64位机器可以兼容32位和64位固件。注意:如果使用64位固件需要保证后续所有环节使用兼容64位的软件,我就遇到了 SysLinux不支持64位,导致卡死的问题。

使用如下命令编译

git clone git://git.ipxe.org/ipxe.git
make [platform]/[driver].[extension]

Driver:主要选择支持的网卡驱动类型,一般选ipxe(表示所有支持的网卡,但可能导致生成的启动文件过大,如果过大可以酌情选其它)

Boot type:和启动方式、启动介质有关,参考下表:
image.png

编译时添加 EMBED={脚本名称} 可以关联一个启动脚本。推荐一个大佬做好的脚本 http://boot.netboot.xyz/menu.ipxe 可以直接使用。

最终命令如下:

git clone git://git.ipxe.org/ipxe.git
cd ./ipxe/src
wget http://boot.netboot.xyz/menu.ipxe
make bin-i386-pcbios/ipxe.pxe EMBED=menu.ipxe

完成之后在/data/ipxe/src/bin-i386-pcbios/ipxe.pxe可以拿到最终的启动文件。

分区:MBR和GPT

MBR

说了这么多还是没说明白到底计算机怎么从磁盘上找到引导程序。答案是:它被固定写死在了 0柱面,0磁头,1扇区的位置通常是512byte,这个位置被称为主扇区(Master Boot Record, MBR)。

MBR主要包含如下数据:

  • 主引导记录(bootloader),负责从活动分区加载并运行系统引导程序。446字节
  • 硬盘分区表项(DPT——disk partition table),由四个分区表项组成,负责记录磁盘的分区情况。64字节。
  • 硬盘有效标志(magic number),代表引导扇区结束,占用2字节。

image.png

Bootloader: 这部分记录了一段较小引导代码,用于去启动硬盘其他分区位置上更大的引导文件,例如linux操作系统的grub目录。

image.png

我们知道一个硬盘的每个分区的第一个扇区叫做boot sector,这个扇区存放的就是操作系统的loader。如上图,第一个分区的boot sector存放着windows的loader,第二个分区放着Linux的loader,第三个第四个由于没有安装操作系统所以空着。至于MBR的bootloader是干嘛呢, bootloader有三个功能:

  • 提供选单:让用户选择进入哪个系统。
  • 读取内核文件:默认启动的loader会被拷贝一份到MBR中,这样就可以直接读取内核了,图中1部分
  • 转交给其他loader:图中2,3部分

Disk Partition table: 这一部分64字节大小被均分为4份,每份大小16字节,每当我们在硬盘上创建出一个新的主分区或者扩展分区时,便会占用1个16字节的大小用于记录这个分区的相关信息(例如起始和截止柱面位置、分区文件系统类型等等)。这就是为什么mbr分区模式最多只能有4个主分区的原因。

MBR的局限

  • 最多只支持4个主分区,超过4个就需要使用扩展分区。
  • 磁盘的最大容量只能到2.2TB

如今我家的硬盘都4T了,MBR早就不能满足需求了。你也不能怪MBR,毕竟人家1983年就提出了,比我的年纪还大。

GPT

为了解决MBR的问题,GPT分区诞生,GPT全称Globally Unique Identifier Partition Table,也叫GUID分区表,它是UEFI规范的一部分(但这并不是说它只支持UEFI,它也支持BIOS方式的引导)。

image.png

GPT分区结构如下:

  • Protective MBR:GPT分区表的最前面部分也保存了和MBR相同的格式和内容称为Protective MBR,这极大的提高了GPT分区表的兼容性。
  • 主GPT Header:这里记录了分区表项目数和每项目大小。
  • 主GPT分区表:包含分区的类型GUID,名称,起始终止位置,该分区的GUID以及分区属性
  • 实际分区
  • 备份GPT分区表: 用于提高安全性,防止主GPT分区表损坏
  • 备份GPT Header: 用于提高安全性,防止主GPT Header损坏

Bootloader写入

使用dd命令结合hexdump可以输出MBR信息

dd if=~/Desktop/volumio-2.799-2020-07-16-x86.img ibs=512 count=1 | hexdump -C

image.png

同样的使用dd命令可以拷贝MBR信息从img文件到物理磁盘。(之前我是分分区写入到磁盘的,导致MBR信息丢失无法引导)

dd if=~/Desktop/volumio-2.799-2020-07-16-x86.img ibs=512 count=1 of=/dev/sda

也可以使用下载的syslinux中的mbr.bin写入

dd bs=440 count=1 conv=notrunc if=/usr/lib/syslinux/bios/mbr.bin of=/dev/sda //MBR分区表
dd bs=440 count=1 conv=notrunc if=/usr/lib/syslinux/bios/gptmbr.bin of=/dev/sda //GPT分区表

内核:vmlinuz和initrd

引导加载程序交棒之后系统进入内核引导阶段。这一步会在内存中运行系统内核和根文件系统。之后根目录下的init shell会被调用执行,完成进一步的初始化操作。

vmlinuz和initrd

vmlinuz是可引导的、压缩的内核。“vm”代表“Virtual Memory”。Linux能够使用硬盘空间作为虚拟内存,因此得名“vm”。vmlinuz是可执行的Linux内核。

initrd是“initial ramdisk”的简写。initrd一般被用来临时的引导硬件到实际内核vmlinuz能够接管并继续引导的状态。initrd 字面上的意思就是"boot loader initialized RAM disk",换言之,这是一块特殊的RAM disk,在载入Linux kernel前,由boot loader予以初始化,启动过程会优先执行initrd的init程序,initrd完成阶段性目标后,kernel 会挂载真正的root file system ,并执行/sbin/init程序。

  • vmlinuz指的是内核,作用:进程管理、内存管理、文件管理、驱动管理、网络管理。
  • initrd.img是一个小的映象, 放的是和启动相关的驱动模块。通常的步骤是先启动内核,然后内核挂载initrd.img,并执行里面的脚本来进一步挂载各种各样的模块。其中最重要的就是根文件系统驱动模块,有了它才能挂载根文件系统,继而运行用户空间的第一个应用程序init或者systemd,完成系统后续的启动

参考

https://blog.csdn.net/qq_41938046/article/details/118417901
http://hmli.ustc.edu.cn/doc/linux/centos7.6-diskless/