云实例初始化工具cloud-init简介

项目简介

cloud-init是一款用于初始化云服务器的工具,它拥有丰富的模块,能够为云服务器提供的能力有:初始化密码、扩容根分区、设置主机名、注入公钥、执行自定义脚本等等,功能十分强大。

目前为止cloud-init是云服务器初始化工具中的事实标准,它几乎适用于所有主流的Linux发行版,也是各大云厂商正在使用的默认工具,社区活跃。基于Python语言使得它能够轻易跨平台、跨架构运行,良好的语法抽象使得它适配新模块、新发行版十分容易。

基本概念

实例数据与数据源

实例数据(Instance data)是cloud-init用来处理并配置实例的数据集合。根据用途,可以将实例数据划分为如下三类:

  • metadata:一系列字典格式的元数据,用作模板渲染、模块运行等等。
  • userdata:启动实例时用户能够指定的数据(单实例数据)
  • vendordata:云基座传入的数据(全局数据)

在openstack中,metadata、userdata均在用户创建实例时通过相应参数传入,而vendordata则由nova-api-metadata服务启动时指定。

这些数据可能来源于许多地方,cloud-init使用数据源一词表示实例数据的来源,目前内置的数据源有:OpenStack、ConfigDrive、Amazon EC2、Azure等等,不同的数据源也表明了不同的实例数据搜索方式。cloud-init在实例内部启动时并不知道从哪里才能够找到实例数据,它会根据预设的一个数据源的列表一个一个查找实例数据,而首个能够找到实例数据的数据源将成为这次启动的数据源。

如果cloud-init指定了OpenStack数据源,那么实例数据将均通过HTTP API请求获取,不同类别的实例数据有着不同的获取方式:

而如果指定了ConfigDrive数据源,那么实例数据将均通过文件获取,cloud-init会搜索标记有CONFIG-2标签的分区,随后将该分区挂载至临时目录中,并读取其中包含实例数据的文件:

  • metadata:file://TMPDIR/openstack/latest/meta_data.json
  • userdata:file://TMPDIR/openstack/latest/user_data
  • vendordata:file://TMPDIR/openstack/latest/vendor_data.json、file://TMPDIR/openstack/latest/network_data.json

所有数据源的参考文档:Datasources — cloud-init 22.1 documentation (cloudinit.readthedocs.io)

启动阶段

在整个系统启动的过程中,cloud-init的执行包括5个阶段,执行阶段从前到后分别为:Generator、Local、Network、Config、Final,每一个阶段都有着它们各自的作用。

Generator

  • systemd服务:/usr/lib/systemd/system-generators/cloud-init-generator
  • 运行于:系统刚启动时

generator是最先运行的阶段,它的功能包括:

  1. 判断是否需要禁止运行cloud-init。generator会根据如下条件判断:
    1. /etc/cloud/cloud-init.disabled文件是否存在。
    2. 内核参数中是否包括有cloud-init=disabled配置项。
  2. 筛选可用的数据源。generator为每个数据源都写了判断函数,运行判断函数后返回可用数据源的列表,存放于datasource_list配置项并写入/run/cloud-init/cloud.cfg中。

部分情况下,generator对/run/cloud-init/cloud.cfg文件的写入会对cloud-init的运行产生影响,此时可以通过修改/etc/cloud/ds-identify.cfg配置文件,从而更改generator的行为。

# /etc/cloud/ds-identify.cfg
# 该策略表示启动cloud-init,但不筛选可用数据源
policy: enabled,found=all,maybe=none,notfound=disabled

Local

  • systemd服务:/usr/lib/systemd/system/cloud-init-local.service
  • 运行于:根目录挂载并可读写后

Local阶段的主要功能为:

  1. 搜索数据源。在datasource_list配置项中搜索第一个可用的数据源,作为本次启动的数据源。数据源搜索细节如下:
    1. 尝试从缓存恢复。若存在obj.pkl缓存,为trust模式或ds.check_instance_id()返回true,则从缓存恢复。
    2. 搜索数据源,遍历datasource_list配置项中的数据源,找出第一个可用的数据源,并返回。
  2. 应用网络配置(不拉起网络)。网络配置的来源如下:
    1. 数据源,使用从数据源获取的网络配置,渲染并写入网络配置至磁盘中。
    2. 回退,如果无数据源,则写入一个默认的dhcp网络配置。
    3. 不应用,如果cloud-init配置文件中有如下配置项:network: {config: disabled},或者存在/var/lib/cloud/data/upgraded-network文件,则不应用网络配置。

