
- はじめに
- cloud-initとは
- Proxmox VEで簡単にcloud-initを体験してみよう
- cloud-initの仕組み
- cloud-initをカスタムしてみよう
- cloud-initのデバッグ
- さいごに
はじめに
ITインフラ本部 インフラ部 ネットワークグループ NREチームの山口です。私の所属しているチームではネットワーク運用の自動化やそれらにまつわるシステムの開発をしています。
本記事では、仮想マシンの初期設定を自動化するcloud-initについて、オープンソースの仮想化プラットフォーム「Proxmox Virtual Environment(以下、Proxmox VEと呼ぶ)」1を使って解説します。Proxmox VEは企業環境から自宅ラボまで幅広く使われている実用的な仮想化基盤です。
記事の読み方:
- cloud-initの技術的な仕組みを知りたい方 → 「cloud-initの仕組み」から
- Proxmox VEでの実践的な活用法を知りたい方 → 「Proxmox VEで簡単にcloud-initを体験してみよう」と合わせて「cloud-initをカスタムしてみよう」へ
cloud-initとは
cloud-initは、クロスプラットフォームのクラウドインスタンス初期化において広く採用されているマルチディストリビューション方式です。主要なパブリッククラウド、プライベートクラウド、ベアメタルインストールなど、幅広い環境でサポートされています。cloud-initはインスタンスの起動時に、実行されているクラウドを識別し、それに応じてシステムを初期化し、ネットワーク、ストレージ、SSHキー、パッケージなどのプロビジョニングを行います。
Proxmox VEでもcloud-initを正式にサポートしています。cloud-initを利用することでインスタンスを作成したあとに毎回行っている共通の設定やパッケージのインストールを自動化できます。
Proxmox VEで簡単にcloud-initを体験してみよう
cloud-initの詳細に入る前にProxmox VEの機能を使って簡単にcloud-initによるインスタンスの管理を体験してみましょう。これだけでも十分に活用できるはずです。
cloud-initイメージの取得
まずはcloud-initのベースイメージを準備します。様々なディストリビューションからcloud-initとしてすぐに利用可能なイメージが配布されています。今回はUbuntu24.04 nobleのcloud-initイメージを利用します。下記のリンクから実行するサーバーのCPUアーキテクチャに一致したイメージをダウンロードしてください。
https://cloud-images.ubuntu.com/noble/current/
イメージのダウンロード、アップロードはWebUIから行うこともできます。
- 直接WebUIからダウンロードする場合: サイドバー localストレージ > ISO Images > Download from URL
- ローカルからWebUIにアップロードする場合: サイドバー localストレージ > ISO Images > Upload

WebUIからイメージを保存した場合は下記のパスに格納されます。
/var/lib/vz/template/iso/
CLIからコマンドでダウンロードする場合:
Proxmox VEノードにログインして作業します。
# イメージの格納先は好きな場所で大丈夫です。テンプレート作成時にパスを指定するため、場所はメモしておいてください。 wget https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img -P /var/lib/vz/template/iso/
テンプレートの作成
テンプレートとはインスタンスをLinked Cloneする際のベースとなる読み取り専用のインスタンスです。cloud-initの利用に必要なわけではありませんが、テンプレートからクローンしてインスタンスを作成することで基本の設定を共通化できます。また、クローン方式をLinked CloneにすることでCopy On Writeによって記憶領域をすべてコピーする必要がなくなり、一部をテンプレートから参照するため、起動を高速化できます。2
今回作成するテンプレートのパラメーターは公式ドキュメントに記載された手順を参考に記載しています。それぞれのパラメーターは必要に応じて変更してください。またこれらの設定はWebUIからも変更できます。
Proxmox VEノードにログインして作業します。
# メモリー、接続ネットワーク、SCSIコントローラーの設定 qm create 9000 --name ubuntu24-04 --memory 2048 --net0 virtio,bridge=vmbr0 --scsihw virtio-scsi-pci # ダウンロードしたディスクイメージをローカルLVMストレージにインポートし、SCSIドライブとして接続 qm set 9000 --scsi0 local-lvm:0,import-from=/var/lib/vz/template/iso/noble-server-cloudimg-amd64.img # cloud-initのCDドライブを構成 qm set 9000 --ide2 local-lvm:cloudinit # シリアルコンソールを設定。qm terminal <vmid> でCLIからコンソールに接続できるようになる qm set 9000 --serial0 socket --vga serial0 # Cloud-Initイメージから直接起動できるようにするため、Bootパラメータを order=scsi0 に設定 qm set 9000 --boot order=scsi0 # デフォルトユーザー設定 qm set 9000 --ciuser init-user --cipassword your_password # テンプレートの作成 qm template 9000
つぎのコマンドで設定を確認します。
qm config 9000
インスタンスのクローン
テンプレートからクローンを作成することで同じ設定のインスタンスを作成できます。
WebUIからクローンを作成する:
対象のテンプレートを右クリック > Cloneを選択 > ModeをLinked Cloneにする > Cloneをクリック。

CLIからクローンを作成する:
つぎのコマンドでテンプレートからクローンします(デフォルト Linked Clone)。
qm clone 9000 123 --name ubuntu-clone
cloud-initのパラメーターを変更する
cloud-initのパラメーターはインスタンス作成後でも変更できます。ただし設定が反映されるのは再起動後からです。
この際、内部的にはuser-dataやnetwork-configと呼ばれるcloud-configフォーマットの構成ファイルが自動的に作成され内容が編集されています。これについては後述します。
WebUIからcloud-initのパラメーターを変更する:
対象のインスタンス > Cloud-Init > 対象のパラメーターを選択 > Editをクリック。

