JhGU's personal site

关于如何配置无盘工作站的笔记

前言

  • 利用tftp服务器提供grub、内核以及initrd
  • 利用nfs提供根文件系统

服务器操作系统

采用nixos linux

dhcp服务器配置

nix文件

services.dhcpd4={
    enable = true;
    interfaces=["eno1"];
    configFile = "/etc/nixos/dhcpd/dhcpd.conf";
};

dhcpd.conf文件

default-lease-time 600;
max-lease-time 7200;
authoritative;
ddns-update-style interim;
log-facility local1; # see dhcpd.nix

option subnet-mask 255.255.255.0;
option broadcast-address 192.168.1.255;
option routers 192.168.1.1;
option domain-name-servers 192.168.1.1,8.8.8.8;
option architecture-type code 93 = unsigned integer 16;

subnet 192.168.1.0 netmask 255.255.255.0 {
       range 192.168.1.100 192.168.1.200;
       next-server 192.168.1.251;

        
class "pxeclients" {
  match if substring (option vendor-class-identifier, 0, 9) = "PXEClient";
  
  if option architecture-type = 00:00 {
  } elsif option architecture-type = 00:09 {
  } elsif option architecture-type = 00:07 {
    filename "/grub/x86_64-efi/core.efi";    #该文件路径与tftp服务器中文件的分布有关
  } elsif option architecture-type = 00:06 {
  }
 }
}

host diskless{
  hardware ethernet 98:FA:9B:0E:F6:89;
  fixed-address 192.168.1.28;
  option host-name "diskless";
}

tftp服务配置

确定文件位置

mkdir -p /srv/tftproot
ln -s /srv/tftproot /srv/diskless

nix文件

services.atftpd={
    enable = true;
    root = "/srv/tftproot";
    extraOptions = [
        "--verbose=7"
    ];
};

构建用于启动无盘工作站的根文件系统

由于服务器的操作系统是nixos,which用来启动无盘有些麻烦,我们选用archlinux作为无盘工作站的操作系统。

利用archlinuxpacstrap命令,可以在一个运行中的archlinux中,构建出一个根文件系统,用于启动无盘工作站。

但是为了在nixos中运行pacstrap,这里采用singularity先创建一个archlinux的环境,这需要用到如下的定义文件archlinux.def

BootStrap: docker
From: archlinux

%post
yes |pacman -Syu
yes |pacman -S btrfs-progs arch-install-scripts


%files 
    
%environment
    export LC_ALL=C
    export PATH=/usr/local/bin:$PATH

%labels
    Author JhGU

%setup

然后执行如下的命令

sudo singularity build --sandbox archlinux archlinux.def

进入刚刚生成的archlinux

sudo singularity shell -B /home -B /srv:/srv ./archlinux

参考 https://wiki.archlinux.org/title/Diskless_system

执行如下命令

export root=/srv/tftproot/arch
mkdir -p "$root"
pacstrap -d "$root" base linux linux-firmware mkinitcpio-nfs-utils nfs-utils

然后打开$root/etc/mkinitcpio.conf,进行如下编辑:

  • MODULES中加入e1000enfsv3,如果必要,可加入无盘工作站的网卡驱动模块。
  • BINARIES中加入/usr/bin/mount.nfs
  • HOOKS中加入net

经过编辑得到的完整mkinitcpio.conf内容如下(已删除注释)

MODULES=(e1000e nfsv3 )
BINARIES=(/usr/bin/mount.nfs)
FILES=()
HOOKS=(base udev autodetect modconf block filesystems keyboard fsck net )

接下来执行

arch-chroot "$root" mkinitcpio -p linux
pacman --root "$root" --dbpath "$root/var/lib/pacman" -S grub
arch-chroot "$root" grub-mknetdir --net-directory=/boot --subdir=grub
arch-chroot /srv/tftproot/arch/ grub-mkconfig -o /boot/grub.cfg

这里假定dhcptftpnfs服务器都设置在192.168.1.251