Network

  • systemd服务:/usr/lib/systemd/system/cloud-init.service
  • 运行于:网络服务启动之后

Network阶段的主要功能为:

  1. self.datasource.setup(),在uesr-data和vendor-data处理之前调用,用于网络启动后再次更新数据源,目前仅用于azure获取fabric数据并填充进fabric_data。
  2. 存储与渲染userdata和vendor_data。
  3. self.datasource.activate(),该方法在user-data和vendor-data渲染后,init_modules执行前调用。
  4. 运行cloud_init_modules中配置的模块。

Config

  • systemd服务:/usr/lib/systemd/system/cloud-config.service
  • 运行于:Network阶段之后

Config阶段的主要功能是运行cloud_config_modules中配置的模块。

Final

  • systemd服务:/usr/lib/systemd/system/cloud-final.service
  • 运行于:Config阶段之后

Config阶段的主要功能是运行cloud_final_modules中配置的模块。

cloud-config

cloud-config是cloud-init的配置文件,它用于控制cloud-init的行为,比如说该运行哪些功能,以及每个功能如何运行等等。和其他软件的配置文件相比,cloud-config具有高度定制化的特点,除了云服务器本身的cloud-config以外,cloud-init还能够从vendordata、userdata甚至内核参数中获取cloud-config,从而使得用户能够方便地利用cloud-init定制自己的云服务器。

模块

模块(Modules)是cloud-init运行的主体,所有我们需要的用来初始化云服务器的功能都是通过执行模块而实现的。以set_hostname模块为例,该模块的功能是设置主机名,当运行该模块时,它会读取cloud-config中的hostname参数,并将其中值设置为云实例的主机名。

每个模块都有名称、运行频率、配置参数这三大要素,其中运行频率表示一个模块该在什么时候运行,通常有两种运行频率:1. once-per-instance,表示仅在实例首次启动时运行;2. always,表示实例每次启动都运行。

是否运行某个模块、何时运行模块、怎么运行模块,这些都可以在cloud-config中配置,下面截取部分cloud-config,可以看到在这个云实例中:

  • network阶段将运行ssh模块,config阶段将运行mounts、locale等模块,final阶段将运行scripts开头的一些模块;
  • set-passwords模块默认的运行频率是once-per-instance,可将其调整为always,表示每次启动云实例时都会设置密码;
  • ssh_pwauth是set-passwords模块的配置项,ssh_deletekeys、ssh_genkeytypes是ssh模块的配置项,它们描述了该如何运行模块。
# /etc/cloud/cloud.cfg
ssh_pwauth: 1
ssh_deletekeys: 1
ssh_genkeytypes: ['rsa', 'ecdsa', 'ed25519']

cloud_init_modules:
 - ssh

cloud_config_modules:
 - mounts
 - locale
 - [set-passwords,always]

cloud_final_modules:
 - scripts-per-once
 - scripts-per-boot
 - scripts-per-instance
 - scripts-user

所有模块的参考文档:https://cloudinit.readthedocs.io/en/latest/topics/modules.html

发行版

cloud-init设计支持绝大多数的主流linux发行版,诸如Centos、Ubuntu、Arch、Fedora等等,然而不同的linux发行版可能在使用方式上有着或多或少的不同,因此cloud-init需要针对不同的发行版进行一定程度的抽象,形成一个统一层并提供给上层的模块使用。就算是这样,某些模块也只能提供给特定的发行版使用,比如apk_configure模块只能alpine使用,而apt_configure模块则只能在ubuntu、debian上使用,这点在使用cloud-init的过程中需要稍加注意。

基础用法

我们在使用cloud-init时一般不会特别地去执行cloud-init相关的命令,在云实例的启动过程中操作系统会按正常流程去执行cloud-init,不过当需要开发/调试cloud-init的时候,这些命令会带来很大的帮助。

执行cloud-init的四个阶段:

