为 Azure 中的映像准备 Linux

适用于:✔️ Linux VM ✔️ 灵活规模集

Azure 平台服务级别协议 (SLA) 仅适用于运行 Linux 操作系统的虚拟机 (VM),前提是使用某个认可的分发版。 对于认可的分发版,Microsoft Azure 市场提供预配置的 Linux 映像。 有关详细信息,请参阅:

在 Azure 上运行的其他所有分发版(包括社区支持的分发版和非认可的分发版)都有一些先决条件。

本文重点介绍有关在 Azure 上运行 Linux 分发版的一般准则。 本文无法详尽无遗,因为每个分发版不同。 即使满足本文介绍的所有条件,也可能需要显著调整 Linux 系统,使其正常运行。

常规 Linux 安装说明

  • Azure 不支持 Hyper-V 虚拟硬盘 (VHDX) 格式。 Azure 仅支持固定 VHD。 可以使用 Hyper-V 管理器或 Convert-VHD cmdlet 将磁盘转换为 VHD 格式。 如果使用 VirtualBox,请在创建磁盘时选择“固定大小”,而不要选择默认(动态分配)大小。

  • Azure 支持 Gen1(BIOS 引导)和 Gen2(UEFI 引导)虚拟机。

  • 必须在内核中启用虚拟文件分配表 (VFAT) 内核模块。

  • VHD 允许的最大大小为 1,023 GB。

  • 在安装 Linux 系统时,建议使用标准分区而不是逻辑卷管理器 (LVM)。 对于许多安装,LVM 是默认值。

    使用标准分区可避免 LVM 名称与克隆的 VM 发生冲突,特别是在 OS 磁盘曾经连接到另一台相同的 VM 进行故障排除的情况下。 可以在数据磁盘上使用 LVMRAID

  • 需要对装载用户定义的函数 (UDF) 文件系统的内核支持。 在 Azure 上首次启动时,预配配置将通过附加到来宾的 UDF 格式媒体传递到 Linux VM。 Azure Linux 代理必须装载 UDF 文件系统才能读取其配置和预配 VM。

  • 低于 2.6.37 的 Linux 内核版本不支持具有更大 VM 大小的 Hyper-V 上的非一致性内存访问 (NUMA)。 此问题主要影响使用上游 Red Hat 2.6.32 内核的较旧分发版。 它已在 Red Hat Enterprise Linux (RHEL) 6.6 (kernel-2.6.32-504) 中修复。

    如果系统运行版本低于 2.6.37 的自定义内核或版本低于 2.6.32-504 的基于 RHEL 的内核,则这些系统必须在 grub.conf 中的内核命令行上设置启动参数 numa=off。 有关详细信息,请参阅 Red Hat KB 436883

  • 不要在 OS 磁盘上配置交换分区。 可以将 Linux 代理配置为在临时资源磁盘上创建交换文件,如本文稍后所述。

  • 对 Azure 上的所有 VHD,其虚拟大小必须保持为 1 MB(1024 x 1024 字节)。 从原始磁盘转换为 VHD 时,请确保在转换前的原始磁盘大小是 1 MB 的倍数,如本文稍后所述。

  • 使用最新的分发版本、包和软件。

  • 删除用户和系统帐户、公钥、敏感数据、不必要的软件和应用程序。

注意

Cloud-init 版本 21.2 或更高版本会删除 UDF 要求。 但如果未启用 udf 模块,CD-ROM 将不会在预配期间装载,这会阻止应用自定义数据。 解决方法是应用用户数据。 但是,与自定义数据不同,用户数据不会加密。 有关详细信息,请参阅 cloud-init 文档中的用户数据格式

在没有 Hyper-V 的情况下安装内核模块

Azure 在 Hyper-V 虚拟机监控程序上运行,因此 Linux 需要某些内核模块才能在 Azure 中运行。 如果具有在 Hyper-V 外部创建的虚拟机,Linux 安装程序可能无法在初始 RAM(initrd 或 initramfs)中包含 Hyper-V 的驱动程序,除非 VM 检测到它正在 Hyper-V 环境中运行。

