云主机启动加速实践—静态IP注入篇

网易云平台支持用户方便快速的申请各种资源,创建自己的云主机。但是在之前的部署中云主机启动速度较慢,用户体验不够理想。最近我们对云主机的启动速度进项了一次专项优化,效果比较明显。今天就和大家分享一下其中较为重要的静态IP注入方案的设计和实现。

瓶颈分析

所谓知己知彼彼百战不殆。想优化云主机的启动速度,必须先找到有哪些性能瓶颈。 在网易云架构下,启动一个云主机的流程如下: 从统计结果来看,从用户下发创建云主机指令到最终可以通过ssh连接虚拟机大约耗时90秒左右。通过日志分析,从虚拟机启动到guest os引导完成大约耗时80s。主要的性能瓶颈存在于guest os启动阶段。在这里我们使用了bootchart2工具来分析guest os的启动性能。

bootchart2是Linux下的一款性能分析软件,可以分析操作系统启动过程中各个服务的时间、内存、CPU和磁盘IO等性能数据。并通过数据和图表的方式展现出来。

安装bootchart2:

apt-get install bootchart2

使用方法:

在/boot/grub/grub.cfg文件中添加内核启动参数

linux   /boot/vmlinuz-3.18.20-nce-amd64 root=/dev/vda1 ro  console=tty0 console=ttyS0,115200 quiet init=/sbin/bootchartd

再次启动就会在系统的/var/log/目录下生成bootchart.png文件。

从bootchartd的统计结果来看,云主机操作系统启动流程中networking服务消耗了大约60s,是主要的性能瓶颈。networking服务通过/etc/network/interfaces文件配置云主机网卡,而默认的interfaces文件指定通过DHCP方式获取网卡配置。

由此可以确认,云主机启动速度较慢的主要性能瓶颈在于使用了DHCP方式获取云主机网卡配置

静态IP注入

既然通过DHCP方式获取云主机网卡配置信息耗时过大,我们考虑在启动阶段为云主机注入网卡配置信息来解决这个问题。

需要注入的信息

  1. 云主机网卡IP
  2. 云主机网卡路由
  3. DNS
  4. 网卡udev规则

    网卡的IP,route和DNS信息是网卡配置的必要信息,比较容易理解。那为什么还需要注入udev规则呢?因为我们注入的ip信息都是以网卡名称为标识的,如果云主机内部的网卡名称是不可控的,就有可能出现以下的情况:

    某云主机配置了两个网卡,我们默认两个网卡的编号分别为eth0和eth1,把相关的配置文件注入到云主机内。但是云主机内部已经存在编号为eth0的网卡规则,在加载这两个网卡的时候就会分配到eth1和eth2两个编号。这时候两个网卡初始化就会出现异常了。
    

    注入信息的获取

    明确了上述需要注入的信息之后,就需要考虑从哪里获取这些信息了。

云主机网卡的IP,路由及DNS信息可以由nova从neutron接口获取,只需要把接口返回数据解析成注入云主机的文件即可。路由信息注入之后还需要使用interface的up/down事件触发。另外为了使网络快速连通,还需要在网卡配置生效之后发送一定数量的arp包。最终注入的interfaces文件格式如下

# The loopback network interface
auto lo
iface lo inet loopback

auto eth0
allow-hotplug eth0
iface eth0 inet static
        address 10.180.81.249
        netmask 255.255.254.0
        broadcast 10.180.81.255
        gateway 10.180.80.1
        mtu 1400

        post-up arping -A -U  -I eth0 -c 120  10.180.81.249 &
        down route del -net 10.180.8.0 netmask 255.255.252.0 gateway 10.180.80.1 dev eth0
        up route add -net 10.180.8.0 netmask 255.255.252.0 gateway 10.180.80.1 dev eth0
        down route del -net 10.180.82.0 netmask 255.255.254.0 gateway 10.180.80.1 dev eth0
        up route add -net 10.180.82.0 netmask 255.255.254.0 gateway 10.180.80.1 dev eth0
        down route del -net 169.254.169.254 netmask 255.255.255.255 gateway 10.180.80.1 dev eth0
        up route add -net 169.254.169.254 netmask 255.255.255.255 gateway 10.180.80.1 dev eth0

