jimyag's Blog

深入理解 cloud-init:配置、流程与数据源

概述

cloud-init 是云实例初始化的行业标准工具,支持所有主流云平台(AWS、Azure、GCP、OpenStack 等)。它在实例首次启动时自动配置网络、存储、SSH 密钥、用户账户等,实现云实例的自动化部署。

本文深入分析 cloud-init 的:

  • 配置类型与格式
  • 启动执行流程
  • 条件判断机制
  • 数据源架构

一、配置类型

cloud-init 处理三类配置数据:meta-datauser-datavendor-data

1.1 Meta-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-keysSSH 公钥列表
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-configYAML 配置文件Final 阶段
Shell Script#!Bash 脚本Final 阶段
Boothook#cloud-boothook早期钩子脚本Local 阶段
Include#includeURL 列表(下载更多配置)Network 阶段
Mime Multi PartContent-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 EC2Amazon EC2
AzureMicrosoft Corporation
GCEGoogle Compute Engine
VMwareVMware, Inc.
LXDLXD

2.3 Local 阶段(本地阶段)

systemd 服务cloud-init-local.service

时序要求

1
2
3
# systemd 依赖关系
Before=network-pre.target
After=hv_kvp_daemon.service

目的

  1. 查找本地数据源(不需要网络)
  2. 应用网络配置

执行流程

  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

网络配置来源优先级

  1. 内核命令行 ip= 参数
  2. 数据源提供的网络配置
  3. 系统配置 /etc/cloud/cloud.cfg
  4. 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

目的

  1. 处理需要网络的数据源
  2. 下载并处理 user-data
  3. 执行 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[配置管理工具]

执行顺序

  1. runcmd 指令
  2. /var/lib/cloud/scripts/per-instance/
  3. /var/lib/cloud/scripts/per-boot/
  4. /var/lib/cloud/scripts/user/
  5. Package 安装(packages:
  6. 配置管理工具(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_INSTANCEonce-per-instance每个实例执行一次用户创建、包安装
PER_ALWAYSalways每次启动都执行日志清理、临时文件
PER_ONCEonce永远只执行一次系统级初始化

频率控制实现

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 判断会产生误判:

  1. 元数据服务不稳定,无法获取 instance-id
  2. 攻击者可以伪造数据源,触发重置

解决方案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 支持的数据源

公有云数据源

数据源文件平台
DataSourceEc2DataSourceEc2.pyAWS EC2
DataSourceAzureDataSourceAzure.pyMicrosoft Azure
DataSourceGCEDataSourceGCE.pyGoogle Compute Engine
DataSourceOracleDataSourceOracle.pyOracle Cloud
DataSourceIBMCloudDataSourceIBMCloud.pyIBM Cloud
DataSourceAliYunDataSourceAliYun.py阿里云
DataSourceHetznerDataSourceHetzner.pyHetzner Cloud

私有云数据源

数据源文件平台
DataSourceOpenStackDataSourceOpenStack.pyOpenStack
DataSourceVMwareDataSourceVMware.pyVMware vSphere
DataSourceMAASDataSourceMAAS.pyUbuntu MAAS
DataSourceSmartOSDataSourceSmartOS.pyJoyent SmartOS
DataSourceCloudStackDataSourceCloudStack.pyApache CloudStack

本地/测试数据源

数据源文件用途
DataSourceNoCloudDataSourceNoCloud.py本地测试、ISO 启动
DataSourceConfigDriveDataSourceConfigDrive.pyOpenStack ConfigDrive
DataSourceLXDDataSourceLXD.pyLXD 容器
DataSourceWSLDataSourceWSL.pyWindows Subsystem for Linux

4.4 NoCloud 数据源详解

NoCloud 是最常用的本地测试数据源。

数据来源

  1. 种子目录

    1
    2
    3
    
    /var/lib/cloud/seed/nocloud/
    ├── meta-data    # 实例元数据
    └── user-data    # 用户配置
    
  2. 内核命令行

    1
    2
    
    # GRUB 配置
    GRUB_CMDLINE_LINUX="ds=nocloud-net;s=http://example.com/seed/"
    
  3. ISO 镜像

    1
    2
    3
    
    # 创建种子 ISO
    genisoimage -output seed.iso -volid cidata -joliet -rock \
      user-data meta-data
    
  4. 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/hostsConfig
cc_resolv_conf配置 DNSConfig

用户和认证模块

模块功能阶段
cc_users_groups创建用户和组Config
cc_ssh配置 SSHConfig
cc_set_passwords设置用户密码Config
cc_ssh_import_id导入 SSH 密钥Config
cc_ssh_authkey_fingerprintsSSH 密钥指纹Final

存储模块

模块功能阶段
cc_disk_setup磁盘分区Network
cc_mounts挂载点配置Config
cc_growpart扩展分区Config
cc_resizefs扩展文件系统Config

软件包模块

模块功能阶段
cc_package_update_upgrade_install包安装和更新Config
cc_apt_configureAPT 源配置Config
cc_yum_add_repoYUM 源配置Config
cc_zypper_add_repoZypper 源配置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 的核心机制:

  1. 配置类型:meta-data(实例元信息)、user-data(用户配置)、vendor-data(厂商配置)
  2. 启动流程:Detect → Local → Network → Config → Final 五个阶段
  3. 条件判断:基于 instance-id 判断首次启动,使用信号量控制执行频率
  4. 数据源:32 个数据源实现,支持所有主流云平台

理解这些核心概念,可以更好地控制和调试云实例的初始化过程。

#Cloud-Init #Cloud #Linux #Devops #Virtualization