Posted on 14 September 2021.
On our troubled journey to set up an unattended installation of Ubuntu server VMs, we accidentally ran into Subiquity, which seems to be the successor of the Debian installer on Ubuntu, natively supported from Ubuntu server 20.04.
Subiquity really transformed a complex problem, where we couldn't make preseeds work, into a simple solution for us. That's why I am sharing this with you today, hoping it will help others implement their own automatic deployment systems.
Ubuntu unattended installation with preseeds
Our use case
So we had a specific use case where we wanted to be able to set up Linux VMs on a Windows Hyper-V hypervisor, without any additional services apart from:
- The hypervisor itself
- A computer with Ansible(the "deployment computer")
- A network connection between the two. Only connections from the deployment computer toward the hypervisor are allowed (i.e. the hypervisor will not be able to access any services that may be hosted on the deployment computer)
I should also mention that we didn't have the usual network services like DHCP, etc.
That being said, we needed to be able to launch a VM on the hypervisor that would perform an unattended installation using an Ubuntu ISO installer image, pushed to the hypervisor from the deployment computer.
The expected result is a fully configured VM with all parameters set, either generic (like maybe keyboard layout) or specific (like its hostname, network configuration including IP addresses, disk partitioning, etc).
Preseed came to the rescue
We first tried to use the old unattended installer that is based on preseed files. I am calling it old because It dates back to the creation of Debian-based distros over a decade ago, and did not evolve all that much in recent years. It's also the only one we knew when we started this thing. So that's naturally the one we tried to use first.
This system is based on the fact that every question that is asked during the installation can be answered by a line in the file called "preseed".
The preseed file uses a very simple syntax:
# Notes # - d-i stands for "debian installer" d-i <section> <answer_type> <answer_value>
So, what exactly happens when you start a new installation with this installer? Well, it basically skips all question prompts it already has the answers to in your preseed file.
An unattended installation is then just an installation that started with a preseed file that already contained all answers that are required during the installation process.
The installer can get a preseed file from within the installer ISO image, or from the network using the HTTP protocol.
The limitations we faced
We faced a few problems when we tried to set up our unattended installation with the preseed system.
First, the answers you need to give are not well documented, or at least the documentation is in my opinion quite hard to understand for people that are not fully acquainted with these kinds of tools. Also, the debugging process is long and painful since you can see configuration errors only by trying to install your OS from scratch each time. A spelling mistake in a section of the file will cause the installer to prompt the question anyway, without telling you the mistake you made. So debugging can be a long frustrating process in my experience.
In the end, it's just a question of patience, and you should get through anyway.
The real problem we had in our case, which made us look for another solution, was that we didn't succeed in giving the preseed file to the installer in the first place.
Indeed, as mentioned above, we knew only two ways to do this:
- By storing the preseed file directly in the installer ISO image
- By making it accessible on an HTTP server somewhere on the network
Unfortunately, none of these were acceptable in our case, since:
- Storing the preseed in the installer ISO doesn't seem like a good solution when you want to perform a per VM customization (e.g. for IP addresses)
- Neither our hypervisor nor the VMs stored on it had access to any HTTP server (that was one of the main assumptions/constraints from above)
We tried to give the preseed to the installer using a small ISO attached as an additional device to the VM, that the installer would have to mount and use dynamically. But we couldn't make this work.
Maybe because it is not possible, or because we just didn’t find the proper way to do it.
Anyway, the fact is that we spent quite some time trying to do it and in the end, it didn't work and we had no idea how to make it work.
That's when we found a mention of a cloud-init-like unattended installation for Ubuntu servers in Packer's documentation: Subiquity.
What is Subiquity?
If you do not explicitly search for "Subiquity" or "Ubuntu 20.04 server" on the Internet, chances are you will never find anything about it. And it would be a shame because it's really a great tool, that is really easy to understand and to set up!
Asks no questions by default
One of the things that can be annoying with preseed is when it bothers us with the keyboard layout (because we cannot manage to find the proper syntax to fill in this question in the preseed file) when we seldom really care for this on a server installation. Or maybe that's something we can deal with later with another tool like Ansible.
Well, Subiquity has the good property to use default properties silently in most cases.
Subiquity’s config is described in yaml files, which are more structured and understandable than preseeds.
Cloud-init under the hood
cloud-init (or at least some part of it) in order to get and compute this config. This means that the concepts you may already know about cloud-init will help you a lot to make your way through giving your configuration to Subiquity.
And since cloud-init is more and more used nowadays, that's a pretty good advantage I think😀
Curtin under the hood
I'm not sure whether
curtin is a well-known tool. What I do know, is that it works well and can take care of the partitioning part for you and support LVM.
With preseeds, you can specify partitioning options, and optionally use the Debian recipes syntax. When well set up it works fine. But making it work when you don't know it at all is definitely more complicated.
With Subiquity, you can use a simple yaml syntax that is then given to curtin during the installation.
So Subiquity is a great tool, easy to understand. Yet, there is always room for perfection.
At the time we tried it we saw the following, minor limitations:
- The installation process never stops to ask us the "Autoinstall?" question, where you need to answer "yes" with your keyboard in order to continue. Which is unfortunate when you want a fully automated process. To my knowledge, there is still no workaround for this issue.
- Custom mount options were not used during the installation. It seems that there is an ongoing issue on this in Subiquity since curtin could handle it but Subiquity is just not forwarding this part of the partitioning configuration
How subiquity helped us
So now, let's just come back to our initial use case.
With Subiquity, we implemented it very quickly by:
- Creating a small custom ISO containing all our configurations as
user-data, with the
ci-datalabel ("cloud-init data")
- Running the new VM with both the installer ISO and the ci-data ISO
- Using the proper boot command and specifying the autoinstall mode
What happens next is that during the installation, the cloud-init tooling will look up many data sources, and will find our ISO with the label ci-data on it.
It will mount it and process our configuration. Finally, it will run the installation fully.
So we didn't need any external service to give the installation process our configuration. It was pretty straightforward. And furthermore, the config we needed was so easy to describe using Subiquity that we didn't really feel the drawback of switching to a new tool and needing to learn how to use it.
I hope you will find this story about how we ran into Subiquity and what benefits it can bring you interesting. I won't say this tool is objectively better than others. What I can say, however, is that it enabled us to reach our goal easily and quickly, and it gave us a comforting feeling of control over what we were doing.