最后还要生成云主机的udev规则。前面我们提到,云主机的网卡编号必须外部可控。因此我们需要在外部维护一份云主机网卡port id和网卡编号的匹配规则,并把这份规则保存在nova的system metadata中。

{'ports_pattern':
    {
    'ca7d6814-a67a-4509-bcbb-e9a9dbfc0c80': '1', 
    'fdcf8a8b-3b23-436c-8e24-edacb61bebaa': '0'
    }
}

信息注入的方式

有了需要注入的信息,下面就要考虑信息注入的方式。在这里我们采用了openstack云主机信息注入的标准方式configdrive。

configdrive是openstack提供的一种metadata数据源。如果设置了使用ConfigDrive,在云主机启动的时候会为虚拟机增加一个存储设备,根据参数配置的不同可以是cdrom或者磁盘设备。这个存储设备中会加入云主机的metadata数据,还支持加入用户自定义的数据

因为cdrom设备支持热迁移场景比较复杂,所以我们选择了使用磁盘设备作为configdrive的载体。为了防止用户私自篡改这些敏感

guest os配置文件写入

完成以上步骤之后,在云主机内部已经可以看到这些信息了,但是信息只是在云主机的一个磁盘设备上,并没有写入正确的文件,还是不能生效。在openstack平台上虚拟机内部可以安装cloud-init来实现metadata数据源的自动导入。

cloud-init可以根据配置文件灵活使用各种数据源对guest-os做初始化配置,原生支持openstack的configdrive数据源。

cloud-init服务在rc2阶段启动,而networking服务在rcS阶段启动。因为配置文件必须在networking服务启动之前注入,而guestos内部各种服务依赖关系复杂,调整依赖关系容易出现问题。因此我们直接在networking服务之前注入了一个自有的服务inject_network。通过这个服务完成网卡配置文件的注入,之后networking服务启动时就可以根据这些配置文件完成网络的初始化。

inject_network服务模仿cloud-init的实现方式。具体逻辑如下: 找到configdrive数据源所在的存储设备

blkid -t TYPE="vfat" -odevice

从设备中将注入虚拟机的网卡配置信息拷贝到临时目录下 判断当前是否为云主机的第一次启动,如果是则把配置文件拷贝到指定目录下。并根据udev规则设置网卡的名称

ip link set ${old_name} name ${new_name}

至此,我们就完成了云主机静态IP的注入操作。

特殊场景的考虑

以上只是云主机启动的注入流程。除此以外我们还需要考虑云主机热插拔网卡的场景。 在热插网卡场景下,必须在网卡挂载到云主机之前就把网卡配置和udev规则写入到云主机内部。这里我们用到了qemu-guest-agent。具体流程如下:

兼容性问题

因为镜像中需要有我们的inject_network服务才能生效,所以某些镜像是不支持静态IP注入的。这时候就需要考虑这个方案对老镜像的兼容性。这里我们采取的方式是新镜像在上传到glance时指定一个support_inject_ip的参数,只有包含这个参数的镜像文件才会应用静态IP注入的逻辑,老镜像则沿用原有业务逻辑。

遇到的问题

  1. 在debian系统中默认是不支持对kvm平台虚拟网卡应用udev规则的。
    # ignore interfaces with locally administered or null MAC addresses
    # and VMWare, Hyper-V, KVM, Virtualbox and Xen virtual interfaces
    ENV{MATCHADDR}=="?[2367abef]:*",        ENV{MATCHADDR}=""
    
    如果我们的网卡编号不是连续的,系统会自动将编号空洞填上而不是使用我们注入的udev规则。这里解决的办法是忽略此项配置,让udev规则可以正常应用。
    # ignore interfaces with locally administered or null MAC addresses
    # and VMWare, Hyper-V, KVM, Virtualbox and Xen virtual interfaces
    ENV{MATCHADDR}=="?[2367abef]:*",        GOTO="globally_administered_whitelist"
    

小结

至此静态IP的注入方案已经介绍完毕,来看看我们的优化效果吧。guestos的启动时间已经从80秒下降到了5s左右

 

本文来自网易实践者社区,经作者岳文远授权发布。