
在腾讯云上创建实例后,盯着控制台的状态从「创建中」跳到「初始化中」再跳到「运行中」,却迟迟等不来 SSH 登录成功的那一刻——这种经历对运维来说并不陌生。问题往往出在 Cloud-Init 这个必须顺畅跑完的初始化环节上。我们整理了围绕腾讯云 Cloud-Init 初始化失败排查最常见的几类现象,以及它们会给生产环境带来什么样的连锁后果。
本文由 云国际站代理商『云老大 飞弟:@yunlaoda360 / YunLaoDa-服务器服务商•撰写』如需转载请注明!

Cloud-Init 的失败很少以单一的报错界面呈现,多数时候它伪装成“服务器启不动了”“脚本没跑”“密码登录不了”这类表象,让初涉云运维的人绕一大圈才回到日志文件上。它的五个执行阶段(local, network, config, final, modules)只要其中一个卡住,整台云服务器的交付流程就会断裂,而控制台并不会主动报告“Cloud-Init 出错”。
最常见的卡顿位置是在 config 或 final 阶段,幕后推手通常是元数据服务 169.254.169.254 不可达。Cloud-Init 需要通过这个内网地址拉取主机名、用户脚本等信息,安全组误封禁、自定义镜像修改过路由规则,或者 VPC 内 DNS 异常,都会导致 waiting for metadata 超时。有些案例中,即便网络畅通,Python 环境损坏或 / 分区剩余空间不足 200MB 也会中断 Cloud-Init 的后续执行,控制台显示长时间“系统启动中”,用户只能通过串口日志或 VNC 登录进去捞现场。
User-Data 脚本没被执行是最直接的业务冲击——预设的安全基线、磁盘挂载、监控 Agent 安装全部落空,后续部署相当于在裸系统上从头补课。SSH 密钥注入失败同样棘手,如果事先没设置密码,只能先重置密码或通过 VNC 登录,在严格的安全策略下这类操作流程往往被审批卡死。还要警惕一种隐蔽场景:cloud-init status 报告“done”,cloud-init-output.log 里却没有任何 User-Data 输出,控制台已标记“初始化完成”,但业务组件依然缺失。这通常意味着脚本在 early stage 就已报错退出,控制台状态仅反映元数据交互是否完成,无法替代对 cloud-init-output.log 的逐行检查。
在实例内运行 cloud-init status 可以拿到最直接的判断——返回“done”代表所有模块执行完毕,“error”则说明某个阶段失败。遇到 status: error 时,优先翻查 /var/log/cloud-init.log 里的 WARNING 与 ERROR 行,比盲目查看 /var/log/messages 高效得多。如果 User-Data 脚本无效果,先试着手动 curl http://169.254.169.254/latest/user-data,确认脚本内容是否已被注入;超时就排查本地安全组与路由,内容为空则检查控制台输入时是否遗漏了开头的 #!/bin/bash。腾讯云文档推荐的 cloud-init ≥ 20.3 版本在这里同样关键,老旧的 17.x 版本对高版本内核和部分模块支持不足,也会让状态判断本身变得不可靠。

Cloud-Init 是云服务器首次启动时自动运行的初始化框架,负责从元数据服务获取配置并注入系统:设置主机名、配置网络、写入 SSH 密钥、执行 User-Data 脚本。它并非云厂商专属工具,但在国内云环境中,其稳定性高度依赖基础镜像的适配质量。腾讯云官方建议自定义镜像预装 Cloud-Init 20.3 及以上版本——17.x 旧版本在高并发元数据请求时容易出现超时,已成为不少企业上云时「初始化卡死」的隐性根因。
Cloud-Init 按顺序走过五个阶段:local、network、config、modules、final。local 阶段只为系统写入基本标识,不依赖网络;到 network 阶段开始尝试连通元数据服务(169.254.169.254),这是后续所有配置下发的前提。真正让运维踩坑的是 config 与 final 阶段:前者执行模块加载(如磁盘挂载、用户创建),后者运行 User-Data 脚本。一旦某阶段出现异常,cloud-init status 会返回 error,多数情况下问题卡在网络不可达、Python 依赖缺失或磁盘空间不足 200MB 导致的脚本中断——这些都不会直接提示到控制台的状态面板上。
排查初始化失败的入口是三条日志路径。/var/log/cloud-init.log 记录完整运行流程与阶段级错误,其中的 “WARNING” 和 “ERROR” 行往往是第一突破口;/var/log/cloud-init-output.log 则专门保存 User-Data 脚本的 stdout/stderr,脚本静默失败时,这里面通常只有开头几行而缺少预期输出。结合 systemd 日志(journalctl -u cloud-init)可确认服务是否被强行终止。一个普遍的误判是依赖 /var/log/messages 找线索——实际上 Cloud-Init 的详细错误极少写入 syslog,直接查阅专用日志才是最短路径。
日志是排查Cloud-Init问题的第一现场,但面对动辄上千行的输出,抓不住重点等于白看。在实际处理过的案例中,有两类日志能最快锁定根因:一是cloud-init.log中的模块执行顺序与错误堆栈,二是cloud-init-output.log中用户脚本的具体报错行。前者告诉你哪个阶段出了问题,后者告诉你为什么出了问题——两者的定位逻辑完全不同。
串口日志的价值在于“外部视角”。当服务器连SSH都进不去时,控制台串口输出是唯一能看到的现场。在腾讯云控制台进入实例详情页,点击“串口日志”即可获取启动全过程的实时打印。重点关注几个特征字符串:出现waiting for metadata意味着网络初始化模块受阻,通常与安全组拦截169.254.169.254元数据服务地址有关;出现modules:config后长时间无输出,往往是挂载磁盘或写入fstab时遇到异常;而熬到modules:final阶段才报错,则基本能判定是User-Data脚本本身的问题,与Cloud-Init框架无关。这条分水岭能省掉一半的误判时间。