打开$root/boot/grub.cfg进行如下编辑:

  • 删除其中的set root...search --no-floppy ...
  • 将其中的linux..语句修改为linux /arch/boot/vmlinuz-linux add_efi_memmap ip=:::::eth0:dhcp nfsroot=192.168.1.251:/srv/diskless/arch rootdelay=10
  • 将其中的initrd..语句修改为initrd /arch/boot/initramfs-linux.img

编辑得到的grub文件内容如下

### BEGIN /etc/grub.d/00_header ###
insmod part_gpt
insmod part_msdos
if [ -s $prefix/grubenv ]; then
  load_env
fi
if [ "${next_entry}" ] ; then
   set default="${next_entry}"
   set next_entry=
   save_env next_entry
   set boot_once=true
else
   set default="0"
fi

if [ x"${feature_menuentry_id}" = xy ]; then
  menuentry_id_option="--id"
else
  menuentry_id_option=""
fi

export menuentry_id_option

if [ "${prev_saved_entry}" ]; then
  set saved_entry="${prev_saved_entry}"
  save_env saved_entry
  set prev_saved_entry=
  save_env prev_saved_entry
  set boot_once=true
fi

function savedefault {
  if [ -z "${boot_once}" ]; then
    saved_entry="${chosen}"
    save_env saved_entry
  fi
}

function load_video {
  if [ x$feature_all_video_module = xy ]; then
    insmod all_video
  else
    insmod efi_gop
    insmod efi_uga
    insmod ieee1275_fb
    insmod vbe
    insmod vga
    insmod video_bochs
    insmod video_cirrus
  fi
}

if [ x$feature_default_font_path = xy ] ; then
   font=unicode
else
insmod part_gpt
insmod ext2
#set root='hd0,gpt1'
font="/usr/share/grub/unicode.pf2"
fi

if loadfont $font ; then
  set gfxmode=auto
  load_video
  insmod gfxterm
  set locale_dir=$prefix/locale
  set lang=C
  insmod gettext
fi
terminal_input console
terminal_output gfxterm
if [ x$feature_timeout_style = xy ] ; then
  set timeout_style=menu
  set timeout=5
# Fallback normal timeout code in case the timeout_style feature is
# unavailable.
else
  set timeout=5
fi
### END /etc/grub.d/00_header ###

### BEGIN /etc/grub.d/10_linux ###
menuentry 'Arch Linux' --class arch --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-simple-4a890386-d889-4847-bbfc-c4ab8d3d6c26' {
	load_video
	set gfxpayload=keep
	insmod gzio
	insmod part_gpt
	insmod ext2
	#set root='hd0,gpt1'
	echo	'Loading Linux linux ...'
	linux	/arch/boot/vmlinuz-linux add_efi_memmap ip=:::::eth0:dhcp nfsroot=192.168.1.251:/srv/diskless/arch rootdelay=10
	echo	'Loading initial ramdisk ...'
	initrd	/arch/boot/initramfs-linux.img
}

### BEGIN /etc/grub.d/41_custom ###
if [ -f  ${config_directory}/custom.cfg ]; then
  source ${config_directory}/custom.cfg
elif [ -z "${config_directory}" -a -f  $prefix/custom.cfg ]; then
  source $prefix/custom.cfg
fi
### END /etc/grub.d/41_custom ###

注意:archlinux的mkinitcpio命令生成initramfs-linux.img文件的默认权限是-rw------,需要修改为-rw-r--r-,否则tftp将无法读取该文件。

接下来将/arch/boot/grub复制到/srv/tftproot/,如果不执行此操作,可能会启动失败,原因不明。

nfs设置

https://wiki.archlinux.org/title/Diskless_system,使用的是nfsv4,但是在我的测试中,存在一些问题,所以这里采用nfsv3

相应的配置文件如下

services.nfs.server={
    enable = true;
    exports = ''
/srv/diskless 		      192.168.1.0/24(rw,no_root_squash,no_subtree_check)
/srv/diskless/arch	      192.168.1.0/24(rw,no_root_squash,no_subtree_check)
'';
};

注意其中最重要的一个参数是no_root_squash,该参数可以确保无盘工作站的root用户能以特权权限操作挂载的nfs根分区。

最后,启动系统

在自检阶段,选择pxe over ipv4启动方式,然后稍等一会儿,应该就能进入无盘工作站的系统了。