VPS首次开机后,cloud-init长时间停在running、软件包安装失败或SSH密钥没有写入,先不要连续执行cloud-init clean或重跑整套初始化。正确做法是先确定卡在哪个阶段,保留日志和当前配置,再按网络、元数据、用户数据和systemd依赖逐层修复。cloud-init的状态恢复,不等于把所有初始化动作再执行一遍。
先判断是等待、失败还是已经完成
不同发行版和镜像的单元名称可能略有差异,先读取本机实际状态:
cloud-init status --long
cloud-init query --all 2>/dev/null | head -c 4000
systemctl status cloud-init-local cloud-init cloud-config cloud-final --no-pager
systemctl list-jobsstatus --long显示的是总体状态;cloud-init-local、cloud-config和cloud-final分别对应不同阶段。若某个阶段仍在等待,不要只看控制台“初始化中”的字样。list-jobs可以提示systemd是否在等挂载、网络或其他单元。
先把现场保存下来:
sudo journalctl -b -u cloud-init-local -u cloud-init -u cloud-config -u cloud-final --no-pager > /root/cloud-init-journal.txt
sudo cp -a /var/log/cloud-init.log /root/cloud-init.log.$(date +%s)
sudo cp -a /var/log/cloud-init-output.log /root/cloud-init-output.log.$(date +%s)
sudo cloud-init query userdata 2>/dev/null > /root/cloud-init-userdata.txt日志中可能包含用户名、令牌或密码片段,提交给服务商前应先脱敏。不要把完整日志直接贴到公开论坛。
按四个阶段定位停点
| 阶段 | 主要任务 | 常见停点 |
|---|---|---|
| local | 读取本地数据源、网络前的早期准备 | 磁盘、挂载或数据源探测异常 |
| config | 用户、SSH、写文件和基础配置 | YAML格式、权限或模块参数错误 |
| final | 安装软件包、执行脚本和启动服务 | DNS、软件源、外部下载或脚本阻塞 |
| cleanup | 记录完成标记并收尾 | 前置任务未退出,服务重复触发 |
/var/log/cloud-init.log适合看模块级调用,cloud-init-output.log更接近用户脚本的标准输出。找到第一条有因果意义的错误比追最后一行“failed”更重要:后者经常只是前面超时或退出码的结果。
网络和DNS:能连IP不代表初始化能下载
cloud-init的final阶段经常要访问软件源、镜像仓库或元数据服务。先在不改配置的情况下验证:
ip addr
ip route
getent hosts archive.ubuntu.com 2>/dev/null
resolvectl status 2>/dev/null || cat /etc/resolv.conf
curl -I --connect-timeout 5 https://archive.ubuntu.com/如果IP可达而域名解析失败,应沿解析器、resolv.conf、DNS服务和出站53端口排查,可参考VPS能访问IP却解析不了域名的分层排查。不要为了让初始化“先跑完”而永久写死一个未经验证的DNS地址;云厂商的内网软件源和元数据地址也可能依赖特定路由。
用户数据和YAML:先验证格式,再谈模块
用户数据常见问题包括缩进错误、Tab字符、未加引号的特殊值、脚本没有shebang,以及脚本返回非零退出码。可以先检查文件中是否存在明显的Tab或不可见字符,再把复杂脚本拆到独立文件测试。不要在生产实例上直接重复执行包含分区、删除或迁移动作的脚本。
sudo sed -n '1,240p' /var/lib/cloud/instance/user-data.txt
sudo sed -n 'l' /var/lib/cloud/instance/user-data.txt
sudo cloud-init devel schema --config-file /var/lib/cloud/instance/user-data.txt部分旧版本不提供cloud-init devel schema,命令失败不等于配置错误,此时应以日志和发行版文档为准。脚本最好具备幂等性:重复运行不会重复创建用户、覆盖证书或追加相同的配置行。
软件包安装卡住时,先查锁、代理和交互提示
如果日志停在apt、dnf或脚本下载,先确认是否有另一个包管理进程、仓库证书错误、代理环境变量或等待交互输入:
ps -ef | grep -E '[a]pt|[d]pkg|[d]nf|[y]um'
sudo journalctl -b -u apt-daily.service -u apt-daily-upgrade.service --no-pager
env | grep -iE '^(http|https|all|no)_proxy='
sudo grep -RIn 'DPkg::|Acquire::.*Proxy' /etc/apt 2>/dev/null不要直接删除包管理器锁文件,也不要用-y掩盖需要人工确认的变更。若初始化依赖代理,明确区分cloud-init服务环境和交互终端环境;两者不会自动共享所有变量。
systemd依赖未就绪:检查顺序而不是盲目重启
用户数据常会在数据库、数据盘或网络服务未就绪时启动应用。此时先检查实际单元、依赖和本次启动日志,再调整排序、拉起关系或应用重试,可结合systemd服务依赖启动失败的三层排查。cloud-init负责初始化,systemd负责服务编排,不能用一个层面的重试替代另一个层面的健康检查。
什么时候可以重跑,怎样避免重复破坏
- 先做快照或至少保存配置、日志和数据目录清单。
- 确认用户脚本中没有不可逆操作,必要时先注释外部副作用。
- 只针对失败模块做受控验证,不要直接清空整个
/var/lib/cloud。 - 需要重新模拟首次启动时,再评估
cloud-init clean的影响,并准备控制台或带外登录。 - 重跑后记录新的实例状态和日志,确认完成标记、SSH、网络、软件包与服务都通过。
cloud-init clean --logs --seed会影响实例下次启动时的识别和日志,不能当作普通“刷新按钮”。不同镜像对数据源、实例ID和清理行为的支持也不同,执行前要查对应版本文档。
恢复后验收清单
cloud-init status --long为完成状态,且没有新的失败单元;- SSH密钥、主机名、时区、网络路由和DNS符合预期;
- 软件包版本、配置文件权限和服务启停状态已记录;
- 应用能完成一次无破坏性的健康检查;
- 初始化日志已归档并去除敏感字段;
- 将修复后的用户数据和镜像版本纳入下一次恢复演练,可参考VPS备份恢复后的完整性验收。
结论
VPS的cloud-init故障排查顺序应是“确认阶段—保存证据—验证网络和数据源—检查用户数据—核对systemd依赖—受控重跑—完整验收”。先定位实际阻塞点,再决定是否重跑,才能避免重复创建、重复安装或覆盖已有数据。初始化完成的标志不是页面显示成功,而是配置、服务和日志都能被复核。






