selfinst构建自安装的系统镜像

Table of Contents

无人值守安装(unattended install)

主流linux发行版一般都有自己的无人值守安装系统,例如redhat系的Anaconda(anaconda-ks.cfg)、suse系的AutoYaST(autoinst.xml) 、debian系的Debian-Installer(preseed.cfg)。

其实它们的工作流程都是类似的,无非就是下载官方的基础iso镜像,再配上一个描述文件,最后生成一个新的iso镜像。所以我把流程理了一下,写了个selfinst的项目,把整个流程脚本化,接口只要提供源iso、描述文件、目标iso这三个参数就可以了。

我目前所有的linux 机器统一都用debian,因此只写了selfinst-debian.sh这一个脚本。不过暂时足够我使用了,后续如果有需求再添加就行,反正基础流程已经统一了,再写起来也是大同小异。

以debian为例子

debian-installer使用的preseed.cfg作为描述文件,所以描述文件的第二个参数需要提供preseed.cfg,跑起来也非常简单,只需要像下面这样。

两种方式使用

selfinst-debian.sh
#or
selfinst-debian.sh [src.iso]  [preseed.cfg] [dst.iso]

1.第一种就是直接不带参数使用selfinst-debian.sh,它会根据脚本中一个叫iso_name的变量下载指定版本的ISO,然后生成一个带有preseed前缀的新镜像。 2.第二种则是提供已经下载的src.iso和描述文件以及设置要生成的dst.iso路径,这样就会根据提供的镜像生成新的无人值守镜像,名字就是dst提供的文件名。

下面简单介绍一下selfinst-debian的工作流程

参数获取

path_src_iso=$(readlink -f "$1")
path_preseed=$(readlink -f "$2")
path_dst_iso=$(readlink -f "$3")

PATH_SCRIPT=$(dirname "$(realpath "$0")")
PATH_WORK="$PATH_SCRIPT/iso-debian"
PATH_SRC="$PATH_WORK/iso-decompressed"
PATH_DST="$PATH_WORK/iso-new"

src iso、preseed.cfg、dst iso三个变量通过脚本参数获取,然会通过PATH_WORK SRC DST三个变量构建出iso-debian、iso-decompreseed、iso-new三个目录,分别用来存放源镜像,解压后的iso以及新生成的ISO。

镜像下载以及较检

  iso=""
iso_name=$(wget -q -O - https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/ | grep -oP 'debian-\d+\.\d+\.\d+-amd64-netinst\.iso' | sort -r | head -n 1)
if [ -z "$iso_name" ]; then
    echo "Failed to find the latest ISO name"
    exit 1
fi

mkdir -p $PATH_WORK $PATH_SRC $PATH_DST
cd $PATH_WORK

if [ "$path_src_iso" = "" ] ; then
    if [ ! -f "$PATH_WORK/$iso_name" ] ; then
        wget https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/$iso_name
        if [ $? -ne 0 ]; then
            echo "Failed to download $iso_name"
            exit 1
        fi
        wget https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/SHA512SUMS
        if [ $? -ne 0 ]; then
            echo "Failed to download SHA512SUMS"
            exit 1
        fi

        computed_checksum=$(sha512sum $iso_name | awk '{ print $1 }')
        expected_checksum=$(grep $iso_name SHA512SUMS | awk '{ print $1 }')
        if [ "$computed_checksum" = "$expected_checksum" ]; then
            echo "ISO sha512 checksum matches!"
        else
            echo "ISO sha512 checksum does not match!"
            exit 1
        fi
    fi
    iso=$PATH_WORK/$iso_name
else
    if [ ! -e "$path_src_iso" ] ; then
        echo "Source ISO file $path_src_iso does not exist!"
        exit 1
    fi
    cp $path_src_iso $PATH_WORK
    iso=$path_src_iso
fi

cat $iso | bsdtar -C "$PATH_SRC" -xf -

如果没有提供源镜像,就会下载最新的镜像并用sha512校检,然后解压到iso-decompressed目录中。

描述文件生成

#------------------------- preseed -----------------------------------
preseed_hostname="selfinst"
preseed_nameservers="114.114.114.114"
preseed_mirror_url="mirrors.ustc.edu.cn"
preseed_password_root="selfinstroot"
preseed_user="g"
preseed_password_g="selfinstg"
preseed_package="openssh-server build-essential vim syncthing barrier"

if [ "$path_preseed" = "" ]; then
cat << EOF > ./preseed.cfg
### Localization
d-i debian-installer/locale string en_US
d-i console-keymaps-at/keymap select us
d-i keyboard-configuration/xkb-keymap select us

### Network configuration
d-i netcfg/choose_interface select auto
d-i netcfg/get_hostname string $preseed_hostname
d-i netcfg/get_domain string lan
d-i netcfg/get_nameservers string $preseed_nameservers

