cloud-init 26.1 进阶实战与运维

面向运维场景的 cloud-init 26.1 进阶实战,覆盖多数据源对比与收窄、四阶段与 systemd 单元、实例缓存与 clean 重跑、single 单模块执行、analyze/collect-logs/query 诊断三件套与 26.1 对 21.1 版本差异速查,全部命令实测通过。

cloud-initcloud-initUbuntuUbuntu 22.04Linux运维2026/10/17

cloud-init 26.1 进阶实战与运维

目标:面向日常运维与排障,讲清 cloud-init 26.1 的进阶操作——数据源清单管理与查询、systemd 单元与四阶段手工运维、实例缓存与 clean 重跑、单模块执行、status/analyze/collect-logs/query 诊断命令集,并给出 26.1 与 21.1 的版本差异速查。全部命令已在 Ubuntu 22.04、cloud-init 26.1-0ubuntu1~22.04.1 环境逐条实测。

一、环境信息

  • 操作系统:Ubuntu 22.04(jammy),x86_64
  • 内核:6.8.0-138-generic
  • cloud-init:26.1-0ubuntu1~22.04.1
  • 数据源:NoCloud(seed 目录 /var/lib/cloud/seed/nocloud/,instance-id 为 iid-cloudinit-demo-001)
  • 关键路径:/run/cloud-init/(运行态状态)、/var/lib/cloud/(实例缓存与脚本目录)、/var/log/cloud-init.log(主日志)
  • 基础概念(四阶段/数据源/user-data 形态)见《cloud-init 26.1 快速入门与 NoCloud 数据源实战》,安装过程见《cloud-init 26.1 Ubuntu 22.04 安装指南》

二、前置条件

  1. cloud-init 已安装且完成过一次初始化:
cloud-init status

实测回显:

status: done
  1. 以下命令均以 root 执行。
  2. 本文多处演示「清理 → 重跑」,会重放 user-data 中的动作(建用户、写文件、装包等),在承载业务的机器上操作前先确认 user-data 内容幂等。

三、多数据源清单管理与查询

3.1 本机数据源清单

cloud-init 启动时按 datasource_list 顺序探测数据源,第一个命中的生效。Ubuntu 打包把清单放在 /etc/cloud/cloud.cfg.d/90_dpkg.cfg,实测 31 项:

cat /etc/cloud/cloud.cfg.d/90_dpkg.cfg

实测回显:

# to update this file, run dpkg-reconfigure cloud-init
datasource_list: [ NoCloud, ConfigDrive, OpenNebula, DigitalOcean, Azure, AltCloud, VMware, OVF, MAAS, GCE, OpenStack, CloudSigma, SmartOS, Bigstep, Scaleway, AliYun, Ec2, CloudStack, Hetzner, IBMCloud, Oracle, Exoscale, RbxCloud, UpCloud, Vultr, LXD, NWCS, Akamai, WSL, CloudCIX, None ]

💡 技术拓展:常见数据源横向对比——

数据源元数据来源探测耗时特征典型场景
NoCloud本地 seed 目录/ISO 或 URL秒级(本地文件)私有环境、镜像构建
ConfigDrivecidata 卷标配置盘秒级(本地挂载)OpenStack、自建虚拟化
Ec2169.254.169.254 HTTP 服务未命中时最长等 120 秒AWS 及兼容云
OpenStack元数据服务 + ConfigDrive网络就绪后较快公有/私有 OpenStack
AliYun / Azure / GCE / Vultr 等各平台元数据服务平台内秒级,平台外超时对应公有云
None无立即生效固定镜像、禁用初始化

关键差异在「探测代价」:本地类(NoCloud/ConfigDrive)零网络开销,网络类在非目标平台上会白等探测超时。生产机务必按实际平台收窄清单(见 3.3 小节),本演示环境因使用 NoCloud 种子,清单顺序无需调整即可命中第一项。

3.2 查询当前命中的数据源

三个层次的查询命令:

cloud-id

实测回显:

nocloud
cloud-init status --long

实测回显:

status: done
extended_status: done
boot_status_code: enabled-by-sysvinit
last_update: Thu, 01 Jan 1970 00:45:18 +0000
detail: DataSourceNoCloud [seed=/var/lib/cloud/seed/nocloud]
errors: []
recoverable_errors: {}
cloud-init query ds

实测回显:

{
 "_doc": "EXPERIMENTAL: The structure and format of content scoped under the 'ds' key may change in subsequent releases of cloud-init.",
 "meta_data": {
  "dsmode": "net",
  "instance-id": "iid-cloudinit-demo-001",
  "local-hostname": "cloudinit-demo"
 }
}

💡 说明:detail 字段给出数据源实现类与 seed 位置;query ds 树里是原始 meta-data 内容。/run/cloud-init/cloud-id-nocloud 这类标记文件也会按命中的数据源生成。

3.3 收窄数据源清单

写一个编号更靠后的 .cfg 覆盖清单(文件名 91-ds-limit.cfg),清理后重跑生效:

printf 'datasource_list: [ NoCloud, ConfigDrive ]' > /etc/cloud/cloud.cfg.d/91-ds-limit.cfg
cloud-init clean --logs
cloud-init init --local
cloud-init init
cloud-init modules --mode=config
cloud-init modules --mode=final

重跑后确认合并视图中的清单已收窄:

python3 -c "import json; print(json.load(open('/run/cloud-init/combined-cloud-config.json')).get('datasource_list'))"

实测回显:

['NoCloud', 'ConfigDrive']

验证完成后删除该文件并再跑一轮,恢复 31 项清单:

rm -f /etc/cloud/cloud.cfg.d/91-ds-limit.cfg
python3 -c "import json; print(len(json.load(open('/run/cloud-init/combined-cloud-config.json')).get('datasource_list')))"

实测回显:

31

四、systemd 单元与四阶段运维

4.1 单元文件与关键依赖

四阶段各对应一个 systemd 单元。查看单元注册与关键指令:

systemctl list-unit-files "cloud-init*"

实测回显:

UNIT FILE                   STATE   VENDOR PRESET
cloud-init-hotplugd.service static  -
cloud-init-local.service    enabled enabled
cloud-init.service          enabled enabled
cloud-init-hotplugd.socket  enabled enabled
cloud-init.target           static  -
5 unit files listed.
grep -E '^(ExecStart|Before|After|RequiresMountsFor|Condition)' /lib/systemd/system/cloud-init-local.service

实测回显:

ExecStart=/usr/bin/cloud-init init --local
Before=network-pre.target
RequiresMountsFor=/var/lib/cloud
ConditionPathExists=!/etc/cloud/cloud-init.disabled
ConditionKernelCommandLine=!cloud-init=disabled
grep -E '^(ExecStart|After|TasksMax)' /lib/systemd/system/cloud-final.service

实测回显:

ExecStart=/usr/bin/cloud-init modules --mode=final
After=multi-user.target
After=network-online.target
After=systemd-user-sessions.service
TasksMax=infinity

💡 说明:init-local 排在 network-pre.target 之前(本地数据源可决定网络怎么配);两个 Condition 行是整机禁用的官方开关(见 4.3 小节)。cloud-init.target 是聚合 target,由四个服务单元的 WantedBy 关系串成依赖链。

4.2 手工执行四阶段

无 systemd 运行的环境(救援模式、chroot、最小化环境)或调试时,手工按序执行,效果与开机自动执行一致(完整四阶段回显见《cloud-init 26.1 快速入门与 NoCloud 数据源实战》5.2 小节):