CLIからcloud-initのパラメーターを変更する:
qm set 123 \ --ciuser change-user \ --cipassword your_password \ --sshkey ~/.ssh/id_rsa.pub \ --ipconfig0 ip=192.0.2.1/24,gw=192.0.2.254 \ --ciupgrade false \ --nameserver 192.0.2.53 \ --searchdomain example.com
上から:
- デフォルトユーザー
- デフォルトユーザーパスワード
- SSH公開鍵
- IPアドレス・GW
- 最初の起動後に自動でパッケージアップグレードを実行するか
- DNSサーバー
- DNS検索ドメイン
インスタンスの起動:
qm start 123
インスタンスが起動するとcloud-initで設定したパラメーターが反映されているはずです。
以上がProxmox VEでの基本的なcloud-initの活用でした。ここからはcloud-initの内部的な仕組みを詳しく見ていきましょう。
cloud-initの仕組み
ここからはcloud-initの仕組みについて詳しく見ていきます。cloud-initで利用する構成ファイルの書き方やインスタンスが起動された後、実際にどのようにcloud-initが動作するかを一緒に学んでいきましょう。
cloud-config
cloud-initによって構成されるパラメーターの設定は「cloud-config」と呼ばれる特定のユーザーデータ形式によって管理されています。cloud-configはYAML形式のファイルです。
下記は先ほどの手順で作成したインスタンスのcloud-configのうちuser-dataとnetwork-configです。入力したパラメーターが記述されているのがわかります。今回紹介した2つの構成ファイルの他に2種類、合計4種類の構成ファイルがありますが、それらは後ほど紹介します。
- user-data
#cloud-config hostname: ubuntu-clone manage_etc_hosts: true fqdn: ubuntu-clone.example.com user: change-user password: $5XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ssh_authorized_keys: - ssh-rsa AAAAB3XXXXXXXXX... chpasswd: expire: False users: - default
- network-config
network: version: 2 ethernets: eth0: match: macaddress: "00:00:5e:00:53:01" addresses: - "192.0.2.1/24" nameservers: addresses: - 192.0.2.53 search: - example.com set-name: "eth0" routes: - to: "default" via: "192.0.2.254"
User-data formats
cloud-configの書式を見ていきます。
1行目に記載されているコメント行は使用されているユーザーデータ形式を表すヘッダーとして機能します。これは必ず指定しなければなりません。
#cloud-config
cloud-initはこのヘッダーを確認することでユーザーデータ形式を特定し処理を行います。今回は基本的なcloud-configの説明にとどめて、それぞれの詳細には立ち入りませんが、Jinjaテンプレートを利用して動的にユーザーデータを構成することや複数のユーザーデータをまとめたり様々な機能をサポートしています。
| USER-DATA FORMAT | HEADER | CONTENT-TYPE |
|---|---|---|
| Cloud config data | #cloud-config |
text/cloud-config |
| User-data script | #! |
text/x-shellscript |
| Cloud boothook | #cloud-boothook |
text/cloud-boothook |
| MIME multi-part | Content-Type: multipart/mixed |
multipart/mixed |
| Cloud config archive | #cloud-config-archive |
text/cloud-config-archive |
| Jinja template | ## template: jinja |
text/jinja2 |
| Include file | #include |
text/x-include-url |
| Part handler | #part-handler |
text/part-handler |
引用:cloud-init documentation. User-data formats.
Modules
2行目以降に続く行はモジュールを示します。キーバリュー方式でキーにモジュール名、バリューにその引数を渡します。ここで指定したモジュールがインスタンスを起動した際に各フェーズごとに実行され、インスタンスを構成します。
例えば、起動時に特定のコマンドを実行したい場合はruncmdモジュールを利用します。
#cloud-config runcmd: - [ls, -l, /]
こちらではpackageモジュールを利用して下記を実行します。
- パッケージ情報を更新
- slをインストール
#cloud-config package_update: true packages: - sl
このようにインスタンスの初期構成に必要な処理を簡単に記述できます。
なお、実行タイミングはモジュールによって異なります。例えば上記の例では runcmd モジュールが先にconfigステージで動作し、そのあとに package モジュールがfinalステージで動作します。この実行タイミングは /etc/cloud/cloud.cfg で定義されています。
# The modules that run in the 'init' stage cloud_init_modules: - seed_random - bootcmd - write_files - growpart - resizefs - disk_setup - mounts - set_hostname - update_hostname - update_etc_hosts - ca_certs - rsyslog - users_groups - ssh - set_passwords # The modules that run in the 'config' stage cloud_config_modules: - wireguard - snap - ubuntu_autoinstall - ssh_import_id - keyboard - locale - grub_dpkg - apt_pipelining - apt_configure - ubuntu_pro - ntp - timezone - disable_ec2_metadata - runcmd - byobu # The modules that run in the 'final' stage cloud_final_modules: - package_update_upgrade_install - fan - landscape - lxd - ubuntu_drivers - write_files_deferred - puppet - chef - ansible - mcollective - salt_minion - reset_rmc - scripts_vendor - scripts_per_once - scripts_per_boot - scripts_per_instance - scripts_user - ssh_authkey_fingerprints - keys_to_console - install_hotplug - phone_home - final_message - power_state_change
利用可能なモジュールの詳細については下記のリンクを参照してください。
cloud-init documentation. Module reference.
cloud-initステージ遷移
cloud-initは5つのブートステージがあります。先ほどモジュールの紹介の中で、実行タイミングがモジュールによって異なるという話をしました。その実行タイミングというのが各ブートステージに当たります。
下記はcloud-initのブートステージの遷移を表した図です。

