IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    制作 Arch Linux 系统模板镜像

    Phoenix Nemo发表于 2020-09-14 04:20:20
    love 0

    阿里云镜像制作踩坑记。

    此文章主要记录按照阿里云 Customized Linux 制作 VPC 镜像的过程。一些部分也可用作制作其他平台镜像的参考。

    当然记录的原因主要是 Arch 上的 cloud-init 打死无法在阿里云上修改 root 密码,就很气。

    建立虚拟机

    因为要制作 Customized Linux,所以第一步无法在阿里云平台上使用公共镜像制作。本机启动一个 Virtual Box,新建虚拟机,虚拟磁盘选择 RAW/IMG 格式即可。

    按照一般步骤安装 Arch Linux,需要整个磁盘仅有一个分区。虽然很多平台支持多分区的镜像文件,但是莫名在这里踩了坑所以。

    (另外吐槽:vps2arch 居然不帮我把 base-devel 装全了?!)

    系统配置

    安装一些必需的包。

    1
    # pacman -S qemu-guest-ga openssh

    启用服务。

    1
    2
    3
    # systemctl enable qemu-ga
    # systemctl enable sshd
    # systemctl enable systemd-networkd

    网络配置

    哪个魂淡跟我讲 VPC 是 DHCP?装着 cloud-init 的 Arch 就可以自动设置内网 IP,这个没装的就 GG。

    修改文件 /etc/systemd/network/default.network

    1
    2
    3
    4
    5
    [Match]
    Name=en*

    [Network]
    DHCP=ipv4

    总之先这样放着。

    定制脚本

    根据阿里云的文档,cloud init 不生效的时候需要用约定好的配置文件和脚本完成各种兼容动作。

    新建目录 /aliyun_custom_image

    新建文件 /usr/bin/aliyun-custom-os,写入内容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    #!/bin/bash

    os_conf_dir=/aliyun_custom_image
    os_conf_file=${os_conf_dir}/os.conf

    load_os_conf() {
    if [[ -f $os_conf_file ]]; then
    . $os_conf_file
    echo $password
    return 0
    else
    return 1
    fi
    }

    cleanup() {
    # ensure $os_conf_file is deleted, to avoid repeating config system
    rm $os_conf_file >& /dev/null
    # ensure $os_conf_dir is exitst
    mkdir -p $os_conf_dir
    }

    config_password() {
    if [[ -n $password ]]; then
    password=$(echo $password | base64 -d)
    if [[ $? == 0 && -n $password ]]; then
    echo "root:$password"
    echo "root:$password" | chpasswd
    fi
    fi
    }
    config_hostname() {
    if [[ -n $hostname ]]; then
    echo "$hostname" > /etc/hostname
    hostnamectl set-hostname $hostname
    fi
    }
    config_network() {
    if [[ -n $eth0_ip_addr ]]; then
    config_interface
    systemctl restart systemd-networkd
    fi
    }
    config_interface() {
    mask2cdr $eth0_netmask
    cat << EOF > /etc/systemd/network/default.network
    # Generated by Aliyun Custom OS helper
    # DO NOT EDIT THIS FILE! IT WILL BE OVERWRITTEN

    [Match]
    Name=$(ip link | awk -F: '$0 !~ "lo|vir|wl|^[^0-9]"{print $2a;getline}' | sed -e 's/^[[:space:]]*//')

    [Network]
    Address=$eth0_ip_addr/$netmask
    Gateway=$eth0_gateway

    [Link]
    MACAddress=$eth0_mac_address

    [Address]
    Address=$eth0_ip_addr/$netmask
    EOF
    echo "nameserver 1.1.1.1" > /etc/resolv.conf
    for ns in $dns_nameserver
    do
    echo "nameserver $ns" >> /etc/resolv.conf
    done
    }

    mask2cdr() {
    # Assumes there's no "255." after a non-255 byte in the mask
    local x=${1##*255.}
    set -- 0^^^128^192^224^240^248^252^254^ $(( (${#1} - ${#x})*2 )) ${x%%.*}
    x=${1%%$3*}
    netmask=$(( $2 + (${#x}/4) ))
    }

    if load_os_conf ; then
    config_password
    config_hostname
    config_network
    cleanup
    else
    echo "not load $os_conf_file"
    fi

    赋予执行权限

    1
    # chmod +x /usr/bin/aliyun-custom-os

    新建 systemd unit 文件 /usr/lib/systemd/system/aliyun-custom-os.service 写入内容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    [Unit]
    Description=Aliyun Custom OS Helper Script

    [Service]
    Type=oneshot
    ExecStart=/usr/bin/aliyun-custom-os
    TimeoutSec=30
    StandardInput=tty
    RemainAfterExit=yes

    [Install]
    WantedBy=multi-user.target

    然后启用这个服务

    1
    systemctl enable aliyun-custom-os

    挂载镜像

    正常 shutdown 虚拟机,然后拿到镜像文件的路径。例如 ~/vm/archlinux.img。

    接下来需要将此镜像挂载到宿主机系统中修改、清理文件。首先确定镜像文件中的分区位置:

    1
    2
    $ file ~/vm/archlinux.img
    archlinux.img: x86 boot sector; partition 1: ID=0x83, active, starthead 32, startsector 2048, 41938944 sectors, code offset 0x63

    得知 startsector 为 2048

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    $ fdisk -l ~/vm/archlinux.img
    You must set cylinders.
    You can do this from the extra functions menu.

    Disk archlinux.img: 0 MB, 0 bytes
    255 heads, 63 sectors/track, 0 cylinders
    Units = cylinders of 16065 * 512 = 8225280 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes
    Disk identifier: 0x91d8e293

    Device Boot Start End Blocks Id System
    archlinux.img1 * 1 2611 20969472 83 Linux
    Partition 1 has different physical/logical endings:
    phys=(1023, 254, 63) logical=(2610, 180, 2)

    得知 sectorsize 为 512。

    使用 mount 命令带 offset 参数挂载镜像中的分区:

    1
    2
    $ sudo mkdir -p /mnt/img
    $ sudo mount -t ext4 -o loop,offset=$((2048*512)) /path/to/archlinux.img /mnt/img/ # 更改 -t auto 或者其他此分区使用的文件系统格式

    就可以 cd /mnt/img 看到镜像里的 rootfs 啦。

    清理/检查文件

    要删除的:

    1
    2
    3
    4
    5
    # rm /root/.bash_history # _(:з」∠)_
    # rm /etc/ssh/ssh_host_* # 强制每次部署的时候重新生成密钥对
    # rm -r /var/log/* # 清理不需要的日志
    # rm -r /var/cache/* # 清理缓存
    # rm /etc/resolv.conf.bak # 避免恢复成制作时的 DNS

    要检查的:

    /etc/hosts - 我不知道为什么,第一次的时候把这个文件留空了(:з」∠)

    /etc/resolv.conf - 鉴于总是有人喜欢手动修改这个文件,所以直接把它写成静态文件好了。内容例如

    1
    2
    nameserver 8.8.8.8
    nameserver 8.8.4.4

    /etc/ssh/sshd_config 中是否允许 root 密码登陆。

    准备镜像

    退出 /mnt/img 目录,然后卸载镜像

    1
    # umount /mnt/img

    (可选)使用 qemu-img 转换镜像格式到 VHD,减少镜像文件大小。特别是对国内的小水管上传(心疼

    1
    $ qemu-img convert -f raw -O vpc archlinux.img archlinux.vhd

    上传镜像

    在相同的 region 创建一个 OSS bucket,然后创建一个 RAM 子用户赋予 OSS 写权限并创建 Access Key,使用 OSSBrowser 上传准备好的 VHD 文件。

    上传完毕后,在 ECS 标签下的镜像标签即可导入镜像。如果是第一次操作,需要给 ECS 授权访问 OSS。在导入的页面提示中提供了授权的链接。镜像内容配置如下:

    • OSS Object 地址:镜像文件在 OSS 中的 URL
    • Image 名称:archlinux-2018.1-x86_64 … 等符合要求即可
    • 操作系统:Linux
    • 系统盘大小:40GB
    • 系统架构:x86_64
    • 系统平台:Customized Linux
    • 镜像格式:VHD(如果是 img 就选 RAW)
    • 镜像描述:随便写啦。

    确定后应该就会开始制作镜像了。

    测试

    因为没有做经典实例的兼容,这个镜像只能用于 VPC 的实例。总体而言,cloud-init 本来兼容的 Arch 却无法更改 root 密码(其他的倒是没问题),所以才选择了用一个 dirty 的方案来实现。

    不知道应该说阿里云的工程师对自定义镜像的考虑周到还是对不同发行版的考虑欠妥…?

    最后庆幸倒腾来去上传了好多遍 20G 的文件,日本运营商家宽带宽对等真的是帮了大忙,不然一个镜像制作不知道要到什么时候 > > (斜眼看国内三大运营商

    参考:

    • https://www.alibabacloud.com/help/zh/faq-detail/51138.htm
    • https://serverfault.com/questions/842964/bash-script-to-retrieve-name-of-ethernet-network-interface
    • https://wiki.archlinux.org/index.php/systemd-networkd
    • https://stackoverflow.com/questions/20762575/explanation-of-convertor-of-cidr-to-netmask-in-linux-shell-netmask2cdir-and-cdir


沪ICP备19023445号-2号
友情链接