概述
cloud-init 是云实例初始化的行业标准工具,支持所有主流云平台(AWS、Azure、GCP、OpenStack 等)。它在实例首次启动时自动配置网络、存储、SSH 密钥、用户账户等,实现云实例的自动化部署。
本文深入分析 cloud-init 的:
- 配置类型与格式
- 启动执行流程
- 条件判断机制
- 数据源架构
一、配置类型
cloud-init 处理三类配置数据:meta-data、user-data 和 vendor-data。
meta-data 是云平台提供的实例元信息,描述实例本身的基本属性。
格式
YAML 格式,包含实例标识和平台信息:
1
2
3
4
5
6
7
8
9
10
11
12
|
# meta-data 示例
instance-id: i-87018aed # 必需:实例唯一标识
local-hostname: web-server-01 # 建议:主机名
public-keys: # 建议:SSH 公钥
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQ...
placement:
availability-zone: us-east-1a # 可用区
local-ipv4: 10.223.26.178 # 内网 IP
public-ipv4: 184.72.174.120 # 公网 IP
instance-type: m1.large # 实例类型
ami-id: ami-fd4aa494 # 镜像 ID
security-groups: default # 安全组
|
必需字段
| 字段 |
说明 |
示例 |
instance-id |
实例唯一标识,用于判断首次启动 |
i-87018aed |
建议字段
| 字段 |
说明 |
local-hostname |
实例主机名 |
public-keys |
SSH 公钥列表 |
placement/availability-zone |
可用区信息 |
来源
meta-data 通常由云平台元数据服务提供:
- AWS EC2:
http://169.254.169.254/latest/meta-data/
- Azure:
http://169.254.169.254/metadata/instance
- GCE:
http://metadata.google.internal/computeMetadata/v1/instance/
1.2 User-data(用户配置)
user-data 是用户提供的初始化配置,控制实例的具体设置。
格式类型
user-data 支持多种格式:
| 格式 |
标识 |
说明 |
执行时机 |
| Cloud Config |
#cloud-config |
YAML 配置文件 |
Final 阶段 |
| Shell Script |
#! |
Bash 脚本 |
Final 阶段 |
| Boothook |
#cloud-boothook |
早期钩子脚本 |
Local 阶段 |
| Include |
#include |
URL 列表(下载更多配置) |
Network 阶段 |
| Mime Multi Part |
Content-Type |
混合多种格式 |
各阶段 |
Cloud Config 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
#cloud-config
# 用户管理
users:
- name: deploy
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
ssh_authorized_keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQ...
# 软件包安装
packages:
- nginx
- docker.io
- python3-pip
# 写文件
write_files:
- path: /etc/nginx/sites-available/app
content: |
server {
listen 80;
server_name _;
location / {
proxy_pass http://127.0.0.1:8000;
}
}
permissions: '0644'
# 运行命令
runcmd:
- [ systemctl, enable, nginx ]
- [ systemctl, start, nginx ]
- docker run -d -p 8000:8000 myapp:latest
# 挂载点
mounts:
- [ /dev/sdb, /data, ext4, defaults, 0, 2 ]
# SSH 配置
ssh_authorized_keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQ...
|
Shell Script 示例
1
2
3
4
5
6
7
8
9
10
11
|
#!/bin/bash
set -euo pipefail
echo "=== 开始初始化 ==="
apt-get update
apt-get install -y nginx docker.io
systemctl enable nginx docker
systemctl start nginx docker
echo "=== 初始化完成 ==="
|
Boothook 示例(最早执行)
1
2
3
|
#cloud-boothook
# 这个脚本在 Local 阶段执行,早于网络配置
echo "Boothook executed at $(date)" > /tmp/boothook.log
|
1.3 Vendor-data(厂商配置)
vendor-data 是云厂商提供的配置,通常包含:
- 云平台特定的优化设置
- 监控 Agent 安装
- 平台集成脚本
格式与 user-data 相同,但优先级低于 user-data。
二、启动执行流程
cloud-init 的启动分为五个阶段,每个阶段由独立的 systemd 服务管理。
2.1 启动阶段总览
graph TD
A[Detect<br/>平台检测] --> B[Local<br/>网络前阶段]
B --> C[Network<br/>网络阶段]
C --> D[Config<br/>配置阶段]
D --> E[Final<br/>最终阶段]
B -->|阻塞| F[network-pre.target]
C -->|阻塞| G[sshd.service]
subgraph 服务依赖
F
G
H[network-online.target]
end
2.2 Detect 阶段(平台检测)
目的:检测运行平台,决定是否启用 cloud-init。
执行者:ds-identify 工具
关键逻辑:
1
2
3
4
5
|
# ds-identify 检测逻辑
1. 检查 DMI 信息(制造商、产品名)
2. 检查 /sys/class/dmi/id/product_uuid
3. 检查内核命令行参数
4. 匹配已知云平台特征
|
结果:
- 找到平台 → 启用 cloud-init 服务
- 未找到平台 → 禁用 cloud-init 服务
平台检测示例:
| 平台 |
DMI 标识 |
| AWS EC2 |
Amazon EC2 |
| Azure |
Microsoft Corporation |
| GCE |
Google Compute Engine |
| VMware |
VMware, Inc. |
| LXD |
LXD |
2.3 Local 阶段(本地阶段)
systemd 服务:cloud-init-local.service
时序要求:
1
2
3
|
# systemd 依赖关系
Before=network-pre.target
After=hv_kvp_daemon.service
|
目的:
- 查找本地数据源(不需要网络)
- 应用网络配置
执行流程:
graph TD
A[开始] --> B{查找本地数据源}
B -->|找到| C[读取 meta-data]
B -->|未找到| D[等待网络数据源]
C --> E{有网络配置?}
E -->|有| F[渲染网络配置]
E -->|无| G[使用 fallback 配置]
F --> H[写入 /etc/network/interfaces.d/]
G --> I[DHCP on eth0]
H --> J[退出,等待网络启动]
I --> J
D --> J
网络配置来源优先级:
- 内核命令行
ip= 参数
- 数据源提供的网络配置
- 系统配置
/etc/cloud/cloud.cfg
- Fallback:DHCP on eth0
关键代码(cloudinit/sources/__init__.py):
1
2
3
4
5
6
7
|
class DataSource:
network_config_sources = (
NetworkConfigSource.CMD_LINE, # 1. 内核命令行
NetworkConfigSource.INITRAMFS, # 2. Initramfs
NetworkConfigSource.SYSTEM_CFG, # 3. 系统配置
NetworkConfigSource.DS, # 4. 数据源
)
|
2.4 Network 阶段(网络阶段)
systemd 服务:cloud-init-network.service
时序要求:
1
2
3
4
5
|
# systemd 依赖关系
After=cloud-init-local.service
After=NetworkManager.service
Before=network-online.target
Before=sshd.service
|
目的:
- 处理需要网络的数据源
- 下载并处理 user-data
- 执行 early handlers
执行流程:
graph TD
A[网络就绪] --> B[连接元数据服务]
B --> C[下载 user-data]
C --> D[下载 vendor-data]
D --> E{处理 #include}
E -->|有| F[递归下载 URL 内容]
E -->|无| G{解压 gzip}
F --> G
G --> H{解析格式}
H -->|Shell Script| I[存储到 scripts/]
H -->|Cloud Config| J[合并配置]
H -->|Boothook| K[立即执行]
I --> L[继续下一阶段]
J --> L
K --> L
数据处理顺序:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
def process_user_data(raw_data):
# 1. Gzip 解压
if raw_data.startswith(b'\x1f\x8b'):
raw_data = gzip.decompress(raw_data)
# 2. Mime Multi Part 处理
if is_mime_multipart(raw_data):
for part in parse_mime(raw_data):
process_user_data(part) # 递归处理
# 3. 根据开头标识分发处理
if raw_data.startswith('#!'):
handle_shell_script(raw_data)
elif raw_data.startswith('#cloud-config'):
handle_cloud_config(raw_data)
elif raw_data.startswith('#cloud-boothook'):
handle_boothook(raw_data)
elif raw_data.startswith('#include'):
handle_include(raw_data)
|
2.5 Config 阶段(配置阶段)
systemd 服务:cloud-config.service
时序要求:
1
|
After=network-online.target
|
目的:运行配置模块(不影响关键启动)
执行的模块(来自 /etc/cloud/cloud.cfg):
1
2
3
4
5
6
7
8
9
|
cloud_config_modules:
- ssh_import_id
- keyboard
- locale
- set_passwords
- rsyslog
- ca-certs
- ntp
- timezone
|
2.6 Final 阶段(最终阶段)
systemd 服务:cloud-final.service
时序要求:
1
2
|
# 在大多数服务之后运行
Before=rc-local.service
|
目的:执行用户脚本和最终配置
执行内容:
graph LR
A[runcmd] --> B[scripts/per-instance]
B --> C[scripts/per-boot]
C --> D[scripts/user]
D --> E[package 安装]
E --> F[配置管理工具]
执行顺序:
runcmd 指令
/var/lib/cloud/scripts/per-instance/
/var/lib/cloud/scripts/per-boot/
/var/lib/cloud/scripts/user/
- Package 安装(
packages:)
- 配置管理工具(Puppet、Ansible、Chef)
三、条件判断机制
cloud-init 使用多重机制判断是否执行特定配置。
3.1 首次启动判断
核心问题:如何判断当前启动是"首次启动"还是"后续启动"?
判断逻辑:
graph TD
A[启动] --> B{缓存存在?}
B -->|否| C[首次启动]
B -->|是| D{instance-id 匹配?}
D -->|是| E[后续启动]
D -->|否| F[新实例首次启动]
C --> G[执行所有 per-instance 配置]
E --> H[只执行 per-boot 配置]
F --> G
关键代码(cloudinit/stages.py):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
def check_if_new_instance_id(self):
# 从缓存读取上次的 instance-id
previous_id = self.get_previous_instance_id()
# 从数据源获取当前 instance-id
current_id = self.datasource.get_instance_id()
# 比较
if previous_id == NO_PREVIOUS_INSTANCE_ID:
return True # 首次启动
elif previous_id != current_id:
return True # 新实例(从镜像创建)
else:
return False # 后续启动
|
缓存位置:
1
2
3
|
/var/lib/cloud/instance/obj.pkl # Pickled 对象缓存
/var/lib/cloud/data/previous-instance-id # 上次 instance ID
/var/lib/cloud/data/instance-id # 当前 instance ID
|
3.2 执行频率控制
cloud-init 定义了三种执行频率:
| 频率 |
常量 |
说明 |
典型用途 |
| PER_INSTANCE |
once-per-instance |
每个实例执行一次 |
用户创建、包安装 |
| PER_ALWAYS |
always |
每次启动都执行 |
日志清理、临时文件 |
| PER_ONCE |
once |
永远只执行一次 |
系统级初始化 |
频率控制实现:
1
2
3
4
5
|
/var/lib/cloud/instance/sem/
├── config-ssh # 已执行标记
├── config-users_groups
├── config-runcmd
└── scripts-per-instance/
|
判断逻辑:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
def run_module(module_name, freq):
sem_file = f"/var/lib/cloud/instance/sem/{module_name}"
if freq == PER_ALWAYS:
return True # 总是执行
if freq == PER_INSTANCE:
if os.path.exists(sem_file):
return False # 已执行,跳过
else:
touch(sem_file)
return True
if freq == PER_ONCE:
global_sem = f"/var/lib/cloud/sem/{module_name}"
if os.path.exists(global_sem):
return False
else:
touch(global_sem)
return True
|
3.3 manual_cache_clean 配置
问题场景:某些情况下,instance-id 判断会产生误判:
- 元数据服务不稳定,无法获取 instance-id
- 攻击者可以伪造数据源,触发重置
解决方案:manual_cache_clean 配置
1
2
|
# /etc/cloud/cloud.cfg
manual_cache_clean: true
|
行为对比:
| 配置 |
instance-id 不匹配时 |
行为描述 |
false(默认) |
清理缓存,重新初始化 |
check 模式,信任数据源 |
true |
不清理,保持缓存 |
trust 模式,信任缓存 |
安全影响:
1
2
3
4
5
|
# 手动清理缓存(重置实例)
cloud-init clean
# 或手动删除
rm -rf /var/lib/cloud/instance/*
|
四、数据源架构
数据源(DataSource)是 cloud-init 获取配置数据的抽象接口。
4.1 数据源类层次
graph TD
A[DataSource 基类] --> B[DataSourceEc2]
A --> C[DataSourceAzure]
A --> D[DataSourceGCE]
A --> E[DataSourceOpenStack]
A --> F[DataSourceNoCloud]
A --> G[DataSourceVMware]
A --> H[DataSourceLXD]
A --> I[其他 24 个...]
subgraph 公有云
B
C
D
end
subgraph 私有云
E
G
end
subgraph 本地测试
F
H
end
4.2 DataSource 基类接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
class DataSource(metaclass=abc.ABCMeta):
# 基本属性
dsname = "_undef" # 数据源名称
dsmode = DSMODE_NETWORK # 运行模式
# 必须实现的方法
@abc.abstractmethod
def _get_data(self):
"""获取数据,返回 True/False 表示是否成功"""
pass
# 可选重写的方法
def get_userdata(self):
"""返回 user-data"""
return self.userdata
def get_instance_id(self):
"""返回 instance-id"""
return self.metadata.get('instance-id')
def get_public_ssh_keys(self):
"""返回 SSH 公钥列表"""
return self.metadata.get('public-keys', [])
def get_network_config(self):
"""返回网络配置"""
return self._network_config
|
4.3 支持的数据源
公有云数据源
| 数据源 |
文件 |
平台 |
| DataSourceEc2 |
DataSourceEc2.py |
AWS EC2 |
| DataSourceAzure |
DataSourceAzure.py |
Microsoft Azure |
| DataSourceGCE |
DataSourceGCE.py |
Google Compute Engine |
| DataSourceOracle |
DataSourceOracle.py |
Oracle Cloud |
| DataSourceIBMCloud |
DataSourceIBMCloud.py |
IBM Cloud |
| DataSourceAliYun |
DataSourceAliYun.py |
阿里云 |
| DataSourceHetzner |
DataSourceHetzner.py |
Hetzner Cloud |
私有云数据源
| 数据源 |
文件 |
平台 |
| DataSourceOpenStack |
DataSourceOpenStack.py |
OpenStack |
| DataSourceVMware |
DataSourceVMware.py |
VMware vSphere |
| DataSourceMAAS |
DataSourceMAAS.py |
Ubuntu MAAS |
| DataSourceSmartOS |
DataSourceSmartOS.py |
Joyent SmartOS |
| DataSourceCloudStack |
DataSourceCloudStack.py |
Apache CloudStack |
本地/测试数据源
| 数据源 |
文件 |
用途 |
| DataSourceNoCloud |
DataSourceNoCloud.py |
本地测试、ISO 启动 |
| DataSourceConfigDrive |
DataSourceConfigDrive.py |
OpenStack ConfigDrive |
| DataSourceLXD |
DataSourceLXD.py |
LXD 容器 |
| DataSourceWSL |
DataSourceWSL.py |
Windows Subsystem for Linux |
4.4 NoCloud 数据源详解
NoCloud 是最常用的本地测试数据源。
数据来源
-
种子目录:
1
2
3
|
/var/lib/cloud/seed/nocloud/
├── meta-data # 实例元数据
└── user-data # 用户配置
|
-
内核命令行:
1
2
|
# GRUB 配置
GRUB_CMDLINE_LINUX="ds=nocloud-net;s=http://example.com/seed/"
|
-
ISO 镜像:
1
2
3
|
# 创建种子 ISO
genisoimage -output seed.iso -volid cidata -joliet -rock \
user-data meta-data
|
-
DMI 数据:
1
2
|
# 在虚拟机配置中设置
# System Serial Number = "ds=nocloud"
|
查找顺序
1
2
3
4
5
6
7
8
9
10
11
12
|
def _get_data(self):
sources = [
self._get_data_from_dmi(), # 1. DMI 序列号
self._get_data_from_cmdline(), # 2. 内核命令行
self._get_data_from_seed_dir(), # 3. 种子目录
self._get_data_from_iso(), # 4. ISO 镜像
]
for source in sources:
if source:
return True
return False
|
使用示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
# 1. 创建种子目录
sudo mkdir -p /var/lib/cloud/seed/nocloud
# 2. 写入 meta-data
cat <<EOF | sudo tee /var/lib/cloud/seed/nocloud/meta-data
instance-id: my-test-instance
local-hostname: test-vm
EOF
# 3. 写入 user-data
cat <<EOF | sudo tee /var/lib/cloud/seed/nocloud/user-data
#cloud-config
packages:
- nginx
runcmd:
- systemctl enable nginx
- systemctl start nginx
EOF
# 4. 重置并重新运行
sudo cloud-init clean
sudo cloud-init init
|
五、目录结构详解
5.1 /var/lib/cloud 结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
/var/lib/cloud/
├── instance -> instances/i-87018aed/ # 当前实例符号链接
├── instances/
│ └── i-87018aed/
│ ├── boot-finished # 启动完成标记
│ ├── cloud-config.txt # 合并后的完整配置
│ ├── user-data.txt # 原始 user-data
│ ├── user-data.txt.i # 处理后的 user-data
│ ├── obj.pkl # Pickled 对象缓存
│ ├── datasource # 数据源信息
│ ├── sem/ # 信号量目录(执行标记)
│ │ ├── config-ssh
│ │ ├── config-users_groups
│ │ ├── scripts-per-instance
│ │ └── ...
│ ├── scripts/ # 脚本目录
│ │ ├── per-instance/ # 每实例执行一次
│ │ ├── per-boot/ # 每次启动执行
│ │ ├── per-once/ # 永远执行一次
│ │ └── user/ # 用户脚本
│ ├── data/ # 实例数据
│ └── handlers/ # 处理器状态
├── scripts/
│ ├── per-instance/ # 全局 per-instance 脚本
│ ├── per-boot/ # 全局 per-boot 脚本
│ └── per-once/ # 全局 per-once 脚本
├── seed/
│ └── nocloud/ # NoCloud 种子目录
│ ├── meta-data
│ └── user-data
├── sem/ # 全局信号量
│ └── scripts.once
├── data/ # 全局数据
│ ├── previous-datasource
│ ├── previous-instance-id
│ └── previous-hostname
└── handlers/ # 全局处理器
|
5.2 状态文件
/var/lib/cloud/data/status.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
{
"v1": {
"init-local": {
"errors": [],
"start": 1712345678.123,
"end": 1712345679.456
},
"init": {
"errors": [],
"start": 1712345680.789,
"end": 1712345685.012
},
"modules-config": {
"errors": [],
"start": 1712345686.345,
"end": 1712345690.678
},
"modules-final": {
"errors": [],
"start": 1712345691.901,
"end": 1712345695.234
},
"datasource": "DataSourceEc2",
"stage": null
}
}
|
/var/lib/cloud/data/result.json
1
2
3
4
5
6
|
{
"v1": {
"datasource": "DataSourceEc2",
"errors": []
}
}
|
六、配置模块详解
6.1 模块分类
cloud-init 的 63 个配置模块按功能分类:
网络配置模块
| 模块 |
功能 |
阶段 |
cc_set_hostname |
设置主机名 |
Network |
cc_update_hostname |
更新主机名 |
Config |
cc_update_etc_hosts |
更新 /etc/hosts |
Config |
cc_resolv_conf |
配置 DNS |
Config |
用户和认证模块
| 模块 |
功能 |
阶段 |
cc_users_groups |
创建用户和组 |
Config |
cc_ssh |
配置 SSH |
Config |
cc_set_passwords |
设置用户密码 |
Config |
cc_ssh_import_id |
导入 SSH 密钥 |
Config |
cc_ssh_authkey_fingerprints |
SSH 密钥指纹 |
Final |
存储模块
| 模块 |
功能 |
阶段 |
cc_disk_setup |
磁盘分区 |
Network |
cc_mounts |
挂载点配置 |
Config |
cc_growpart |
扩展分区 |
Config |
cc_resizefs |
扩展文件系统 |
Config |
软件包模块
| 模块 |
功能 |
阶段 |
cc_package_update_upgrade_install |
包安装和更新 |
Config |
cc_apt_configure |
APT 源配置 |
Config |
cc_yum_add_repo |
YUM 源配置 |
Config |
cc_zypper_add_repo |
Zypper 源配置 |
Config |
脚本执行模块
| 模块 |
功能 |
阶段 |
cc_bootcmd |
启动命令 |
Network |
cc_runcmd |
运行命令 |
Final |
cc_scripts_per_instance |
实例脚本 |
Final |
cc_scripts_per_boot |
启动脚本 |
Final |
cc_scripts_user |
用户脚本 |
Final |
6.2 模块执行频率配置
1
2
3
4
5
6
7
|
# /etc/cloud/cloud.cfg
cloud_config_modules:
- mounts # 默认 PER_INSTANCE
- ssh # 默认 PER_INSTANCE
- [apt_update_upgrade, always] # 强制 PER_ALWAYS
- runcmd
- puppet
|
七、实战示例
7.1 创建完整云配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
#cloud-config
# 1. 系统设置
hostname: web-server-01
fqdn: web-server-01.example.com
manage_etc_hosts: true
timezone: Asia/Shanghai
locale: en_US.UTF-8
# 2. 用户管理
users:
- name: deploy
primary_group: deploy
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
groups: [docker, sudo]
ssh_authorized_keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7...
- name: app
system: true
shell: /usr/sbin/nologin
home: /opt/app
# 3. 软件包
package_update: true
package_upgrade: false
packages:
- nginx
- docker.io
- docker-compose
- python3-pip
- git
# 4. 写文件
write_files:
- path: /etc/nginx/sites-available/app
content: |
upstream backend {
server 127.0.0.1:8000;
}
server {
listen 80;
server_name _;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
permissions: '0644'
- path: /opt/app/docker-compose.yml
content: |
version: '3'
services:
web:
image: myapp:latest
ports:
- "8000:8000"
restart: always
permissions: '0644'
owner: app:app
# 5. 挂载点
mounts:
- [ /dev/sdb, /data, ext4, "defaults,noatime", 0, 2 ]
- [ /dev/sdc, /logs, ext4, "defaults,noatime", 0, 2 ]
# 6. 运行命令
runcmd:
# 启用服务
- systemctl enable nginx docker
- systemctl start nginx docker
# 配置 Nginx
- ln -sf /etc/nginx/sites-available/app /etc/nginx/sites-enabled/
- rm -f /etc/nginx/sites-enabled/default
- systemctl reload nginx
# 启动应用
- cd /opt/app && docker-compose up -d
# 7. SSH 配置
ssh_authorized_keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7...
# 8. 最终消息
final_message: "云实例初始化完成!运行时间: $UPTIME 秒"
|
7.2 本地测试流程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
# 1. 创建种子目录
sudo mkdir -p /var/lib/cloud/seed/nocloud
# 2. 写入配置
cat <<EOF | sudo tee /var/lib/cloud/seed/nocloud/meta-data
instance-id: test-vm-001
local-hostname: test-vm
EOF
cat <<EOF | sudo tee /var/lib/cloud/seed/nocloud/user-data
#cloud-config
packages:
- nginx
runcmd:
- systemctl enable nginx
- systemctl start nginx
EOF
# 3. 清理并重新初始化
sudo cloud-init clean --logs
sudo cloud-init init --local
sudo cloud-init init
# 4. 检查状态
cloud-init status --wait
cloud-init query -f '{{ instance_data }}'
|
7.3 调试技巧
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
# 查看执行日志
journalctl -u cloud-init-local.service
journalctl -u cloud-init-network.service
journalctl -u cloud-config.service
journalctl -u cloud-final.service
# 查看 cloud-init 日志
cat /var/log/cloud-init-output.log
cat /var/log/cloud-init.log
# 分析执行时间
cloud-init analyze show
cloud-init analyze dump
# 查询实例数据
cloud-init query instance-id
cloud-init query ds.meta_data
# 验证配置语法
cloud-init schema --config-file user-data
|
总结
cloud-init 的核心机制:
- 配置类型:meta-data(实例元信息)、user-data(用户配置)、vendor-data(厂商配置)
- 启动流程:Detect → Local → Network → Config → Final 五个阶段
- 条件判断:基于 instance-id 判断首次启动,使用信号量控制执行频率
- 数据源:32 个数据源实现,支持所有主流云平台
理解这些核心概念,可以更好地控制和调试云实例的初始化过程。