Cloud-init: Difference between revisions

From Leo's Notes
This page was last edited on 19 February 2022, at 00:15.
No edit summary
No edit summary
Line 85: Line 85:
}}
}}


== Example Cloud Configs ==
== Cloud Configs ==
See: https://cloudinit.readthedocs.io/en/latest/topics/examples.html
See: https://cloudinit.readthedocs.io/en/latest/topics/examples.html
Modules: https://cloudinit.readthedocs.io/en/latest/topics/modules.html
=== Example cloud-config snippets ===
Here are some cloud config snippets. You can combine any number of these into a single file.
==== Create a new user ====
Create a new user in the wheel group. The passwd hash can be obtained by running '<code>openssl passwd -1 -salt CUSTOMSALT</code>'.
{{Highlight
| code = #cloud-config
users:
  - name: leo
    lock_passwd: false
    inactive: false
    gecos: Leo Leung
    primary_group: leo
    groups: users, wheel
    passwd: $1$ADUODeAy$eCJ1lPSxhSGmSvrmWxjLC1
| lang = yaml
}}
==== Enable the root account and set a root password ====
Most VM templates disable the root account. To enable the root account and set the root password:
{{Highlight
| code = #cloud-config
disable_root: false
chpasswd:
  list:
    - root:password
  expire: false
| lang = yaml
}}
==== Install specific packages ====
You can add additional packages or perform system updates when the system first comes up.
{{Highlight
| code = #cloud-config
packages:
- tmux
- git
package_update: true
package_upgrade: true
| lang = yaml
}}
==== Create a file ====
Use the <code>write_files</code> module to create files. You can use other encodings like base64 as well.
{{Highlight
| code = #cloud-config
write_files:
  - path: /hello
    content: {{!}}
      Hello CloudInit
    permissions: '0644'
| lang = yaml
}}
==== Resize the root LVM volume on startup ====
Assuming that your LVM is the 3rd partition, you could do something like this:
{{Highlight
| code = #cloud-config
write_files:
  - path: /usr/bin/expand_lvm_root
    content: {{!}}
      #!/bin/bash
      /usr/bin/growpart /dev/vda 3
      /usr/sbin/pvresize -y -q /dev/vda3
      /usr/sbin/lvresize -y -q -r -l +100%FREE /dev/mapper/*root
      /usr/sbin/xfs_growfs /
    permissions: '0700'
runcmd:
  - /usr/bin/expand_lvm_root
| lang = yaml
}}
==== POST data when done booting ====
Cloud Init can POST to a web server once the system is ready. This could be useful to automate an Ansible call on the new VM, for example.
{{Highlight
| code = #cloud-config
phone_home:
    url: http://webhook-server/$INSTANCE_ID/
    post: [ pub_key_dsa, pub_key_rsa, pub_key_ecdsa, instance_id ]
    tries: 10
| lang = yaml
}}


==Troubleshooting==
==Troubleshooting==

Revision as of 00:15, 19 February 2022

Cloud-init is a program that configures a guest virtual machine when it first boots based on configuration data that's supplied to it via network or a storage volume. It is capable of setting things like the guest's network configuration (IP addresses, routes, resolvers), account credentials and SSH authorized keys.

Building Cloud-Init images

Linux images that support Cloud-Init should have the Cloud-Init package installed and enabled on start up.

Distro Commands
RHEL / Rocky Linux / CentOS
## Cloud Init
# yum -y install cloud-init cloud-utils-growpart gdisk
# systemctl enable cloud-init.service
Alpine Linux
## Cloud Init
# apk add cloud-init

## Start Cloud-Init on Boot
# rc-update add cloud-init default
# rc-update add cloud-init-local default
# rc-update add cloud-config default
# rc-update add cloud-final default

Default user

The default username for the non-super user account is 'cloud-user'. Some distros use a different default user such as 'alpine' for Alpine Linux or 'ubuntu' for Ubuntu. If you wish to override this username, you need to edit the cloud.cfg configuration file. For my Rocky Linux image, I ran the following to change the default username.

## The default user is 'cloud-user'. we'll change this to 'rocky'.
# sed -i "s/name: cloud-user/name: rocky/" /etc/cloud/cloud.cfg
Do not create the default user account
I noticed that some Packer builds create the default user account as part of the initial image set up. Don't do this. It's intended for the username to be overridden by the initial VM cloud-init configuration. Cloud-Init will take care of the user account creation, SSH authorized key setup, and also setting up the "Please login as the user X rather than the user Y" message on the root account.

The default user has their password locked, thereby requiring authentication using SSH keys. You may change this by modifying cloud.cfg as well:

# sed -i "s/lock_passwd: true/lock_passwd: false/" /etc/cloud/cloud.cfg

Hypervisor support

Proxmox

Proxmox supports cloud-init using either the NoCloud (v1, used for Linux based guest OSes) or ConfigDrive (v2, used for Windows based guest OSes) data sources. To learn more about different Cloud-Init data sources, see https://cloudinit.readthedocs.io/en/latest/topics/datasources.html#known-sources.

A NoCloud configuration data is passed to the guest using a 4MB CD-ROM image. If you are creating a new VM, you must attach this CD-ROM device under 'Hardware' -> 'Add' -> 'CloudInit Drive'. Any changes made to the Cloud-Init settings will require this CD-ROM image to be regenerated.

When building images for use with Proxmox, it's recommended to add the following cloud.cfg.d file to restrict the data sources to only the supported options. Otherwise, you may have the VM try downloading the configs using some other method until it times out.

# /etc/cloud/cloud.cfg.d/99_pve.cfg
datasource_list: [ NoCloud, ConfigDrive ]

CloudStack

CloudStack has its own data source type.

# cat <<EOF > /etc/cloud/cloud.cfg.d/99_cloudstack.cfg
datasource:
  CloudStack: {}
  None: {}
datasource_list:
  - CloudStack
EOF

Issues:

  • I can't get the root password unlocked and set. Account is always locked even with the following:
#cloud-config
users:
  - name: root
    lock_passwd: false
    inactive: false
    password: $1$ADUODeAy$Tp1Tp9Sqb42DV3ux2it1L.

Cloud Configs

See: https://cloudinit.readthedocs.io/en/latest/topics/examples.html

Modules: https://cloudinit.readthedocs.io/en/latest/topics/modules.html

Example cloud-config snippets

Here are some cloud config snippets. You can combine any number of these into a single file.

Create a new user

Create a new user in the wheel group. The passwd hash can be obtained by running 'openssl passwd -1 -salt CUSTOMSALT'.

#cloud-config
users:
  - name: leo
    lock_passwd: false
    inactive: false
    gecos: Leo Leung
    primary_group: leo
    groups: users, wheel
    passwd: $1$ADUODeAy$eCJ1lPSxhSGmSvrmWxjLC1

Enable the root account and set a root password

Most VM templates disable the root account. To enable the root account and set the root password:

#cloud-config
disable_root: false
chpasswd:
  list:
    - root:password
  expire: false

Install specific packages

You can add additional packages or perform system updates when the system first comes up.

#cloud-config
packages:
 - tmux
 - git

package_update: true
package_upgrade: true

Create a file

Use the write_files module to create files. You can use other encodings like base64 as well.

#cloud-config
write_files:
  - path: /hello
    content: |
      Hello CloudInit
    permissions: '0644'

Resize the root LVM volume on startup

Assuming that your LVM is the 3rd partition, you could do something like this:

#cloud-config
write_files:
  - path: /usr/bin/expand_lvm_root
    content: |
      #!/bin/bash
      /usr/bin/growpart /dev/vda 3
      /usr/sbin/pvresize -y -q /dev/vda3
      /usr/sbin/lvresize -y -q -r -l +100%FREE /dev/mapper/*root
      /usr/sbin/xfs_growfs /
    permissions: '0700'

runcmd:
  - /usr/bin/expand_lvm_root

POST data when done booting

Cloud Init can POST to a web server once the system is ready. This could be useful to automate an Ansible call on the new VM, for example.

#cloud-config
phone_home:
    url: http://webhook-server/$INSTANCE_ID/
    post: [ pub_key_dsa, pub_key_rsa, pub_key_ecdsa, instance_id ]
    tries: 10

Troubleshooting

Cloud-Init isn't setting the static IP address

On Alpine Linux, Cloud-Init runs but the VM is still obtaining the IP address via DHCP.

After much debugging, I believe the issue comes down to the networking service starting before cloud-init. As a result, the network configs in /etc/network/interfaces isn't being written to by Cloud-Init in time and the networking service uses DHCP. The fix here is to make networking start after cloud-init. To do this, remove the networking service from the boot runlevel and re-add it to the default runlevel. Because 'networking' comes after 'cloud-init', cloud-init should run before networking.

As part of my packer provisioning script, I added the following lines:

## Start ntworking after cloudinit?
# rc-update del networking boot     
# rc-update add networking default