使用不同虚拟化系统(如 VirtualBox 或 KVM)来准备 Linux 映像时,可能需要重新生成 initrd,以确保至少 hv_vmbushv_storvsc 内核模块可在初始 RAM 磁盘上使用。 在基于上游 Red Hat 分发版的系统上(可能还包括其他系统),这是一个已知问题。

重新生成 initrd 或 initramfs 映像的机制可能会因分发版而有所不同。 查阅分发的文档或相应过程的支持。 下面是使用 mkinitrd 实用工具重新生成 initrd 的示例:

  1. 备份现有 initrd 映像:

    cd /boot
    sudo cp initrd-`uname -r`.img  initrd-`uname -r`.img.bak
    
  2. 使用 hv_vmbushv_storvsc 内核模块重新生成 initrd:

    sudo mkinitrd --preload=hv_storvsc --preload=hv_vmbus -v -f initrd-`uname -r`.img `uname -r`
    

调整 VHD 大小

Azure 上的 VHD 映像必须已将虚拟大小调整为 1MB。 通常,通过 Hyper-V 创建的 VHD 会正确对齐。 如果 VHD 未正确对齐,则尝试从 VHD 创建映像时,可能会收到类似于以下示例的错误消息:

The VHD http://<mystorageaccount>.blob.core.chinacloudapi.cn/vhds/MyLinuxVM.vhd has an unsupported virtual size of 21475270656 bytes. The size must be a whole number (in MBs).

在这种情况下,可使用 Hyper-V 管理器控制台或 Resize-VHD PowerShell cmdlet 调整 VM 大小。 如果不是在 Windows 环境中运行,我们建议使用 qemu-img 转换(如果需要)并调整 VHD 大小。

注意

QEMU 2.2.1 版和一些更高版本的 qemu-img 中有一个已知 bug,导致 VHD 格式不正确。 此问题已在 QEMU 2.6 中修复。 建议使用版本 2.2.0 或更低版本,或使用 2.6 或更高版本。

  1. 使用 qemu-imgvbox-manage 等工具直接调整 VHD 大小可能会导致 VHD 无法启动。 建议先使用以下代码将 VHD 转换为原始磁盘映像。

    如果 VM 映像是作为原始磁盘映像创建的,则可以跳过此步骤。 在一些虚拟机监控程序(如 KVM)中,默认情况下会将 VM 映像创建为原始磁盘映像。

    sudo qemu-img convert -f vpc -O raw MyLinuxVM.vhd MyLinuxVM.raw
    
  2. 计算磁盘映像所需的大小,使虚拟大小调整为 1MB。 以下 Bash shell 脚本使用 qemu-img info 来确定磁盘映像的虚拟大小,然后将大小计算到下个 1 MB:

    rawdisk="MyLinuxVM.raw"
    vhddisk="MyLinuxVM.vhd"
    
    MB=$((1024*1024))
    size=$(qemu-img info -f raw --output json "$rawdisk" | \
    gawk 'match($0, /"virtual-size": ([0-9]+),/, val) {print val[1]}')
    
    rounded_size=$(((($size+$MB-1)/$MB)*$MB))
    
    echo "Rounded Size = $rounded_size"
    
  3. 使用 $rounded_size 调整原始磁盘的大小:

    sudo qemu-img resize MyLinuxVM.raw $rounded_size
    
  4. 将原始磁盘转换回固定大小的 VHD:

    sudo qemu-img convert -f raw -o subformat=fixed,force_size -O vpc MyLinuxVM.raw MyLinuxVM.vhd
    

    或者,对于 2.6 之前的 QEMU 版本,删除 force_size 选项:

    sudo qemu-img convert -f raw -o subformat=fixed -O vpc MyLinuxVM.raw MyLinuxVM.vhd
    

Linux 内核要求

