背景

现在无论是公有云还是私有云,虚拟机还是裸金属,在交付过程中或多或少总有很多定制化的需求,比如密码定制,磁盘挂载,agent启动等等。有些场景可以将定制化的需求放在镜像内,但对于一些业务种类,规模较多的场景来说,频繁更新镜像并不是一个好事情,cloud-init 的方式就会比较合理,通过启动时候注入的方式,完成了主机的初始化,且就目前来说,cloud-init已经成为了云主机初始化的一个行业标准,并几乎适用于所有主流的Linux发行版。

关键词

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

以openstack为例,上述的metadata, userdata,可以在用户创建虚拟机的时候基于参数传入,而vendordata则由nova-api-metadata服务启动时指定。

启动顺序

和linux启动顺序类似,cloud-init启动也分为5个阶段,分别是Generator->Local->Network->Config->Final。
其服务基于systemd进行管理,所以每个阶段都可以找到对应的service的systemd的配置文件,并且通过systemd来控制启动顺序。


Generator

  • 启动文件位置: /usr/lib/systemd/system-generators/cloud-init-generator

其本质上就是一个shell脚本,用于做两件事,分别是判断是否启动cloud-init,以及选择可用的数据源,截取了2段脚本内的函数:

  1. generator先通过读取cmdline,判断是否存在cloud-init=disabled的选项,如果存在,则不启动,此外会判断/etc/cloud/cloud-init.disabled文件是否存在,同理,如果存在,则不启动。

    local search result="error" ret=""
    for search in kernel_cmdline etc_file default; do
        if $search; then
            debug 1 "$search found $_RET"
            [ "$_RET" = "$ENABLE" -o "$_RET" = "$DISABLE" ] &&
                result=$_RET && break
        else
            ret=$?
            debug 0 "search $search returned $ret"
        fi
    done

		# enable AND ds=found == enable
    # enable AND ds=notfound == disable
    # disable || <any> == disabled
    if [ "$result" = "$ENABLE" ]; then
        debug 1 "checking for datasource"
        check_for_datasource
        ds=$_RET
        if [ "$ds" = "$NOTFOUND" ]; then
            debug 1 "cloud-init is enabled but no datasource found, disabling"
            result="$DISABLE"
        fi
    fi

    if [ "$result" = "$ENABLE" ]; then
        if [ -e "$link_path" ]; then
                debug 1 "already enabled: no change needed"
        else
            [ -d "${link_path%/*}" ] || mkdir -p "${link_path%/*}" ||
                debug 0 "failed to make dir $link_path"
            if ln -snf "$CLOUD_SYSTEM_TARGET" "$link_path"; then
                debug 1 "enabled via $link_path -> $CLOUD_SYSTEM_TARGET"
            else
                ret=$?
                debug 0 "[$ret] enable failed:" \
                    "ln $CLOUD_SYSTEM_TARGET $link_path"
            fi
        fi
        : > "$RUN_ENABLED_FILE"
    elif [ "$result" = "$DISABLE" ]; then
        if [ -f "$link_path" ]; then
            if rm -f "$link_path"; then
                debug 1 "disabled. removed existing $link_path"
            else
                ret=$?
                debug 0 "[$ret] disable failed, remove $link_path"
            fi
        else
            debug 1 "already disabled: no change needed [no $link_path]"
        fi
        if [ -e "$RUN_ENABLED_FILE" ]; then
            rm -f "$RUN_ENABLED_FILE"
        fi
    else
        debug 0 "unexpected result '$result' 'ds=$ds'"
        ret=3
    fi
    return $ret
}

  1. 基于/usr/libexec/cloud-init/ds-identify的策略, 进行源的datasource的选择, 将可用的datasource写入/run/cloud-init/cloud.cfg 内。

check_for_datasource() {
    local ds_rc=""
    local dsidentify="/usr/libexec/cloud-init/ds-identify"
    if [ ! -x "$dsidentify" ]; then
        debug 1 "no ds-identify in $dsidentify. _RET=$FOUND"
        return 0
    fi
    $dsidentify
    ds_rc=$?
    debug 1 "ds-identify rc=$ds_rc"
    if [ "$ds_rc" = "0" ]; then
        _RET="$FOUND"
        debug 1 "ds-identify _RET=$_RET"
        return 0
    fi
    _RET="$NOTFOUND"
    debug 1 "ds-identify _RET=$_RET"
    return 1
}

