cloud-init and Cloud Images

Level: Expert Module: Virtualization & Containers 14 min read Lesson 60 of 66

Overview

  • What you’ll learn: How to use cloud-init to automate the initialization of virtual machines and cloud instances, including datasource concepts, boot stages, user-data formats, cloud-config modules, the NoCloud datasource for local VMs, and debugging techniques.
  • Prerequisites: QEMU/KVM Virtual Machines (Lesson 55), Libvirt and virsh Management (Lesson 56), basic YAML knowledge
  • Estimated reading time: 22 minutes

Introduction

When you launch a virtual machine or cloud instance, it starts from a generic base image that needs to be customized for its specific role: setting the hostname, creating user accounts, installing packages, writing configuration files, and starting services. Doing this manually is slow and error-prone. Cloud-init automates this entire process, running during the first boot (and optionally on subsequent boots) to transform a generic image into a fully configured, purpose-built machine.

Cloud-init is the industry standard for instance initialization across all major cloud platforms including AWS, Azure, GCP, and OpenStack. It is pre-installed in virtually all cloud images for Ubuntu, Debian, RHEL, CentOS, and other distributions. Beyond cloud environments, cloud-init works equally well with local QEMU/KVM virtual machines using the NoCloud datasource, making it invaluable for automated testing, development, and on-premises infrastructure.

In this lesson, you will understand cloud-init’s datasource architecture and boot stages, write user-data in both shell script and cloud-config YAML formats, explore the most useful cloud-config modules, configure the NoCloud datasource for local virtual machines, and learn how to debug cloud-init when things go wrong.

Datasources and Boot Stages

Cloud-init retrieves its configuration from a datasource — an interface to the metadata service provided by the platform where the instance is running. Each cloud platform has its own metadata API endpoint, and cloud-init includes a datasource implementation for each one.

# Common datasources
# AWS EC2:        http://169.254.169.254/latest/meta-data/
# Azure IMDS:     http://169.254.169.254/metadata/instance
# GCP:            http://metadata.google.internal/computeMetadata/v1/
# OpenStack:      http://169.254.169.254/openstack/latest/
# NoCloud:        local filesystem (ISO or seed directory)

# Check which datasource cloud-init is using
$ cloud-init query ds
DataSourceNoCloud [seed=/var/lib/cloud/seed/nocloud]

# Query instance metadata
$ cloud-init query instance-id
i-abc123def456

$ cloud-init query local-hostname
web-server-01

$ cloud-init query region
us-east-1

Cloud-init executes in four distinct boot stages, each serving a specific purpose:

# Boot stage 1: Generator (cloud-init-generator)
# Determines if cloud-init should run on this boot

# Boot stage 2: Local (cloud-init-local.service)
# Runs before networking is up; applies network configuration
# This is where NoCloud datasource reads its seed data

# Boot stage 3: Network (cloud-init.service)
# Runs after networking; fetches metadata from remote datasources
# Creates users, sets SSH keys, writes files

# Boot stage 4: Config and Final (cloud-config.service, cloud-final.service)
# Runs package installation, configuration management
# Executes user scripts, signals completion

# View the boot stage timeline
$ cloud-init analyze show
-- Boot Record 01 --
|    Event    |    Duration    |      Description       |
|-------------|----------------|------------------------|
| init-local  |  0.523s        | Cloud-init local stage |
| init-network|  2.145s        | Cloud-init network     |
| modules-config| 1.234s       | Config modules         |
| modules-final|  3.567s       | Final modules          |

# Check cloud-init status
$ cloud-init status
status: done

$ cloud-init status --long
status: done
boot_status_code: enabled-by-generator
detail: DataSourceNoCloud [seed=/var/lib/cloud/seed/nocloud]

Understanding the boot stages is important for troubleshooting. Network-dependent configuration (like package installation) cannot happen in the local stage. If cloud-init reports an error, knowing which stage failed helps identify the root cause.

User-Data Formats

User-data is the primary mechanism for providing custom configuration to cloud-init. It supports multiple formats, with cloud-config YAML and shell scripts being the most common. Cloud-init detects the format automatically based on the content’s first line.

