When it comes to automation, I’m a big consumer of PowerCLI. Automation can be leveraged in many contexts, such as:
- Virtual machines lifecycles
- Infrastructure configuration
- Remediation and self-healing
- …and much more!
As much as I like PKS (Pivotal Container Service), I also need to work on regular Kubernetes: for that, I require the ability to deploy a number of virtual machines based on a Linux template and to configure them with different settings (masters, etcd, and workers nodes). I wanted to try something else than PowerCLI / PowerShell: I started with Ansible as I’m already familiar with it. Please note that I will not describe Ansible concepts and how it works. I will cover only the virtual machine deployment aspect in this article.
Which Ansible Module Should I Use?
The first step is to identify which Ansible module needs to used to deploy the virtual machines. Although VMs can be deployed in many different cloud providers (AWS, Azure, Rackspace, etc.), my heart (and my lab) is with VMware. You will find all official Ansible modules on this page, the ones concerning VMware being prefixed with ‘vmware_‘ (61 modules as of today).
There are actually 2 modules to create, delete and manage VMs in vSphere:
- vmware_guest (requires Python 2.6 and PyVmomi): this is the module that we will use. This module can be used to create new virtual machines from templates or other virtual machines, manage power state of virtual machine, modify various virtual machine components (network, disk, customization, etc.), rename a virtual machine and remove a virtual machine with associated components.
- vsphere_guest_module (deprecated): this module used pysphere and will be removed in Ansible 2.9 < this module should not be used!
How to Deploy Multiple Virtual Machines?
Although it is straightforward to deploy a single virtual machine by using the provided examples, it is a different story when you need to deploy multiple VM. Indeed, some parameters need to be unique per virtual machine, such as the name at a minimum. All other settings could potentially be identical or retrieved dynamically (such as IP via DHCP). In my context, I would like to define for every VM its name, the datastore it will be deployed to, the IP and some notes.
I choose to use an inventory file to describe those parameters; the syntax is the following:
prod-k8s-master01 deploy_vsphere_datastore='NFS-PROD01' guest_custom_ip='192.168.5.77' guest_notes='Master #1'
prod-k8s-master02 deploy_vsphere_datastore='NFS-PROD02' guest_custom_ip='192.168.5.78' guest_notes='Master #2'
prod-k8s-master03 deploy_vsphere_datastore='NFS-PROD01' guest_custom_ip='192.168.5.79' guest_notes='Master #3'
prod-k8s-worker01 deploy_vsphere_datastore='NFS-PROD01' guest_custom_ip='192.168.5.81' guest_notes='Worker #01'
prod-k8s-worker02 deploy_vsphere_datastore='NFS-PROD02' guest_custom_ip='192.168.5.82' guest_notes='Worker #02'
prod-k8s-worker03 deploy_vsphere_datastore='NFS-PROD01' guest_custom_ip='192.168.5.83' guest_notes='Worker #03'
prod-k8s-worker04 deploy_vsphere_datastore='NFS-PROD02' guest_custom_ip='192.168.5.84' guest_notes='Worker #04'
prod-k8s-worker05 deploy_vsphere_datastore='NFS-PROD01' guest_custom_ip='192.168.5.85' guest_notes='Worker #05'
Everything else will be fixed in a YAML answerfile.
Ansible Playbook, Role, Answerfile and Inventory
Multiple tasks related to a single matter can be grouped in a role: I created a deploy-vsphere-template role which will be called by my playbook. Check below the different files I created for my need:
│ └── deploy-vsphere-template
│ └── tasks
│ └── main.yml
- ansible.cfg: Ansible configuration file
- answerfile.yml: YAML file to store parameters which defines the vSphere infrastructure and virtual machine common options
- deploy-kubernetes-prod.yml: my playbook
- roles/deploy-vsphere-templates/tasks.main.yml: task that deploys the virtual machines using the vmware_guest module
- vms-to-deploy: inventory file
Now, it’s time to run the playbook!
\❯ ansible-playbook -i vms-to-deploy deploy-kubernetes-prod.yml
The -i parameter is used to specify my inventory file which list the virtual machines names and associated parameters.
You can check the code on the GitHub repository.
Tips & Considerations
If you begin with Ansible, you may not be successful the very first time. I compiled a few information below to help you with the specific context of this blog post:
- If your target is to deploy multiple VMs, the vmware_guest module will clone (or deploy) simultaneously as many VMs as the number of vSphere concurrent operations.
- Ansible works with an inventory and will try by default to connect to the hosts described in this inventory, which in our case don’t exist yet: because of that, you need to set gather_facts to false.
- Ansible will also try to connect to the hosts in the inventory file to execute the tasks. You can avoid this using the delegate_to option (set to localhost here).
- You can find guest_id values here. In my situation, I have a CentOS 7 64 bits template, but I had to set it to rhel7_64Guest to workaround some challenge in my current template.
- I used an inventory file to describe the virtual machine: that is not the only way of doing that, I could have also used an Ansible list of items (with_items).
I’m sure some people are screaming and saying that Ansible should not be used as a deployment tool. 🙂 To be honest, deploying multiple VMs with Ansible was more an exercise than something else. My final goal is to use Terraform to take care about the deployment, and I’ll publish an article on that very soon: stay tuned!