民生银行:基于 Ironic 实现 X86 裸机自动化装机实践与优化

阅读数:4082 2019 年 4 月 27 日

1 X86 裸机管理背景

目前我们测试环境存在大量分散在不同网络区域的各种型号的物理服务器,主要用于给需要物理机的开发人员临时测试使用,使用完毕后通过工单申请回收。这些服务器分布在不同的网络区域,大多数服务器的网络布线和接入均已经就绪。

用户在工单系统中申请一台物理服务器时,审批通过后由变更经理派发任务给实施人员,再由实施人员手动通过带外及 ISO 镜像安装操作系统。手动安装操作系统从 ISO 镜像准备到装机完成大概需要 20 多分钟的时间,交付时间长,并且均是重复的体力工作,实施人员工作繁重。

因此我们从 2018 年下半年开始思考如何优化如上流程,使用自动化工具替代之前的纯手动装机模式。

通过云平台、系统管理和开发部门深入分析,总结 X86 裸机装机的需求如下:

  1. 支持行内标准镜像,操作系统需要支持配置 LVM 并创建 rootvg。
  2. 操作系统支持配置主机名,支持注入 ssh 密钥以及初始化 root 密码。
  3. 操作系统支持多网卡以及 bond 配置。
  4. 服务器支持分配在不同的网段及 VLAN。
  5. 支持 HBA 卡识别过滤。
  6. 支持装机网卡和业务网卡复用。
  7. 能够支持裸机分布在不同的网段和 VLAN,不需要调整服务器原有的网络布线和接入。

目前实现 X86 自动化装机的工具有很多,如 Foreman、cloudboot 等,我们调研发现能够同时满足如上需求并且易于与现有的云管集成的只有 OpenStack Ironic 方案。

于是我们从 2018 年 12 月开始对 OpenStack Ironic 进行完全自主技术预研,之所以没有找厂商方案,是因为厂商的方案大多数需要绑定特定服务器或者 SDN 交换机。

2 OpenStack Ironic 简介

2.1 关于 OpenStack Ironic 项目

Ironic 是 OpenStack 裸机管理组件,负责裸机资源的分配以及生命周期管理,通过该组件可以为 OpenStack 云平台提供裸机服务。Ironic 通常会协同 OpenStack 其他服务一起工作,具体分工如下:

  • Keystone:认证与授权。
  • Glance:为裸机提供镜像服务。
  • Nova:负责裸机调度,提供裸机服务。
  • Ironic:裸机管理,包括裸机信息录入、硬件信息自动发现等。
  • Neutron:裸机网络管理和配置。
  • Swift:保存 Ironic 部署日志以及 ConfigDrive 数据

以上,比较容易混淆的是 Ironic 组件和 Nova 组件的分工,Ironic 是裸机管理服务,可以类同为企业的 IT 资产管理系统,而 Nova 是提供裸机服务的,可以认为是给用户分配物理服务器的组件。底层技术实现上,Ironic 是 Nova 其中一种 Compute Driver 接口实现,和 Libvirt 平行,一个裸机 node 对应 Nova 的一个 Hypervisor 实例。Nova 会通过调用 Libvirt 的 Driver 实现虚拟机创建、开关机、重启等操作,通过调用 PXE 以及 IPMI 实现裸机的装机、开关机、重启等操作。

更多关于 Ironic 项目的介绍可参考官方文档。

2.2 Ironic 部署架构

我们的 Ironic 部署架构如图 2-1:

图 2-1 ironic 部署架构

管理节点部署了 Ironic 相关组件、nova-compute 服务 (ironic driver) 以及 TFTP Server,它有两个逻辑网卡(均配置了 bond),功能如下:

  • 网卡 1(eth0):Trunk 接入,部署 (provision) 网络与业务网络复用网卡,根据 VLAN 号区分不同的网络。每个网络会启动一个 DHCP Server 用于裸机的 PXE 引导。同时也是 clean 网络、rescue 网络复用网卡。
  • 网卡 2(eth1):Access 接入,OpenStack 管理网,用于 OpenStack 的各个组件通信。该网络三层打通了物理服务器的带外网络访问关系,用于调用 IPMI 指令。

