PXE 批量安装 Ubuntu 20.04.2

原文链接:https://www.cpweb.top/1698

一、介绍

  Ubuntu 20.04 的服务器安装程序支持新的操作模式:自动安装(automated installation)。自动安装可以通过自动安装配置提前回答所有这些配置问题,并使安装过程无需任何交互即可运行。
  在 Ubuntu 18.04 中,用的应答文件是 preseeds(预配置文件),它基于 debian-installer(aka di)来实现自动安装的。需要注意的是,如果你使用的是 cobbler,那你应该使用 ubuntu-20.04-legacy-server-amd64.iso,而不是 live-server(应该是缺少netboot),Ubuntu 20.04 已没有 server。
  Ubuntu 20.04 自动安装在以下主要方面与之前的版本有所不同:
   • 应答文件格式完全不同。现在是:cloud-init config,通常为 yaml。而之前是:debconf-set-selections 格式。
   • 当前提条件中不存在问题的答案时,di会停止并要求用户输入。而自动安装不是这样的:默认情况下,如果根本没有任何自动安装配置,则安装程序将使用任何未回答问题的默认设置(如果没有默认问题,安装程序将失败)。在自动安装中可以将配置中的特定部分指定为“交互式”,这意味着安装程序仍将停止并询问这些部分。
  

二、环境

  在 ubuntu 18.04.5 上部署 autoinstall 来批量安装 ubuntu 20.04.2。还是典型的 PXE + TFTP + HTTP + DHCP + Subiquity(ubuntu 服务器安装程序) 。镜像为:ubuntu-20.04.2-live-server-amd64.iso。注意,引导方式为UEFI

主机 系统 IP地址
server ubuntu 18.04.5 10.0.0.4
node1 未安装操作系统 -
node2 未安装操作系统 -

  

三、部署

1、安装相关软件

isc-dhcp-server :用来给客户端主机分配可用的IP地址。
tftpd-hpa :用来给客户端主机提供引导及驱动文件。
apache2 :用来给客户端主机提供镜像、应答文件以及一些自定义的文件脚本之类的。

root@server:~# apt-get -y install tftpd-hpa apache2 isc-dhcp-server whois

  

2、配置 tftp 和 apache

root@server:~# cat > /etc/apache2/conf-available/tftp.conf <<EOF
<Directory /var/lib/tftpboot>
        Options +FollowSymLinks +Indexes
        Require all granted
</Directory>
Alias /tftp /var/lib/tftpboot
EOF
root@server:~# a2enconf tftp
root@server:~# systemctl restart apache2

准备引导文件:

root@server:~# mount /var/lib/tftpboot/ubuntu-20.04.2-live-server-amd64.iso /mnt/
root@server:~# cp /mnt/casper/vmlinuz /var/lib/tftpboot/
root@server:~# cp /mnt/casper/initrd /var/lib/tftpboot/
root@server:~# umount  /mnt

准备grub

root@server:~# wget http://archive.ubuntu.com/ubuntu/dists/focal/main/uefi/grub2-amd64/current/grubnetx64.efi.signed -O /var/lib/tftpboot/pxelinux.0
root@server:~# mkdir -p /var/lib/tftpboot/grub
root@server:~# cat > /var/lib/tftpboot/grub/grub.cfg <<EOF
default=autoinstall
timeout=0
timeout_style=menu
menuentry "Focal Live Installer - automated" --id=autoinstall {
    echo "Loading Kernel..."
    linux /vmlinuz ip=dhcp url=http://10.0.0.4/tftp/ubuntu-20.04.2-live-server-amd64.iso autoinstall ds=nocloud-net\;s=http://10.0.0.4/tftp/
    echo "Loading Ram Disk..."
    initrd /initrd
}
menuentry "Focal Live Installer" --id=install {
    echo "Loading Kernel..."
    linux /vmlinuz ip=dhcp url=http://10.0.0.4/tftp/ubuntu-20.04.2-live-server-amd64.iso
    echo "Loading Ram Disk..."
    initrd /initrd
}
EOF

  