### Mirror settings
# mirror and apt setup
d-i mirror/protocol string http
d-i mirror/country string manual
d-i mirror/http/hostname string $preseed_mirror_url
d-i mirror/http/directory string /debian
#d-i mirror/suite string stable
#d-i mirror/suite string testing
d-i mirror/suite string unstable
d-i mirror/http/proxy string

### Apt setup
d-i apt-setup/non-free boolean true
d-i apt-setup/contrib boolean true
#d-i apt-setup/use_mirror boolean false
#d-i apt-setup/enable-source-repositories boolean true
d-i apt-setup/disable-cdrom-entries boolean true
d-i apt-setup/services-select multiselect main, security
d-i apt-setup/security_host string $preseed_mirror_url

d-i apt-setup/cdrom/set-first boolean false
d-i apt-setup/cdrom/set-next boolean false
d-i apt-setup/cdrom/set-failed boolean false

### Package selection
tasksel tasksel/first multiselect standard, web-server, kde-desktop
tasksel tasksel/desktop multiselect kde
d-i pkgsel/include string $preseed_package
d-i pkgsel/upgrade select none
d-i pkgsel/language-packs multiselect en, zh
d-i pkgsel/update-policy select none

### Account setup
d-i passwd/root-password password $preseed_password_root
d-i passwd/root-password-again password $preseed_password_root
d-i passwd/user-fullname string $preseed_user
d-i passwd/username string $preseed_user
d-i passwd/user-password password $preseed_password_g
d-i passwd/user-password-again password $preseed_password_g

### Clock and time zone setup
d-i clock-setup/utc boolean false
d-i time/zone string Asia/Shanghai

### Partitioning
d-i partman-auto/disk string /dev/sda
d-i partman-auto/method string regular
d-i partman-lvm/device_remove_lvm boolean true
d-i partman-md/device_remove_md boolean true
d-i partman-auto/choose_recipe select atomic
d-i partman-partitioning/confirm_write_new_label boolean true
d-i partman/choose_partition select finish
d-i partman/confirm boolean true
d-i partman/confirm_nooverwrite boolean true

popularity-contest popularity-contest/participate boolean false

### Boot loader installation
d-i grub-installer/only_debian boolean true
d-i grub-installer/bootdev string default

### Finishing up the installation
d-i finish-install/keep-consoles boolean true
d-i finish-install/reboot_in_progress note

描述文件我将preseed.cfg里常见的几个参数写到了几个变量里,放一起改起来比较方便,然后在用echo重定向生成一个preseed.cfg文件。

preseed_hostname="selfinst"
preseed_nameservers="114.114.114.114"
preseed_mirror_url="mirrors.ustc.edu.cn"
preseed_password_root="selfinstroot"
preseed_user="g"
preseed_password_g="selfinstg"
preseed_package="openssh-server build-essential vim syncthing barrier"

设置preseed

chmod -R +w $PATH_SRC
gunzip $PATH_SRC/install.amd/initrd.gz
echo preseed.cfg | cpio -H newc -o -A -F $PATH_SRC/install.amd/initrd
gzip $PATH_SRC/install.amd/initrd
chmod -w -R $PATH_SRC/install.amd/

#regenerate md5sum
cd $PATH_SRC
chmod +w md5sum.txt
find -follow -type f ! -name md5sum.txt -print0 | xargs -0 md5sum > md5sum.txt
chmod -w md5sum.txt

#remove startup menu
sed -i '/default vesamenu.c32/d' isolinux/isolinux.cfg

debian需要将preseed.cfg写入到install.amd/initrd的文件中,然后在重新给镜像计算md5值。然后通过删除isolinux.cfg里default vesamenu.c32,使镜像启动之后不会被menu的选择影响停住。

生成新的镜像

if [ "$path_dst_iso" = "" ]; then
    genisoimage -r -J -b isolinux/isolinux.bin -c isolinux/boot.cat \
                -no-emul-boot -boot-load-size 4 -boot-info-table \
                -o $PATH_DST/preseed-$iso_name $PATH_SRC
else
    genisoimage -r -J -b isolinux/isolinux.bin -c isolinux/boot.cat \
                -no-emul-boot -boot-load-size 4 -boot-info-table \
                -o $path_dst_iso $PATH_SRC
fi

最后就是用genisoimage把解压后以及设置过preseed.cfg的镜像,指定好-o输出位置然后生成新的ISO。

最后

开源社区里其实已经有一些生成自定义镜像的工具了。但是我更喜欢自动动手用官方的方式来生产镜像,并且统一成自己想要的接口,这样可以更好得对工具进行定制化,来满足我自己的需求。