Local

  • 启动文件位置: /usr/lib/systemd/system/cloud-init-local.service

Local启动需要在文件系统挂载之后,网络服务启动之前进行:

After=systemd-remount-fs.service
Before=NetworkManager.service
Before=network-pre.target
Before=shutdown.target
RequiresMountsFor=/var/lib/cloud

在Local阶段,cloud-init主要会通过以下三种方式进行网络配置(仅做配置,不拉起网络服务):

  • 通过元数据配置网络
  • 通过cloud-init 配置"dhcp on eth0"
  • 禁止配置网络

Network

  • 启动文件位置:/usr/lib/systemd/system/cloud-init.service

cloud-init的网络服务,在网络服务启动之后启动,该阶段用于网络启动后再次更新数据源,然后渲染userdata和vendor_data,以及执行该阶段下的模块。

Wants=cloud-init-local.service
Wants=sshd-keygen.service
Wants=sshd.service
After=cloud-init-local.service
After=systemd-networkd-wait-online.service
After=network.service
Before=network-online.target
Before=sshd-keygen.service
Before=sshd.service
Before=systemd-user-sessions.service

该阶段和上述2个阶段不同,该阶段下存在很多可执行模块,默认的可执行模块可以在cloud.cfg中找到,即:cloud_init_modules:

cloud_init_modules:
 - migrator
 - source-address
 - pip-source
 - seed_random
 - bootcmd
 - write-files
 - [ growpart, once-per-instance ]
 - [ resizefs, once-per-instance ]
 - disk_setup
 - mounts
 - set_hostname
 - update_hostname
 - update_etc_hosts
 - ca-certs
 - rsyslog
 - users-groups
 - ssh

可以看到,该步骤下存在多个模块,比如修改主机名,启动syslog等等

Config

  • 启动文件位置:/usr/lib/systemd/system/cloud-config.service

该阶段启动在Network阶段之后,该阶段下同样存在很多可执行模块,默认的可执行模块可以在cloud.cfg中找到,即:cloud_config_modules, 主要包含一些系统配置,如ssh, yum源,时区等。

# The modules that run in the 'config' stage
cloud_config_modules:
 - ssh-import-id
 - locale
 - set-passwords
 - spacewalk
 - yum-add-repo
 - ntp
 - timezone
 - disable-ec2-metadata
 - runcmd
# - ntp-conf
# - chrony-conf

Final

  • 启动文件位置:/usr/lib/systemd/system/cloud-final.service

该阶段启动在Config阶段之后,该阶段的主要功能是运行cloud_final_modules中配置的模块,如一些用户脚本,个人服务等。

# The modules that run in the 'final' stage
cloud_final_modules:
 - package-update-upgrade-install
 - puppet
 - chef
 - mcollective
 - salt-minion
 - rightscale_userdata
 - scripts-vendor
 - scripts-per-once
 - scripts-per-boot
 - scripts-per-instance
 - scripts-user
 - ssh-authkey-fingerprints
 - keys-to-console
 - phone-home
 - final-message
 - power-state-change

模块

初始化服务器的任务均以模块化进行,每个模块包含名称、运行频率、配置参数这三大要素。

  • 模块名称: 用于识别该模块。
  • 运行频率: 表示一个模块可以在什么时候执行,主要分为两种: once-per-instance(首次启动时执行),always(每次启动都执行)。
  • 配置参数: 类似一个全局变量,在配置文件头部定义,用于描述如何运行各类模块。

常用命令

调试相关

# 手动初始化local阶段
cloud-init init --local
# 手动初始化network阶段
cloud-init init
# 手动初始化config阶段
cloud-init modules --mode=config
# 手动初始化final阶段
cloud-init modules --mode=final
# 还原所有cloud-init配置并重启
cloud-init clean -r

查询相关

# 查询cloud-id
cloud-id
# 查询cloud-init执行状态
cloud-init status -l
# 查询可用的metadata
cloud-init query -l
# 查询指定的metadata信息
cloud-init query $name
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