裸机节点的网卡数量不等,根据业务需要,一般会配置 4 个网卡,其中一个网卡用于带外,两个网卡用于做 bond,为用户的业务网络,另一张网卡做心跳网络(可选),这些网卡均提前做好了布线和接入。

由于服务器的布线和接入均是现成的,因此我们的 Ironic 没有管理交换机配置,只需要根据 VLAN 选择不同的 Neutron Network 即可,因此使用的网络模型是 flat。

我们在 2019 年 1 月份部署了一个测试环境,其间也遇到了不少的 " 坑 ",如 Web console 打不开、镜像不支持 LVM、SLES 操作系统不支持 bond 配置等问题,我们针对这些问题进行了大量的优化开发工作,本文下一节将详细介绍我们在实践过程中遇到的问题以及解决方案。

3 使用 Ironic 遇到的那些 " 坑 "

3.1 Web 终端 bug 修复

虚拟机可以通过 VNC 实现 Web 终端远程访问,用户可以通过 Web 页面远程访问操作系统。但显然裸机没有 VNC 的概念。

不过裸机的 Web 终端可通过 SOL(IPMI Serial Over LAN)实现。我们知道虚拟机的 VNC 是通过 nova-novncproxy 代理转发到计算节点 libvirt VNC 的。裸机的 console 同理也是通过层层代理转发实现的,首先 conductor 节点通过 socat 把 IPMI SOL 转发到本地的一个端口中,如 15900,然后通过 nova-serialconsoleproxy 负责转发到 conductor 节点绑定的端口 15900,即用户访问裸机的 console 流程如下:

Web console -> nova-serialconsoleproxy -> ironic-conductor -> IPMI SOL

不过社区代码存在一个 bug #1809418,nova-serialproxy 校验 ironic 裸机 console 地址时,由于类型不一致导致校验 100% 失败,结果无法通过 nova get-serial-console 获取 web console 地址。我们发现这个问题并及时修复这个 bug,目前已提交补丁代码到社区并合并到 master 分支及 rocky 分支。新版本的 Nova 不会再出现这个问题了。

3.2 标准化镜像问题

目前社区推荐制作 Ironic 镜像方式是通过 disk-image-builder(DIB) 工具,官方文档参考 Build the ironic image。

然而,通过 DIB 工具制作的镜像不符合行内的镜像模板要求,比如 LVM 配置、安全加固等。为了满足行内镜像规范要求,我们想到的最快捷的方法是直接使用行内的标准虚拟机镜像。

但是,物理服务器和虚拟机的镜像是有区别的,其中最明显的区别就是驱动问题。虚拟机只需要安装上 virtio 驱动即可启动,物理服务器则必须安装兼容的硬件驱动,否则可能由于硬盘不识别导致操作系统启动失败。

目前很多硬件驱动都是通过内核模块的形式动态加载,操作系统启动时通常会先加载一个临时文件系统 initramfs,然后由 initramfs 挂载真正的 root 文件系统,加载内核模块。因此 initramfs 能够识别硬盘是操作系统启动成功的前提。因此当一个操作系统镜像从一台虚拟机或者一个物理机迁移到另一个物理机时,往往需要更新 initramfs 以适配目标物理机的硬件驱动。

我们调研了很多关于生成 initramfs 的方法,但往往适配了某一种机型后,启在另外一种机型又启动失败了,进入 dracut shell 界面发现根本识别不了块设备。目前我们的物理服务器机型和型号都非常多,如果针对每一个机型都制作一个镜像,则镜像会非常多,并且每来一个新的机型都需要重新制作镜像。

我们受 DIB 工具 dracut-ramdisk element 的启发(感谢社区),研究了其生成 initramfs 的方法,并结合我们对 LVM 的需求,实现了能够兼容我们目前使用到的所有机型 (如 Dell GW730XD、ProLiant DL380 Gen9)initramfs 生成方法,部分脚本如下:

复制代码
KERNEL_VERSION=1.2.3 # 内核版本
# 需要在 initramfs 安装的工具
BINARY_DEPS="tail head awk ifconfig cut expr route ping nc wget tftp grep"
DRACUT_DRIVERS="virtio virtio_net virtio_blk"
dracut -f -N \
--install "$BINARY_DEPS" \
--kernel-cmdline "rd.shell rd.driver.pre=ahci" \
--kver "${KERNEL_VERSION}" \
--add-drivers "$DRACUT_DRIVERS" \
--add lvm \
--mdadmconf \
--lvmconf \
-o "dash plymouth" \
my-ramdisk