画像引用:cloud-init documentation. Boot stages
- Detect 最初のステージです。ds-identifyと呼ばれるプラットフォーム識別ツールが実行されます。このツールは、インスタンスがどのプラットフォームで実行されているかを検出します。このツールはinitシステムに統合されており、プラットフォームが見つからない場合はcloud-initを無効にし、有効なプラットフォームが検出された場合はcloud-initを有効にします。
- Local このステージではローカルのデータソースの探索の実行とネットワーク構成をシステムに適用します。データソースはつぎの項で解説します。
- Network
このステージでは検出されたユーザーデータを処理します。また、
cloud_init_modulesにリストされたモジュールが実行されます。これらを処理するため、この時点で設定されているすべてのネットワークがオンラインになっている必要があります。このステージが完了すると、シリアルコンソールやSSH経由でシステムにアクセスできるようになります。 - Config
このステージは
cloud_config_modulesにリストされているモジュールを実行します。 - Final
最後のステージです。
cloud_final_modulesにリストされているモジュールを実行します。
データソースとランタイム構成ファイル
冒頭でも紹介したとおり「cloud-initは、クロスプラットフォームのクラウドインスタンス初期化において広く採用されているマルチディストリビューション方式です。」
そのためプラットフォームごとに対応した データソース により自動的にランタイム構成ファイルを検出します。3
Proxmox VEでは2つのデータソースをサポートしています。4
- NoCloud:Linuxでインスタンスを作成した場合のデフォルト
- configdrive2:Windowsでインスタンスを作成した場合のデフォルト
NoCloudでは実行時に4つのランタイム構成(cloud-config)ファイルを検出し、インスタンスを構成します。5ProxmoxVEでは qm set コマンドの --cicustom オプションでランタイム構成ファイルをインスタンスに渡し、インスタンスの起動時にこれをマウントすることでローカルからランタイム構成ファイルを検出できます。
qm set 9000 --cicustom "user=<volume>,network=<volume>,meta=<volume>,vendor=<volume>"
それぞれのランタイム構成ファイルの役割はつぎのとおりです。
- user-data: ユーザーがインスタンスを構成できるようにする構成ファイルです。Proxmox VE のWebUIや
qm setコマンドからインスタンスのcloud-configを操作した場合、自動的にこのファイルが更新されます。 - meta-data: クラウドからインスタンスに提供される情報を含む構成ファイルです。インスタンスIDと、その他のクラウド固有のキーを含める必要があります。これは Proxmox VEによって自動的に作成されるため基本的に変更する必要はありません。
- vendor-data: クラウド固有のデフォルト設定を提供するために使用できます。記述する内容は user-data と同じ形式です。この設定はユーザーデータによって上書きされます。
- network-config: クラウド固有のネットワーク設定が行われます。Proxmox VE のWebUIや
qm setコマンドからインスタンスのcloud-configを操作した場合、自動的にこのファイルが更新されます。
cloud-initをカスタムしてみよう
cloud-initの動作を確認するために自作のcloud-configを作成し、実際にインスタンスのvendor-dataに登録します。なぜuser-dataではないのかというと、user-dataを更新するとカスタムのcloud-configが優先され、WebUIや qm set コマンドで設定したcloud-initのパラメーターが上書きされてしまうためです。
そのため今回はvendor-dataを利用します。実際に利用する場合は要件に合わせて使い分けてください。
カスタムのcloud-configファイルを作成します。ファイルは /var/lib/vz/snippets/ に配置してください。
vi /var/lib/vz/snippets/custom-vendor.yml
ここではつぎのモジュールの動作を確認します。この3つのモジュールはそれぞれ異なるステージで実行されます。
- Networkステージ:write_files
- Configステージ:runcmd
- Finalステージ:packages (slコマンドをインストール)
write_filesのcontentをencoding: b64 としているため、文字列をBase64で符号化したものを設定します。実際に書き込まれるときはデコードされ、元の文字列が見えるはずです。
#cloud-config runcmd: - [ls, -l, /] write_files: - encoding: b64 content: | 44GT44GT44G+44Gn6KiY5LqL44KS6Kqt44KT44Gn44GP44Gg44GV44KK44GC44KK44GM44Go44GG 44GU44GW44GE44G+44GZ77yB44Gd44KT44Gq5o6i5rGC5b+D44KS44KC44Gj44Gf44GC44Gq44Gf 44Gr77yBCgpETU0g44Gn44Gv44CB44ON44OD44OI44Ov44O844Kv44Kw44Or44O844OX44GuIE5S Re+8iE5ldHdvcmsgUmVsaWFiaWxpdHkgRW5naW5lZXJpbmfvvInjg4Hjg7zjg6Djga7ku7LplpPj gpLli5/pm4bjgZfjgabjgYTjgb7jgZnjgIIKTlJFIOOBr+ODjeODg+ODiOODr+ODvOOCr+OBruS/ oemgvOaAp+OCkueiuuS/neOBmeOCi+OBoOOBkeOBp+OBquOBj+OAgeiHquWLleWMluOChOOCveOD leODiOOCpuOCp+OCoueahOOBquOCouODl+ODreODvOODgeOBp+mBi+eUqOOCkuaUueWWhOOBl+OB puOBhOOBj+OBk+OBqOOCkuODn+ODg+OCt+ODp+ODs+OBqOOBl+OBpuOBhOOBvuOBmeOAggoK44ON 44OD44OI44Ov44O844Kv44Gu55+l6K2Y44KS44OZ44O844K544Gr44CB44KC44Gj44Go6Ieq5YuV 5YyW44KS5Y+W44KK5YWl44KM44Gm44G/44Gf44GE5pa5CuOCs+ODvOODieOChOODhOODvOODq+OC kumnhuS9v+OBl+OBpuOAjOODjeODg+ODiOODr+ODvOOCr+mBi+eUqOOCkumAsuWMluOBleOBm+OB n+OBhOOAjeOBqOaAneOBo+OBpuOBhOOCi+aWuQrjgqTjg7Pjg5Xjg6njga7mlLnlloTjgavkuLvk vZPnmoTjgavlj5bjgorntYTjgb/jgZ/jgYTmlrkK44ON44OD44OI44Ov44O844Kv5qmf5Zmo44Gu 44Oh44OI44Oq44Kv44K544Gu5Y+O6ZuG44KE5rS755So44KS44GX44Gf44GE44Go5oCd44Gj44Gm 44GE44KL5pa5CuOBneOCk+OBquaAneOBhOOCkuaMgeOBo+OBn+aWueOBr+OAgeOBnOOBsuOCq+OC uOODpeOCouODq+mdouirh+OCguOChOOBo+OBpuOBhOOCi+OBruOBp+awl+i7veOBq+OBqeOBhuOB nu+8gQpodHRwczovL3BpdHRhLm1lL21hdGNoZXMvVFhneG9LU2NHaUZFCg== owner: root:root path: /var/tmp/message.txt permissions: '0644' package_update: true packages: - sl
つぎのコマンドでcloud-configを登録します。Proxmox VE Ver8.4時点ではWebUIからの登録はできません。 qm コマンドを使用します。
qm set 9000 --cicustom "vendor=local:snippets/custom-vendor.yml"
インスタンスをクローンして起動します。パッケージをインストールするためにインターネットに接続する必要があります。DHCPを利用するかIPアドレスの設定をしてください。
qm clone 9000 124 --name ubuntu-custom qm set 124 --ipconfig0 ip=192.0.2.124/24,gw=192.0.2.254 --nameserver 192.0.2.53 qm start 124 # シリアルコンソールで接続。接続後は Ctrl+o で接続を終了できます。 qm terminal 124
起動直後にシリアルコンソールでターミナルに接続するとcloud-initのログが出力されているのがわかります。このログは保存されているため後から見返すことができます。詳細については「cloud-initのデバッグ」の項で解説します。
インスタンスにログインしたあと下記のコマンドを実行し、 status: done と出力されていればcloud-initの処理はすべて終了しています。
cloud-init status
ファイルを見てみましょう。先ほど custom-vendor.yml 内の write_files モジュールで定義したファイルが作成されています🎉
中に何が書かれているかはぜひご自身で確かめてください!
init-user@ubuntu-custom:~$ ls /var/tmp/message.txt
/var/tmp/message.txt
また、 sl コマンドも実行可能になっているはずです。
cloud-initのデバッグ
今までcloud-initの基本的な仕組みと簡単なcloud-configの動作を見てきました。実行時のログをみて裏側でどのように処理されているか確認してみましょう。
/var/log 配下に2つのログファイルが作成されています。
init-user@ubuntu-custom:~$ sudo ls /var/log/cloud-init*
/var/log/cloud-init-output.log /var/log/cloud-init.log
/var/log/cloud-init-output.log
このログはcloud-init実行時の各ステージからの出力をキャプチャしたものです。
ログを見ていくと runcmd で実行したコマンドの結果やパッケージのダウンロードログが出力されているのがわかります。
lsコマンド
total 76 lrwxrwxrwx 1 root root 7 Apr 22 2024 bin -> usr/bin drwxr-xr-x 2 root root 4096 Feb 26 2024 bin.usr-is-merged drwxr-xr-x 5 root root 4096 Nov 5 06:43 boot drwxr-xr-x 18 root root 3900 Nov 5 06:42 dev drwxr-xr-x 106 root root 4096 Nov 5 06:43 etc drwxr-xr-x 3 root root 4096 Nov 5 06:42 home lrwxrwxrwx 1 root root 7 Apr 22 2024 lib -> usr/lib drwxr-xr-x 2 root root 4096 Apr 8 2024 lib.usr-is-merged lrwxrwxrwx 1 root root 9 Apr 22 2024 lib64 -> usr/lib64 drwx------ 2 root root 16384 Oct 26 12:54 lost+found drwxr-xr-x 2 root root 4096 Oct 26 12:51 media drwxr-xr-x 2 root root 4096 Oct 26 12:51 mnt drwxr-xr-x 2 root root 4096 Oct 26 12:51 opt dr-xr-xr-x 160 root root 0 Nov 5 06:41 proc drwx------ 3 root root 4096 Oct 26 12:54 root drwxr-xr-x 28 root root 860 Nov 5 06:43 run lrwxrwxrwx 1 root root 8 Apr 22 2024 sbin -> usr/sbin drwxr-xr-x 2 root root 4096 Mar 31 2024 sbin.usr-is-merged drwxr-xr-x 2 root root 4096 Nov 5 06:42 snap drwxr-xr-x 2 root root 4096 Oct 26 12:51 srv dr-xr-xr-x 13 root root 0 Nov 5 06:41 sys drwxrwxrwt 14 root root 4096 Nov 5 06:43 tmp drwxr-xr-x 12 root root 4096 Oct 26 12:51 usr drwxr-xr-x 13 root root 4096 Nov 5 06:41 var
- パッケージダウンロード
The following NEW packages will be installed: sl 0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded. Need to get 12.7 kB of archives. After this operation, 60.4 kB of additional disk space will be used. Get:1 http://archive.ubuntu.com/ubuntu noble/universe amd64 sl amd64 5.02-1 [12.7 kB] Fetched 12.7 kB in 0s (33.2 kB/s) Selecting previously unselected package sl. (Reading database ... 106393 files and directories currently installed.) Preparing to unpack .../archives/sl_5.02-1_amd64.deb ... Unpacking sl (5.02-1) ... Setting up sl (5.02-1) ... Processing triggers for man-db (2.12.0-4build2) ...
/var/log/cloud-init.log
実行された各アクションのデバッグ出力を含む非常に詳細なログです。このログを追うことで実際にcloud-initがどのように動作しているかを確認できます。少し覗いてみましょう。
これはログの最初の2行です。
PID1(systemd)によってinit-local(cloud-init-local.service)が実行されます。これが2つ目のブートステージであるLocalステージの開始です。
2025-11-05 06:42:00,986 - log_util.py[DEBUG]: Cloud-init v. 25.2-0ubuntu1~24.04.1 running 'init-local' at Wed, 05 Nov 2025 06:42:00 +0000. Up 14.94 seconds. 2025-11-05 06:42:00,987 - main.py[INFO]: PID [1] started cloud-init 'init-local'.
Localステージ
Localステージが始まると、キャッシュのチェックやログの書き込み行われます。下記はその処理後のログです。インスタンスのディストリビューションの特定と利用可能なデータソースを探索します。 Detected DataSourceNoCloud と表示されているとおり、NoCloudの検出に成功しています。
2025-11-05 06:42:01,022 - stages.py[DEBUG]: Using distro class <class 'cloudinit.distros.ubuntu.Distro'> 2025-11-05 06:42:01,023 - sources[DEBUG]: Looking for data source in: ['NoCloud', 'None'], via packages ['', 'cloudinit.sources'] that matches dependencies ['FILESYSTEM'] 2025-11-05 06:42:01,028 - sources[DEBUG]: Searching for local data source in: ['DataSourceNoCloud'] 2025-11-05 06:42:01,028 - handlers.py[DEBUG]: start: init-local/search-NoCloud: searching for local data from DataSourceNoCloud 2025-11-05 06:42:01,028 - sources[DEBUG]: Seeing if we can get any data from <class 'cloudinit.sources.DataSourceNoCloud.DataSourceNoCloud'> 2025-11-05 06:42:01,029 - sources[DEBUG]: Update datasource metadata and network config due to events: boot-new-instance 2025-11-05 06:42:01,029 - sources[DEBUG]: Detected DataSourceNoCloud
その後、ランタイム構成ファイルの探索が始まります。この環境では最終的に/dev/sr0(0番目のCD-ROM)からランタイム構成ファイルを検出しています。
2025-11-05 06:42:01,030 - util.py[DEBUG]: Reading from /var/lib/cloud/seed/nocloud/user-data (quiet=False) 2025-11-05 06:42:01,030 - util.py[DEBUG]: Reading from /var/lib/cloud/seed/nocloud/meta-data (quiet=False) 2025-11-05 06:42:01,030 - util.py[DEBUG]: Reading from /var/lib/cloud/seed/nocloud/vendor-data (quiet=False) 2025-11-05 06:42:01,030 - util.py[DEBUG]: Reading from /var/lib/cloud/seed/nocloud/network-config (quiet=False) 2025-11-05 06:42:01,030 - util.py[DEBUG]: Reading from /var/lib/cloud/seed/nocloud-net/user-data (quiet=False) 2025-11-05 06:42:01,030 - util.py[DEBUG]: Reading from /var/lib/cloud/seed/nocloud-net/meta-data (quiet=False) 2025-11-05 06:42:01,031 - util.py[DEBUG]: Reading from /var/lib/cloud/seed/nocloud-net/vendor-data (quiet=False) 2025-11-05 06:42:01,031 - util.py[DEBUG]: Reading from /var/lib/cloud/seed/nocloud-net/network-config (quiet=False) 2025-11-05 06:42:01,031 - subp.py[DEBUG]: Running command ['blkid', '-tTYPE=vfat', '-odevice'] with allowed return codes [0, 2] (shell=False, capture=True) 2025-11-05 06:42:01,104 - performance.py[DEBUG]: Running ['blkid', '-tTYPE=vfat', '-odevice'] took 0.073 seconds 2025-11-05 06:42:01,104 - subp.py[DEBUG]: Running command ['blkid', '-tTYPE=iso9660', '-odevice'] with allowed return codes [0, 2] (shell=False, capture=True) 2025-11-05 06:42:01,117 - performance.py[DEBUG]: Running ['blkid', '-tTYPE=iso9660', '-odevice'] took 0.013 seconds 2025-11-05 06:42:01,117 - subp.py[DEBUG]: Running command ['blkid', '-tLABEL=CIDATA', '-odevice'] with allowed return codes [0, 2] (shell=False, capture=True) 2025-11-05 06:42:01,129 - performance.py[DEBUG]: Running ['blkid', '-tLABEL=CIDATA', '-odevice'] took 0.012 seconds 2025-11-05 06:42:01,129 - subp.py[DEBUG]: Running command ['blkid', '-tLABEL=cidata', '-odevice'] with allowed return codes [0, 2] (shell=False, capture=True) 2025-11-05 06:42:01,142 - performance.py[DEBUG]: Running ['blkid', '-tLABEL=cidata', '-odevice'] took 0.012 seconds 2025-11-05 06:42:01,142 - subp.py[DEBUG]: Running command ['blkid', '-tLABEL_FATBOOT=cidata', '-odevice'] with allowed return codes [0, 2] (shell=False, capture=True) 2025-11-05 06:42:01,153 - performance.py[DEBUG]: Running ['blkid', '-tLABEL_FATBOOT=cidata', '-odevice'] took 0.012 seconds 2025-11-05 06:42:01,154 - DataSourceNoCloud.py[DEBUG]: Attempting to use data from /dev/sr0
実際に検出に利用されたコマンドを確認しましょう。sr0のディスクにcidataというラベルが付与されているのがわかります。cloud-initはこのラベルを目印にしてランタイム構成ファイルを探索しています。
init-user@ubuntu-custom:~$ blkid -tLABEL=cidata -odevice /dev/sr0 init-user@ubuntu-custom:~$ lsblk -f /dev/sr0 NAME FSTYPE FSVER LABEL UUID FSAVAIL FSUSE% MOUNTPOINTS sr0 iso966 cidata 2025-11-05-15-41-36-00
探索が完了するとディスクをマウントし、内容を読み取ります。読み取りが完了するとすぐにディスクはアンマウントされます。
2025-11-05 06:42:01,155 - subp.py[DEBUG]: Running command ['mount', '-o', 'ro', '-t', 'auto', '/dev/sr0', '/run/cloud-init/tmp/tmplxf0cgdy'] with allowed return codes [0] (shell=False, capture=True) 2025-11-05 06:42:01,256 - performance.py[DEBUG]: Running ['mount', '-o', 'ro', '-t', 'auto', '/dev/sr0', '/run/cloud-init/tmp/tmplxf0cgdy'] took 0.101 seconds 2025-11-05 06:42:01,257 - util.py[DEBUG]: Reading from /run/cloud-init/tmp/tmplxf0cgdy//user-data (quiet=False) 2025-11-05 06:42:01,261 - util.py[DEBUG]: Reading 230 bytes from /run/cloud-init/tmp/tmplxf0cgdy//user-data 2025-11-05 06:42:01,261 - util.py[DEBUG]: Reading from /run/cloud-init/tmp/tmplxf0cgdy//meta-data (quiet=False) 2025-11-05 06:42:01,265 - util.py[DEBUG]: Reading 54 bytes from /run/cloud-init/tmp/tmplxf0cgdy//meta-data 2025-11-05 06:42:01,265 - util.py[DEBUG]: Reading from /run/cloud-init/tmp/tmplxf0cgdy//vendor-data (quiet=False) 2025-11-05 06:42:01,269 - util.py[DEBUG]: Reading 1630 bytes from /run/cloud-init/tmp/tmplxf0cgdy//vendor-data 2025-11-05 06:42:01,270 - util.py[DEBUG]: Reading from /run/cloud-init/tmp/tmplxf0cgdy//network-config (quiet=False) 2025-11-05 06:42:01,274 - util.py[DEBUG]: Reading 328 bytes from /run/cloud-init/tmp/tmplxf0cgdy//network-config 2025-11-05 06:42:01,274 - subp.py[DEBUG]: Running command ['umount', '/run/cloud-init/tmp/tmplxf0cgdy'] with allowed return codes [0] (shell=False, capture=True)
実際にマウントして中身を確認してみましょう。
4つのランタイム構成ファイルが確認できました。さらにファイルの中を確認すると見覚えのある設定が記述されているはずです。
# CD-ROMのマウント init-user@ubuntu-custom:~$ sudo mkdir /mnt/config init-user@ubuntu-custom:~$ sudo mount /dev/sr0 /mnt/config mount: /mnt/config: WARNING: source write-protected, mounted read-only. # CD-ROMの中のファイルを確認 init-user@ubuntu-custom:~$ ls /mnt/config meta-data network-config user-data vendor-data
設定の読み取りが完了した後は最初にネットワークの設定が行われます。
※一部情報はマスクしています。
2025-11-05 06:42:01,356 - networking.py[DEBUG]: net: all expected physical devices present
2025-11-05 06:42:01,356 - stages.py[DEBUG]: applying net config names for {'version': 1, 'config': [{'type': 'physical', 'name': 'eth0', 'mac_address': 'xx:xx:xx:xx:xx:xx', 'subnets': [{'type': 'static', 'address': 'x.x.x.x', 'netmask': '255.255.255.0', 'gateway': 'x.x.x.x'}]}, {'type': 'nameserver', 'address': ['x.x.x.x'], 'search': ['xxxxxxxx']}]}
finish: init-local のログが出力され無事にLocalステージが終了しました。そのすぐ後にcloud-init.service が起動し、Networkステージが開始されます。
2025-11-05 06:42:02,502 - handlers.py[DEBUG]: finish: init-local: SUCCESS: searching for local datasources 2025-11-05 06:42:05,365 - log_util.py[DEBUG]: Cloud-init v. 25.2-0ubuntu1~24.04.1 running 'init' at Wed, 05 Nov 2025 06:42:05 +0000. Up 19.33 seconds. 2025-11-05 06:42:05,366 - main.py[INFO]: PID [1] started cloud-init 'init'.
Networkステージ
ユーザーデータファイルのヘッダーを確認しユーザーデータ形式ごとにそれぞれ処理を行っています。この後、ベンダーデータでも同様の処理を行います。
2025-11-05 06:42:05,518 - handlers.py[DEBUG]: start: init-network/consume-user-data: reading and applying user-data
2025-11-05 06:42:05,519 - stages.py[DEBUG]: Added default handler for {'text/cloud-config-jsonp', 'text/cloud-config'} from CloudConfigPartHandler: [['text/cloud-config', 'text/cloud-config-jsonp']]
2025-11-05 06:42:05,519 - stages.py[DEBUG]: Added default handler for {'text/x-shellscript'} from ShellScriptPartHandler: [['text/x-shellscript']]
2025-11-05 06:42:05,519 - stages.py[DEBUG]: Added default handler for {'text/x-shellscript-per-boot'} from ShellScriptByFreqPartHandler: [['text/x-shellscript-per-boot']]
2025-11-05 06:42:05,519 - stages.py[DEBUG]: Added default handler for {'text/x-shellscript-per-instance'} from ShellScriptByFreqPartHandler: [['text/x-shellscript-per-instance']]
2025-11-05 06:42:05,519 - stages.py[DEBUG]: Added default handler for {'text/x-shellscript-per-once'} from ShellScriptByFreqPartHandler: [['text/x-shellscript-per-once']]
2025-11-05 06:42:05,520 - stages.py[DEBUG]: Added default handler for {'text/cloud-boothook'} from BootHookPartHandler: [['text/cloud-boothook']]
2025-11-05 06:42:05,520 - stages.py[DEBUG]: Added default handler for {'text/jinja2'} from JinjaTemplatePartHandler: [['text/jinja2']]
2025-11-05 06:42:05,520 - handlers[DEBUG]: Calling handler CloudConfigPartHandler: [['text/cloud-config', 'text/cloud-config-jsonp']] (__begin__, None, 3) with frequency once-per-instance
2025-11-05 06:42:05,520 - handlers[DEBUG]: Calling handler ShellScriptPartHandler: [['text/x-shellscript']] (__begin__, None, 2) with frequency once-per-instance
2025-11-05 06:42:05,520 - handlers[DEBUG]: Calling handler ShellScriptByFreqPartHandler: [['text/x-shellscript-per-boot']] (__begin__, None, 2) with frequency once-per-instance
2025-11-05 06:42:05,520 - handlers[DEBUG]: Calling handler ShellScriptByFreqPartHandler: [['text/x-shellscript-per-instance']] (__begin__, None, 2) with frequency once-per-instance
2025-11-05 06:42:05,520 - handlers[DEBUG]: Calling handler ShellScriptByFreqPartHandler: [['text/x-shellscript-per-once']] (__begin__, None, 2) with frequency once-per-instance
2025-11-05 06:42:05,521 - handlers[DEBUG]: Calling handler BootHookPartHandler: [['text/cloud-boothook']] (__begin__, None, 2) with frequency once-per-instance
2025-11-05 06:42:05,521 - handlers[DEBUG]: Calling handler JinjaTemplatePartHandler: [['text/jinja2']] (__begin__, None, 3) with frequency once-per-instance
2025-11-05 06:42:05,521 - handlers[DEBUG]: {'MIME-Version': '1.0', 'Content-Type': 'text/cloud-config', 'Content-Disposition': 'attachment; filename="part-001"'}
2025-11-05 06:42:05,521 - handlers[DEBUG]: Calling handler CloudConfigPartHandler: [['text/cloud-config', 'text/cloud-config-jsonp']] (text/cloud-config, part-001, 3) with frequency once-per-instance
2025-11-05 06:42:05,521 - util.py[DEBUG]: Attempting to load yaml from string of length 230 with allowed root types (<class 'dict'>,)
2025-11-05 06:42:05,523 - cloud_config.py[DEBUG]: Merging by applying [('dict', ['replace']), ('list', []), ('str', [])]
2025-11-05 06:42:05,524 - handlers[DEBUG]: Calling handler CloudConfigPartHandler: [['text/cloud-config', 'text/cloud-config-jsonp']] (__end__, None, 3) with frequency once-per-instance
2025-11-05 06:42:05,537 - performance.py[DEBUG]: Dumping yaml took 0.013 seconds
2025-11-05 06:42:05,538 - util.py[DEBUG]: Writing to /var/lib/cloud/instances/2ded61c9a451b9df3586001759916bffb7efc148/cloud-config.txt - wb: [600] 266 bytes
2025-11-05 06:42:05,539 - handlers[DEBUG]: Calling handler ShellScriptPartHandler: [['text/x-shellscript']] (__end__, None, 2) with frequency once-per-instance
2025-11-05 06:42:05,539 - handlers[DEBUG]: Calling handler ShellScriptByFreqPartHandler: [['text/x-shellscript-per-boot']] (__end__, None, 2) with frequency once-per-instance
2025-11-05 06:42:05,539 - handlers[DEBUG]: Calling handler ShellScriptByFreqPartHandler: [['text/x-shellscript-per-instance']] (__end__, None, 2) with frequency once-per-instance
2025-11-05 06:42:05,539 - handlers[DEBUG]: Calling handler ShellScriptByFreqPartHandler: [['text/x-shellscript-per-once']] (__end__, None, 2) with frequency once-per-instance
2025-11-05 06:42:05,539 - handlers[DEBUG]: Calling handler BootHookPartHandler: [['text/cloud-boothook']] (__end__, None, 2) with frequency once-per-instance
2025-11-05 06:42:05,539 - handlers[DEBUG]: Calling handler JinjaTemplatePartHandler: [['text/jinja2']] (__end__, None, 3) with frequency once-per-instance
2025-11-05 06:42:05,539 - handlers.py[DEBUG]: finish: init-network/consume-user-data: SUCCESS: reading and applying user-data
write_files モジュールで /var/tmp/message.txt ファイルを作成したことがわかります。同様にユーザー設定やSSH、ホスト名の設定などのモジュールもこのタイミングで実行されています。
2025-11-05 06:42:05,794 - handlers.py[DEBUG]: start: init-network/config-write_files: running config-write_files with frequency once-per-instance 2025-11-05 06:42:05,795 - util.py[DEBUG]: Writing to /var/lib/cloud/instances/2ded61c9a451b9df3586001759916bffb7efc148/sem/config_write_files - wb: [644] 24 bytes 2025-11-05 06:42:05,795 - helpers.py[DEBUG]: Running config-write_files using lock (<FileLock using file '/var/lib/cloud/instances/2ded61c9a451b9df3586001759916bffb7efc148/sem/config_write_files'>) 2025-11-05 06:42:05,795 - util.py[DEBUG]: Writing to /var/tmp/message.txt - wb: [644] 955 bytes 2025-11-05 06:42:05,796 - util.py[DEBUG]: Changing the ownership of /var/tmp/message.txt to 0:0 2025-11-05 06:42:05,796 - handlers.py[DEBUG]: finish: init-network/config-write_files: SUCCESS: config-write_files ran successfully and took 0.002 seconds
finish: init-network のログが出力され無事にNetworkステージが終了しました。その後cloud-config.serviceが起動し、Configステージが開始されます。
2025-11-05 06:42:07,828 - handlers.py[DEBUG]: finish: init-network: SUCCESS: searching for network datasources 2025-11-05 06:42:11,755 - log_util.py[DEBUG]: Cloud-init v. 25.2-0ubuntu1~24.04.1 running 'modules:config' at Wed, 05 Nov 2025 06:42:11 +0000. Up 25.60 seconds. 2025-11-05 06:42:11,756 - main.py[INFO]: PID [1] started cloud-init 'modules:config'.
Configステージ
この段階ではモジュールの実行のみが行われます。以下のログでは runcmd モジュールが実行されています。
2025-11-05 06:42:12,653 - modules.py[DEBUG]: Running module runcmd (<module 'cloudinit.config.cc_runcmd' from '/usr/lib/python3/dist-packages/cloudinit/config/cc_runcmd.py'>) with frequency once-per-instance 2025-11-05 06:42:12,654 - handlers.py[DEBUG]: start: modules-config/config-runcmd: running config-runcmd with frequency once-per-instance 2025-11-05 06:42:12,654 - util.py[DEBUG]: Writing to /var/lib/cloud/instances/2ded61c9a451b9df3586001759916bffb7efc148/sem/config_runcmd - wb: [644] 24 bytes 2025-11-05 06:42:12,656 - helpers.py[DEBUG]: Running config-runcmd using lock (<FileLock using file '/var/lib/cloud/instances/2ded61c9a451b9df3586001759916bffb7efc148/sem/config_runcmd'>) 2025-11-05 06:42:12,656 - util.py[DEBUG]: Shellified 1 commands. 2025-11-05 06:42:12,657 - util.py[DEBUG]: Writing to /var/lib/cloud/instances/2ded61c9a451b9df3586001759916bffb7efc148/scripts/runcmd - wb: [700] 24 bytes 2025-11-05 06:42:12,658 - handlers.py[DEBUG]: finish: modules-config/config-runcmd: SUCCESS: config-runcmd ran successfully and took 0.005 seconds
finish: modules-config のログが出力されこちらも無事に処理が終了しました。その後cloud-final.serviceが起動し、Finalステージが開始されます。
2025-11-05 06:42:12,665 - handlers.py[DEBUG]: finish: modules-config: SUCCESS: running modules for config 2025-11-05 06:42:16,175 - log_util.py[DEBUG]: Cloud-init v. 25.2-0ubuntu1~24.04.1 running 'modules:final' at Wed, 05 Nov 2025 06:42:16 +0000. Up 30.12 seconds. 2025-11-05 06:42:16,175 - main.py[INFO]: PID [1] started cloud-init 'modules:final'.
Finalステージ
Finalステージではパッケージのインストールやユーザースクリプトの実行が行われます。
下記は package_update_upgrade_install モジュールのログです。パッケージの更新や sl コマンドがインストールされているのがわかります。
2025-11-05 06:42:16,236 - handlers.py[DEBUG]: start: modules-final/config-package_update_upgrade_install: running config-package_update_upgrade_install with frequency once-per-instance 2025-11-05 06:42:16,236 - util.py[DEBUG]: Writing to /var/lib/cloud/instances/2ded61c9a451b9df3586001759916bffb7efc148/sem/config_package_update_upgrade_install - wb: [644] 24 bytes 2025-11-05 06:42:16,237 - helpers.py[DEBUG]: Running config-package_update_upgrade_install using lock (<FileLock using file '/var/lib/cloud/instances/2ded61c9a451b9df3586001759916bffb7efc148/sem/config_package_update_upgrade_install'>) 2025-11-05 06:42:16,237 - util.py[DEBUG]: Writing to /var/lib/cloud/instances/2ded61c9a451b9df3586001759916bffb7efc148/sem/update_sources - wb: [644] 22 bytes 2025-11-05 06:42:16,238 - helpers.py[DEBUG]: Running update-sources using lock (<FileLock using file '/var/lib/cloud/instances/2ded61c9a451b9df3586001759916bffb7efc148/sem/update_sources'>) 2025-11-05 06:42:16,238 - apt.py[DEBUG]: Waiting for APT lock 2025-11-05 06:42:16,241 - apt.py[DEBUG]: APT lock available 2025-11-05 06:42:16,242 - subp.py[DEBUG]: Running command ['eatmydata', 'apt-get', '--option=Dpkg::Options::=--force-confold', '--option=Dpkg::options::=--force-unsafe-io', '--assume-yes', '--quiet', 'update'] with allowed return codes [0] (shell=False, capture=False) 2025-11-05 06:42:44,136 - performance.py[DEBUG]: Running ['eatmydata', 'apt-get', '--option=Dpkg::Options::=--force-confold', '--option=Dpkg::options::=--force-unsafe-io', '--assume-yes', '--quiet', 'update'] took 26.924 seconds 2025-11-05 06:42:44,138 - apt.py[DEBUG]: Waiting for APT lock 2025-11-05 06:42:44,139 - apt.py[DEBUG]: APT lock available 2025-11-05 06:42:44,139 - subp.py[DEBUG]: Running command ['eatmydata', 'apt-get', '--option=Dpkg::Options::=--force-confold', '--option=Dpkg::options::=--force-unsafe-io', '--assume-yes', '--quiet', 'dist-upgrade'] with allowed return codes [0] (shell=False, capture=False) 2025-11-05 06:43:23,107 - performance.py[DEBUG]: Running ['eatmydata', 'apt-get', '--option=Dpkg::Options::=--force-confold', '--option=Dpkg::options::=--force-unsafe-io', '--assume-yes', '--quiet', 'dist-upgrade'] took 38.968 seconds 2025-11-05 06:43:23,108 - subp.py[DEBUG]: Running command ['snap', 'get', 'system', '-d'] with allowed return codes [0] (shell=False, capture=True) 2025-11-05 06:43:24,038 - performance.py[DEBUG]: Running ['snap', 'get', 'system', '-d'] took 0.930 seconds 2025-11-05 06:43:24,039 - subp.py[DEBUG]: Running command ['snap', 'refresh'] with allowed return codes [0] (shell=False, capture=True) 2025-11-05 06:43:24,135 - performance.py[DEBUG]: Running ['snap', 'refresh'] took 0.095 seconds 2025-11-05 06:43:24,136 - helpers.py[DEBUG]: update-sources already ran (freq=once-per-instance) 2025-11-05 06:43:24,137 - subp.py[DEBUG]: Running command ['apt-cache', 'pkgnames'] with allowed return codes [0] (shell=False, capture=True) 2025-11-05 06:43:24,613 - performance.py[DEBUG]: Running ['apt-cache', 'pkgnames'] took 0.477 seconds 2025-11-05 06:43:24,661 - apt.py[DEBUG]: Waiting for APT lock 2025-11-05 06:43:24,662 - apt.py[DEBUG]: APT lock available 2025-11-05 06:43:24,662 - subp.py[DEBUG]: Running command ['eatmydata', 'apt-get', '--option=Dpkg::Options::=--force-confold', '--option=Dpkg::options::=--force-unsafe-io', '--assume-yes', '--quiet', 'install', 'sl'] with allowed return codes [0] (shell=False, capture=False) 2025-11-05 06:43:30,507 - performance.py[DEBUG]: Running ['eatmydata', 'apt-get', '--option=Dpkg::Options::=--force-confold', '--option=Dpkg::options::=--force-unsafe-io', '--assume-yes', '--quiet', 'install', 'sl'] took 5.844 seconds 2025-11-05 06:43:30,509 - handlers.py[DEBUG]: finish: modules-final/config-package_update_upgrade_install: SUCCESS: config-package_update_upgrade_install ran successfully and took 73.302 seconds
最後に finish: modules-final が出力されcloud-initのすべての処理が完了します。
2025-10-30 10:34:03,097 - handlers.py[DEBUG]: finish: modules-final: SUCCESS: running modules for final
さいごに
本記事ではProxmox VEを使ってcloud-initの基本的な使い方から内部の仕組み、デバッグ方法までを解説しました。cloud-initは一見複雑に見えますが、各ステージの役割とログの見方を理解すれば、トラブルシューティングもスムーズに行えます。
Proxmox VEの標準機能だけでも十分便利ですが、user-dataやvendor-dataを活用することで、より高度なプロビジョニングの自動化が可能になります。ぜひ本記事を参考に、cloud-initを使った効率的なインフラ構築にチャレンジしてみてください。