Skip to main content
  1. Posts/

Provisioning VM KVM Menggunakan Cloud-Init

·5 mins·
cloud-init libvirt kvm
Table of Contents

Cloud-init adalah framework open-source yang digunakan untuk menginisialisasi dan mengonfigurasi VM secara otomatis saat first boot. Cloud-init memungkinkan otomatisasi konfigurasi seperti:

  • Pembuatan user
  • Pengaturan password & SSH key
  • Konfigurasi jaringan
  • Instalasi paket dan service

Cloud-init membaca data konfigurasi dari berbagai datasource, seperti:

  • File ISO (NoCloud)
  • Metadata provider cloud (AWS, GCP, OpenStack, dsb.)
  • Layanan metadata jaringan

Setelah membaca konfigurasi, cloud-init mengeksekusi instruksi sesuai file yang diberikan.

Prerequisites
#

Pastikan sistem sudah terinstal:

  • QEMU / KVM
  • Libvirt
  • genisoimage

Contoh instalasi di Ubuntu / Debian:

sudo apt install qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils genisoimage

Struktur File Konfigurasi Cloud-init
#

Buat direktori untuk menyimpan file konfigurasi:

mkdir -p /opt/cloudinit

Cloud-init menggunakan tiga file utama:

File Fungsi
meta-data Identitas instance
user-data Konfigurasi sistem & user
network-config Konfigurasi jaringan (opsional)

File meta-data
#

Buat file meta-data (minimal):

instance-id: 215b8931ab97deea82032a53cfcd8b0fb4302241
local-hostname: cloud-vm

Generate instance-id:

openssl rand -hex 20

File user-data
#

Buat file user-data:

#cloud-config
hostname: cloud-vm
manage_etc_hosts: true
fqdn: cloud-vm.example.com

ssh_pwauth: false
disable_root: true

chpasswd:
  expire: false

users:
  - name: devops
    gecos: DevOps Admin
    groups:
      - sudo
      - wheel
    sudo: ALL=(ALL) NOPASSWD:ALL
    lock_passwd: true
    ssh_authorized_keys:
      - ssh-rsa AAAAB3NzaC1yc2E... devops@host
    shell: /bin/bash

write_files:
  - path: /etc/ssh/sshd_config.d/99-hardening.conf
    content: |
      PermitRootLogin no
      PasswordAuthentication no
      PubkeyAuthentication yes
      AllowUsers devops      

swap:
  filename: /swapfile
  size: auto
  maxsize: 1G

package_update: true
package_upgrade: false
package_reboot_if_required: true

packages:
  - curl
  - wget
  - nano
  - zip
  - unzip
  - lsof
  - net-tools
  - screen
  - git
  - python3-pip
  - htop
  - unzip

timezone: Asia/Jakarta

runcmd:
  - sort -u /etc/fstab -o /etc/fstab
  - pip3 install "resolvelib>=0.5.3,<0.9.0"
  - echo "Cloud-init completed on $(date)" > /root/cloud-init.log

final_message: |
  Cloud-init completed successfully.
  Host: $hostname
  Version: $version
  Datasource: $datasource
  Uptime: $uptime  

File network-config
#

Buat file network-config untuk konfigurasi network statis (opsional).

version: 1
config:
    - type: physical
      name: eth0
      mac_address: 'bc:24:11:79:f9:5e'
      subnets:
      - type: static
        address: '192.168.100.111'
        netmask: '255.255.255.0'
        gateway: '192.168.100.208'
      - type: dhcp6
    - type: nameserver
      address:
      - '192.168.100.10'
      - '1.1.1.1'
      search:
      - 'example.com'

Buat Cloud-init ISO atau Disk Image
#

Cloud-init NoCloud datasource membutuhkan media (ISO atau disk) berisi file:

  • user-data
  • meta-data
  • network-config (opsional)

Membuat Cloud-init ISO (Recommended) #

Gunakan genisoimage atau mkisofs:

genisoimage -quiet -iso-level 3 -J -R -V cidata \
  -o /opt/cloudinit.iso \
  /opt/cloudinit/user-data \
  /opt/cloudinit/meta-data \
  /opt/cloudinit/network-config

👉 Best practice: sebut file satu per satu, jangan folder, agar kompatibel dengan semua hypervisor.

Alternatif Modern (Cloud-Localds – Lebih Rapi).

cloud-localds /opt/cloudinit.iso user-data meta-data --network-config=network-config

Membuat Cloud-init Disk Image (QCOW2)
#

genisoimage -quiet -iso-level 3 -J -R -V cidata -o /opt/cloudinit.iso /opt/cloudinit | qemu-img convert -f raw -O qcow2 /opt/cloudinit.iso /opt/cloudinit.qcow2

Deploy VM dengan virt-install
#

virt-install \
  --name ubuntu-vm \
  --memory 2048 \
  --vcpus 2 \
  --disk path=vm1.qcow2,format=qcow2,bus=virtio \
  --disk path=/opt/cloudinit.iso,device=cdrom \
  --network network=default \
  --os-variant ubuntu24.04 \
  --import \
  --graphics none \
  --noautoconsole

Integrasi Cloud-init ke Libvirt XML Template
#

Menggunakan File ISO
#

<disk type="file" device="cdrom">
  <driver name="qemu" type="raw"/>
  <source file="/opt/cloudinit.iso"/>
  <target dev="sda" bus="sata"/>
  <readonly/>
</disk>

Menggunakan Disk Image (Virtio)
#

<disk type="file" device="disk">
  <driver name="qemu" type="qcow2"/>
  <source file="/opt/cloudinit.qcow2"/>
  <target dev="vda" bus="virtio"/>
</disk>

Re-run Cloud-init (Reset State)
#

Cloud-init hanya berjalan sekali saat first boot.