# Format 1: Shell script (starts with #!)
#!/bin/bash
echo "Hello from cloud-init" > /var/log/cloud-init-hello.log
apt-get update
apt-get install -y nginx
systemctl enable --now nginx

# Format 2: Cloud-config YAML (starts with #cloud-config)
#cloud-config
hostname: web-server-01
manage_etc_hosts: true
users:
  - name: deploy
    groups: sudo
    shell: /bin/bash
    sudo: ALL=(ALL) NOPASSWD:ALL
    ssh_authorized_keys:
      - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5... deploy@workstation

packages:
  - nginx
  - certbot
  - python3-certbot-nginx

package_update: true
package_upgrade: true

write_files:
  - path: /etc/nginx/sites-available/default
    content: |
      server {
          listen 80;
          server_name _;
          root /var/www/html;
          index index.html;
      }
    owner: root:root
    permissions: '0644'

runcmd:
  - systemctl enable --now nginx
  - echo "Instance initialized at $(date)" >> /var/log/cloud-init-custom.log

final_message: "Cloud-init completed in $UPTIME seconds"
# Format 3: MIME multipart (combine multiple formats)
Content-Type: multipart/mixed; boundary="==BOUNDARY=="
MIME-Version: 1.0

--==BOUNDARY==
Content-Type: text/cloud-config; charset="us-ascii"

#cloud-config
packages:
  - nginx

--==BOUNDARY==
Content-Type: text/x-shellscript; charset="us-ascii"

#!/bin/bash
echo "Custom setup complete" > /tmp/setup-done

--==BOUNDARY==--

Cloud-config is the preferred format because it is declarative, idempotent, and handles error cases gracefully. Shell scripts offer maximum flexibility but require careful error handling. MIME multipart allows combining both formats in a single user-data payload, which is useful when you need both declarative configuration and custom scripting.

Cloud-Config Modules

Cloud-config modules are the building blocks of cloud-init configuration. Each module handles a specific aspect of instance setup. Here are the most commonly used modules for server provisioning.

#cloud-config

# --- User and Group Management ---
users:
  - default                          # keep the default user
  - name: admin
    gecos: Admin User
    groups: [sudo, docker]
    shell: /bin/bash
    sudo: ALL=(ALL) NOPASSWD:ALL
    lock_passwd: true
    ssh_authorized_keys:
      - ssh-ed25519 AAAAC3... admin@company

groups:
  - docker

# --- SSH Configuration ---
ssh_pwauth: false                    # disable password SSH login
disable_root: true
ssh_authorized_keys:                 # keys for default user
  - ssh-ed25519 AAAAC3... ops@company

# --- Disk and Filesystem ---
disk_setup:
  /dev/vdb:
    table_type: gpt
    layout: true
    overwrite: false

fs_setup:
  - label: data
    filesystem: ext4
    device: /dev/vdb1

mounts:
  - [/dev/vdb1, /data, ext4, "defaults,noatime", "0", "2"]

# --- System Configuration ---
timezone: Asia/Taipei
locale: en_US.UTF-8
ntp:
  enabled: true
  servers:
    - 0.pool.ntp.org
    - 1.pool.ntp.org

# --- Write Files ---
write_files:
  - path: /etc/sysctl.d/99-custom.conf
    content: |
      net.core.somaxconn = 65535
      vm.swappiness = 10
    owner: root:root
    permissions: '0644'

  - path: /opt/scripts/health-check.sh
    content: |
      #!/bin/bash
      curl -sf http://localhost/health || exit 1
    permissions: '0755'

# --- Run Commands ---
runcmd:
  - sysctl --system
  - systemctl daemon-reload
  - [sh, -c, "echo Instance $(cloud-init query instance-id) ready"]

# --- Power State ---
power_state:
  mode: reboot
  message: "Rebooting after initial configuration"
  timeout: 30
  condition: true

Cloud-config modules execute in a defined order: user creation happens before package installation, which happens before runcmd scripts. The write_files module is especially useful for deploying configuration files, while runcmd handles one-time setup commands that run during the final boot stage. The power_state module can trigger a reboot after configuration, which is useful when kernel updates have been installed.