进入系统后,首看/var/log/cloud-init.log。这份日志按阶段顺序打印每个模块的执行结果,任何模块标有WARNING或ERROR都值得排查。一个常被忽略的细节是:即便某步骤标记为OK,也不代表真正执行成功——比如磁盘挂载模块可能因为目标路径已存在文件而跳过操作,日志只记了“skipped due to existing data”而不会报错。因此排查时不能只看报错行,还得顺着模块名回溯前后三行逻辑。对于腾讯云环境,DataSource相关日志要格外留意,它记录了Cloud-Init从元数据服务拉取配置的全过程,如果此处出现超时或403,直接检查实例所在VPC的路由表和防火墙规则就能解决。
journalctl -u cloud-init的优势在于按时间切片和关键词过滤。如果服务器已经运行了一段时间,cloud-init.log可能被轮转,但systemd日志依然保留了历史记录。常见的用法是journalctl -u cloud-init --since "5 min ago"看最近的初始化行为,或者journalctl -u cloud-init | grep -E "fail|error|timeout"做快速负面筛查。实际排查中发现,部分Python依赖缺失导致Cloud-Init静默失败的问题,在cloud-init.log里可能只是含糊地记录了模块返回非零状态码,而journalctl中会暴露出底层的ImportError或依赖缺失信息。这个互补关系很多运维人员不了解,结果在cloud-init.log里死磕半天找不到线索,换个数据源就立刻明朗了。
把 User-Data 写进控制台只是第一步,真正要命的是它有没有被正确送达并执行。实际工单中,约六成的 Cloud-Init 异常最终都出在用户数据环节——要么注入失败,要么脚本静默报错,把“已初始化”的假象留给运维去拆盲盒。下面三个排查方向,能帮你把灰盒变成白盒。
首先确认脚本到底有没有落到实例里。登录服务器后执行 curl -s http://169.254.169.254/latest/user-data,若能在终端里看到自己写的 Bash 脚本,说明元数据链路是通的;如果超时或无输出,基本可以判定安全组出站规则、VPC 路由或云平台元数据服务存在阻断。有经验的运维在排查这类问题时,会顺手检查 cloud-init status 的输出,若显示“status: error”且日志卡在“waiting for metadata”阶段,十有八九是网络层面的限制。遇到这种跨产品联动故障,像云老大这类服务商的技术支持团队通常会建议先梳理实例所在安全组的出站策略,再决定是否需要回滚至基础网络配置进行隔离测试。

