more detail pxe deploy in ironic

前面提过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.vmlinuzmy-image.initrd files

ramdisk-image-create ubuntu deploy-ironic -o my-deploy-ramdisk, 得到deploy image:my-deploy-ramdisk.kernelmy-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)

kernelinitramfs分别对应为内核和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 add instance_info/capabilities='{"boot_option": "local"}'即部署完之后,重启从本地启动


如何执行?

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

你可能感兴趣的:(more detail pxe deploy in ironic)