cloud-init init --local
Cloud-init v. 26.1-0ubuntu1~22.04.1 running 'init-local' at Sat, 17 Oct 2026 00:08:59 +0000. Up 2194.31 seconds.
cloud-init init
Cloud-init v. 26.1-0ubuntu1~22.04.1 running 'init' at Sat, 17 Oct 2026 00:09:00 +0000. Up 2194.85 seconds.
cloud-init modules --mode=config
Cloud-init v. 26.1-0ubuntu1~22.04.1 running 'modules:config' at Sat, 17 Oct 2026 00:09:01 +0000. Up 2195.56 seconds.
cloud-init modules --mode=final
Cloud-init v. 26.1-0ubuntu1~22.04.1 running 'modules:final' at Sat, 17 Oct 2026 00:09:01 +0000. Up 2195.83 seconds.
Cloud-init v. 26.1-0ubuntu1~22.04.1 finished at Sat, 17 Oct 2026 00:09:01 +0000. Datasource DataSourceNoCloud [seed=/var/lib/cloud/seed/nocloud].  Up 2195.90 seconds

4.3 禁用与恢复

两种官方禁用开关,任选其一:

方式一,禁用标记文件(利用 4.1 小节的 ConditionPathExists=!/etc/cloud/cloud-init.disabled 条件):

touch /etc/cloud/cloud-init.disabled

方式二,内核启动参数(利用 ConditionKernelCommandLine=!cloud-init=disabled),编辑引导配置给内核追加 cloud-init=disabled。

恢复时删除标记文件或移除内核参数:

rm -f /etc/cloud/cloud-init.disabled

⚠️ 注意:禁用只影响「后续引导」,已完成初始化的机器上已创建的用户/文件不会回滚。确认单元状态用 systemctl list-unit-files "cloud-init*"(回显见 4.1 小节),每个单元单独一行核对,不要用一条命令批量传多个单元名。

五、实例缓存与重跑机制

5.1 instance-id 缓存原理

cloud-init 以 instance-id 为键缓存实例数据:id 不变时,重跑只执行 per-boot 类动作,cloud-config 主体(建用户/写文件/装包)因「该实例已处理过」被跳过。缓存证据:

cat /run/cloud-init/.instance-id

实测回显:

iid-cloudinit-demo-001
ls /var/lib/cloud/instances/

实测回显:

iid-cloudinit-demo-001

因此「改了 user-data 不生效」是缓存机制的预期行为,两种重跑方式见 5.2 与 5.3 小节。

5.2 方式一:换 instance-id 触发重新初始化

模拟新实例(真实云平台上换 instance-id 由平台自动完成)。修改 meta-data 的 instance-id:

sed -i 's/iid-cloudinit-demo-001/iid-cloudinit-demo-002/' /var/lib/cloud/seed/nocloud/meta-data
cloud-init clean --logs

重跑四阶段(命令见 4.2 小节)后,新实例目录生成且 user-data 重新执行:

ls /var/lib/cloud/instances/

实测回显:

iid-cloudinit-demo-002
cat /var/log/script-userdata.log

实测回显:

script user-data executed on cloudinit-demo at Fri Oct 16 17:59:41 UTC 2026
uid=1000(deploy) gid=1000(deploy) groups=1000(deploy),4(adm),27(sudo)

💡 说明:旧实例目录 iid-cloudinit-demo-001 被清走,/var/lib/cloud/instances/ 只保留当前实例。runcmd/脚本类动作随新实例重放。

5.3 方式二:cloud-init clean 清理重跑

clean 子命令删除运行数据让 cloud-init 回到「未开始」状态。先看参数(实测节选):

cloud-init clean --help

实测回显(节选):

usage: /usr/bin/cloud-init clean [-h] [-l] [--machine-id] [-r] [-s]
                                 [-c {all,ssh_config,network,datasource,fstab} [{all,ssh_config,network,datasource,fstab} ...]]

options:
  -h, --help            show this help message and exit
  -l, --logs            Remove cloud-init logs.
  --machine-id          Set /etc/machine-id to 'uninitialized\n' for golden
                        image creation. On next boot, systemd generates a new
                        machine-id. Remove /etc/machine-id on non-systemd
                        environments.
  -r, --reboot          Reboot system after logs are cleaned so cloud-init re-
                        runs.
  -s, --seed            Remove cloud-init seed directory /var/lib/cloud/seed.