NoCloud Datasource for Local VMs

The NoCloud datasource allows you to use cloud-init with local QEMU/KVM virtual machines, without any cloud infrastructure. You provide user-data and meta-data through either an ISO image or a local seed directory. This is essential for testing cloud-init configurations locally before deploying to production cloud environments.

# Method 1: Create a seed ISO image
$ mkdir -p /tmp/cloud-init-seed

# Create meta-data file
$ cat > /tmp/cloud-init-seed/meta-data <<EOF
instance-id: test-vm-001
local-hostname: test-vm
EOF

# Create user-data file
$ cat > /tmp/cloud-init-seed/user-data <<EOF
#cloud-config
hostname: test-vm
users:
  - name: testuser
    groups: sudo
    shell: /bin/bash
    sudo: ALL=(ALL) NOPASSWD:ALL
    ssh_authorized_keys:
      - ssh-ed25519 AAAAC3... test@workstation
packages:
  - htop
  - vim
  - curl
runcmd:
  - echo "VM provisioned at $(date)" > /var/log/provision.log
EOF

# Optional: Create network-config
$ cat > /tmp/cloud-init-seed/network-config <<EOF
version: 2
ethernets:
  enp1s0:
    dhcp4: true
EOF

# Generate the seed ISO
$ genisoimage -output seed.iso -volid cidata -joliet -rock 
    /tmp/cloud-init-seed/user-data 
    /tmp/cloud-init-seed/meta-data 
    /tmp/cloud-init-seed/network-config

# Download an Ubuntu cloud image
$ wget https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img

# Create a working copy
$ cp jammy-server-cloudimg-amd64.img test-vm.qcow2
$ qemu-img resize test-vm.qcow2 20G

# Launch the VM with the seed ISO
$ qemu-system-x86_64 
    -enable-kvm 
    -m 2048 
    -smp 2 
    -cpu host 
    -drive file=test-vm.qcow2,format=qcow2,if=virtio 
    -drive file=seed.iso,format=raw,if=virtio 
    -netdev user,id=net0,hostfwd=tcp::2222-:22 
    -device virtio-net-pci,netdev=net0 
    -nographic

# SSH into the VM after boot
$ ssh -p 2222 testuser@localhost
# Method 2: Use virt-install with cloud-init (libvirt)
$ virt-install 
    --name test-vm 
    --ram 2048 
    --vcpus 2 
    --cpu host-passthrough 
    --os-variant ubuntu22.04 
    --disk path=test-vm.qcow2,format=qcow2,bus=virtio 
    --disk path=seed.iso,device=cdrom 
    --network network=default,model=virtio 
    --graphics none 
    --console pty,target_type=serial 
    --import 
    --noautoconsole

# Method 3: Seed directory (no ISO needed)
$ sudo mkdir -p /var/lib/cloud/seed/nocloud
$ sudo cp user-data meta-data /var/lib/cloud/seed/nocloud/

The seed ISO must have the volume label cidata for cloud-init to recognize it. The instance-id in meta-data is critical: cloud-init uses it to determine if the instance has already been initialized. Changing the instance-id forces cloud-init to re-run. Cloud images are pre-configured with cloud-init and a disabled default password, so the SSH key in user-data is typically the only way to access the instance.

Debugging Cloud-Init

When cloud-init does not behave as expected, several log files and diagnostic commands help identify the problem. Cloud-init provides comprehensive logging and analysis tools.

# Primary log file
$ sudo cat /var/log/cloud-init.log | tail -50

# Output log (stdout/stderr from user scripts)
$ sudo cat /var/log/cloud-init-output.log

# Analyze boot performance
$ cloud-init analyze show

# View the rendered cloud-config (after template processing)
$ sudo cloud-init query userdata

# Validate user-data syntax before deployment
$ cloud-init schema --config-file user-data.yml

# Check the instance data
$ cloud-init query --all

# Re-run cloud-init for debugging (clean first)
$ sudo cloud-init clean
$ sudo cloud-init init
$ sudo cloud-init modules --mode=config
$ sudo cloud-init modules --mode=final

