部署到 vSphere 时,静态 IP 地址要求 vRealize Automation Cloud Assembly 生成 vSphere 自定义规范,这可能会干扰 cloud-init 命令。

问题

  • vRealize Automation Cloud Assembly 蓝图包括 assignment: static,以将静态 IP 地址应用于 vSphere 虚拟机。
  • 该蓝图还包含 cloudConfig 部分,其中包括使用 cloud-init 运行的初始化命令。
  • 要为虚拟机提供静态 IP,vRealize Automation Cloud Assembly 应动态生成要应用的 vSphere 自定义规范。
  • 每次应用自定义规范时,最后一个操作将重新启动虚拟机。
  • 自定义规范不知道 cloud-init 命令正在运行,因此重新启动会中断这些命令。
  • Cloud-init 命令仅在首次引导时运行,并且在中断时不会自动恢复。
  • 生成的虚拟机仅完成部分配置。

解决办法

创建一个包含 cloud-init 定时禁用的计算机模板。然后,根据模板部署计算机,以便在执行 cloud-init 之前进行自定义规范和重新启动操作。

示例过程 — Ubuntu 18.04

以下步骤适用于 Ubuntu 18.04。您可能需要进行调整,并采用此处列出的适用于其他 Linux 版本或产品的观点。

  1. 创建虚拟机,并使用所需的版本更新和软件包使其保持最新。

    请注意,其他 Linux 产品可能未预安装 cloud-init,但 Ubuntu 18.04 已经预安装了 cloud-init。

  2. 重新配置 cloud-init,将数据源设置为 OVF。

    sudo dpkg-reconfigure cloud-init

  3. 编辑以下文件。

    /etc/cloud/cloud.cfg

    1. 通过添加以下行,启用传统客户机操作系统自定义 (GOSC)。

      disable_vmware_customization: true

    2. 请确保已启用网络配置。删除或注释掉禁用设置(如果存在)。
      network:
        # config: disabled

      或者,检查以下目录中的所有配置文件。

      /etc/cloud/cloud.cfg.d/*

      删除所有包含 network: {config: disabled} 设置的文件。

  4. 编辑以下文件。

    /usr/lib/tmpfiles.d/tmp.conf

    • 注释掉该设置,以防止临时目录被清除。

      # D /tmp 1777 root root –

  5. 编辑以下文件。

    /lib/systemd/system/open-vmtools.service

    • [Unit] 部分下添加以下行,将 open-vmtools 配置为在 dbus.service 之后启动。

      After=dbus.service

  6. 创建新的空文件以禁用 cloud-init。

    sudo touch /etc/cloud/cloud-init.disabled

  7. 创建 re_init sh 脚本。在针对自定义规范暂停的 cron 作业延迟后,脚本将重新启用并初始化 cloud-init。
    sudo rm -rf /etc/cloud/cloud-init.disabled
    sudo cloud-init init
    sleep 20
    sudo cloud-init modules --mode config
    sleep 20
    sudo cloud-init modules --mode final
  8. 添加脚本的运行权限。

    sudo chmod +x re_init.sh

  9. 创建一个 cron 作业,该文件将在启动时睡眠 90 秒后运行。键入 crontab -e,然后输入以下内容:

    @reboot ( sleep 90 ; sh /script_path/delay_init.sh )

    如果自定义规范和重新启动需要更长时间才能完成,您可以应用超过 90 秒的时间。

  10. 创建用于清理模板的 cleaner.sh 脚本。将 cloudadmin 替换为您在安装操作系统期间设置的用户。

    示例脚本特定于 Ubuntu。要为其他 Linux 产品创建脚本,请确保包含突出显示的必需部分。

    #!/bin/bash
    
    # Add usernames to add to /etc/sudoers for passwordless sudo users=("ubuntu" "cloudadmin") for user in "${users[@]}" do cat /etc/sudoers | grep ^$user RC=$? if [ $RC ! = 0 ]; then bash -c "echo \"$user ALL=(ALL) NOPASSWD:ALL\" >> /etc/sudoers" fi done
    
    #grab Ubuntu Codename
    codename="$(lsb_release -c | awk {'print $2}')"
    
    
    #Stop services for cleanup
    service rsyslog stop
    
    #clear audit logs
    if [ -f /var/log/audit/audit.log ]; then
    cat /dev/null > /var/log/audit/audit.log
    fi
    if [ -f /var/log/wtmp ]; then
    cat /dev/null > /var/log/wtmp
    fi
    if [ -f /var/log/lastlog ]; then
    cat /dev/null > /var/log/lastlog
    fi
    
    #cleanup persistent udev rules
    if [ -f /etc/udev/rules.d/70-persistent-net.rules ]; then
    rm /etc/udev/rules.d/70-persistent-net.rules
    fi
    
    #cleanup /tmp directories
    rm -rf /tmp/*
    rm -rf /var/tmp/*
    
    #cleanup current ssh keys
    #rm -f /etc/ssh/ssh_host_*
    
    #cat /dev/null > /etc/hostname
    
    #cleanup apt
    apt-get clean
    
    #Clean Machine ID truncate -s 0 /etc/machine-id rm /var/lib/dbus/machine-id ln -s /etc/machine-id /var/lib/dbus/machine-id #Clean Cloud-init cloud-init clean --logs --seed
    
    #cleanup shell history
    history -w
    history -c
    
  11. 添加模板清理脚本的运行权限。

    sudo chmod +x cleaner.sh

  12. 在 Ubuntu 18.04 中,清理脚本需要 root 特权。编辑以下文件。

    /etc/ssh/sshd_config

    1. 请确保您可以切换到 root 用户。

      PermitRootLogin yes

    2. 为 root 用户设置密码。

      sudo passwd root

  13. 运行清理脚本。

    sudo ./script_path/cleaner.sh

  14. (可选)为安全起见,请恢复步骤 12 以防止继续进行 root 登录。
  15. 关闭虚拟机,然后使用 vSphere 将其转换为模板。

模板更新

每次更新模板时,cron 作业都会运行。如果更新所需的时间超过延迟(例如 90 秒),则需要重新添加 /etc/cloud/cloug-init.disabled 文件,并在关闭模板前重新运行清理脚本。否则,在首次引导时不会禁用 cloud-init,自定义规范重新启动将中断 cloud-init 命令。

故障排除

如果您怀疑 vSphere 自定义规范阻止 cloud-init 完成,请暂时禁用自定义规范,并确定 cloud-init 是否可按预期完成。要暂时禁用自定义规范,请使用 customizationGuestOs: false 属性。

    properties:
      image: ubuntu
      cpuCount: 1
      totalMemoryMB: 8192
      customizationGuestOs: false