Quick and dirty RHEL8 VM for testing on your Mac using just vagrant and virtualbox
I'm often asked a quick RHEL question, which entails me trying something out to provide the best answers. I don't want to maintain a number of servers just for this. So it would be nice to be able to fire up a VM on demand, with minimal effort.
Luckily, there are some excellent tools we can use for this. I use a Mac for my day-to-day duties, so it would be nice to be able to do things locally where I can.
Firstly, we need a hypervisor able to provide the underlying virtualisation. I use virtualbox. Secondly, we need something able to define and orchestrate our VM. Vagrant is perfect for this. Along with a few plugins we don't need anything else.
I wanted to create a RHEL8 VM where I could try out the latest osbuild tooling for creating RHEL images from blueprints for various platforms. This was my use case for the platform.
Firstly, let's create a directory where we can store various information:
mkdir ~/rhel-builds cd ~/rhel-builds
If you don't have virtualbox, then download it. As I hadn't used it for a while, I took the opportunity to update my version:
vboxmanage --version 6.1.16r140961
I'm running MacOS Big Sur 11.0.1 which now mandates kernel extensions are signed. This was problematic with older versions of virtualbox, now has seems to be sorted. Due to the update, I had to reboot my Mac, but the installation process will inform you if required.
Now download and 'install' vagrant. I moved the binary to /usr/local/bin which is on my $PATH. I took the opportunity to upgrade this as well:
vagrant version Installed Version: 2.2.14 Latest Version: 2.2.14 You're running an up-to-date version of Vagrant!
Vagrant has a raft of plugins for extending functionality and I'm going to make use of a few here for [potential] disk resizing, adding a data disk to the VM, and being able to register it automatically with subscription-manager:
vagrant plugin list vagrant-disksize (0.1.3, global) vagrant-persistent-storage (0.0.49, global) vagrant-registration (1.3.4, global)
I'll explain their use in a minute when we look through the Vagrantfile, but for now just install them:
for p in vagrant-disksize vagrant-persistent-storage vagrant-registration; do vagrant plugin install $p; done
You'll see output like this:
Installing the 'vagrant-disksize' plugin. This can take a few minutes... Installed the plugin 'vagrant-disksize (0.1.3)'! Installing the 'vagrant-persistent-storage' plugin. This can take a few minutes... Installed the plugin 'vagrant-persistent-storage (0.0.49)'! Installing the 'vagrant-registration' plugin. This can take a few minutes...
Installed the plugin 'vagrant-registration (1.3.4)'!
Now we have the tooling installed, we just need a Vagrantfile, which will define what we create. I'll break it down into sections for explanation, but will provide the complete file at the bottom of this article. The # comment lines are just to make things a little clearer and can be stripped out to streamline further.
# THE VAGRANT SOURCE BASE BOX config.vm.box = "generic/rhel8"
We don't even need to download an ISO, as we can just fetch a 'box' from the provided Vagrant Cloud. This boxes are versioned, so you can match a particular box. I'm just going to use the latest. If you're running an older version, vagrant will notify you and you can update quite simply using 'vagrant box update'
# NAME THE VM SO WE CAN IDENTIFY IT config.vm.define "rhelbuilds" config.vm.hostname = "rhelbuilds"
I use the above 2 lines just for easier identification, otherwise vagrant tends to name things like 'default' which I don't find particularly useful.
# DISKS # use this via the vagrant-disksize plugin to increase the default primary disk size: #config.disksize.size = '200GB'
Now let's look at storage. I leave the above lines in commented out just in case I want to resize the primary root disk at some point. Virtualbox seems to create a 130Gb disk OOB for me, which is normally plenty, but there's nothing worse than running out of space!
# use this via the vagrant-persistent-storage plugin to add other disks config.persistent_storage.enabled = true config.persistent_storage.diskdevice = '/dev/sdb' config.persistent_storage.location = "datadisk.vdi" config.persistent_storage.size = 20000 config.persistent_storage.mountname = 'data' config.persistent_storage.filesystem = 'ext4' config.persistent_storage.mountpoint = '/data' config.persistent_storage.volgroupname = 'datavg'
We may want to import/attach a disk with existing data, or store of files etc as part of the testing, so we can create/attach a 'data' disk for this if we like using the above. It'll create a 20Gb .vdi disk in the local directory, and mount it as /data formatted as ext4. It'll also create a datavg for it, as we're using LVM by default.
# NETWORKING - gets DHCP address from my local net config.vm.network "public_network", bridge: "en0: Wi-Fi (Wireless)"
Now for networking. I want to do as little as possible here, but have an internet facing address, so I use public_network which uses my default DHCP service to get a valid IP. Note that this is very insecure, this the docs point out time and time again. If you are concerned, RTFM!
How do you know what interface to use?
vboxmanage list bridgedifs | grep "^Name:" Name: en0: Wi-Fi (Wireless) Name: en5: USB Ethernet(?) Name: ap1 Name: en1: Thunderbolt 1 Name: en2: Thunderbolt 2 Name: en3: Thunderbolt 3 Name: en4: Thunderbolt 4 Name: bridge0 Name: awdl0 Name: llw0
So my broadband router serves up a local IP using DHCP, and it's associated with a bridge network. There is also a NAT'd interface creating as well by default, which can be used allows me to get internet access from within the VM, but I want to connect from my local MAC terminal, hence using the bridge.
# SHARED HOST FOLDER config.vm.synced_folder "~/Downloads", "/vagrant_data"
I add the above in, in case I want to get files in and out from my local Mac drive. ~Downloads works for me!
# RHSM BITS using the vagrant-registration plugin config.registration.username = ENV['SUB_USERNAME'] config.registration.password = ENV['SUB_PASSWORD'] #config.registration.pools = ['pool1', 'pool2'] config.registration.unregister_on_halt = false
As we're using RHEL, we need to use subscription-manager to get access to the entitlements we might need for software testing. This can be automated using the vagrant-registration plugin, which is neat.
If you don't have access to RHEL subs, you can get free access using the Red Hat Developers route. Just register for your free account!
You can hardcode the username and password in the Vagrantfile, which isn't best practice, so I use ENV variables, which get picked up by the plugin. To do this, just set:
export SUB_USERNAME="your_rhn_id" export SUB_PASSWORD="your_rhn_password"
config.registration.pools = ['pool1', 'pool2'] can be used to narrow down selections to particular pools if required.
As I might want to stop and restart this VM a bit, I use config.registration.unregister_on_halt = false to keep my subscription enabled to save time.
Ok, last bit! In this section, we're specifying the size of the VM:
vb.gui = false vb.name = "rhelbuilds" vb.memory = "2048" vb.cpus = "2"
This is pretty self explanatory. I set the GUI to false, to stop virtualbox firing that up on screen, as I don't need it. You can still go into virtualbox at any stage to watch what's happening or get console access from there.
Ok, that's config done! You can create the complete Vagrantfile using:
cat >Vagrantfile<<EOF # -*- mode: ruby -*- # vi: set ft=ruby : Vagrant.configure("2") do |config| # THE VAGRANT SOURCE BASE BOX config.vm.box = "generic/rhel8" # NAME THE VM SO WE CAN IDENTIFY IT config.vm.define "rhelbuilds" config.vm.hostname = "rhelbuilds" # DISKS # use this via the vagrant-disksize plugin to increase the default primary disk size: #config.disksize.size = '200GB' # use this via the vagrant-persistent-storage plugin to add other disks config.persistent_storage.enabled = true config.persistent_storage.diskdevice = '/dev/sdb' config.persistent_storage.location = "datadisk.vdi" config.persistent_storage.size = 20000 config.persistent_storage.mountname = 'data' config.persistent_storage.filesystem = 'ext4' config.persistent_storage.mountpoint = '/data' config.persistent_storage.volgroupname = 'datavg' # NETWORKING - gets DHCP address from my local net config.vm.network "public_network", bridge: "en0: Wi-Fi (Wireless)" # SHARED HOST FOLDER config.vm.synced_folder "~/Downloads", "/vagrant_data" # RHSM BITS using the vagrant-registration plugin config.registration.username = ENV['SUB_USERNAME'] config.registration.password = ENV['SUB_PASSWORD'] #config.registration.pools = ['pool1', 'pool2'] config.registration.unregister_on_halt = false # VM CUSTOMISATIONS config.vm.provider "virtualbox" do |vb| vb.gui = false vb.name = "rhelbuilds" vb.memory = "2048" vb.cpus = "2" end end EOF
We can now create the VM!
vagrant up
Bringing machine 'rhelbuilds' up with 'virtualbox' provider... ==> rhelbuilds: Using /Users/pgriffit/rhel-builds/datadisk.vdi for persistent storage. ==> rhelbuilds: Importing base box 'generic/rhel8'... ==> rhelbuilds: Matching MAC address for NAT networking... ==> rhelbuilds: Using /Users/pgriffit/rhel-builds/datadisk.vdi for persistent storage. ==> rhelbuilds: Checking if box 'generic/rhel8' version '3.1.20' is up to date... ==> rhelbuilds: Setting the name of the VM: rhelbuilds ==> rhelbuilds: Clearing any previously set network interfaces... ==> rhelbuilds: Preparing network interfaces based on configuration... rhelbuilds: Adapter 1: nat rhelbuilds: Adapter 2: bridged ==> rhelbuilds: Forwarding ports... rhelbuilds: 22 (guest) => 2222 (host) (adapter 1) ==> rhelbuilds: Using /Users/pgriffit/rhel-builds/datadisk.vdi for persistent storage. ==> rhelbuilds: Running 'pre-boot' VM customizations... ==> rhelbuilds: Using /Users/pgriffit/rhel-builds/datadisk.vdi for persistent storage. ==> rhelbuilds: ** Persistent Storage Volume exists, not creating ** ==> rhelbuilds: Using /Users/pgriffit/rhel-builds/datadisk.vdi for persistent storage. ==> rhelbuilds: ** Attaching persistent storage ** ==> rhelbuilds: Booting VM... ==> rhelbuilds: Waiting for machine to boot. This may take a few minutes... rhelbuilds: SSH address: 127.0.0.1:2222 rhelbuilds: SSH username: vagrant rhelbuilds: SSH auth method: private key rhelbuilds: rhelbuilds: Vagrant insecure key detected. Vagrant will automatically replace rhelbuilds: this with a newly generated keypair for better security. rhelbuilds: rhelbuilds: Inserting generated public key within guest... rhelbuilds: Removing insecure key from the guest if it's present... rhelbuilds: Key inserted! Disconnecting and reconnecting using new SSH key... ==> rhelbuilds: Machine booted and ready! ==> rhelbuilds: Registering box with vagrant-registration... ==> rhelbuilds: Checking for guest additions in VM... rhelbuilds: The guest additions on this VM do not match the installed version of rhelbuilds: VirtualBox! In most cases this is fine, but in rare cases it can rhelbuilds: prevent things such as shared folders from working properly. If you see rhelbuilds: shared folder errors, please make sure the guest additions within the rhelbuilds: virtual machine match the version of VirtualBox you have installed on rhelbuilds: your host and reload your VM. rhelbuilds: rhelbuilds: Guest Additions Version: 5.2.44 rhelbuilds: VirtualBox Version: 6.1 ==> rhelbuilds: Using /Users/pgriffit/rhel-builds/datadisk.vdi for persistent storage. ==> rhelbuilds: ** Managing persistent storage ** ==> rhelbuilds: Setting hostname... ==> rhelbuilds: Configuring and enabling network interfaces... ==> rhelbuilds: Mounting shared folders... rhelbuilds: /vagrant_data => /Users/pgriffit/Downloads
Login to your new fresh VM and have fun knowing that if you screw things up, you can vagrant halt/destroy/up again to start over!
vagrant status Current machine states: rhelbuilds running (virtualbox) The VM is running. To stop this VM, you can run `vagrant halt` to shut it down forcefully, or you can run `vagrant suspend` to simply suspend the virtual machine. In either case, to restart it again, simply run `vagrant up`. vagrant ssh [vagrant@rhelbuilds ~]$ cat /etc/redhat-release Red Hat Enterprise Linux release 8.3 (Ootpa) [vagrant@rhelbuilds ~]$ sudo -i [root@rhelbuilds ~]# df -h Filesystem Size Used Avail Use% Mounted on devtmpfs 891M 0 891M 0% /dev tmpfs 909M 0 909M 0% /dev/shm tmpfs 909M 17M 892M 2% /run tmpfs 909M 0 909M 0% /sys/fs/cgroup /dev/mapper/rhel_rhel8-root 70G 2.2G 68G 4% / /dev/sda1 1014M 185M 830M 19% /boot /dev/mapper/datavg-data 20G 45M 19G 1% /data vagrant_data 932G 261G 672G 28% /vagrant_data tmpfs 182M 0 182M 0% /run/user/1000