镜像要求

Linux

为了使基于 Linux 的镜像在 OpenStack Compute 云中具有完整的功能,有一些要求。对于其中一些要求,您可以安装 cloud-init 包来满足这些要求。在创建自己的镜像之前,请阅读本节,以确保镜像支持您计划使用的 OpenStack 功能。

  • 磁盘分区和启动时调整根分区大小 (cloud-init)

  • 不使用硬编码的 MAC 地址信息

  • SSH 服务器正在运行

  • 禁用防火墙

  • 使用 ssh 公钥访问实例 (cloud-init)

  • 处理用户数据和其他元数据 (cloud-init)

  • Linux 内核中的半虚拟化 Xen 支持(仅限 Linux 内核版本 < 3.0 的 Xen hypervisor)

磁盘分区和启动时调整根分区大小 (cloud-init)

当您创建 Linux 镜像时,必须决定如何分区磁盘。分区方法的选择会影响调整大小的功能,如下面几节所述。

虚拟机镜像中磁盘的大小是在您最初创建镜像时确定的。但是,OpenStack 允许您通过指定不同的 flavor 来启动具有不同大小驱动器的实例。例如,如果您的镜像使用 5 GB 磁盘创建,并且您使用 m1.small flavor 启动实例。结果的虚拟机实例默认情况下具有 20 GB 的主磁盘大小。当实例的磁盘被调整增大时,会在末尾添加零。

您的镜像必须能够在启动时调整其分区大小以匹配用户请求的大小。否则,实例启动后,您必须手动调整分区大小才能访问与 flavor 关联的磁盘大小超过镜像创建时磁盘大小的额外存储空间。

Xen:一个 ext3/ext4 分区(不使用 LVM)

如果您使用 OpenStack XenAPI 驱动程序,Compute 服务会在实例启动时自动调整分区和文件系统。如果满足以下所有条件,则自动调整大小会发生

  • auto_disk_config=True 设置为镜像注册表中的镜像属性。

  • 镜像上的磁盘只有一个分区。

  • 该分区上的文件系统是 ext3 或 ext4。

因此,如果您使用 Xen,我们建议在创建镜像时,创建一个单独的 ext3 或 ext4 分区(不由 LVM 管理)。否则,请继续阅读。

非 Xen 与 cloud-init/cloud-tools:一个 ext3/ext4 分区(不使用 LVM)

您必须为镜像配置以下项目

  • 镜像的分区表描述了镜像的原始大小。

  • 镜像的文件系统填充了镜像的原始大小。

然后,在启动过程中,您必须

  • 修改分区表以使其了解额外的空间

    • 如果您不使用 LVM,则必须修改表以扩展现有根分区以包含此额外空间。

    • 如果您使用 LVM,您可以向分区表添加一个新的 LVM 条目,创建一个新的 LVM 物理卷,将其添加到卷组,并使用根卷扩展逻辑分区。

  • 调整根卷文件系统的大小。

根据您的发行版,支持此功能的 simplest 方法是在您的镜像中安装

  • cloud-init 包,

  • cloud-utils 包,在 Ubuntu 和 Debian 上,它还包含用于扩展分区的 growpart 工具,

  • 如果您使用 Fedora、CentOS 7 或 RHEL 7,则包含 growpart 工具用于扩展分区的 cloud-utils-growpart 包,

  • 如果您使用 Ubuntu 或 Debian,则 cloud-initramfs-growroot 包,它支持在首次启动时调整根分区大小。

安装这些软件包后,镜像在启动时执行根分区调整大小。例如,在 /etc/rc.local 文件中。

如果您无法安装 cloud-initramfs-tools,Robert Plestenjak 在 GitHub 上有一个名为 linux-rootfs-resize 的项目,其中包含使用 growpart 更新 ramdisk 的脚本,以便镜像在启动时正确调整大小。

如果您可以安装 cloud-initcloud-utils 包,我们建议在创建镜像时,创建一个单独的 ext3 或 ext4 分区(不由 LVM 管理)。

非 Xen,没有 cloud-init/cloud-tools:LVM

如果您无法在来宾内部安装 cloud-initcloud-tools,并且您希望支持调整大小,则必须编写一个脚本,镜像在启动时运行该脚本以修改分区表。在这种情况下,我们建议使用 LVM 来管理您的分区。由于 Linux 内核的限制(截至撰写本文),您无法修改当前挂载的分区上的原始磁盘的分区表,但可以对 LVM 执行此操作。

您的脚本必须执行类似以下操作

  1. 检测磁盘上是否有任何额外的空间。例如,解析 parted /dev/sda --script "print free" 的输出。

  2. 使用额外的空间创建一个新的 LVM 分区。例如,parted /dev/sda --script "mkpart lvm ..."

  3. 创建一个新的物理卷。例如,pvcreate /dev/sda6

  4. 使用此物理分区扩展卷组。例如,vgextend vg00 /dev/sda6

  5. 扩展包含根分区的逻辑卷,扩展的空间量。例如,lvextend /dev/mapper/node-root /dev/sda6

  6. 调整根文件系统的大小。例如,resize2fs /dev/mapper/node-root

