CloudStack Templates
This page explains how to create CloudStack templates from which CloudStack VMs are based.
Official documentation: https://docs.cloudstack.apache.org/en/latest/adminguide/templates.html
We require that cloud templates be prepared with cloud-init. cloud-init is a program which passes configuration data (e.g. user's SSH key) from a cloud provider to a VM at boot time. Most "major" GNU/Linux distributions have public cloud images which are (mostly) ready-to-use thanks to cloud-init.
Overview
There are two ways to create a template:
1. Convert the root disk volume of an existing VM into a template 2. Take a QCOW2 file, mount it over NBD, and modify files as necessary
The first option takes more time, but is generally easier. You can use either option; just keep in mind that some commands, such as systemctl
, can only be executed in a live system.
Limitations
Downloading
If you are downloading a template over HTTP(S), there's one weird problem which I found out the hard way - the CloudStack Systems VMs' iptables rules are configured to block outgoing traffic to VLAN 134. I have no idea why CloudStack decides to do this, but you're going to have to work around those rules if you want to download a file hosted on a machine on-campus.
SSH into the Secondary Storage VM, e.g.
ssh -i /var/lib/cloudstack/management/.ssh/id_rsa -p 3922 root@169.254.12.1
Check if the first rule of the OUTPUT chain will accept all traffic:
iptables -L -vn
If not, add such a rule:
iptables -I OUTPUT 1 -j ACCEPT
Uploading
If you are uploading a QCOW2 image directly from your computer, you need to be on the campus network. For some reason the web UI tries to contact the management server directly, which of course has a private IP address. So either configure your browser to use a SOCKS proxy, or use the campus VPN.
Debian
We're going to start with Debian since it's the first template which I created. Other distributions are mostly similar.
From the CloudStack UI, as the admin user, click on Templates, then 'Register Template From URL'. Create a new template using this URL (change the release if necessary): https://cloud.debian.org/images/cloud/bullseye/latest/debian-11-genericcloud-amd64.qcow2. The following checkboxes should be checked: Extractable, HVM (don't make it public - we want members to use our modified templates instead).
Create a new VM using this template. Use the admin keypair during creation. Once it's ready, SSH into it using the admin keypair (on biloba or chamomile):
ssh -i /var/lib/cloudstack/management/.ssh/id_rsa debian@172.19.134.129
Replace the IP address as necessary.
IPv6 setup
We want the IPv6 address in our machines to be derived from the IPv4 address - for example, 172.19.134.129 becomes 2620:101:f000:4903::129. (Yes, I am aware that 129 in decimal is not the same as 129 in hex. But it makes it easier to remember.)
We're going to create some custom scripts which inject an IPv6 address at startup:
mkdir /etc/csc # Here's a script to disable Router Advertisements (RA) and Duplicate Address Detection (DAD) cat << 'EOF' > /etc/csc/ipv6-sysctl.sh #!/bin/bash set -ex while read interface _; do if [ $interface = lo ]; then continue fi sysctl net.ipv6.conf.$interface.accept_ra=0 sysctl net.ipv6.conf.$interface.accept_dad=0 done < <(ip -brief link show) EOF chmod +x /etc/csc/ipv6-sysctl.sh # Here's a script to derive the IPv6 address from the IPv4 address cat << 'EOF' > /etc/csc/ipv6-addr.sh #!/bin/bash set -ex INTERFACE= IPV4_ADDRESS= while read interface _ address; do if ! echo $address | grep -q '^172\.19\.134\.'; then continue fi INTERFACE=$interface IPV4_ADDRESS=$address break done < <(ip -4 -brief addr show) if [ -z "$INTERFACE" ]; then echo "Could not find primary interface" >&2 exit 1 fi NUM=$(echo $IPV4_ADDRESS | grep -oP '^172\.19\.134\.\K(\d+)') IPV6_ADDRESS="2620:101:f000:4903::$NUM/64" ip -6 addr add $IPV6_ADDRESS dev $INTERFACE ip -6 route add default via 2620:101:f000:4903::1 dev $INTERFACE EOF chmod +x /etc/csc/ipv6-addr.sh # Create some systemd services cat <<EOF >/etc/systemd/system/csc-ipv6-sysctl.service [Unit] Description=Disable IPv6 RAs and DAD # needed to avoid a dependency on basic.target DefaultDependencies=no After=network-pre.target Before=networking.service [Service] Type=oneshot RemainAfterExit=yes ExecStart=/etc/csc/ipv6-sysctl.sh [Install] WantedBy=multi-user.target EOF cat <<EOF >/etc/systemd/system/csc-ipv6-addr.service [Unit] Description=Allocate IPv6 address Requires=csc-ipv6-sysctl.service After=csc-ipv6-sysctl.service Requires=network.target After=network.target [Service] Type=oneshot RemainAfterExit=yes ExecStart=/etc/csc/ipv6-addr.sh [Install] WantedBy=multi-user.target EOF # Enable the systemd services systemctl daemon-reload systemctl enable csc-ipv6-sysctl systemctl enable csc-ipv6-addr