Running Gentoo on Digital Ocean
I've used digital ocean for a while and I always wanted to use gentoo on it and finally I took the time to get it to work. In a nutshell their custom images work better if they have cloud-init installed, so It is not a very difficult setup.
A while back Digital Ocean posted on their blog and article detailing how they use qemu and libvirt for running droplents therefore in this guide I'll be using qemu to replicate their environment a little
Requirements
DO custom images should:
- Use one of the following image formats: raw, qcow2, vhdx, vdi, vmdk
- Have cloud-init installed
- VirtIO kernel drivers
In this guide I am going to be assuming you have qemu set up and working
Creating the Image
Creating a gentoo guest on qemu is discussed in detail in the gentoo wiki
Image size
DO Resizes custom images upon droplet creation to make them bigger, but not the other way around so a wise choice is to make the image the same size as the smallest droplet size which is 25GB. I went with a raw image format but in retrospect the qcow2 is probably better and well supported by DO and can be selected with -f qcow2
qemu-img create gentoo.img 25G
Invoking QEMU
These are the parameters that I found worked the best.
qemu-system-x86_64 \
-enable-kvm \
-cpu host \
-drive file=gentoo.img,format=raw,if=virtio \
-netdev user,id=vmnic,hostname=localhost \
-device virtio-net,netdev=vmnic \
-device virtio-rng-pci \
-m 2G \
-smp $(nproc) \
-monitor stdio \
-name "Gentoo VM" \
-boot d \
-cdrom ./install-amd64-minimal-[date].iso
Of course for booting into the system -boot and -cdrom should be omitted. Notice we are using -enable-kvm which is a lot faster and we are using the virtio drivers.
Installation Process
Follow the regular steps in the handbook and also take note of the following notes
Partition
A GPT partition table works great, although MBR should be supported as well. I used the default partition scheme on the handbook for simplicity (bootloader, boot, swap, rootfs)
make.conf
When using QEMU with -enable-kvm and -cpu host the host CPU is emulated within the guest. I have seen droplets with anything from sandy-bridge to skylake processors so it is quite possible that choosing a very ambitious march setting will run fine sometimes and won't boot on other droplets. For this reason I am going with a safer setting
COMMON_FLAGS="-march=x86-64 -O2 -pipe"
fstab
Device names such as /dev/sda1 or /dev/vda2 are not the most reliable especially since our image will be booting in potentially different environments. PARTUUID values are a safer choice here
Check the PARTUUIDs with
blkid
Kernel
The default kernel might not have the VIRTIO drivers enabled, so invoke genkernel with the --menuconfig option
genkernel \
--kernel-config=default \
--menuconfig \
all
On menuconfig search for VIRTIO, then go throguh the results and say Y to all VIRTIO options.
Install cloud-init
To install cloud init run:
emerge --ask app-emulation/cloud-init
Now add the following services
rc-update add cloud-init-local boot
rc-update add cloud-init default
rc-update add cloud-config default
rc-update add cloud-final default
Notice cloud-init-local is added to the boot runlevel and the rest to the default runlevel. We can add configuration files to /etc/cloud/cloud.cfg.d so this is a good place to make sure we are using the ConfigDrive datasource.
# /etc/cloud/cloud.cfg.d/20_datasources.cfg
manage_etc_hosts: true
datasource_list: [ ConfigDrive, DigitalOcean, NoCloud, None ]
Fix cloud-init bugses
Open the gentoo file from cloud-init and fix some bugs
vi `equery files cloud-init | grep gentoo.py`
Around after line 65 and then around like 160 fix these things
diff --git a/cloudinit/distros/gentoo.py b/cloudinit/distros/gentoo.py
index 2bee1c89..6c757818 100644
--- a/cloudinit/distros/gentoo.py
+++ b/cloudinit/distros/gentoo.py
@@ -65,6 +65,8 @@ class Distro(distros.Distro):
nameservers.extend(info['dns-nameservers'])
if dev == 'lo':
continue
+ if 'address' not in info:
+ info['bootproto'] = 'dhcp'
net_fn = self.network_conf_fn + '.' + dev
dns_nameservers = info.get('dns-nameservers')
if isinstance(dns_nameservers, (list, tuple)):
@@ -159,11 +161,9 @@ class Distro(distros.Distro):
except IOError:
pass
if not conf:
- conf = HostnameConf('')
- conf.set_hostname(your_hostname)
- gentoo_hostname_config = 'hostname="%s"' % conf
- gentoo_hostname_config = gentoo_hostname_config.replace('\n', '')
- util.write_file(out_fn, gentoo_hostname_config, 0o644)
+ conf = HostnameConf('hostname=""')
+ conf.set_hostname('hostname="%s"' % your_hostname)
+ util.write_file(out_fn, str(conf), 0o644)
def _read_system_hostname(self):
sys_hostname = self._read_hostname(self.hostname_conf_fn)
Finish up
Cloud init keeps track of whether or not is has run. We need to clean the internal state to trigger the first boot tasks on the next boot
cloud-init clean
Shutdown the machine
shutdown -h now
Now in the host machine, compress the image
gzip < gentoo.img > gentoo.img.gz
Upload the image to DO Spaces with s3cmd (if you need to install it, do emerge net-misc/s3cmd)
s3cmd put gentoo.img.gz s3://spacename
And that is it, now you can Import your custom image from your DO Space and provision a droplet running Gentoo linux!