除非您的镜像是一个需要 /boot 不由 LVM 管理的较旧的 Linux 发行版,否则您不需要 /boot 分区。

不使用硬编码的 MAC 地址信息

您必须删除镜像中的网络持久性规则,因为它们会导致实例中的网络接口作为除 eth0 之外的接口启动。这是因为您的镜像记录了网络接口卡首次安装时的 MAC 地址,并且每次实例启动时该 MAC 地址都不同。您应该更改以下文件

  • /etc/udev/rules.d/70-persistent-net.rules 替换为空文件(包含网络持久性规则,包括 MAC 地址)。

  • /lib/udev/rules.d/75-persistent-net-generator.rules 替换为空文件(这会生成上面的文件)。

  • 删除 Fedora 基于镜像上的 /etc/sysconfig/network-scripts/ifcfg-eth0 中的 HWADDR 行。

注意

如果您删除了网络持久性规则文件,您可能会在启动时收到 udev kernel 警告,这就是为什么我们建议用空文件替换它们的原因。

确保 ssh 服务器运行

您必须将 ssh 服务器安装到镜像中,并确保它在启动时启动,否则您将无法在 OpenStack 内部启动时使用 ssh 连接到您的实例。此软件包通常称为 openssh-server

禁用防火墙

通常,我们建议您禁用镜像中的任何防火墙,并使用 OpenStack 安全组来限制对实例的访问。原因是,在您的实例上安装防火墙可能会使您更难以排除网络问题,如果您无法连接到您的实例。

使用 ssh 公钥访问实例 (cloud-init)

用户访问在 OpenStack 上运行的虚拟机的典型方式是使用 ssh 公钥身份验证。为了使之工作,您的虚拟机镜像必须配置为从 OpenStack 元数据服务或 config drive 下载 ssh 公钥,在启动时。

如果镜像中同时存在 XenAPI 代理和 cloud-initcloud-init 会处理 ssh 密钥注入。系统假定镜像具有 cloud_init_installed 属性时存在 cloud-init

使用 cloud-init 获取公钥

cloud-init 包会自动从元数据服务器获取公钥,并将其放置在一个帐户中。帐户因发行版而异。在基于 Ubuntu 的虚拟机上,该帐户称为 ubuntu,在基于 Fedora 的虚拟机上,该帐户称为 fedora,在基于 CentOS 的虚拟机上,该帐户称为 centos

您可以通过编辑 /etc/cloud/cloud.cfg 文件并添加一个包含不同用户的行来更改 cloud-init 使用的帐户名称。例如,要配置 cloud-init 将密钥放入名为 admin 的帐户中,请在配置文件中使用以下语法

users:
  - name: admin
    (...)

编写自定义脚本以获取公钥

如果您无法或不愿意在来宾内部安装 cloud-init,您可以编写一个自定义脚本来获取公钥并将其添加到用户帐户。

要获取 ssh 公钥并将其添加到 root 帐户,请编辑 /etc/rc.local 文件并在 touch /var/lock/subsys/local 行之前添加以下行。此代码片段取自 rackerjoe oz-image-build CentOS 6 模板

if [ ! -d /root/.ssh ]; then
  mkdir -p /root/.ssh
  chmod 700 /root/.ssh
fi

# Fetch public key using HTTP
ATTEMPTS=30
FAILED=0
while [ ! -f /root/.ssh/authorized_keys ]; do
  curl -f http://169.254.169.254/2025.2/meta-data/public-keys/0/openssh-key > /tmp/metadata-key 2>/dev/null
  if [ $? -eq 0 ]; then
    cat /tmp/metadata-key >> /root/.ssh/authorized_keys
    chmod 0600 /root/.ssh/authorized_keys
    restorecon /root/.ssh/authorized_keys
    rm -f /tmp/metadata-key
    echo "Successfully retrieved public key from instance metadata"
    echo "*****************"
    echo "AUTHORIZED KEYS"
    echo "*****************"
    cat /root/.ssh/authorized_keys
    echo "*****************"
  else
    FAILED=`expr $FAILED + 1`
    if [ $FAILED -ge $ATTEMPTS ]; then
      echo "Failed to retrieve public key from instance metadata after $FAILED attempts, quitting"
      break
    fi
    echo "Could not retrieve public key from instance metadata (attempt #$FAILED/$ATTEMPTS), retrying in 5 seconds..."
    sleep 5
  fi
done

注意

一些 VNC 客户端将 : (冒号) 替换为 ; (分号),将 _ (下划线) 替换为 - (连字符)。如果在 VNC 会话中编辑文件,请确保它是 http: 而不是 http; authorized_keys 而不是 authorized-keys。

处理用户数据和其他元数据 (cloud-init)

除了 ssh 公钥之外,镜像可能需要来自 OpenStack 的其他信息,例如向实例提供用户提交的用户数据。例如,您可能希望在实例启动时设置实例的主机名。或者,您可能希望配置您的镜像,以便在启动时将用户数据内容作为脚本执行。