3、配置DHCP

root@server:~# cp /etc/dhcp/dhcpd.conf /etc/dhcp/dhcpd.conf.bak
root@server:~# cat > /etc/dhcp/dhcpd.conf <<EOF
ddns-update-style none;
subnet 10.0.0.0 netmask 255.255.255.0 {
     option routers             10.0.0.2;
     option domain-name-servers 114.114.114.114;
     option subnet-mask         255.255.255.0;
     range dynamic-bootp        10.0.0.200 10.0.0.220;
     default-lease-time         21600;
     max-lease-time             43200;
     next-server                10.0.0.4;
     filename "pxelinux.0";
}
EOF

  

4、准备 cloud.init config

root@server:~# cat > /var/lib/tftpboot/meta-data <<EOF
instance-id: focal-autoinstall
EOF

  在准备 cloud.init config 前。建议先手动安装一次 ubuntu 20.04.2,在 /var/log/installer/ 目录下会生成一个 autoinstall-user-data ,这是基于当前的系统的应答文件,我们可以以它作为基础,根据实际情况进行修改。

root@server:/var/lib/tftpboot# cat > user-data <<'EOF'
#cloud-config
autoinstall:
  version: 1
  apt:
    primary:
    - arches: [default]
      uri: http://mirrors.aliyun.com/ubuntu
  # The passwords are all 000000
  user-data:
    timezone: Asia/Shanghai
    disable_root: false
    chpasswd:
      list: |
        root:$6$UenIfx4J$MXuFvAbjNjwotUl6CtEtwC.1SnlPqMkBd7oHg02XZ1iNk97eMglUrRO1hQUVvOZEf3M/aEhgyrQ/gTDx4fizz/
  identity:
    hostname: node
    password: $6$m/xrHiECoB3upm$qVLuNKyH67prn/uOKlM9soMSIugK.Bzy8jU.TpYDQhLRDvTQtn1ga6Hv0musEMbIUZNV1AmIwM6r/59ZfRA8X0
    username: test
  keyboard: {layout: us, variant: ''}
  locale: en_US.UTF-8
  ssh:
    install-server: true
  
  storage:
    grub:
      reorder_uefi: False
    config:
    - {ptable: gpt, path: /dev/sda, wipe: superblock-recursive, preserve: false, name: '',
      grub_device: false, type: disk, id: disk-sda}
    - {device: disk-sda, size: 536870912, wipe: superblock, flag: boot, number: 1,
      preserve: false, grub_device: true, type: partition, id: partition-0}
    - {fstype: fat32, volume: partition-0, preserve: false, type: format, id: format-0}
    - {device: disk-sda, size: -1, wipe: superblock, flag: '', number: 2,
      preserve: false, type: partition, id: partition-1}
    - {fstype: ext4, volume: partition-1, preserve: false, type: format, id: format-1}
    - {device: format-1, path: /, type: mount, id: mount-1}
    - {device: format-0, path: /boot/efi, type: mount, id: mount-0}

  packages:
  - linux-generic-hwe-20.04-edge
  late-commands:
  - curtin in-target --target=/target -- wget -P /root/ http://10.0.0.4/tftp/bash/init.sh
  - curtin in-target --target=/target -- wget -P /root/ http://10.0.0.4/tftp/bash/network.sh
  - curtin in-target --target=/target -- bash /root/init.sh

  以上意思我就不注解了,详细请查阅官方文档:https://ubuntu.com/server/docs/install/autoinstall-reference

  

5、脚本准备

我这里准备了两个脚本(根据实际修改):
 • init.sh:用于在系统成功重启之前,在安装成功并安装了所有更新和软件包之后运行的初始化脚本。
 • network.sh:用于快速修改网络配置。在装好的系统上执行此脚本,输入IP,即可将动态地址换成静态地址。