Hyper-V 和 Azure 的 Linux 集成服务 (LIS) 驱动程序会直接影响上游 Linux 内核。 包括最新 Linux 内核版本(例如 3.x)在内的许多分发版已提供这些驱动程序,或以其他方式为其内核提供了这些驱动程序的向后移植版本。

LIS 驱动程序在上游内核中不断更新,并具有新的修补程序和功能。 如果可能,我们建议运行认可的分发版,其中包括这些修补程序和更新。

如果你正在运行 RHEL 版本 6.0 到 6.3 的一个变体,则需要安装适用于 Hyper-V 的最新 LIS 驱动程序。 从 RHEL 6.4+(和派生产品)开始,LIS 驱动程序已包含在内核中,因此,无需其他安装包。

如果需要自定义内核,我们建议使用最新的内核版本(例如 3.8+)。 对于维护自己内核的分发版或供应商,需要定期将 LIS 驱动程序从上游内核向后移植到自定义内核。

即使已运行相对较新的内核版本,我们也强烈建议跟踪 LIS 驱动程序中的任何上游修复,并根据需要向后移植这些修复。 LIS 驱动程序源文件的位置在 Linux 内核源树中的 MAINTAINERS 文件中指定:

    F:    arch/x86/include/asm/mshyperv.h
    F:    arch/x86/include/uapi/asm/hyperv.h
    F:    arch/x86/kernel/cpu/mshyperv.c
    F:    drivers/hid/hid-hyperv.c
    F:    drivers/hv/
    F:    drivers/input/serio/hyperv-keyboard.c
    F:    drivers/net/hyperv/
    F:    drivers/scsi/storvsc_drv.c
    F:    drivers/video/fbdev/hyperv_fb.c
    F:    include/linux/hyperv.h
    F:    tools/hv/

VM 的活动内核必须包含以下修补程序。 此列表并不完整,并未包括所有分发版。

Azure Linux 代理

Azure Linux 代理 (waagent) 在 Azure 中预配 Linux 虚拟机。 可以在 Linux 代理 GitHub 存储库中获取最新版本、报告问题或提交拉取请求。

下面是使用 Azure Linux 代理的一些注意事项:

  • 根据 Apache 2.0 许可证发布 Linux 代理。 许多分发版已为代理提供 .rpm 或 .deb 包。 可以轻松安装和更新这些包。
  • Azure Linux 代理需要 Python v2.6 以上版本。
  • 代理还需要 python-pyasn1 模块。 大多数分发版提供此模块作为可安装的单独包。
  • 在某些情况下,Azure Linux 代理可能与 NetworkManager 不兼容。 分发版提供的许多包(.rpm 或 .deb)都将 NetworkManager 配置为与 waagent 包冲突。 在这些情况下,安装 Linux 代理包时,代理将卸载 NetworkManager。
  • Azure Linux 代理必须至少是支持的最低版本

注意

确保已启用 udfvfat 模块。 禁用 udf 模块将导致预配失败。 禁用 vfat 模块会导致预配和启动失败。 如果这两个条件都存在,Cloud-init 版本 21.2 或更高版本可以预配 VM,而无需 UDF:

  • 使用 SSH 公钥而不是密码创建了 VM。
  • 未提供任何自定义数据。