其中:

  • -N 参数禁用 Host-Only 模式,这样会尽可能多的安装硬件驱动,而不是仅加载当前宿主机的硬件驱动。
  • rd.driver.pre 预先加载 ahci 模块是为了能够识别系统的 SATA 硬盘。
  • –add 添加的 lvm 驱动则是为了支持硬盘的 LVM 卷。

另外我们发现某些机型在开启了 drucut-cmdline 的 resume 选项后会随机启动失败,因此我们在 grub 中取消了 resume 选项。

3.3 网卡选择策略优化

虚拟机的网卡都是虚拟的 tap 设备,而裸机的网卡则不一样,它是真实的网卡并且关联了物理接入信息,因此这就存在一个 Neutron port 与裸机的网卡关联问题,更具体的说,假设创建一个新的裸机实例时,使用 VLAN 100 的 VLAN 类型网卡,分配了一个 Neutron 虚拟 port,而物理服务器往往有 8 个甚至更多的网卡,ironic 如何知道是哪个网卡的接入匹配 port 的配置呢?如果匹配不对,则网络必然不通,因为该网卡可能并没有接入交换机或者配置的 VLAN 不一致,相当于把 IP 地址配在了一个错误的网卡上。

当只分配一个网络时,ironic 会选择其中一个开启了 PXE 的网卡作为候选网卡,但如果存在多个网络时,则可能匹配失败,为什么会出现这种问题呢?我们分析了社区实现的网卡选择算法。代码位于 ironic/drivers/modules/network/common.py 模块的 get_free_port_like_object 方法:

复制代码
def get_free_port_like_object(task, vif_id, physnets):
# ... 省略部分代码
def sort_key(port_like_obj):
is_pg = isinstance(port_like_obj, objects.Portgroup)
if is_pg:
pg_physnets = network.get_physnets_by_portgroup_id(
task, port_like_obj.id)
pg_physnet = pg_physnets.pop()
physnet_matches = pg_physnet in physnets
pxe_enabled = True
else:
physnet_matches = port_like_obj.physical_network in physnets
pxe_enabled = port_like_obj.pxe_enabled
return (physnet_matches, is_pg, pxe_enabled)
sorted_free_plos = sorted(free_port_like_objs, key=sort_key, reverse=True)
return sorted_free_plos[0]

从代码中我们可以总结社区实现的网卡从高到低的优先选择顺序为:

  1. 匹配 physical network 的 ironic port;
  2. 如果是 portgroup,则优先选择 portgroup;
  3. 选择开启了 pxe 的 ironic port;
  4. 根据 Ironic 的 node 的录入顺序选择 ironic port。

注意以上的 physical network 并不是对应 neutron 的 network,而是对应 ovs 配置的 bridge mapping 中的 physical network。而 ironic port 其实对应的就是裸机的物理网卡,portgroup 即 bond 配置,在网卡调度时也被当作网卡的逻辑 port,和物理 port 一同调度。

这里假设我们的 physical network 都是一样的,并且没有配置 bond,所有的网卡都开启了 pxe,则启动裸机实例时必须满足 nova boot 的–nic 参数选择的 neutron network 和 ironic port 的录入顺序一致,否则必然发生 port 映射错位。

我们的场景通常都需要配置多网络,但不能要求用户严格按照裸机网卡的录入顺序选择网络,因此我们对网卡选择策略进行了优化。在裸机 port 录入时,我们添加了 segmentation_id 标签 (extra key),在物理网卡选择时,如果 Neutron 的 port 关联的 network 的 segmentation_id 与 ironic port 的 segmentation_id 一致,则优先选择该物理网卡,实现的部分代码如下:

复制代码
def sort_key(port):
port_seg_id = int(port.extra.get("segmentation_id", ""))
network_seg_id = int(vif_network.get('provider:segmentation_id'))
seg_match = port_seg_id == network_seg_id
# ... 省略其它代码部分
return (seg_match, physnet_matches, is_pg, pxe_enabled)