Untuk menjalankan ulang:

cloud-init clean --logs
reboot

Notes
#

1. Cloud-init Datasource Check
#

cloud-init status --long
cloud-init query ds

2. Debug Cloud-init
#

less /var/log/cloud-init.log
less /var/log/cloud-init-output.log

3. Force Re-run Specific Modules
#

cloud-init clean
cloud-init init
cloud-init modules --mode=config
cloud-init modules --mode=final

Cloud-config Reference
#

Ansible Integration (cloud-init)
#

Cloud-init dapat menginstal dan menjalankan Ansible secara otomatis.

ansible:
  install_method: distro       # distro | pip | none
  package_name: ansible         # default: ansible

  pull:
    url: https://github.com/holmanb/vmboot.git
    playbook_name: ubuntu.yml

  galaxy:
    actions:
      - ['ansible-galaxy', 'collection', 'install', 'community.general']
      - ['ansible-galaxy', 'collection', 'install', 'ansible.posix']

Catatan:

  • pull mode cocok untuk immutable bootstrap
  • Untuk production CI/CD biasanya pakai Ansible controller terpisah, bukan cloud-init

Disk Setup (Partition + Filesystem + Mount)
#

Cloud-init bisa membuat partition otomatis pada disk baru.

device_aliases:
  disk1: /dev/sdb

disk_setup:
  disk1:
    table_type: gpt
    layout: true
    overwrite: true

fs_setup:
  - device: disk1
    filesystem: ext4
    label: fs1

mounts:
  - [/dev/sdb1, /mnt1]

Growpart (Resize Disk Partition)
#

Digunakan untuk auto-resize disk (cloud volume).

growpart:
  devices:
    - /dev/sdb1
  ignore_growroot_disabled: false
  mode: auto

NTP Configuration
#

ntp:
  enabled: true
  ntp_client: chrony
  servers:
    - 0.pool.ntp.org
    - 1.pool.ntp.org

Cloud-config examples
#

Writing Arbitrary Files (write_files)
#

Cloud-init dapat menulis file dengan berbagai encoding.

Base64 encoded file:

#cloud-config
write_files:
  - encoding: b64
    content: CiMgVGhpcyBmaWxlIGNvbnRyb2xzIHRoZSBzdGF0ZSBvZiBTRUxpbnV4...
    owner: root:root
    path: /etc/sysconfig/selinux
    permissions: '0644'

Multi-line text file:

  - content: |
      # My new /etc/sysconfig/samba file
      SMBDOPTIONS="-D"      
    path: /etc/sysconfig/samba
    permissions: '0644'

Binary file (ELF, scripts, tools):

  - content: !!binary |
      f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAAwARAAAAAAABAAAAAAAAAAJAV...      
    path: /bin/arch
    permissions: '0555'

Gzip compressed binary:

  - encoding: gzip
    content: !!binary |
      H4sIAIDb/U8C/1NW1E/KzNMvzuBKTc7IV8hIzcnJVyjPL8pJ4QIA6N+MVxsAAAA=      
    path: /usr/bin/hello
    permissions: '0755'

Run Commands Very Early at Every Boot (boothook)
#

#cloud-boothook dieksekusi sangat awal, bahkan sebelum network fully configured.

#cloud-boothook
#!/bin/sh
echo "192.168.1.130 us.archive.ubuntu.com" >> /etc/hosts

Run Script Every Boot (script-per-boot)
#

Cocok untuk compliance check, auto-healing, ephemeral cloud workloads.

#cloud-config
runcmd:
  - curl -fsSL https://bootstrap.corp.local/agent.sh | bash

write_files:
  - path: /var/lib/cloud/scripts/per-boot/healthcheck.sh
    permissions: '0755'
    content: |
      #!/bin/bash
      curl -X POST https://health.corp.local/report      

Cloud-init memiliki mekanisme untuk menjalankan skrip shell secara otomatis tergantung di mana skrip tersebut ditempatkan.

Direktori Kapan Skrip Dijalankan Karakteristik
/var/lib/cloud/scripts/per-instance Sekali per instance (biasanya saat boot pertama setelah VM dibuat) Skrip hanya dijalankan sekali untuk setiap instance baru. Jika instance di-reboot, skrip tidak dijalankan lagi. Cocok untuk inisialisasi khusus instance.
/var/lib/cloud/scripts/per-once Sekali saja, tidak peduli berapa kali instance dibuat atau di-reboot Skrip dijalankan hanya sekali sepanjang hidup sistem. Setelah itu tidak akan pernah dijalankan lagi. Biasanya untuk konfigurasi permanen yang tidak boleh diulang.
/var/lib/cloud/scripts/per-boot Setiap kali sistem boot Skrip dijalankan setiap kali mesin di-reboot. Cocok untuk tugas yang harus dilakukan berulang kali, misalnya membersihkan cache atau memulai ulang layanan.
/var/lib/cloud/scripts/vendor Tergantung vendor cloud atau distribusi Direktori ini biasanya digunakan oleh penyedia cloud atau distribusi OS untuk menaruh skrip bawaan. Perilakunya bisa berbeda tergantung vendor, tapi umumnya digunakan untuk konfigurasi tambahan dari penyedia.

Related

Implementasi VLAN Trunking di KVM/Libvirt
·2 mins
libvirt libvirt kvm
Jenis Storage Pool Libvirt
·2 mins
libvirt libvirt kvm
Memahami Format XML Domain Libvirt
·41 mins
libvirt libvirt kvm
Mengubah Data SMBIOS System Information di libvirt
·2 mins
libvirt libvirt kvm
Menghubungkan USB Host ke KVM Libvirt
·1 min
libvirt kvm libvirt
Implementing Firewall for VMs with IPtables
·3 mins
iptables linux iptables kvm libvirt