参数解读:-l 连日志一起删;-r 清完立即重启(真实服务器重跑首选);-s 连种子目录一起删(golden image 制作时用,种子是投放来源时千万不要加);-c 定向清理某类生成配置(ssh_config/network/datasource/fstab);--machine-id 把 machine-id 置为未初始化(制作模板镜像时避免多机重复 machine-id)。

实测 clean 过程(保留 seed):

cloud-init clean --logs
echo $?

实测回显:

0
ls /var/lib/cloud/

实测回显:

seed
cloud-init status

实测回显:

status: not started

💡 说明:clean --logs 后 /var/lib/cloud/ 只剩 seed 种子目录,状态回到 not started;重跑四阶段即完成一次全新的初始化(四阶段回显见 4.2 小节)。若加了 -s,连 seed 也删除,ls /var/lib/cloud/ 输出为空。

5.4 status --wait:自动化同步点

自动化脚本里用 --wait 阻塞等待初始化完成(未完成时阻塞,完成后立即返回)。实测:clean 后后台启动四阶段,wait 阻塞直至完成:

cloud-init status --wait

实测回显(等待重跑完成后输出):

status: done
echo $?

实测回显:

0

已完成的机器上执行则立即返回,无副作用。失败场景下返回非 0,可据此中断自动化流程。

六、单模块执行与脚本频率

6.1 single:单独运行某个模块

调试某个模块时不必重跑整个四阶段,single 可按指定频率执行单模块。参数(实测节选):

cloud-init single --help | head -8

实测回显:

usage: /usr/bin/cloud-init single [-h] --name NAME
                                  [--frequency {instance,always,once}]
                                  [--report] [--file FILES]
                                  [argument ...]

positional arguments:
  argument              Any additional arguments to pass to this module.

参数解读:--name 模块名(如 write_files、runcmd);--frequency 三档——instance(默认,每实例一次)、always(每次都跑)、once(仅下一次);--file 提供独立的 user-data 配置文件。

实测:用独立配置单独跑 write_files 模块写一个文件:

cat > /tmp/single-cfg.yaml <<'CFG'
#cloud-config
write_files:
  - path: /tmp/single-test.txt
    content: hello-single
CFG
cloud-init single --name write_files --frequency always --file /tmp/single-cfg.yaml

实测回显:

Cloud-init v. 26.1-0ubuntu1~22.04.1 running 'single' at Sat, 17 Oct 2026 00:19:12 +0000. Up 2806.58 seconds.
cat /tmp/single-test.txt

实测回显:

hello-single

6.2 scripts 四类频率目录

/var/lib/cloud/scripts/ 下按执行频率分四个目录,由 final 阶段的 scripts_* 系列模块执行:

ls /var/lib/cloud/scripts/

实测回显:

per-boot
per-instance
per-once
vendor
目录执行频率执行模块
per-once全生命周期一次scripts_per_once
per-instance每个实例一次(instance-id 变更即重跑)scripts_per_instance
per-boot每次引导都跑scripts_per_boot
vendor发行版/镜像厂商预置脚本scripts_vendor

实测:放一个 per-boot 脚本并触发 final 阶段:

cat > /var/lib/cloud/scripts/per-boot/10-hello <<'EOF'
#!/bin/sh
echo "per-boot ran at $(date)" >> /var/log/per-boot.log
EOF
chmod +x /var/lib/cloud/scripts/per-boot/10-hello
cloud-init modules --mode=final
cat /var/log/per-boot.log

实测回显:

per-boot ran at Fri Oct 16 17:59:14 UTC 2026

⚠️ 注意:脚本必须有可执行位且shebang 正确;per-instance/per-once 的执行记录在 /var/lib/cloud/sem/ 信号目录,手工删除对应 sem 文件可强制重跑。

七、诊断三件套:analyze / collect-logs / query

7.1 analyze:启动耗时分析

analyze 有 4 个子命令(blame/show/dump/boot)。show 按时间线展示每个阶段的每个事件,blame 按耗时倒排:

cloud-init analyze show

实测回显(节选,保留首尾阶段):

-- Boot Record 01 --
The total time elapsed since completing an event is printed after the "@" character.
The time the event takes is printed after the "+" character.

Starting stage: init-local
|`->no cache found (duration: 0.000s) @00.00400s +00.00000s
|`->found local data from DataSourceNoCloud (duration: 0.287s) @00.06900s +00.28700s
Finished stage: (init-local) 00.38100 seconds

Starting stage: init-network
|`->restored from cache with run check: DataSourceNoCloud [seed=/var/lib/cloud/seed/nocloud] (duration: 0.002s) @00.58600s +00.00200s
|`->config-ssh ran successfully and took 0.928 seconds (duration: 0.928s) @00.65900s +00.92800s
Finished stage: (init-network) 01.03900 seconds

Starting stage: modules-final
|`->config-package_update_upgrade_install ran successfully and took 4.102 seconds (duration: 4.102s) @02.15900s +04.10100s
Finished stage: (modules-final) 04.15600 seconds
cloud-init analyze blame | head -8

实测回显(节选):

-- Boot Record 01 --
     04.10100s (modules-final/config-package_update_upgrade_install)
     00.92800s (init-network/config-ssh)
     00.28700s (init-local/search-NoCloud)
     00.04600s (modules-config/config-grub_dpkg)
     00.03600s (modules-config/config-apt_configure)
     00.02800s (modules-final/config-keys_to_console)

💡 说明:blame 排第一的通常是 package_update_upgrade_install(装包耗时)与 config-ssh(主机密钥生成),首启慢先查这两项;search-NoCloud 耗时异常则检查 seed 路径。

7.2 collect-logs:一键打包诊断材料

cloud-init collect-logs --tarfile /tmp/cloud-init-logs.tar.gz

实测回显:

Wrote /tmp/cloud-init-logs.tar.gz
WARNING:
Sensitive data may have been included in the collected logs.
Please review the contents of the tarball before sharing or
rerun with --redact-sensitive to redact sensitive data.
ls -l /tmp/cloud-init-logs.tar.gz

实测回显:

-rw-r--r-- 1 root root 55989 Oct 17 00:19 /tmp/cloud-init-logs.tar.gz
tar -tzf /tmp/cloud-init-logs.tar.gz | head -12

实测回显:

cloud-init-logs-2026-10-17/
cloud-init-logs-2026-10-17/dmesg.txt
cloud-init-logs-2026-10-17/etc/
cloud-init-logs-2026-10-17/etc/cloud/
cloud-init-logs-2026-10-17/etc/cloud/cloud.cfg.d/
cloud-init-logs-2026-10-17/etc/cloud/cloud.cfg.d/README
cloud-init-logs-2026-10-17/etc/cloud/cloud.cfg.d/90_dpkg.cfg
cloud-init-logs-2026-10-17/etc/cloud/cloud.cfg.d/05_logging.cfg
cloud-init-logs-2026-10-17/etc/cloud/cloud.cfg.d/99-disable-network-config.cfg
cloud-init-logs-2026-10-17/etc/cloud/cloud.cfg
cloud-init-logs-2026-10-17/etc/cloud/build.info
cloud-init-logs-2026-10-17/version

⚠️ 注意:默认打包可能包含 user-data 中的密码等敏感信息(本文 chpasswd 的 root 口令即在其中),对外提供日志前务必加 --redact-sensitive 重打:

cloud-init collect-logs --tarfile /tmp/redacted.tar.gz --redact-sensitive

实测回显(脱敏模式无 WARNING):

Wrote /tmp/redacted.tar.gz

7.3 query:查询实例数据树

query 读取 /run/cloud-init/instance-data.json 的标准化数据树。列出全部可用键:

cloud-init query --list-keys

实测回显:

_beta_keys
availability-zone
availability_zone
base64_encoded_keys
cloud-name
cloud_id
cloud_name
combined_cloud_config
distro
distro_release
distro_version
ds
features
instance-id
instance_id
kernel_release
local-hostname
local_hostname
machine
merged_cfg
merged_system_cfg
platform
public_ssh_keys
python_version
region
sensitive_keys
subplatform
sys_info
system_info
system_platform
userdata
v1
variant
vendordata

点号路径取值(v1 是标准化视图,推荐):

cloud-init query v1.instance_id

实测回显:

iid-cloudinit-demo-001
cloud-init query v1.local_hostname

实测回显:

cloudinit-demo

--format 自定义输出(变量用双花括号引用):

cloud-init query --format '{{v1.cloud_name}} / {{v1.instance_id}}'

实测回显:

unknown / iid-cloudinit-demo-001

💡 说明:cloud_name: unknown 是 NoCloud 本地种子不携带云标识的正常取值。--all 输出完整 JSON 树(含 combined_cloud_config、sys_info、features 等),配合 python3 -m json.tool 或 jq 阅读。

八、26.1 与 21.1 版本差异速查

运维多版本混合环境(如 Ubuntu 26.1 与 CentOS 8 自带的 21.1)时,以下差异实测确认,命令脚本要注意兼容:

维度26.1(Ubuntu 22.04 实测)21.1(CentOS 8 实测)
schema 子命令有(cloud-init schema -c <file>)无,报 invalid choice: 'schema'
query 键风格连字符与下划线并存(instance-id/instance_id 均可)仅下划线(instance-id 报 Undefined key)
status --long 字段extended_status/boot_status_code/errors/recoverable_errorsstatus/time/detail 三字段
子命令清单init,modules,single,query,features,analyze,devel,collect-logs,clean,status,schema少 schema,多 dhclient-hook
模块清单命名下划线(write_files/users_groups/set_passwords)连字符旧名(write-files/users-groups/set-passwords)+ migrator/rh_subscription
日志前缀log_util.py[WARNING]:util.py[WARNING]:
devel make-mime有(-a 文件:类型 多部分合成)无 make-mime(21.1 devel 子命令集更小)

跨版本投放 user-data 时以公共键集为准(users/packages/runcmd/write_files 等核心指令两版兼容);校验动作只在 26.1 上做,21.1 靠首启日志观察。

九、常见问题(FAQ)

Q1:cloud-init status --wait 一直阻塞不返回? 说明初始化未完成,通常是网络类数据源在探测超时(Ec2 未命中最长等 120 秒)或某阶段卡住。另开终端看 /var/log/cloud-init.log 尾部定位卡住的模块;确认数据源场景后按 3.3 小节收窄 datasource_list 可消除探测等待。

Q2:cloud-init clean 之后系统会回到什么状态? 回到「未初始化」:/var/lib/cloud/ 实例缓存与 sem 信号清空(保留 seed 时种子仍在),/run/cloud-init/ 运行态清空,加 --logs 连两个日志文件一起删。已落盘的系统变更(用户、文件、包)不会回滚。重跑后即按当前 seed/user-data 重新执行一遍完整首启。

Q3:analyze 输出里有多个 Boot Record 怎么回事? 每次引导产生一个 Boot Record,show/blame 默认全部列出。只看最新一次时按 Record 顺序取最后一段;对照两次首启的 blame 可定位「这次为什么变慢」。

Q4:collect-logs 的 tar 包里都有什么? 实测包含四类:dmesg.txt 与 version(内核与版本信息)、etc/cloud/(全部配置)、run/cloud-init/(运行态 JSON 与实例数据)、var/log/(cloud-init 两个日志)。共 51 个条目(实测 tar -tzf | wc -l),即官方支持渠道要求的标准诊断包。

Q5:怎么判断一台机器的 cloud-init 到底跑没跑过? 三个判据任选:cloud-init status(not started/running/done 三态);ls /var/lib/cloud/instances/ 有实例目录即跑过;cat /run/cloud-init/result.json 有 v1.datasource 即有数据源命中记录。

十、参考链接