您可以通过元数据服务访问此信息,或参考 将元数据存储在配置驱动器上。由于 OpenStack 元数据服务与 Amazon EC2 元数据服务的 2009-04-04 版本兼容,请参阅 Amazon EC2 文档中的 使用实例元数据,了解有关如何检索用户数据的详细信息。

支持此类型功能的 simplest 方法是将 cloud-init 包安装到您的镜像中,默认情况下,该镜像将用户数据视为可执行脚本,并设置主机名。

确保镜像将引导日志写入控制台

您必须配置镜像,以便内核将引导日志写入 ttyS0 设备。特别是,必须将 console=tty0 console=ttyS0,115200n8 参数传递给启动时的内核。

如果您的镜像使用 grub2 作为引导加载程序,那么在 grub 配置文件中应该有一行。例如,/boot/grub/grub.cfg,看起来像这样

linux /boot/vmlinuz-3.2.0-49-virtual root=UUID=6d2231e4-0975-4f35-a94f-56738c1a8150 ro console=tty0 console=ttyS0,115200n8

如果 console=tty0 console=ttyS0,115200n8 没有出现,您必须修改您的 grub 配置文件。通常,您不应该直接更新 grub.cfg,因为它是由自动生成的。相反,您应该编辑 /etc/default/grub 文件并修改 GRUB_CMDLINE_LINUX_DEFAULT 变量的值

GRUB_CMDLINE_LINUX_DEFAULT="console=tty0 console=ttyS0,115200n8"

接下来,更新 grub 配置文件。在基于 Debian 的操作系统(如 Ubuntu)上,运行此命令

# update-grub

在基于 Fedora 的系统(如 RHEL 和 CentOS)以及 openSUSE 上,运行此命令

# grub2-mkconfig -o /boot/grub2/grub.cfg

内核中的半虚拟化 Xen 支持(仅 Xen 超visor)

在 Linux 内核版本 3.0 之前,Linux 内核的主线分支不支持半虚拟化的 Xen 虚拟机实例(Xen 所说的 DomU 客户机)。如果您正在运行带有半虚拟化的 Xen 超visor,并且想要为较旧的 Linux 发行版创建一个镜像,该发行版具有低于 3.0 的内核,则必须确保镜像启动的内核已编译了 Xen 支持。

管理镜像缓存

使用 nova.conf 文件中的选项来控制是否以及在多长时间内将未使用的基本镜像存储在 /var/lib/nova/instances/_base/ 中。如果您已配置实例的实时迁移,则所有计算节点共享一个通用的 /var/lib/nova/instances/ 目录。

有关 OpenStack 中 libvirt 镜像的信息,请参阅 Pádraig Brady 的 OpenStack libvirt 镜像的生命周期

镜像缓存管理配置选项

配置选项=默认值

(类型) 描述

preallocate_images=none

(StrOpt) VM 镜像预分配模式

none

不预先进行任何存储分配。

space

在实例启动时完全分配存储。 $instance_dir/ 镜像被 fallocated 以立即确定是否有足够的空间,并可能由于持续的分配避免以及更好的块分配局部性而提高 VM I/O 性能。

remove_unused_base_images=True

(BoolOpt) 是否应该删除未使用的基本镜像?设置为 True 时,删除基本镜像的间隔由以下两个设置确定。如果设置为 False,Compute 将永远不会删除基本镜像。

remove_unused_original_minimum_age_seconds=86400

(IntOpt) 未使用的未调整大小的基本镜像如果比此时间年轻,则不会被删除。默认值为 86400 秒,即 24 小时。

remove_unused_resized_minimum_age_seconds=3600

(IntOpt) 未使用的调整大小的基本镜像如果比此时间年轻,则不会被删除。默认值为 3600 秒,即一小时。

要查看设置如何影响正在运行的实例的删除,请检查存储镜像的目录

# ls -lash /var/lib/nova/instances/_base/

/var/log/compute/compute.log 文件中,查找标识符

2012-02-18 04:24:17 41389 WARNING nova.virt.libvirt.imagecache [-] Unknown base file: /var/lib/nova/instances/_base/06a057b9c7b0b27e3b496f53d1e88810a0d1d5d3_20
2012-02-18 04:24:17 41389 INFO nova.virt.libvirt.imagecache [-] Removable base files: /var/lib/nova/instances/_base/06a057b9c7b0b27e3b496f53d1e88810a0d1d5d3 /var/lib/nova/instances/_base/06a057b9c7b0b27e3b496f53d1e88810a0d1d5d3_20
2012-02-18 04:24:17 41389 INFO nova.virt.libvirt.imagecache [-] Removing base file: /var/lib/nova/instances/_base/06a057b9c7b0b27e3b496f53d1e88810a0d1d5d3

由于 86400 秒(24 小时)是 remove_unused_original_minimum_age_seconds 的默认时间,您可以等待该时间间隔以查看基本镜像被删除,或者在 nova.conf 文件中将该值设置为较短的时间。更改 nova.conf 文件中的设置后,重新启动所有 nova 服务。