常规 Linux 系统要求

  1. 修改 GRUB 或 GRUB2 中的内核引导行并包含以下参数,以便将所有控制台消息发送到第一个串行端口。 这些消息可以协助 Azure 支持人员调试任何问题。

    GRUB_CMDLINE_LINUX="rootdelay=300 console=ttyS0 earlyprintk=ttyS0 net.ifnames=0"
    

    我们还建议删除以下参数(如果存在):

    rhgb quiet crashkernel=auto
    

    图形界面式引导和安静引导在云环境中不适用,因为云环境中需要将所有日志都发送到串行端口。 如果需要,可以保留对 crashkernel 选项的配置,但此参数会将虚拟机中的可用内存量至少减少 128 MB。 对于较小的 VM 大小,降低可用内存可能会有问题。

  2. 完成编辑 /etc/default/grub 后,运行以下命令以重新生成 GRUB 配置:

    sudo grub2-mkconfig -o /boot/grub2/grub.cfg
    
  3. 使用 dracut 为 initramfs 添加 Hyper-V 模块:

    cd /boot
    sudo cp initramfs-<kernel-version>.img <kernel-version>.img.bak 
    sudo dracut -f -v initramfs-<kernel-version>.img <kernel-version> --add-drivers "hv_vmbus hv_netvsc hv_storvsc"
    sudo grub-mkconfig -o /boot/grub/grub.cfg 
    sudo grub2-mkconfig -o /boot/grub2/grub.cfg 
    

    使用 mkinitramfs 为 initrd 添加 Hyper-V 模块:

    cd /boot
    sudo cp initrd.img-<kernel-version>  initrd.img-<kernel-version>.bak
    sudo mkinitramfs -o initrd.img-<kernel-version> <kernel-version>  --with=hv_vmbus,hv_netvsc,hv_storvsc
    sudo update-grub 
    
  4. 请确保已安装 SSH 服务器且已将其配置为在引导时启动。 此配置通常是默认值。

  5. 安装 Azure Linux 代理。

    Azure Linux 代理是在 Azure 上设置 Linux 映像所必需的。 许多分发版以 .rpm 或 .deb 包的形式提供代理。 包通常称为 WALinuxAgentwalinuxagent。 还可以按照《Azure Linux 代理指南》中的步骤手动安装代理。

    注意

    确保已启用 udfvfat 模块。 删除或禁用它们将导致预配或启动失败。 Cloud-init 版本 21.2 或更高版本会删除 UDF 要求。

    运行以下命令之一,安装 Azure Linux 代理、cloud-init 和其他必要的实用工具。

    对 Red Hat 或 CentOS 使用此命令:

    sudo yum install -y WALinuxAgent cloud-init cloud-utils-growpart gdisk hyperv-daemons
    

    对 Ubuntu/Debian 使用此命令:

    sudo apt install walinuxagent cloud-init cloud-utils-growpart gdisk hyperv-daemons
    

    对 SUSE 使用此命令:

    sudo zypper install python-azure-agent cloud-init cloud-utils-growpart gdisk hyperv-daemons
    

    然后在所有分发版上启用代理和 cloud-init:

    sudo systemctl enable waagent.service
    sudo systemctl enable cloud-init.service
    
  6. 不要在 OS 磁盘上创建交换空间。

    可以使用 Azure Linux 代理或 cloud-init 通过本地资源磁盘配置交换空间。 在 Azure 上预配后,此资源磁盘将附加到 VM。 本地资源磁盘是临时磁盘,并可能在取消预配 VM 时被清空。 以下程序块演示如何配置此交换。

    如果选择 Azure Linux 代理,请在 /etc/waagent.conf 中修改以下参数:

    ResourceDisk.Format=y
    ResourceDisk.Filesystem=ext4
    ResourceDisk.MountPoint=/mnt/resource
    ResourceDisk.EnableSwap=y
    ResourceDisk.SwapSizeMB=2048    ## NOTE: Set this to your desired size.
    

    如果选择 cloud-init,请将 cloud-init 配置为处理预配:

    sudo sed -i 's/Provisioning.Agent=auto/Provisioning.Agent=cloud-auto/g' /etc/waagent.conf
    sudo sed -i 's/ResourceDisk.Format=y/ResourceDisk.Format=n/g' /etc/waagent.conf
    sudo sed -i 's/ResourceDisk.EnableSwap=y/ResourceDisk.EnableSwap=n/g' /etc/waagent.conf
    

    若要将 cloud-init 配置为格式化和创建交换空间,有两个选项:

    • 每次通过 customdata 创建 VM 时传入 cloud-init 配置。 我们建议使用此方法。
    • 使用映像中的 cloud-init 指令在每次创建 VM 时配置交换空间。

    使用 cloud-init 创建 .cfg 文件以配置交换空间:

    sudo echo 'DefaultEnvironment="CLOUD_CFG=/etc/cloud/cloud.cfg.d/00-azure-swap.cfg"' >> /etc/systemd/system.conf
    sudo cat > /etc/cloud/cloud.cfg.d/00-azure-swap.cfg << EOF
    #cloud-config
    # Generated by Azure cloud image build
    disk_setup:
      ephemeral0:
        table_type: mbr
        layout: [66, [33, 82]]
        overwrite: True
    fs_setup:
      - device: ephemeral0.1
        filesystem: ext4
      - device: ephemeral0.2
        filesystem: swap
    mounts:
      - ["ephemeral0.1", "/mnt/resource"]
      - ["ephemeral0.2", "none", "swap", "sw,nofail,x-systemd.requires=cloud-init.service,x-systemd.device-timeout=2", "0", "0"]
    EOF
    
  7. 配置 cloud-init 来处理预配:

    1. 为 cloud-init 配置 waagent

      sudo sed -i 's/Provisioning.Agent=auto/Provisioning.Agent=cloud-init/g' /etc/waagent.conf
      sudo sed -i 's/ResourceDisk.Format=y/ResourceDisk.Format=n/g' /etc/waagent.conf
      sudo sed -i 's/ResourceDisk.EnableSwap=y/ResourceDisk.EnableSwap=n/g' /etc/waagent.conf
      

      如果要迁移特定虚拟机,并且不想创建通用化映像,请在 /etc/waagent.conf 配置中设置 Provisioning.Agent=disabled

    2. 配置装载:

      sudo echo "Adding mounts and disk_setup to init stage"
      sudo sed -i '/ - mounts/d' /etc/cloud/cloud.cfg
      sudo sed -i '/ - disk_setup/d' /etc/cloud/cloud.cfg
      sudo sed -i '/cloud_init_modules/a\\ - mounts' /etc/cloud/cloud.cfg
      sudo sed -i '/cloud_init_modules/a\\ - disk_setup' /etc/cloud/cloud.cfg
      
      
    3. 配置 Azure 数据源:

      sudo echo "Allow only Azure datasource, disable fetching network setting via IMDS"
      sudo cat > /etc/cloud/cloud.cfg.d/91-azure_datasource.cfg <<EOF
      datasource_list: [ Azure ]
      datasource:
         Azure:
           apply_network_config: False
      EOF
      
    4. 如果配置了一个交换文件,请删除现有交换文件:

      if [[ -f /mnt/resource/swapfile ]]; then
      echo "Removing swapfile" #RHEL uses a swap file by default
      swapoff /mnt/resource/swapfile
      rm /mnt/resource/swapfile -f
      fi
      
    5. 配置 cloud-init 日志记录:

      sudo echo "Add console log file"
      sudo cat >> /etc/cloud/cloud.cfg.d/05_logging.cfg <<EOF
      
      # This tells cloud-init to redirect its stdout and stderr to
      # 'tee -a /var/log/cloud-init-output.log' so the user can see output
      # there without needing to look on the console.
      output: {all: '| tee -a /var/log/cloud-init-output.log'}
      EOF
      
  8. 运行以下命令以取消预配虚拟机。

    注意

    如果要迁移特定的虚拟机,但不希望创建通用化映像,请跳过取消预配步骤。 运行命令 waagent -force -deprovision+user 将使源计算机不可用。 此步骤仅用于创建通用映像。

    sudo rm -f /var/log/waagent.log
    sudo cloud-init clean
    sudo waagent -force -deprovision+user
    sudo rm -f ~/.bash_history
    sudo export HISTSIZE=0
    

    在 VirtualBox 上,运行 waagent -force -deprovision 后,可能会看到一条错误消息显示 [Errno 5] Input/output error。 此错误消息并不重要,可以忽略它。

  9. 关闭虚拟机并将 VHD 上传到 Azure。

后续步骤

使用 Azure CLI 从自定义磁盘创建 Linux VM