Virtual Machines: Configure, Share and Automate
Creating and using virtual machines is pretty easy with Vagrant, VirtualBox and Ansible.
Introduction
Virtualisation describes a broad range of approaches to creating a simulated version of a computing environment. It is a key tool in modern software development, allowing the creation of virtual instances of file systems and even physical devices.
On the host computer a virtual operating system (OS) can be generated as guest, which is abstracted away from the computer's underlying hardware. This allows the development and testing of software in an isolated environment.
Virtualisation — and related technologies like containerisation — are often discussed in the context of large-scale, enterprise-level applications. For the prospective learner it can be difficult to know where to start. This how-to will demonstrate the use of Vagrant, VirtualBox and Ansible to create and manage a virtual machine (VM) on Linux.
After reading, you will be able to run a command to generate a VM that shares files with your computer and comes with a game (the classic Zork) pre-installed. The general use-case is one in which it is necessary to repeatably create a computing environment that can run particular software, with optional sharing or logging of persistent information between the host and guest machine.
Install Vagrant
Vagrant makes the process of setting up and managing VMs quick and painless. On a Debian-based distribution — like Ubuntu — run the following commands to install:
Install VirtualBox
VirtualBox will provide the virtual environments that Vagrant initialises and configures:
Install Ansible
Ansible will enable automation of tasks in the VM. It can be run on the host or guest machine. If it is to be run on the host it will need to be installed first.
The Ansible documentation states that a regular pip install
may not work and this was true for me on Ubuntu. In this is the case, first install pipx
before invoking it to install Ansible as follows:
Create a VM
To set up a VM first create some directories to organise your VM(s). The VMs on my computer are kept in /vagrant_projects/
. I will be using CentOS for this test project so the working directory is named /centOS_test/
.
&&
&&
Running the following commands will first initialise a Vagrant environment, generating a Vagrantfile
, and then create the CentOS VM according to the configuration specified in that file.
Note that the generic/centos7
is one of many "Vagrant Boxes" that can be found at the link below:
https://app.vagrantup.com/boxes/search
Run the VM
To use the VM run Vagrants's ssh
command and a centos7 prompt should appear:
# [vagrant@centos7 ~]$
It should now be possible to update packages using the default package manager YUM:
# [vagrant@centos7 ~]$
Python is installed by default and should work right away:
# [vagrant@centos7 ~]$
You can also install additional packages like the classic text adventure Zork. This task will later be automated using Ansible.
# [vagrant@centos7 ~]$
# Welcome to Dungeon. This version created 11-MAR-91.
# You are in an open field west of a big white house with a boarded
# front door.
# There is a small mailbox here.
If you have trouble using regular terminal commands like clear
then you might need to change the active terminal type. This was the case with my default Kitty
terminal but was easily resolved with:
# [vagrant@centos7 ~]$
TERM=xterm-256color
Exit and Destroy
To exit CentOS press <ctrl>+<D> or type exit
and press <return>. The VM can be re-entered at any time from the vagrant environment using vagrant ssh
. The state upon exiting will be preserved, including any installed packages and saved files.
If you want to destroy the VM to free up space on your host machine then run vagrant destroy
after exiting the VM.
Note, that the Vagrantfile
will remain in the /centOS_test/
directory. This means that if you cd
into that vagrant environment and run vagrant up
you will again create the VM according to the configuration Vagrantfile
specifies.
Configure the VM with Vagrant
Each time vagrant up
is run the VM is created according to the configuration specified in the Vagrantfile
. By default this file is documented with helpful comments that explain several useful configuration options.
For the purpose of this article the configuration will be modified to achieve two basic things:
- Share files between the host and guest in a dedicated folder
- Automate a YUM package installation with Ansible
Share between guest and host
This is an example of the current directory structure on the host machine:
vagrant_projects
├── centOS_test
│ └── Vagrantfile
└── shared
└── hello.md
I created a shared
folder containing a markdown file with a basic greeting. To access this file in the virtual machine add the following line to Vagrantfile
:
# Vagrantfile
config.vm.synced_folder ,
The first argument string is the relative path to the shared folder on the host machine. The second argument is the name of the shared folder that will be placed in the home directory in the virtual machine.
Run vagrant up
, check that you are in the home directory and display its contents:
# [vagrant@centos7 ~]$
# /home/vagrant/
Still in the VM, change the name of the file and edit its contents in Vim
; for example, you may want to save notes from old Zork runs that will help you in future adventures. The changes you make in the file should be reflected in the /shared/
folder on your host machine.
# [vagrant@centos7 ~]$
# write notes on zork and save with :wq
# this will update the file on host
If the VM is later destroyed and recreated the contents of the file will remain as they will be shared from the host.
Automate with Ansible
If there is a specific task that needs to be run each time that the VM is created Ansible will help.
In centOS_test
create playbook.yml
and reference it in Vagrantfile
:
# Vagrantfile
config.vm.provision do
ansible.playbook =
end
Imagine you want a VM specifically for the purpose of playing Zork but you want to destroy the VM after each game to free up space on your computer. For convenience, you want Zork to be automatically installed every time you (re)create that VM.
In playbook.yml
a task is defined that checks if a YUM package with the NAME Zork is PRESENT in the VM. When the user logs in to the VM, it will BECOME a user with sudo
privileges. If the package is not present it will be sudo installed using yum. If it is installed then no installation will be performed. This is the playbook:
# playbook.yml
---
- name: Testing Ansible Automation with Zork
hosts: all
become: yes
tasks:
- name: Ensure Zork is installed
yum: name=zork state=present
After vagrant up
is run check if Zork has been automatically installed:
Success! You can run zork
and start playing:
# [vagrant@centos7 ~]$
# You are in an open field west of a big white house with a boarded
# front door.
# There is a small mailbox here.
# > mailbox
# What should I do with the mailbox?
# > open
# Opening the mailbox reveals:
# A leaflet.
# > leaflet
# What should I do with the leaflet?
# > take
# Taken.
# > eat leaflet
# I don't think that the leaflet would agree with you.
# > take
# You already have it.
# ...
Dispose of the VM as usual with vagrant destroy
.
Both playbook.yml
and Vagrantfile
still remain in the /centOS_test/
directory. Each time vagrant up
is run subsequently the VM will be created with the shared folder and a version of Zork pre-installed.