网易云平台支持用户方便快速的申请各种资源,创建自己的云主机。但是在之前的部署中云主机启动速度较慢,用户体验不够理想。最近我们对云主机的启动速度进项了一次专项优化,效果比较明显。今天就和大家分享一下其中较为重要的静态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方式获取云主机网卡配置
既然通过DHCP方式获取云主机网卡配置信息耗时过大,我们考虑在启动阶段为云主机注入网卡配置信息来解决这个问题。
网卡的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的载体。为了防止用户私自篡改这些敏感
完成以上步骤之后,在云主机内部已经可以看到这些信息了,但是信息只是在云主机的一个磁盘设备上,并没有写入正确的文件,还是不能生效。在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注入的逻辑,老镜像则沿用原有业务逻辑。
# 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左右
本文来自网易实践者社区,经作者岳文远授权发布。