root@server:~# mkdir /var/lib/tftpboot/bash
root@server:~# cd /var/lib/tftpboot/bash/
root@server:/var/lib/tftpboot/bash# vim init.sh
root@server:/var/lib/tftpboot/bash# vim network.sh

init.sh:

#!/bin/bash

sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
systemctl restart sshd
systemctl stop ufw.service
systemctl disable ufw.service
echo -e "NTP=ntp1.aliyun.com\nFallbackNTP=ntp.ubuntu.com" >> /etc/systemd/timesyncd.conf
systemctl restart systemd-timesyncd

cat >> /etc/security/limits.conf << EOF
*		soft		nofile	655350
*		hard		nofile	655350
*		soft		nproc	655350
*		hard		nproc	655350
root		soft		nofile	655350
root		hard		nofile	655350
root		soft		nproc	655350
root		hard		nproc	655350
EOF

cp /etc/sysctl.conf /etc/sysctl.conf.bak
cat > /etc/sysctl.conf << EOF
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.ip_forward = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
kernel.sysrq = 0
kernel.core_uses_pid = 1
net.ipv4.tcp_syncookies = 1
kernel.msgmnb = 65536
kernel.msgmax = 65536
kernel.shmmax = 68719476736
kernel.shmall = 4294967296
net.ipv4.tcp_max_tw_buckets = 6000
net.ipv4.tcp_sack = 1
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_rmem = 4096        87380   4194304
net.ipv4.tcp_wmem = 4096        16384   4194304
net.core.wmem_default = 8388608
net.core.rmem_default = 8388608
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.netdev_max_backlog = 262144
net.ipv4.tcp_max_orphans = 3276800
net.ipv4.tcp_max_syn_backlog = 262144
net.ipv4.tcp_timestamps = 0
net.ipv4.tcp_synack_retries = 1
net.ipv4.tcp_syn_retries = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_mem = 94500000 915000000 927000000
net.ipv4.tcp_fin_timeout = 1
net.ipv4.tcp_keepalive_time = 30
net.ipv4.ip_local_port_range = 1024    65000
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.default.secure_redirects = 0
EOF
      
/sbin/sysctl -p

rm -rf /root/init.sh

network.sh:

#!/bin/bash
cd /etc/netplan
cp *.yaml network.yaml.bak
read -p "please ip address: " IP
Gateway=`echo $IP | awk -F '.' '{print $1"."$2"."$3".2"}'`
sed -i 's#critical: true#addresses: ['${IP}'/24]#' *.yaml
sed -i '/dhcp-identifier: mac/d' *.yaml
sed -i '0,/dhcp4: true/{s/dhcp4: true/gateway4: '${Gateway}'/}' *.yaml 
netplan apply
cd

四、自动部署

  以上的部署步骤,我都写成了脚本。并将镜像、脚本和一些文件,打包成了一个压缩包。压缩包下载地址:

用法:
将下载下来的压缩包上传到服务器,解压缩,进入解压出来的 autoinstall 目录。

root@server:~# tar -xf autoinstall.tar.gz
root@server:~# cd autoinstall/
root@server:~/autoinstall# ll
total 1188120
-rw-r--r-- 1 root root       5506 Apr 17 15:51 autoinstall.sh
-rw-r--r-- 1 root root       2104 Apr 17 15:43 init.sh
-rw-r--r-- 1 root root        334 Apr 17 16:04 network.sh
-rw-r--r-- 1 root root    1435512 Apr  4 11:30 pxelinux.0
-rw-r--r-- 1 root root 1215168512 Apr  4 11:30 ubuntu-20.04.2-live-server-amd64.iso

根据实际情况修改 autoinstall.sh,主要修改以下几个地方:

# pxe服务端地址,root和普通用户test的密码
pxe_default_server='10.0.0.4'
root_passwd=`mkpasswd -m sha-512 '000000'`
test_passwd=`mkpasswd -m sha-512 '000000'`


