前面提过ironic的deploy过程,主要关注了vendor passthru中的方式是如何调用的,这次决定从配置开始记录下来,以免忘记又得看代码重查。
不得不说官网的流程图是最详细的资料,之前刚开始学习ironic时,看流程图和各模块之间的关系图时比较迷茫,现在重新回过头去看,一切变得非常清晰:
http://docs.openstack.org/developer/ironic/deploy/user-guide.html
如何配置?
在ironic的官方配置文档中,除了配置ironic自身的服务,glance,network,keystone等,还有一些ironic特有的配置, 比如:
1. 制作image,使用diskimage-builder,运行命令需要连接网络,运行一回的生成结果保存以复用,产生镜像:
disk-image-create ubuntu baremetal dhcp-all-interfaces -o my-image, 得到image:my-image.qcow2,my-image.vmlinuz 和my-image.initrd files
ramdisk-image-create ubuntu deploy-ironic -o my-deploy-ramdisk, 得到deploy image:my-deploy-ramdisk.kernel 和my-deploy-ramdisk.initramfs
btw:diskimage-builder是openstack的一个项目,用来产生openstack中所需的镜像,是TripeO工具库中的工具
这几个文件都是什么呢,可能看名字有些困惑,下面来一一说明,
qcow2: 镜像文件,之前讲镜像格式的时候提过,除了qcow2还有很多中镜像文件,镜像文件我理解成将一系列文件按照某个格式组成单一文件,可以包含文件分区信息,
系统文件,作为一个可以启动的整体时(whole disk),包含引导文件等。(镜像文件就像是用一个文件模拟块设备,但是还包含更多)
vmlinuz: 内核镜像文件,可被引导程序加载(有时候会看到vmlinux,这个是未压缩的内核,不能被引导程序加载)
initrd:boot loader initialized RAM disk, 内核镜像启动之后会挂载initrd,并执行其中的脚本加载各种模块,发现root分区,挂载root分区,执行/sbin/init,该
程序执行/etc目录下的 inittab, init.d/rcS 等初始化脚本,之后会umount掉initrd
如果没有initrd文件,内核镜像启动后会尝试直接发现并挂载root分区,但是initrd可以增加启动灵活性,如果root分区是ext3格式,vmlinuz挂载root分区需要加载ext3
模块,否则无法挂载root分区,此时使用initrd加载ext3模块,之后再挂载root分区即可(在mark link中有详细说明)
deploy image:(先看mark link中pxe启动的讲解,centos引导程序pxelinux.0以及配置文件pxelinux.cfg)
kernel和initramfs分别对应为内核和initrd(至于initrd和initramfs的区别,网上有一些讲解,不深入了解了就),官网文档中形容其作用为:
used initially for preparing the server (creating disk partitions) before the actual OS deploy.
生成镜像后,将镜像上传至glance,将deploy_kernel和deploy_ramdisk加到driver_info中。
pxe setup需要配置tftp server
pxe uefi setup需要将elio bootloader放置与tftp 目录中并指定properties/capabilities='boot_mode:uefi'
如果是ipxe启动,则需要做ipxe相关的配置
如果需要web console,配置 shellinabox
设置boot option:
ironic node-update
如何执行?
1. validate deploy configration
driver_utils.validate_boot_mode_capability(node)
#验证boot mode, 需为bios,uefi或者None,boot mode的设置方式可以为instance_info/capabilities或instance_info/deploy_boot_mode
driver_utils.validate_boot_option_capability(node)
#验证boot option,需为local,netboot或者None
是否为secure boot,如果配置为是,则为boot mode为uefi
若ipxe 为enable的,需指定http_url和http_root, 且boot mode不能为UEFI
验证:if(boot_mode == 'uefi' and node.driver_internal_info.get('is_whole_disk_image') and boot_option != 'local'): 否则报错
意为:如果是 whole disk image,且boot mode是UEFI时,若不是从本地硬盘启动则报错
d_info = _parse_deploy_info(node)
#将instance_info和driver_info放入d_info中, 其中instance_info包括:image_resource,(若不是whole disk image且image resource不是glance image,
instance_info包含kernel和ramdisk), root_gb, deploy_key(内部使用), swap_mb, ephemeral_gb(如果是whole disk image,指定swap_mb和ehpemeral_gb
是没用的), ephemeral_format(默认为ext4), configdrive, preserve_ephemeral (如果是while disk image,则ephemeral_format, configdrive, preserve_ephemeral
均无用)
driver_info包括:‘deploy_kernel', 'deploy_ramdisk'
iscsi_deploy.validate(task)
验证task.ports,是否有port和该node绑定(mac address),
验证root_device, valid的属性包括:'size', 'model', 'wwn', 'serial', 'vendor'
iscsi_deploy.validate_image_properties(task.context, d_info, props)
如果是glance的image,验证image是否存在,且不是whole disk image时,验证kernel和ramdisk是否存在
2.prepare
这里只关注pxe,ipxe的不再提及
pxe_info = _get_image_info(node, task.context)
#得到image_info, 其中包括
1. 得到 href 和 tftp path for deploy kernel and ramdisk, 如果是whole disk image就可以了, 其中tftp path为tftproot/node_uuid/label
2. 不是whole disk image还要得到kernel_id 和 ramdisk_id的href和 tftp路径
pxe_options = _build_pxe_config_options(node, pxe_info,task.context)
根据上面得到的pxe_info进一步得到pxe_options,其内容包括:
pxe_options = {
'deployment_aki_path': deploy_kernel,
'deployment_ari_path': deploy_ramdisk,
'pxe_append_params': CONF.pxe.pxe_append_params,
'tftp_server': CONF.pxe.tftp_server,
'aki_path': kernel,
'ari_path': ramdisk
'deployment_id': node['uuid'],
'deployment_key': deploy_key,
'iscsi_target_iqn': "iqn-%s" % node.uuid,
'ironic_api_url': ironic_api,
'disk': CONF.pxe.disk_devices,
'boot_option': boot_option,
'boot_mode': _get_boot_mode(node),
# NOTE: The below entry is a temporary workaround for bug/1433812
'coreos.configdrive': 0,
'root_device'
#agent, ironic python agent
'ipa-api-url': ironic_api,
'ipa-driver-name': node.driver,
}
pxe_utils.create_pxe_config(task, pxe_options, pxe_config_template)
pxe config 文件路径为 tftp_root/node_uuid/config
根据pxe_config.template或者uefi_pxe_config.template生成config文件(使用jinja2)
如果是uefi启动,给每个ip地址绑定一个pxe启动所需的template文件,这样从任意一个ip(所在网卡)启动即可
如果不是,则给每个mac地址绑定一个PXE启动template文件
_cache_ramdisk_kernel(task.context, node, pxe_info)
将deploy_kernel, deploy_ramdisk, kernel, ramdisk拷贝到conductor所在的tftp_root
如果provision state是ACTIVE状态时, 看看node.driver_internal_info[ 'root_uuid_or_disk_id'] 是否存在, 存在则表示deploy阶段已经结束,切换到
service mode从kernel/ramdisk 启动,否则警告
3. 真正的deploy
iscsi_deploy.cache_instance_image(task.context, task.node)
iscsi_deploy.check_image_size(task)
#在conductor节点生成放置image的目录:/var/lib/ironic/images/node_uuid,
image路径为:/var/lib/ironic/images/node_uuid/disk
将image从glance中放到conductor中的image路径下,得到image的大小(mb), 若image大小
大于之前配置的root_gb, 则报错
_create_token_file(task)
得到token_file的路径: tftp_root/token_node_uuid/, 并写入context中的token(如果token快要过期,得到新的admin token写入)
dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
ipxe的这里不提,比pxe复杂,pxe的话,其dhcp options为:
dhcp_opts.append({'opt_name': 'bootfile-name', 'opt_value': boot_file})
dhcp_opts.append({'opt_name': 'server-ip-address', 'opt_value': CONF.pxe.tftp_server})
dhcp_opts.append({'opt_name': 'tftp-server', 'opt_value': CONF.pxe.tftp_server})
其中boot file 在uefi mode下为elilo.efi, 否则为pxelinux.0
provider = dhcp_factory.DHCPFactory()
provider.update_dhcp(task, dhcp_opts)
其中dhcp_provider默认为neutron, 由setup.cfg可知,对应为neutron = ironic.dhcp.neutron:NeutronDHCPApi
在NeutronDHCPApi的update_dhcp_opts中:
从task port中,对每一个port_id,启用一个neutron client,调用neutron client 的update_port方法(对应rest call 为/v2.0/ports/{port_id},method是PUT),
查看neutron的方法返回值,可知,返回值中有绑定的ip,这样这块网卡(port)就得到了一个ip地址
如果所有的update_port方法都失败,则报错
deploy_utils.try_set_boot_device(task, boot_devices.PXE)
manager_utils.node_power_action(task, states.REBOOT)
将节点的boot order设成PXE启动,然后重启节点, 这时根据PXE从tftp server处拿到pxelinux.0以及配置文件pxelinux.cfg, 引导deploy_vmlinuz和
deploy_initrd进入内存,进行准备操作,完成之后调用ironic的vendor passthru方法pass_bootloader_install_info,收到之后,如果失败就停止deploy并关机,
成功则拿到部署节点的ip地址,连接节点的10000端口,发送done,让节点重新启动。
重启后,向ironic api调用vendor passthru方法:pass_deploy_info:
最重要的为:uuid_dict = iscsi_deploy.continue_deploy(task, **kwargs)
其中kwargs内容包括:
params = {
'address': kwargs.get('address'),
'port': kwargs.get('port', '3260'),
'iqn': kwargs.get('iqn'),
'lun': kwargs.get('lun', '1'),
'image_path': _get_image_file_path(node.uuid),
'node_uuid': node.uuid}
等, 如果返回的kwargs中表明”error“, 则deploy失败,做一些收尾工作。
由于有address, port, iqn,lun等信息,
uuid_dict_returned = deploy_utils.deploy_partition_image(**params) #对于非whole disk image来说调用此处相当于
执行: 使用disk_partitioner进行分区操作,包括swap,root,ephemeral等前面提到的内容, 得到root_uuid_or_disk_id,
并将此值赋予driver_internal_info['root_uuid_or_disk_id'],这就是刚才提到的prepare中的条件
到了最后,看boot_option是loca的,就设置从DISK启动,再次向部署节点发送”done“的信号,部署节点从DISK启动,部署结束
如果boot_option是netboot,执行 deploy_utils.switch_pxe_config(pxe_config_path,root_uuid_or_disk_id, boot_mode, is_whole_disk_image),开启新的引导过程,
部署结束。
到这里,整个PXE的过程就清楚了,:) 网上恐怕就我这篇最详细了吧
mark link
qcow2: http://blog.chinaunix.net/xmlrpc.php?r=blog/article&id=4326024&uid=26299634
http://sinxadmin.iteye.com/blog/866407
pxe 过程详解:http://www.blogjava.net/qileilove/archive/2013/10/14/404942.html
initrd和initramfs区别:http://blog.csdn.net/htttw/article/details/7217706