# local阶段
cloud-init init --local
# network阶段
cloud-init init
# config阶段
cloud-init modules --mode=config
# final阶段
cloud-init modules --mode=final

查询相关:

# 查询cloud-id
cloud-id
# 查询cloud-init执行状态
cloud-init status -l
# 查询metadata
cloud-init query <variable>
cloud-init query -l

清理缓存:

cloud-init clean
rm -rf /var/run/cloud-init/
rm -rf /var/lib/cloud/
rm -rf /var/log/cloud-init.log
rm -rf /etc/sysconfig/network-scripts/ifcfg-*

其他命令:

# 创建一个包含cloud-config(config.yaml)和shell脚本(script.sh)的userdata
cloud-init devel make-mime -a config.yaml:cloud-config -a script.sh:x-shellscript > userdata

使用案例

重置密码

cloud-config方式:

##template: jinja
#cloud-config

runcmd:
 # OpenStack数据源在local阶段无法获取数据,network阶段由于是trust模式,直接从缓存读取数据,无法获取更新后metadata,因此需要事先删除缓存。
 - DIR=/var/lib/cloud/scripts/per-boot/ && mkdir -p $DIR && cd $DIR && echo '#!/bin/bash' > 99-clean-cloudinit-cache.sh && echo 'rm -f /var/lib/cloud/instance/obj.pkl' >> 99-clean-cloudinit-cache.sh && chmod +x 99-clean-cloudinit-cache.sh
 # ConfigDrive数据源无法获取更新后metadata,需在首次启动后修改为OpenStack数据源。
 - echo 'datasource_list: [OpenStack]' > /etc/cloud/cloud.cfg.d/90_openstack_datasource.cfg
 # 首次启动后禁止写入网络配置,防止ConfigDrive中的bond配置被覆盖。
 - touch /var/lib/cloud/data/upgraded-network

{% if ds.meta_data.meta.admin_pass is defined %}
chpasswd:
  list: |
    root:{{ds.meta_data.meta.admin_pass}}
  expire: false
  ssh_pwauth: [true]

phone_home:
  url: http://169.254.169.254/openstack/latest/phone_home?unset=admin_pass
  post:
    - instance_id
  tries: 5

cloud_config_modules:
 - [set-passwords,always]
 - [phone_home,always]
 - ssh
 - runcmd
{% endif %}
{% endif %}

获取更新后metadata的方法,除删除缓存之外,也可实现setup方法,在该方法中获取metadata并填充进数据源中(参考azure数据源)。

将set-passwords和phone_home模块设为always的方法,出了在vendor-data中指定外,也可在activate方法中实现,删除执行这两个模块的信号(参考exoscale数据源)。

ssh公钥注入

使用如下userdata注入ssh公钥:

##template: jinja
#cloud-config

ssh_authorized_keys:
  - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDIe6oWYfMB4m9TDSmyVlONj2UakYKkAnpTsZnYXWHUd6zLQDvCLkeXmLiBTuJ51Dh6nolL4rZETj1wLJpXqcMvDhhOZyA0Ji3ENbeJj0URWNjDpRUc3eUApDOzpa3gHm8yRwXDZsZbFMpqcL5vQjlZJ9K+SmLCvbfk6sG04uJWLsIFrjSkxlRxdWwHwdvMH9tAo3UeQGnzRxdnF4zxrob+vAV7PPqJ9u4J6GwXWykTtS+cmvoPOBWQHL5gULxp2FVt/xTF5xY2IRHpzfNmJlCd4G7HGow/PVD+jsIN57iNwvxU7dvTzg0PVUXGQImJvQ8pkHD6//ghXcLleUQz3f+7Brlc6yEZuDdEy2NFEnYW8CoEymPSXVZaBykiggP2XSGp6gQa1gtT7yy44EEB5kjX9epi+jAwiCRaLDrgSLBWQmJp8435dtoOALZOXb1Br0uOA0a3G3KZr+7v11f09NZgFPzJTrY2d4ZNtOUILOcQkUAhtW6yOwuCiuxVeKM8cjE= webssh@hikcloud

posted @ 2022-05-17 16:51  frankming  阅读(6972)  评论(0编辑  收藏  举报