Rocky 10虚拟机cloud-init user-data修改后不生效?原因与解决

发布时间:2026/9/8 4:46:32
Rocky 10虚拟机cloud-init user-data修改后不生效?原因与解决 这篇文章从一个很具体的现象开始你在 Rocky 10 虚拟机里修改了user-data想趁着重启让 cloud-init 把初始化配置重新跑一遍。结果重启之后hostname 没变、自定义用户没创建、SSH 公钥没有写入整个系统就像根本没读过你改过的文件一样。如果你也遇到过这个问题大概率不是 YAML 缩进写错也不是权限不够而是 cloud-init 本身的设计机制在起作用它默认只在“实例第一次启动”时执行初始化配置之后修改 user-data 并不会触发重跑。换句话说你改的是配置文件但 cloud-init 认为这台机器不需要再被初始化。这篇文章就围绕这个排障过程展开。先讲 cloud-init 的执行机制再带你从日志、信号量、datasource 三层去定位“为什么没重跑”最后给出让 user-data 重新生效的方法。适合正在用 Rocky 10 / RHEL 系虚拟机并且用 cloud-init 做自动化交付、模板克隆、批量初始化的运维和开发同学。1. 排障要点速览问题现象结论修改 user-data 后重启虚拟机配置不生效cloud-init 默认只在首次启动时执行重启不会重跑如何让 user-data 重新生效清理实例状态后重启sudo cloud-init clean --logs sudo reboot为什么不能只靠“改文件 重启”sem 信号量和 instance-id 共同决定模块是否执行排查日志位置/var/log/cloud-init.log、/var/log/cloud-init-output.log、journalctl最容易踩的坑直接改/var/lib/cloud/seed/下的缓存文件而不是 datasource 源头涉及 VMware 克隆时克隆前必须清理 cloud-init 实例状态否则会继承旧的 instance-id这些结论来自 cloud-init 的通用行为。注意具体版本之间可能有细节差异实际操作时以你机器上的 cloud-init 版本输出为准。2. cloud-init 执行机制为什么 user-data 改完不重跑先明确一个基础认知cloud-init 不是一个“每次开机都从零执行所有配置”的工具。它把启动过程拆成多个阶段每个阶段负责不同的初始化任务而且绝大多数模块只在“首次启动”时运行一次。2.1 四个启动阶段在 Rocky 10 这类 systemd 系统上cloud-init 对应四个服务执行顺序是服务名对应阶段主要职责cloud-init-local.serviceLOCAL读取 datasource识别本地元数据是最早的阶段cloud-init-network.serviceNETWORK网络就绪后的配置阶段处理网络相关模块cloud-config.serviceCONFIG执行 user-data 中大部分配置模块比如用户、SSH key、包安装cloud-final.serviceFINAL最后的收尾阶段执行 runcmd 等模块日志里能看到每个阶段的执行记录。可以用下面的命令快速看journalctl -u cloud-init-local.service --no-pager journalctl -u cloud-config.service --no-pager journalctl -u cloud-final.service --no-pager明白这四个阶段之后排障时就能更快地判断你的 user-data 配置最可能在哪个阶段生效以及对应日志应该去哪看。2.2 sem 信号量与 instance-id 决定“要不要跑”cloud-init 在每次执行模块后会在/var/lib/cloud/instance/sem/目录下写入信号量文件用来标记“这个模块已经跑过了”。下次启动时如果发现对应模块的信号量文件存在就不会再执行。查看信号量目录sudo ls -l /var/lib/cloud/instance/sem/输出类似total 0 -rw-r--r-- 1 root root 0 Jan 1 00:00 config_ssh_keys -rw-r--r-- 1 root root 0 Jan 1 00:00 config_users_groups -rw-r--r-- 1 root root 0 Jan 1 00:00 config_runcmd如果你在/var/lib/cloud/instance/下看到了instance-id文件并且内容和当前 datasource 中的一致cloud-init 就会认为这台实例“已经初始化过了”因此不会重新读取 user-data。所以修改 user-data 后重启无效最直接的原因就是实例状态还在信号量还在instance-id 也没变。2.3 instance-id 不变user-data 改了也没用这一点在 NoCloud datasource 下尤其明显。NoCloud 是最常见的本地/虚拟机数据源它通过meta-data文件中的instance-id来标识实例是否变化。如果instance-id没变即使你重挂了新的 user-datacloud-init 仍可能认为这是同一个实例从而跳过初始化。这里有一个容易忽略的细节很多人以为“改了 user-data 文件内容”就等于“创建了新实例”但 cloud-init 不这么认为。它更关注instance-id和运行状态是否变化。在 Rocky 10 上可以通过 datasource 查询来确认当前实例看到的信息sudo cloud-init query -a | grep -E instance_id|_metadata查询结果会直接告诉你cloud-init 当前认为的 instance-id 是什么。3. 完整排查流程下面给出一套通用的排查流程从外到内逐步定位你的虚拟机为什么没有重跑 user-data。3.1 第一步确认 cloud-init 是否正常启动先确认 cloud-init 本身没有报错。如果服务启动失败后面所有配置都不会生效。查看当前状态sudo cloud-init status如果输出是status: done说明整个初始化流程已经执行完成。如果你期望“重启后重新执行”但状态一直是done那问题就出在“cloud-init 不认为需要重新执行”上。如果输出是error或degraded需要进一步看日志sudo journalctl -u cloud-init* --no-pager --since today sudo tail -n 200 /var/log/cloud-init-output.logcloud-init-output.log会记录模块输出信息很多执行失败的原因都能在这里看到。3.2 第二步确认 user-data 有没有被读取如果 cloud-init 正常但配置没生效要确认它到底有没有读到最新的 user-data。两种常见读取路径datasource 源文件例如 NoCloud 的 seed 目录或 ISO 中的 user-data实例缓存文件例如/var/lib/cloud/instance/user-data先查缓存sudo less /var/lib/cloud/instance/user-data再看 datasource 里实际提供的 user-data。如果你用的是 NoCloud seed 目录路径通常是sudo cat /var/lib/cloud/seed/nocloud-net/user-data对比这两个文件的内容是否一致。如果 seed 源文件已经改了但缓存文件还是旧的说明 cloud-init 没有重新拉取 datasource是典型的“未重新执行”行为。3.3 第三步检查 sem 信号量信号量是最直接的证据。sudo ls -l /var/lib/cloud/instance/sem/如果你发现config_users_groups、config_ssh_keys、config_runcmd这些信号量文件存在且时间戳还是第一次启动的时间那就可以断定这些模块已经执行过了当前实例状态没有被清理所以改 user-data 后不会重跑。这里也可以顺手看看/var/lib/cloud/instance/目录的修改时间sudo stat /var/lib/cloud/instance/如果目录创建时间和虚拟机首次启动时间接近说明 cloud-init 只完成了一次初始化。3.4 第四步检查 datasource 是否被正确识别不同数据源的读取方式不同。Rocky 10 虚拟机里常见的 datasource 有NoCloud通过本地 seed 目录、卷标为CIDATA的 CD-ROM/磁盘分区提供 meta-data 和 user-dataConfigDriveOpenStack 常用VMware guestinfoVMware 虚拟化平台可以把 cloud-init 配置注入到 VM 的 guestinfo 变量中查看当前使用的 datasourcesudo cloud-init query datasource或者读取缓存sudo cat /var/lib/cloud/instance/datasource如果 datasource 显示为 None 或者不是预期的类型说明启动时 cloud-init 没有找到有效数据源。这可能是因为当初创建虚拟机时seed.iso 已被摘除卷标不是CIDATA网络阶段没有拿到 guestinfo 数据datasource 没有识别成功user-data 自然不会被读取。4. 让 user-data 重新生效的四种方法确认了原因之后下面这四种方法可以按场景选用。注意以下操作会改变虚拟机初始化状态在生产环境、有上下级依赖的系统上操作前请先确认影响范围必要时先给虚拟机做快照。4.1 方案一cloud-init clean 后重启最常用cloud-init clean是官方推荐的清理实例状态命令。它会把/var/lib/cloud/下的实例数据清掉让 cloud-init 在下次启动时把机器当成新实例处理。sudo cloud-init clean --logs sudo reboot--logs参数会同时清理日志文件方便你重启后从头观察整个初始化过程。如果你想连机器 ID 也一起重置可以执行sudo cloud-init clean --logs --machine-id重置 machine-id 后相当于给系统生成一个新的机器标识克隆场景下比较有用。4.2 方案二只重跑单个模块有时候你不想重置整台虚拟机只想重新执行某个模块比如重新写 SSH 公钥或重新设置 hostname。这时可以用cloud-init single。# 只重跑 hostname 设置模块 sudo cloud-init single --name cc_set_hostname --force # 只重跑用户和组模块 sudo cloud-init single --name cc_users_groups --force # 只重跑 SSH 公钥模块 sudo cloud-init single --name cc_ssh_authorized_keys --force # 重新执行 runcmd sudo cloud-init single --name cc_runcmd --force--force会忽略信号量状态强制执行。这种方式适合快速验证某个模块行为不会对整个系统做全量重置。4.3 方案三手动删除实例状态如果cloud-init clean因故执行不了也可以手动删除实例缓存目录sudo rm -rf /var/lib/cloud/instance sudo reboot下次启动时cloud-init 检测不到已有实例状态会认为实例是首次启动从而重新读取 user-data。这种方式相对粗暴可靠性不如cloud-init clean因为/var/lib/cloud/下还可能存在其他缓存数据。如果你能正常使用 cloud-init 命令优先用方案一。4.4 方案四更换 datasource 或重建 seed如果你用的是 NoCloud 的 seed ISO并且修改的是 ISO 里的 user-data那么重新挂载 ISO 时要注意NoCloud 靠meta-data里的instance-id判断是否为新实例。最简单的做法是在重建 seed 时改一个instance-id同时确保 ISO 卷标是CIDATA。mkdir -p /tmp/seed cat /tmp/seed/meta-data EOF instance-id: rocky10-vm-20250101 local-hostname: rocky10-node EOF cat /tmp/seed/user-data EOF #cloud-config hostname: rocky10-node users: - name: admin ssh_authorized_keys: - ssh-ed25519 AAAA... your-public-key EOF # 生成 CIDATA 卷标的 ISO替换成你机器上可用的 mkisofs/genisoimage/xorriso 命令 genisoimage -output seed.iso -volid cidata -joliet -rock /tmp/seed/然后先卸载旧的 seed 设备再挂载新的 ISO。具体设备名以你虚拟机的实际磁盘为准不要照抄。5. Rocky 10 VMware 场景特别提醒搜索引擎里大量提到 VMware 虚拟机安装、克隆、网络配置说明相当多用户是在 VMware Workstation / ESXi 里跑 Rocky 10。这个环境下有几个 cloud-init 特别容易踩的坑单独展开说一下。5.1 模板克隆与镜像泛化从模板克隆 Rocky 10 虚拟机时如果模板没有清理 cloud-init 状态克隆出来的虚拟机就会继承模板的instance-id和 sem 信号量。结果就是模板里原来是什么 hostname、什么 SSH key克隆机还是什么user-data 即使随虚拟机自定义生效也可能完全不重跑。正确做法是在模板安装完成后关机前执行sudo cloud-init clean --logs --machine-id sudo rm -f /etc/ssh/ssh_host_* # 可选让系统重新生成 SSH host key sudo poweroff这样模板就处于“还没初始化”的干净状态克隆后首次开机才会重新执行 user-data。5.2 CIDATA 卷标与 ISO 挂载VMware 里用 NoCloud 数据源时很多操作者手动创建 seed.iso但忘记卷标。cloud-init 找 NoCloud 设备时要求分区或文件系统卷标为CIDATA如果卷标不对启动时会跳过这个数据源。检查卷标blkid如果某个设备没有正确显示CIDATA卷标推荐用下面的命令重新生成 ISOxorriso -as mkisofs -V CIDATA -J -R -o seed.iso /tmp/seed/另外在真正测试“user-data 是否被重新读取”时如果你只是修改了虚拟机 CD/DVD 上的原 seed.iso 文件要注意 ISO 是否还挂在虚拟机上。重启前请确认虚拟机 CD/DVD 设备里挂载的是新 ISO原始 ISO 没有被云平台“弹出”5.3 guestinfo 数据源VMware 环境里cloud-init 还支持从 VMware Tools 的 guestinfo 变量读取配置。如果你发现用 ISO 方式不生效可以检查虚拟机是否启用了enable-cloud-init之类的配置以及 cloud-init 是否继承了 guestinfo 变量。这类方式依赖 VMware Tools 状态排障时先用命确认 datasourcesudo cloud-init query datasource如果显示不是NoCloud而是None说明当前虚拟化环境没有通过 guestinfo 或 seed 提供数据。6. 常见误区与排查清单常见误区实际情况正确处理改了/var/lib/cloud/seed/nocloud-net/user-data就算改 datasource这个目录有可能只是 seed 缓存改动后 cloud-init 仍按已有实例状态走改完后执行 clean或清掉 instance 缓存重启虚拟机 重新初始化对 cloud-init 来说只有“首次启动”才会完整执行 user-data执行 cloud-init clean 后再重启改 user-data 里的 hostname 重启就生效hostname 模块被 sem 信号量控制不会重复跑用 cloud-init single 单独重跑对应模块克隆后 user-data 会重新执行克隆继承了模板的 instance-id 和状态不一定重跑模板关机前执行 cloud-init clean --logs --machine-id日志里没有报错说明 user-data 执行了可能是模块被跳过不是没有错误检查 sem 信号量文件和 instance-id用 ISO 提供 seed改了 ISO 文件就有效未重新挂载或 instance-id 未变化NoCloud 会忽略重建 CIDATA 卷标 ISO并确保新 instance-id 被识别排查时可以按下面的顺序走sudo cloud-init status确认整体执行状态。sudo tail -n 200 /var/log/cloud-init-output.log看模块输出。sudo ls /var/lib/cloud/instance/sem/判断模块是否被跳过。sudo cat /var/lib/cloud/instance/instance-id对比实例标识。sudo cloud-init query datasource确认数据源。sudo cat /var/lib/cloud/instance/user-data对比配置缓存。这套顺序基本能覆盖 95% 的 “user-data 不重跑”问题。7. 最佳实践与使用建议经过这次排障可以沉淀下来几条实际建议。第一不要把“修改 user-data 后重启”当成常规运维动作。要让 user-data 重新生效先考虑cloud-init clean --logs再重启。单独测试某个模块时用cloud-init single不要每次都全量清理。第二给虚拟机模板做“出厂状态”很重要。在 Rocky 10 模板关机前执行一次cloud-init clean --logs --machine-id并清理临时文件。这样从模板克隆出的虚拟机首次启动时才会老老实实执行新的 user-data。第三注意 CSV 级别的数据备份。cloud-init clean会清掉实例状态如果 user-data 里包含密码、密钥、用户配置清理后重新初始化可能覆盖这部分内容。生产环境操作前先备份/var/lib/cloud/或确认配置本身可以从集中管理平台重新拉取。第四把敏感信息从 user-data 中剥离。user-data 里尽量避免写明文口令优先用 SSH 公钥、Vault、Consul 这类方式下发机密。虚拟机磁盘快照、模板备份都可能暴露其中的敏感内容。第五启用 cloud-init 完成后的标记。如果你的流程依赖初始化完成可以在 user-data 的runcmd里写一个标记文件#cloud-config runcmd: - touch /etc/cloud-init-completed业务系统启动前检查这个文件是否存在避免在初始化还没完成时就把服务拉起来。8. 总结回到最初的问题Rocky 10 虚拟机里 user-data 改了为什么重启后完全不重跑核心原因是 cloud-init 不是每次开机都重新读取配置sem 信号量和 instance-id 决定了模块是否执行。你需要在修改 user-data 后清理实例状态或者按模块单独强制重跑。最容易踩的坑有两个一个是直接改了缓存目录而不是 datasource 源另一个是 VMware 克隆时没有清理原模板的 cloud-init 实例状态。先把/var/lib/cloud/instance/sem/和instance-id查一遍再决定用cloud-init clean还是cloud-init single整个排障流程会清晰很多。

相关新闻