这样就可以通过物理网卡的 segmentation_id 与 Neutron 的虚拟 port 强制关联起来一一映射,解决了网卡匹配错位的情况。

3.4 引入新的裸机调度 filter

Ironic 的 node 会映射成 Nova 的一个 hypervisor,虚拟机和裸机的调度都是通过 nova-scheduler 完成的。虚拟机调度时会选择其中一个超售后的剩余资源大于请求资源 (flavor) 的计算节点 (hypervisor) 作为虚拟机启动的宿主机,这种策略在调度虚拟机时是完全没有问题的。

然而在调度裸机时会出现一个明显的问题:当用户请求一个低配的裸机实例时,可能会调度到一台高配置的物理机。比如用户申请一台 16C32G 的裸机实例,可能调度到一台 32C128G 的物理机上,这显然不是我们所期望的。

因此我们自主研发实现了一个精确资源匹配的 filter,该 filter 会精确匹配 hypervisor 的资源与用户申请选择的 flavor 资源,过滤掉资源不符合的 hypervisor 节点,核心代码如下:

复制代码
def host_passes(self, host_state, spec_obj):
# ... 省略部分代码
requested_vcpus = spec_obj.vcpus
requested_ram = spec_obj.memory_mb
if requested_vcpus != host_state.vcpus_total:
return False
if requested_ram != host_state.total_usable_ram_mb:
return False
return True

该 filter 会首先判断是否裸机调度,如果是则调度时完全匹配用户请求的资源与物理裸机的资源,如果没有匹配的物理机,则直接调度失败,有助于管理员快速地发现物理资源不足问题。

3.5 多网卡 Bond 支持优化

目前最新版本的 cloud-init 通过 configdrive-2 已经支持操作系统的高级网络配置,如 bond 配置,参考官方文档 network config 部分。但是支持的网络配置工具目前仅包含如下三种:

  • eni: Ubuntu 早期版本使用的网络配置方法,通过修改 etc/network/interfaces 文件配置网络。
  • sysconfig:rhel 系列操作系统配置工具,如 CentOS。通过修改 etc/sysconfig/network-scripts/ifdown-ethX 文件配置网卡。
  • netplan:一种较新的网络配置工具,通过 yaml 文件配置网卡,目前 ubuntu 18.04 使用的该工具。

CentOS、Redhat 等操作系统可以通过原生 cloud-init 配置 bond。然而 SUSE 操作系统由于使用的是 wicked 网络配置工具,目前 cloud-init 尚不支持该类型的 bond 配置。

为了解决这个问题,我们自主研发了 wicked 网络配置驱动,实现了 SLES 系列 wicked 网络工具的高级网络配置功能,从而解决了行内 SLES 操作系统的 bond 配置问题。目前准备把这部分功能代码提交到社区。

4 使用 Ironic 实现自动化装机优势

4.1 自动化装机,缩短交付时间

在使用 Ironic 组件之前,我们的开发测试环境的物理服务器安装操作系统都是全手工完成的,实施工程师先要通过带外上传 ISO 镜像,手动启动物理服务器,通过 BIOS 配置启动方式,然后进行一系列的操作系统初始化配置,如设置语言、创建 LVM、主机名等,最后安装 grub 后重启操作系统,如图 4-1:

图 4-1 手动装机流程及预估时间

如上这些步骤几乎都是必须人工交互的重复体力工作,难以做到并行批量装机,一台物理服务器完成操作系统安装大概需要 20 多分钟的时间,耗费了工程师大量的精力和时间,并且难以实现快速交付。

通过 OpenStack Ironic 组件,我们开发测试环境的 X86 物理服务器操作系统安装从原来的纯手工模式转化为全自动模式,用户只需要选择安装的操作系统,无需人工干预即可自动完成操作系统安装。如下是我们测试部分机型实际装机的统计时间:

生产厂商 型号 装机时间
长城 GW-R720 377s
长城 GW-R730XD 362s
惠普 HP-SL2500 484s
联想 R525 G3 538s

如上主要的时间花在物理服务器启动后的硬件检测时间。由于整个过程都是全自动的,因此可以进行批量装机完成操作系统预安装,相对于手动一台一台安装,大大节省了实施人员的时间。

