How to configure cloud-init enabled VMs

This tutorial shows how to configure a cloud-init enabled VM image, that is, a VM image where the cloud-init service has been enabled to run a boot time. Cloud-init is a multi-distribution package that handles early initialization of a VM instance. It can perform various tasks such as configuring users and access credentials, installing packages or setting up mount points. These tasks are defined in a cloud-config file that can be passed to a pcocc VM using the user-data template parameter.

Many distributions provide cloud-init enabled VM images that you can easily import as pcocc templates. More information about downloading and importing these images can be found in pcocc-newvm-tutorial(7).

Note

By default it is not possible to login to cloud-enabled VMs, you must first specify a cloud-config file to setup a SSH key or other authentication mechanism.

This tutorial provides a quick overview of some cloud-config directives which can be used to configure pcocc VMs. The complete documentation of cloud-init capabilities can be found at https://cloudinit.readthedocs.io/en/latest/.

Using cloud-config files with pcocc

A cloud-config file is a YAML formatted file beginning with the #cloud-config pragma and followed by various configuration directives, some of which we will cover in the next sections. It can be passed to pcocc VMs by adding the user-data template parameter, for example:

mycentos:
    inherits: centos7-ci
    user-data: ~/conf

Where ~/conf is the cloud-config file which will be passed to cloud-init at VM boot.

Most cloud-config directives are per-instance, which means they are applied once per instanciated VM, when it first boots. This mechanism relies on the value of instance-id which defaults to a random uuid generated for each instanciated pcocc VM. Alternatively, the instance-id can be set to a fixed value in the VM template definition (see pcocc-templates.yaml(5)). Each time cloud-init runs, it records the current instance-id in the VM filesysterm and only applies per-instance directives if it differs from what was previously recorded.

Setting up user credentials

With cloud-init enabled VMs the first configuration task is often to define user credentials to login to the VM. This can be done with the following syntax:

users:
  - name : demo1
    ssh-authorized-keys:
      - <ssh pub key 1>
      - <ssh pub key 2>
  - name : demo2
    ssh-authorized-keys:
      - <ssh pub key 3>

This defines two demo users, with their respective public SSH keys which have to be copy/pasted in the appropriate fields. You can also provide sudo privileges to a user with the sudo parameter or define its numerical id with the uid parameter:

users:
    - name: demo1
      sudo: ['ALL=(ALL) NOPASSWD:ALL']
      uid: 1247
      ssh-authorized-keys:
        - <ssh pub key 1>

Hostname considerations

By default, cloud-init stores the VM hostname in /etc/hostname which makes it persistent across reboots. This may not be what you want if you plan to instantiate many VMs from the same disk image and need them to find out their hostname dynamically from DHCP. You can inhibit this behaviour with the preserve hostname option:

preserve_hostname: true

This option must also be set in the cloud-init configuration file in the VM to be persistent (see Writing files):

write_files:
  - path: /etc/cloud/cloud.cfg.d/99_hostname.cfg
    permissions: '0644'
    content: |
      preserve_hostname: true

Running early boot commands

Boot commands are executed first in the configuration process. They are run as root. In contrast to other directives, they are run on each boot instead of only once. The cloud-init-per wrapper command can be used to run these boot commands only once. For example, if you are relying on local mirrors of package repositories you may want to disable those configured by default in the cloud-init image. For a CentOS guest you may add:

bootcmd:
 - [ cloud-init-per, instance, yumcleanbase, yum-config-manager, --disable, base]
 - [ cloud-init-per, instance, yumcleanupdates, yum-config-manager, --disable, updates]
 - [ cloud-init-per, instance, yumcleanextras, yum-config-manager, --disable, extras]

Installing packages

You can provide a list of packages to install, for example:

packages:
    - qemu-guest-agent
    - vim
    - gcc
    - gdb

You can also setup additional package repositories for yum:

yum_repos:
   epel_mirror:
    baseurl: http://local-mirror.mydomain/pub/epel/testing/7/$basearch
    enabled: true

Or for apt:

apt:
   primary:
     - arches: [default]
       search:
         - http://local-mirror.mydomain/pub/debian/

You can also ask for packages to be upgraded first:

package_update: false

Writing files

You can write arbitrary files in the VM filesystem. Files are written after packages have been installed which allows for customizing configuration files. For example to write a simple /etc/hosts file for VMs on a private network:

write_files:
  - path: /etc/hosts
    permissions: '0644'
    content: |
      #Host file
      127.0.0.1   localhost localhost.localdomain

      10.252.0.1 vm0-ib0
      10.252.0.2 vm1-ib0
      10.252.0.3 vm2-ib1

Mounting filesystems

You can add entries to the VM fstab to mount filesystems. For example, to mount a 9p filesystem:

mounts:
 - [ optmount, /opt, 9p, 'trans=virtio,version=9p2000.L,msize=262144,nofail', '0', '0']

Running commands

You can run arbitrary commands as root once at the end of the configuration process. Commands will run once all packages have been installed and files written. It can be used to reload a service that you just reconfigured or amend a configuration file:

runcmd:
    - sed -i 's/a/b' /etc/config-file
    - sytemctl restart service

To go further

We only briefly covered part of the capabilities of cloud-init. Please refer to https://cloudinit.readthedocs.io/en/latest/index.html for an exhaustive documentation.