< / > Manuel Aguilera

Running Gentoo on Digital Ocean

2020-10-09

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:

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!