# View per-instance data
$ ls /var/lib/cloud/instance/
boot-finished  datasource  handlers/  obj.pkl  scripts/  sem/  user-data.txt

# Check which modules ran
$ ls /var/lib/cloud/instance/sem/
config_locale  config_ntp  config_ssh  config_timezone  config_users_groups

The /var/lib/cloud/instance/sem/ directory contains semaphore files that prevent modules from running twice. If you need to re-run a specific module, delete its semaphore file and run cloud-init modules. The cloud-init clean command removes all instance state, causing cloud-init to treat the next boot as a fresh first boot. Always validate your cloud-config YAML with cloud-init schema before deployment to catch syntax errors early.

Key Takeaways

  • Cloud-init is the industry standard for automating VM and cloud instance initialization, retrieving configuration from platform-specific datasources including AWS, Azure, GCP, OpenStack, and the local NoCloud datasource.
  • The four boot stages (local, network, config, final) execute in order, with network-dependent operations deferred until networking is available.
  • User-data supports shell scripts (#!), cloud-config YAML (#cloud-config), and MIME multipart formats; cloud-config is preferred for its declarative, idempotent nature.
  • Cloud-config modules handle user creation, SSH key deployment, package installation, file writing, disk setup, and command execution in a defined, reliable order.
  • The NoCloud datasource with a seed ISO enables cloud-init usage on local QEMU/KVM virtual machines, making it possible to test and develop cloud-init configurations without cloud infrastructure.

What’s Next

You have completed the Virtualization & Containers module. You now have a comprehensive understanding of virtualization from hardware-level concepts through hypervisor management, system containers, application containers, multi-container orchestration, and automated instance provisioning. These skills form the foundation for modern infrastructure engineering, cloud computing, and DevOps practices.

繁體中文

概述

  • 學習目標:了解如何使用 cloud-init 自動化虛擬機器和雲端實例的初始化,包括資料來源概念、啟動階段、使用者資料格式、cloud-config 模組、用於本機 VM 的 NoCloud 資料來源以及除錯技術。
  • 先決條件:QEMU/KVM 虛擬機器(第 55 課)、Libvirt 和 virsh 管理(第 56 課)、基本的 YAML 知識
  • 預計閱讀時間:22 分鐘

簡介

當您啟動虛擬機器或雲端實例時,它從一個通用的基礎映像開始,需要針對其特定角色進行自訂:設定主機名稱、建立使用者帳戶、安裝套件、寫入配置檔案和啟動服務。手動執行這些操作既緩慢又容易出錯。Cloud-init 自動化了整個過程,在首次開機時(以及可選地在後續開機時)執行,將通用映像轉換為完全配置的、特定用途的機器。

Cloud-init 是所有主要雲端平台(包括 AWS、Azure、GCP 和 OpenStack)上實例初始化的行業標準。它預先安裝在幾乎所有 Ubuntu、Debian、RHEL、CentOS 和其他發行版的雲端映像中。除了雲端環境之外,cloud-init 使用 NoCloud 資料來源同樣可以在本機 QEMU/KVM 虛擬機器上良好運作,使其對於自動化測試、開發和本地基礎架構非常有價值。

在本課程中,您將了解 cloud-init 的資料來源架構和啟動階段、以 shell 腳本和 cloud-config YAML 格式編寫使用者資料、探索最有用的 cloud-config 模組、為本機虛擬機器設定 NoCloud 資料來源,以及學習如何在出問題時除錯 cloud-init。

資料來源和啟動階段

Cloud-init 從資料來源擷取其配置——這是實例運行平台提供的中繼資料服務的介面。每個雲端平台都有自己的中繼資料 API 端點,cloud-init 為每個平台包含一個資料來源實作。

# 常見的資料來源
# AWS EC2:        http://169.254.169.254/latest/meta-data/
# Azure IMDS:     http://169.254.169.254/metadata/instance
# GCP:            http://metadata.google.internal/computeMetadata/v1/
# NoCloud:        本機檔案系統(ISO 或種子目錄)

# 檢查 cloud-init 正在使用的資料來源
$ cloud-init query ds

# 查詢實例中繼資料
$ cloud-init query instance-id
$ cloud-init query local-hostname

Cloud-init 在四個不同的啟動階段中執行:

# 階段 1:Generator — 決定 cloud-init 是否應在此次開機時執行
# 階段 2:Local — 在網路啟動之前執行;套用網路配置
# 階段 3:Network — 網路啟動後執行;從遠端資料來源擷取中繼資料
# 階段 4:Config 和 Final — 執行套件安裝、配置管理、使用者腳本

# 查看啟動階段時間線
$ cloud-init analyze show

# 檢查 cloud-init 狀態
$ cloud-init status
$ cloud-init status --long

使用者資料格式

使用者資料是向 cloud-init 提供自訂配置的主要機制。它支援多種格式,其中 cloud-config YAML 和 shell 腳本最為常用。

# 格式 1:Shell 腳本(以 #! 開頭)
#!/bin/bash
apt-get update
apt-get install -y nginx
systemctl enable --now nginx

# 格式 2:Cloud-config YAML(以 #cloud-config 開頭)
#cloud-config
hostname: web-server-01
users:
  - name: deploy
    groups: sudo
    shell: /bin/bash
    sudo: ALL=(ALL) NOPASSWD:ALL
    ssh_authorized_keys:
      - ssh-ed25519 AAAAC3... deploy@workstation

packages:
  - nginx
  - certbot

package_update: true
package_upgrade: true

write_files:
  - path: /etc/nginx/sites-available/default
    content: |
      server {
          listen 80;
          server_name _;
          root /var/www/html;
      }

runcmd:
  - systemctl enable --now nginx

final_message: "Cloud-init 在 $UPTIME 秒內完成"

Cloud-config 是首選格式,因為它是宣告式的、冪等的,並且能優雅地處理錯誤情況。Shell 腳本提供最大的靈活性,但需要仔細的錯誤處理。

Cloud-Config 模組

Cloud-config 模組是 cloud-init 配置的建構區塊。每個模組處理實例設定的特定方面。

#cloud-config

# 使用者和群組管理
users:
  - default
  - name: admin
    gecos: Admin User
    groups: [sudo, docker]
    shell: /bin/bash
    sudo: ALL=(ALL) NOPASSWD:ALL
    ssh_authorized_keys:
      - ssh-ed25519 AAAAC3... admin@company

# SSH 配置
ssh_pwauth: false
disable_root: true

# 磁碟和檔案系統
disk_setup:
  /dev/vdb:
    table_type: gpt
    layout: true
mounts:
  - [/dev/vdb1, /data, ext4, "defaults,noatime", "0", "2"]

# 系統配置
timezone: Asia/Taipei
locale: en_US.UTF-8

# 寫入檔案
write_files:
  - path: /etc/sysctl.d/99-custom.conf
    content: |
      net.core.somaxconn = 65535
      vm.swappiness = 10

# 執行命令
runcmd:
  - sysctl --system
  - systemctl daemon-reload

用於本機 VM 的 NoCloud 資料來源

NoCloud 資料來源允許您在本機 QEMU/KVM 虛擬機器上使用 cloud-init,無需任何雲端基礎架構。您透過 ISO 映像或本機種子目錄提供使用者資料和中繼資料。

# 建立種子目錄
$ mkdir -p /tmp/cloud-init-seed

# 建立 meta-data 檔案
$ cat > /tmp/cloud-init-seed/meta-data <<EOF
instance-id: test-vm-001
local-hostname: test-vm
EOF

# 建立 user-data 檔案
$ cat > /tmp/cloud-init-seed/user-data <<EOF
#cloud-config
hostname: test-vm
users:
  - name: testuser
    groups: sudo
    sudo: ALL=(ALL) NOPASSWD:ALL
    ssh_authorized_keys:
      - ssh-ed25519 AAAAC3... test@workstation
packages:
  - htop
  - vim
EOF

# 產生種子 ISO
$ genisoimage -output seed.iso -volid cidata -joliet -rock 
    /tmp/cloud-init-seed/user-data 
    /tmp/cloud-init-seed/meta-data

# 下載 Ubuntu 雲端映像並啟動 VM
$ wget https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img
$ cp jammy-server-cloudimg-amd64.img test-vm.qcow2
$ qemu-img resize test-vm.qcow2 20G

$ qemu-system-x86_64 
    -enable-kvm -m 2048 -smp 2 -cpu host 
    -drive file=test-vm.qcow2,format=qcow2,if=virtio 
    -drive file=seed.iso,format=raw,if=virtio 
    -netdev user,id=net0,hostfwd=tcp::2222-:22 
    -device virtio-net-pci,netdev=net0 
    -nographic

# 開機後 SSH 進入 VM
$ ssh -p 2222 testuser@localhost

除錯 Cloud-Init

當 cloud-init 行為不如預期時,多個日誌檔案和診斷命令可幫助識別問題。

# 主要日誌檔案
$ sudo cat /var/log/cloud-init.log | tail -50

# 輸出日誌
$ sudo cat /var/log/cloud-init-output.log

# 驗證使用者資料語法
$ cloud-init schema --config-file user-data.yml

# 清除並重新執行 cloud-init
$ sudo cloud-init clean
$ sudo cloud-init init
$ sudo cloud-init modules --mode=config
$ sudo cloud-init modules --mode=final

# 查看每個實例的資料
$ ls /var/lib/cloud/instance/

重點摘要

  • Cloud-init 是自動化 VM 和雲端實例初始化的行業標準,從特定平台的資料來源(包括 AWS、Azure、GCP、OpenStack 和本機 NoCloud 資料來源)擷取配置。
  • 四個啟動階段(local、network、config、final)按順序執行,網路相關操作延遲到網路可用時才執行。
  • 使用者資料支援 shell 腳本(#!)、cloud-config YAML(#cloud-config)和 MIME multipart 格式;cloud-config 因其宣告式、冪等的特性而被推薦。
  • Cloud-config 模組以定義好的可靠順序處理使用者建立、SSH 金鑰部署、套件安裝、檔案寫入、磁碟設定和命令執行。
  • NoCloud 資料來源搭配種子 ISO 可在本機 QEMU/KVM 虛擬機器上使用 cloud-init,無需雲端基礎架構即可測試和開發 cloud-init 配置。

下一步

您已完成虛擬化與容器模組。您現在對虛擬化有了全面的了解,從硬體層級的概念到 Hypervisor 管理、系統容器、應用程式容器、多容器編排和自動化實例佈建。這些技能構成了現代基礎架構工程、雲端運算和 DevOps 實踐的基礎。

日本語

概要

  • 学習内容:cloud-init を使用した仮想マシンとクラウドインスタンスの初期化の自動化方法。データソースの概念、ブートステージ、ユーザーデータフォーマット、cloud-config モジュール、ローカル VM 用の NoCloud データソース、デバッグ技術を含みます。
  • 前提条件:QEMU/KVM 仮想マシン(レッスン55)、Libvirt と virsh 管理(レッスン56)、基本的な YAML の知識
  • 推定読了時間:22分

はじめに

仮想マシンやクラウドインスタンスを起動すると、汎用のベースイメージから始まり、特定の役割に合わせてカスタマイズする必要があります:ホスト名の設定、ユーザーアカウントの作成、パッケージのインストール、設定ファイルの書き込み、サービスの起動です。これを手動で行うのは遅くてエラーが発生しやすいです。Cloud-init はこのプロセス全体を自動化し、初回ブート時(およびオプションで以降のブート時)に実行して、汎用イメージを完全に構成された目的特化型マシンに変換します。

Cloud-init は AWS、Azure、GCP、OpenStack を含むすべての主要クラウドプラットフォームでインスタンス初期化の業界標準です。Ubuntu、Debian、RHEL、CentOS、その他のディストリビューションのほぼすべてのクラウドイメージにプリインストールされています。クラウド環境以外でも、cloud-init は NoCloud データソースを使用してローカルの QEMU/KVM 仮想マシンでも同様に機能し、自動テスト、開発、オンプレミスインフラに不可欠です。

このレッスンでは、cloud-init のデータソースアーキテクチャとブートステージを理解し、シェルスクリプトと cloud-config YAML の両方のフォーマットでユーザーデータを作成し、最も便利な cloud-config モジュールを探索し、ローカル仮想マシン用の NoCloud データソースを構成し、問題が発生した時の cloud-init のデバッグ方法を学びます。

データソースとブートステージ

Cloud-init はデータソースから構成を取得します。これはインスタンスが実行されているプラットフォームが提供するメタデータサービスへのインターフェースです。各クラウドプラットフォームには独自のメタデータ API エンドポイントがあり、cloud-init はそれぞれにデータソース実装を含んでいます。

# 一般的なデータソース
# AWS EC2:        http://169.254.169.254/latest/meta-data/
# Azure IMDS:     http://169.254.169.254/metadata/instance
# GCP:            http://metadata.google.internal/computeMetadata/v1/
# NoCloud:        ローカルファイルシステム(ISO またはシードディレクトリ)

# cloud-init が使用しているデータソースを確認
$ cloud-init query ds

# インスタンスメタデータをクエリ
$ cloud-init query instance-id
$ cloud-init query local-hostname

Cloud-init は4つの異なるブートステージで実行されます:

# ステージ 1:Generator — cloud-init がこのブートで実行すべきかを決定
# ステージ 2:Local — ネットワーク起動前に実行;ネットワーク構成を適用
# ステージ 3:Network — ネットワーク起動後に実行;リモートデータソースからメタデータを取得
# ステージ 4:Config と Final — パッケージインストール、構成管理、ユーザースクリプトを実行

# ブートステージのタイムラインを表示
$ cloud-init analyze show

# cloud-init ステータスを確認
$ cloud-init status
$ cloud-init status --long

ユーザーデータフォーマット

ユーザーデータは cloud-init にカスタム構成を提供する主要なメカニズムです。複数のフォーマットをサポートし、cloud-config YAML とシェルスクリプトが最も一般的です。

# フォーマット 1:シェルスクリプト(#! で始まる)
#!/bin/bash
apt-get update
apt-get install -y nginx
systemctl enable --now nginx

# フォーマット 2:Cloud-config YAML(#cloud-config で始まる)
#cloud-config
hostname: web-server-01
users:
  - name: deploy
    groups: sudo
    shell: /bin/bash
    sudo: ALL=(ALL) NOPASSWD:ALL
    ssh_authorized_keys:
      - ssh-ed25519 AAAAC3... deploy@workstation

packages:
  - nginx
  - certbot

package_update: true
package_upgrade: true

write_files:
  - path: /etc/nginx/sites-available/default
    content: |
      server {
          listen 80;
          server_name _;
          root /var/www/html;
      }

runcmd:
  - systemctl enable --now nginx

final_message: "Cloud-init が $UPTIME 秒で完了しました"

Cloud-config は宣言的で冪等であり、エラーケースを優雅に処理するため、推奨されるフォーマットです。シェルスクリプトは最大の柔軟性を提供しますが、慎重なエラー処理が必要です。

Cloud-Config モジュール

Cloud-config モジュールは cloud-init 構成の構成要素です。各モジュールはインスタンスセットアップの特定の側面を処理します。

#cloud-config

# ユーザーとグループ管理
users:
  - default
  - name: admin
    gecos: Admin User
    groups: [sudo, docker]
    shell: /bin/bash
    sudo: ALL=(ALL) NOPASSWD:ALL
    ssh_authorized_keys:
      - ssh-ed25519 AAAAC3... admin@company

# SSH 構成
ssh_pwauth: false
disable_root: true

# ディスクとファイルシステム
disk_setup:
  /dev/vdb:
    table_type: gpt
    layout: true
mounts:
  - [/dev/vdb1, /data, ext4, "defaults,noatime", "0", "2"]

# システム構成
timezone: Asia/Taipei
locale: en_US.UTF-8

# ファイル書き込み
write_files:
  - path: /etc/sysctl.d/99-custom.conf
    content: |
      net.core.somaxconn = 65535
      vm.swappiness = 10

# コマンド実行
runcmd:
  - sysctl --system
  - systemctl daemon-reload

ローカル VM 用 NoCloud データソース

NoCloud データソースにより、クラウドインフラストラクチャなしでローカルの QEMU/KVM 仮想マシンで cloud-init を使用できます。ISO イメージまたはローカルシードディレクトリを通じてユーザーデータとメタデータを提供します。

# シードディレクトリを作成
$ mkdir -p /tmp/cloud-init-seed

# meta-data ファイルを作成
$ cat > /tmp/cloud-init-seed/meta-data <<EOF
instance-id: test-vm-001
local-hostname: test-vm
EOF

# user-data ファイルを作成
$ cat > /tmp/cloud-init-seed/user-data <<EOF
#cloud-config
hostname: test-vm
users:
  - name: testuser
    groups: sudo
    sudo: ALL=(ALL) NOPASSWD:ALL
    ssh_authorized_keys:
      - ssh-ed25519 AAAAC3... test@workstation
packages:
  - htop
  - vim
EOF

# シード ISO を生成
$ genisoimage -output seed.iso -volid cidata -joliet -rock 
    /tmp/cloud-init-seed/user-data 
    /tmp/cloud-init-seed/meta-data

# Ubuntu クラウドイメージをダウンロードして VM を起動
$ wget https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img
$ cp jammy-server-cloudimg-amd64.img test-vm.qcow2
$ qemu-img resize test-vm.qcow2 20G

$ qemu-system-x86_64 
    -enable-kvm -m 2048 -smp 2 -cpu host 
    -drive file=test-vm.qcow2,format=qcow2,if=virtio 
    -drive file=seed.iso,format=raw,if=virtio 
    -netdev user,id=net0,hostfwd=tcp::2222-:22 
    -device virtio-net-pci,netdev=net0 
    -nographic

# ブート後に VM に SSH 接続
$ ssh -p 2222 testuser@localhost

Cloud-Init のデバッグ

cloud-init が期待通りに動作しない場合、いくつかのログファイルと診断コマンドが問題の特定に役立ちます。

# 主要ログファイル
$ sudo cat /var/log/cloud-init.log | tail -50

# 出力ログ
$ sudo cat /var/log/cloud-init-output.log

# ユーザーデータの構文を検証
$ cloud-init schema --config-file user-data.yml

# cloud-init をクリーンして再実行
$ sudo cloud-init clean
$ sudo cloud-init init
$ sudo cloud-init modules --mode=config
$ sudo cloud-init modules --mode=final

# インスタンスごとのデータを表示
$ ls /var/lib/cloud/instance/

重要ポイント

  • Cloud-init は VM とクラウドインスタンスの初期化を自動化する業界標準であり、AWS、Azure、GCP、OpenStack、ローカル NoCloud データソースを含むプラットフォーム固有のデータソースから構成を取得します。
  • 4つのブートステージ(local、network、config、final)は順番に実行され、ネットワーク依存の操作はネットワークが利用可能になるまで延期されます。
  • ユーザーデータはシェルスクリプト(#!)、cloud-config YAML(#cloud-config)、MIME マルチパートフォーマットをサポートします。cloud-config はその宣言的で冪等な性質から推奨されます。
  • Cloud-config モジュールは、定義された信頼できる順序でユーザー作成、SSH キーデプロイメント、パッケージインストール、ファイル書き込み、ディスクセットアップ、コマンド実行を処理します。
  • シード ISO 付きの NoCloud データソースにより、ローカルの QEMU/KVM 仮想マシンで cloud-init を使用でき、クラウドインフラなしで cloud-init 構成のテストと開発が可能になります。

次のステップ

仮想化とコンテナモジュールを修了しました。ハードウェアレベルの概念からハイパーバイザー管理、システムコンテナ、アプリケーションコンテナ、マルチコンテナオーケストレーション、自動化されたインスタンスプロビジョニングまで、仮想化の包括的な理解を身につけました。これらのスキルは、現代のインフラストラクチャエンジニアリング、クラウドコンピューティング、DevOps プラクティスの基盤を形成します。

You Missed