脚本内容本身是另一个重灾区。不少开发者习惯在本地写完脚本直接贴进控制台,却忽略了 Shebang 的缺失、Windows 换行符 (CRLF) 污染或依赖命令未预装的问题。一种低成本的预防方法是在提交前用 bash -n 做语法检查,并将脚本在相同基础镜像的测试实例上跑一遍。需要注意的是,Cloud-Init 在 final 阶段执行 User-Data 时并非以交互式 Shell 运行,环境变量不全,apt-get update 没加 -y 就可能让整个流程卡死。如果你在排查时发现 cloud-init-output.log 里只有零星几行输出就中断,不妨对照脚本逐行还原执行环境。
最直接的验证永远是模拟执行。将 User-Data 脚本保存到 /tmp/test.sh,手动运行 bash -x /tmp/test.sh 可以看到每一步展开的具体命令,瞬间暴露变量引用错误或路径依赖问题。如果线上实例已经出故障,执行 sudo cloud-init clean --logs 然后重启可以清空状态重新初始化,但生产环境下这个操作等同于重放初始化流程,可能会覆盖现有配置。更安全的做法是提取 /var/lib/cloud/instance/user-data.txt 的内容,在隔离的容器或临时实例里完整回放,既还原真实环境又不波及业务。
在实际运维中,Cloud-Init失败的表现远比文档里描述的复杂——控制台显示“运行中”但SSH拒绝连接、实例创建后无法挂载数据盘、预设的监控Agent从未启动。这些问题通常指向三个高频故障点:元数据服务不可达、Python运行环境异常、以及云盘空间不足导致脚本静默中断。以下逐一拆解排查路径。
这是占比最高的失败类型。Cloud-Init启动后首先通过链路本地地址169.254.169.254拉取实例元数据,若该请求超时,整个初始化流程会在local阶段卡死——cloud-init.log常见报错为“url_helper.pyWARNING: Calling 'http://169.254.169.254/2009-04-04/meta-data/instance-id' failed”。排查时先确认实例是否使用了自定义镜像,若镜像中iptables规则或代理设置阻止了该地址的访问,初始化必然失败。腾讯云侧已经对该地址做白名单处理,因此网络层问题通常出在实例内部。用curl -s http://169.254.169.254/latest/meta-data/验证连通性,若返回实例ID等元数据信息则网络正常,若超时则检查/etc/hosts是否被错误修改、安全组出站规则是否禁用了169.254.0.0/16段。一个容易被忽略的场景是:部分运维在User-Data中写入yum update或apt upgrade,这类全量更新可能升级了NetworkManager或systemd-resolved,导致DNS解析链路变更,间接影响元数据服务访问,建议将更新操作后移至脚本末尾执行。
Cloud-Init本质是一个Python应用,对运行环境有明确要求。腾讯云各Linux镜像预装的Cloud-Init版本跨度较大——CentOS 7.x默认搭载17.1,而Ubuntu 22.04已升级至22.4。版本过旧时(17.x),已知存在Python 3.6+环境下的YAML解析兼容性问题,典型错误是module 'yaml' has no attribute 'FullLoader',这会导致所有通过#cloud-config格式编写的用户数据全部跳过执行。修复方式并非简单安装PyYAML,而是需要将Cloud-Init升级至20.3及以上版本,但升级过程本身也可能因依赖冲突中断——比如部分定制AMI中同时存在python2和python3,Cloud-Init的init进程错误调用了python2解释器,在journalctl -u cloud-init中表现为“ImportError: No module named cloudinit”。排查这类问题时,先确认/usr/bin/cloud-init首行的shebang指向的Python版本与实际安装路径是否匹配,再通过cloud-init analyze show查看各阶段耗时,若有模块耗时异常(超过120秒),通常指向依赖缺失导致的内部重试循环。
与其每次初始化失败后钻进日志里翻 error,不如提前在几个关键环节搭好防线。我们梳理了三条投入产出比最高的优化思路,每条都能直接映射到生产环境中那些高频的“莫名卡死”或“脚本跑丢”的现场。
在线上看到的现象级失败里,至少有三分之一是由于 User-Data 脚本缺少 #!/bin/bash 或依赖包未安装就直接调用命令。一个可维护的脚本应当强制 set -e 和 set -o pipefail,并将关键输出重定向到 /var/log/user-data.log,便于事后回溯。写法上尽量幂等——例如安装软件前先检测是否已存在,避免多次执行引发冲突。如果脚本需要拉取外部资源,务必在开头加入重试逻辑,因为元数据服务就绪后的首次外网访问常受 SDN 网络收敛影响,偶发的 5~10 秒延迟就可能让整个 final 阶段无声崩掉。
Cloud-Init 默认的 datasource 超时时间较短,当元数据服务响应抖动时,很容易走到 timeout 分支直接退出,后续 user-data 压根不会被触发。在 /etc/cloud/cloud.cfg 中建议将 datasource 相关的 max_wait 从默认的 2 秒提升到 10 秒,retries 次数调整为 5 次,可以有效覆盖腾讯云可用区内偶尔出现的 ARP 表学习延迟。配合 cloud-init status --wait 做成启动后置检查,在初始化彻底完成之前挡住业务进程的拉起,能大幅减少“服务器看似跑起来了,但实际有一半配置没挂上”的半成品状态。
最彻底的优化是把确定性工作交给镜像构建过程。将常用的运行环境、安全基线、监控 agent 预置进 OS 镜像,Cloud-Init 仅负责注入差异化的配置(如实例 ID、私网 IP),等于把“运行中安装”的高风险操作前移到了构建阶段。一些服务商(例如云老大)已经将这条思路产品化,提供“初始化预检镜像”,实测能将首次部署成功率拉升到 99% 以上。对业务稳定性要求高的场景,还可以在镜像内集成一个轻量 /healthcheck 脚本,启动后自动校验关键进程和挂载点,把“User-Data 执行成功却功能异常”这种暧昧故障直接暴露出来。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。