4.2 标准化操作系统

之前实施工程师手动安装操作系统,不仅需要花费大量的时间,而且很容易配置出错,常见的问题如 LVM 配置不规范、网卡配置错误、镜像源地址错误等,出现配置错误需要实施人员返工,效率不高。

而通过 Ironic 自动化装机,使用的是标准化镜像模板,模板已完成操作系统的所有静态配置,如:

  • LVM 配置,rootvg 大小固定;
  • 镜像源配置;
  • 安全加固配置,如 SSH 访问权限控制;

而操作系统的动态配置则可通过公有云流行的 cloud-init 开源工具完成初始化启动配置,如:

  • 主机名配置;
  • 网卡配置,包括 bond 配置;
  • 密码初始化及 SSH 密钥注入;

操作系统升级、配置更新等操作,直接修改镜像模板即可,大大减少了工作量,提升了效率。

4.3 多网络区域统一管理

目前我们的 X86 物理服务器分布在不同的网络区域,不同网络区域的服务器的 IP 段和 VLAN 接入不一样。

装机工具为了能够管理多网络区域的物理服务器,通常有两种策略:

  1. 部署节点配置多块网卡,手动在交换机上配置与待装机节点相同的网络接入,每个网卡启动 DHCP 服务。
  2. 在物理服务器上添加一块部署网卡用于专门装机时使用,配置与部署节点相同的网络接入。

如上第一种方案非常不灵活,每当有一个新的网络区域加入时,部署节点需要手动再添加一块网卡。而第二种方案需要调整物理服务器原有的网络接入,并且当装机完成后,装机网卡就没用了,白白浪费交换机端口资源。

得益于 OpenStack Neutron 的多租户网络模型,使用 Ironic 方案使用 Neutron 的多租户网络功能,不仅不需要修改 X86 物理服务器的任何网络接入配置,而且部署节点只需要一个 trunk 接入网卡即可适配所有 VLAN 以及 IP 地址段,一个网络区域相当于一个逻辑 VPC(Virtual Private Cloud),如图 4-2。

图 4-2 多网络区域服务器管理

物理服务器部署网络和业务网络可共享复用,当添加一个新网络区域服务器时,只需要创建一个新的 VPC 并指定新的 VLAN 号即可,无需任何的物理交换机配置修改。

4.4 标准 API,便于云管集成

Ironic 是 OpenStack 的标准组件,具有统一的 API 标准,便于云管平台的集成。

5 总结与展望

Ironic 是 OpenStack 裸机管理组件,我们在开源的基础上修复了 Ironic 多个社区 bug,优化了物理服务器的多网卡选择策略以及物理服务器的调度算法,开发了新的驱动实现 SUSE 系列操作系统的高级网络配置。通过 OpenStack Ironic 裸机管理组件实现开发测试环境的 X86 服务器自动装机和管理,替代原来的纯手动模式,从而缩短了物理服务器的装机时间,减少实施人员的工作量,提升装机效率。

然而,这还仅仅只是开始,未来我们还有许多工作需要做,如:

  • 进一步解决物理服务器的性能及硬件故障监控。
  • 作为物理机管理模块,与云管平台集成。

作者信息

  • 付广平:任职民生银行云技术管理中心,负责云计算相关技术研究。毕业于北京邮电大学,从 2013 开始从事 OpenStack 相关工作,参与了 OpenStack Nova、Cinder、Oslo 等项目社区开发,知乎专栏《OpenStack》作者。对 Ceph、Docker 等技术也有一定的了解。

  • 张立新:任职民生银行系统管理中心,目前致力于测试环境管理工作。曾在职进修于中国科学院研究生院计算机技术专业,就业于 HP 从事操作系统和数据库服务支持工作,期间提供建设银行驻厂服务支持工作两年,民生银行服务支持工作一年。

  • 崔增顺:任职民生银行云技术管理中心,目前致力于 IaaS 云平台工作,熟悉服务器 / 操作系统 / 存储等。

  • 马兴超:任职民生银行云技术管理中心,主要负责云管平台的研究、建设和运维工作。曾就职于 IBM 负责小型机维护和灾备相关项目建设。

收藏

评论

微博

发表评论

注册/登录 InfoQ 发表评论