# dhcp信息,根据实际网络来。注意实际网络必须要通外网,因为需要从网络下载内核更新,如果不通会报错。
subnet 10.0.0.0 netmask 255.255.255.0 {
     option routers             10.0.0.2;
     option domain-name-servers 114.114.114.114;
     option subnet-mask         255.255.255.0;
     range dynamic-bootp        10.0.0.200 10.0.0.220;
     default-lease-time         21600;
     max-lease-time             43200;
     next-server                ${pxe_default_server};
     filename "pxelinux.0";

如果想在无网环境下安装,请在 user-data 中去掉 packages 部分。
修改好 autoinstall.sh 后,注意要在 autoinstall 目录下执行脚本。

root@server:~/autoinstall# bash autoinstall.sh

  如果发现 autoinstall.sh 修改错误且已经部署了。请删除 /va/lib/tftpboot 下的所有东西。再到 autoinstall 目录下重新执行脚本。
  放上脚本内容,结构逻辑比较简单,autoinstall.sh:

#!/bin/bash

apt-get -y install tftpd-hpa apache2 isc-dhcp-server whois

pxe_default_server='10.0.0.4'
root_passwd=`mkpasswd -m sha-512 '000000'`
hb_passwd=`mkpasswd -m sha-512 '000000'`

cat > /etc/apache2/conf-available/tftp.conf <<EOF
<Directory /var/lib/tftpboot>
        Options +FollowSymLinks +Indexes
        Require all granted
</Directory>
Alias /tftp /var/lib/tftpboot
EOF
a2enconf tftp
systemctl restart apache2

cp ubuntu-20.04.2-live-server-amd64.iso /var/lib/tftpboot/
mount /var/lib/tftpboot/ubuntu-20.04.2-live-server-amd64.iso /mnt/
cp /mnt/casper/vmlinuz /var/lib/tftpboot/
cp /mnt/casper/initrd /var/lib/tftpboot/
umount  /mnt

#wget http://archive.ubuntu.com/ubuntu/dists/focal/main/uefi/grub2-amd64/current/grubnetx64.efi.signed -O /var/lib/tftpboot/pxelinux.0
cp pxelinux.0 /var/lib/tftpboot/pxelinux.0

mkdir /var/lib/tftpboot/bash
cp init.sh /var/lib/tftpboot/bash
cp network.sh /var/lib/tftpboot/bash


mkdir -p /var/lib/tftpboot/grub
cat > /var/lib/tftpboot/grub/grub.cfg <<'EOF'
default=autoinstall
timeout=0
timeout_style=menu
menuentry "Focal Live Installer - automated" --id=autoinstall {
    echo "Loading Kernel..."
    linux /vmlinuz ip=dhcp url=http://${pxe_default_server}/tftp/ubuntu-20.04.2-live-server-amd64.iso autoinstall ds=nocloud-net\;s=http://${pxe_default_server}/tftp/
    echo "Loading Ram Disk..."
    initrd /initrd
}
menuentry "Focal Live Installer" --id=install {
    echo "Loading Kernel..."
    linux /vmlinuz ip=dhcp url=http://${pxe_default_server}/tftp/ubuntu-20.04.2-live-server-amd64.iso
    echo "Loading Ram Disk..."
    initrd /initrd
}
EOF
sed -i 's#${pxe_default_server}#'${pxe_default_server}'#g' /var/lib/tftpboot/grub/grub.cfg

cat > /var/lib/tftpboot/meta-data <<EOF
instance-id: focal-autoinstall
EOF

cat > /var/lib/tftpboot/user-data <<'EOF'
#cloud-config
autoinstall:
  version: 1
  apt:
    primary:
    - arches: [default]
      uri: http://mirrors.aliyun.com/ubuntu
  user-data:
    timezone: Asia/Shanghai
    disable_root: false
    chpasswd:
      list: |
        root:${root_passwd}
  identity:
    hostname: hb
    password: ${hb_passwd}
    username: hb
  keyboard: {layout: us, variant: ''}
  locale: en_US.UTF-8
  ssh:
    install-server: true
  
  storage:
    grub:
      reorder_uefi: False
    config:
    - {ptable: gpt, path: /dev/sda, wipe: superblock-recursive, preserve: false, name: '',
      grub_device: false, type: disk, id: disk-sda}
    - {device: disk-sda, size: 536870912, wipe: superblock, flag: boot, number: 1,
      preserve: false, grub_device: true, type: partition, id: partition-0}
    - {fstype: fat32, volume: partition-0, preserve: false, type: format, id: format-0}
    - {device: disk-sda, size: -1, wipe: superblock, flag: '', number: 2,
      preserve: false, type: partition, id: partition-1}
    - {fstype: ext4, volume: partition-1, preserve: false, type: format, id: format-1}
    - {device: format-1, path: /, type: mount, id: mount-1}
    - {device: format-0, path: /boot/efi, type: mount, id: mount-0}

  packages:
  - linux-generic-hwe-20.04-edge
  late-commands:
  - curtin in-target --target=/target -- wget -P /root/ http://${pxe_default_server}/tftp/bash/init.sh
  - curtin in-target --target=/target -- wget -P /root/ http://${pxe_default_server}/tftp/bash/network.sh
  - curtin in-target --target=/target -- bash /root/init.sh

EOF
sed -i 's#${root_passwd}#'${root_passwd}'#' /var/lib/tftpboot/user-data
sed -i 's#${hb_passwd}#'${hb_passwd}'#' /var/lib/tftpboot/user-data
sed -i 's#${pxe_default_server}#'${pxe_default_server}'#g' /var/lib/tftpboot/user-data

cp /etc/dhcp/dhcpd.conf /etc/dhcp/dhcpd.conf.bak
cat > /etc/dhcp/dhcpd.conf <<EOF
ddns-update-style none;
subnet 10.0.0.0 netmask 255.255.255.0 {
     option routers             10.0.0.2;
     option domain-name-servers 114.114.114.114;
     option subnet-mask         255.255.255.0;
     range dynamic-bootp        10.0.0.200 10.0.0.220;
     default-lease-time         21600;
     max-lease-time             43200;
     next-server                ${pxe_default_server};
     filename "pxelinux.0";
}
EOF

systemctl restart  tftpd-hpa apache2 isc-dhcp-server

五、注意事项

1、引导方式

  如果,使用 VMware Workstation,请修改虚拟机的引导方式为UEFI(默认为BIOS)。打开虚拟机设置,选择选项,点击高级,找到固件类型,选择UEFI。
  如果使用物理机,请将 BIOS 引导方式修改为 UEFI。

2、无网环境,应答文件如果配置

  前面有提到实际网络必须要通外网,这是因为在应答文件中我定义 packages,即要安装到目标系统的软件包列表,如果不通网会报错,安装程序会一直卡在错误提示哪里,只能重启。当然重启是可以正常进入系统,系统也是没问题的。
  上面说的报错并不会导致系统安装失败,只是安装软件失败,系统在这之前已经安装完毕了。只是后续的应答无法正常进行,像 packages 部分后面是 late-commands,那这一部分就不会执行了。应该是有跳过错误继续进行的指令,自行查阅吧。
  如果想在无网环境下安装,最简单的办法就是在 user-data 中去掉 packages 部分。

参考文章:
https://ubuntu.com/server/docs/install/autoinstall
https://ubuntu.com/server/docs/install/autoinstall-reference
https://askubuntu.com/questions/1235723/automated-20-04-server-installation-using-pxe-and-live-server-image

上一篇:CentOS系统下PXE服务器的搭建与部署


下一篇:VC++得到系统特殊文件夹路径