Managing VMs with Ansible
We got our VMs with Proxmox VE last time, and now I hope to easily manage them. For example, if I want to setting up kubernetes cluster, there are lots of dependencies need to be install, and many many variables that need to manage. So we need a tool to help us manage the VMs. Ansible will be a good choice. Here also comes an idea called infrastructure as code (IaC). We can write the code, usually configuration files to describe the infrastructure we want, and then use the code to create the infrastructure. IaC can be declarative or imperative. Declarative IaC is like the configuration files of kubernetes, we describe the infrastructure we want, and then the tool will create the infrastructure for us. For imperative, we write the code to describe the steps to create the infrastructure, and then the tool will execute the steps for us. Ansible is both declarative and imperative.
Install Ansible
There are some main concepts in Ansible:
- Inventory: the hosts, or simply files that Ansible manage
- Playbook: the code that Ansible execute
- Managed node: the hosts that Ansible manage
- Control node: the host that Ansible run on, for example, I’m using my MacBook to manage the VMs, so my MacBook is the control node, and the VMs are the managed nodes.
So what I only need to do is to install Ansible on my MacBook, and then write the config files on my MacBook to manage the VMs. Quite simple, right?
brew install ansible
Done.
Inventory
The inventory is the hosts that Ansible manage. We can write the inventory in a file, or in a directory. I prefer to use a new directory, since I have no idea about where’s the default inventory of Ansible goes on ARM Macs. On linux it’s usually /etc/ansible/hosts
.
So, in the directory, we can create a file, pve.yaml
that describing the managed nodes. For me, it is the codes below:
pve_vm:
hosts:
vm03:
ansible_host: 192.168.0.70
ansible_user: vm3
vm02:
ansible_host: 192.168.0.174
ansible_user: vm2
vm01:
ansible_host: 192.168.0.108
ansible_user: vm1
So pve_vm is the group, and vm01, vm02, vm03 are the hosts. We test if the inventory is working by running the command below:
$ ansible -i pve.yaml pve_vm --list
hosts (3):
vm03
vm02
vm01
It works pretty well. Then test if we can connect to the VMs:
$ ansible -i pve.yaml pve_vm -m ping
vm02 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
vm01 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
vm03 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
Beautiful. Now we can write a playbook to installing softwares on the VMs. Lets try a simple one first, without any installation at this stage. So we create a file called install.yaml
:
- name: My first play
hosts: pve_vm
tasks:
- name: Ping my hosts
ansible.builtin.ping:
This playbook will actually performs the same as what we done just now, sending a ping request to the vms.
$ ansible-playbook book.yaml -i pve.yaml
PLAY [My first play] *********************************************************************************************************************************
TASK [Gathering Facts] *******************************************************************************************************************************
- name: My first play
ok: [vm01]
ok: [vm02]
ok: [vm03]
TASK [Ping my hosts] *********************************************************************************************************************************
ok: [vm01]
ok: [vm03]
ok: [vm02]
PLAY RECAP *******************************************************************************************************************************************
vm01 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
vm02 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
vm03 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Install softwares
My vms are running ubuntu, so we actually need to use apt
to install softwares. But running apt
requires root permission, so we need to use become
to run the command as root. So we need to add become: true
to the playbook.
But this is not enough, we need to tell Ansible the password of the root user. Yes we can directly write the password in the playbook or the host file, but it is not a good idea since it is not secure. So we can use ansible-vault
to encrypt the password. First we need to create a vault file:
$ ansible-vault create vault.yaml
New Vault password:
Confirm New Vault password:
Enter your password for this vault, remember it. Then write the password of the vms in the file. The format of the vault you enter should be same as a normal yaml file. For example, I have three vms, so I write the password of the root user of each vm in the file:
vm1_pass: vm1_pass
vm2_pass: vm2_pass
vm3_pass: vm3_pass
Then, quit the editor, you can have a look on the yaml vault file again using cat
. It is actually encrypted!
Modified the hosts file, let ansible knows what is the password of the root user for each vm:
pve_vm:
hosts:
vm03:
ansible_host: 192.168.0.70
ansible_user: vm3
ansible_sudo_pass: "{{vm3_pass}}"
vm02:
ansible_host: 192.168.0.174
ansible_user: vm2
ansible_sudo_pass: "{{vm2_pass}}"
vm01:
ansible_host: 192.168.0.108
ansible_user: vm1
ansible_sudo_pass: "{{vm1_pass}}"
After that, we need to add the vault file to the playbook, add the vault to the playbook, and add the software installation task.
- name: My first play
hosts: pve_vm
become: true
vars_files:
- vault.yaml
tasks:
- name: Ping my hosts
ansible.builtin.ping:
- name: Prepare software
apt:
name: docker
state: present
The state
in a task can be present
or absent
, which means install or uninstall. It is kind of a declarative state. You will figure out that when you run the note book once.
$ ansible-playbook book.yaml --ask-vault-password -i pve.yaml
Vault password:
PLAY [My first play] *********************************************************************************************************************************
TASK [Gathering Facts] *******************************************************************************************************************************
ok: [vm02]
ok: [vm01]
ok: [vm03]
TASK [Ping my hosts] *********************************************************************************************************************************
ok: [vm01]
ok: [vm03]
ok: [vm02]
TASK [Prepare software] ******************************************************************************************************************************
changed: [vm01]
changed: [vm03]
changed: [vm02]
PLAY RECAP *******************************************************************************************************************************************
vm01 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
vm02 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
vm03 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
It tells us the prepare software task “Changed”. Why? Because this is a declarative state, it will check if the software is installed or not, if not, it will install it. Previously we don’t have docker installed on these servers, so we got a “changed” result. If we run the playbook again, we will get a “ok” result.
$ ansible-playbook book.yaml --ask-vault-password -i pve.yaml
Vault password:
PLAY [My first play] *********************************************************************************************************************************
TASK [Gathering Facts] *******************************************************************************************************************************
ok: [vm01]
ok: [vm02]
ok: [vm03]
TASK [Ping my hosts] *********************************************************************************************************************************
ok: [vm01]
ok: [vm03]
ok: [vm02]
TASK [Prepare software] ******************************************************************************************************************************
ok: [vm03]
ok: [vm01]
ok: [vm02]
PLAY RECAP *******************************************************************************************************************************************
vm01 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
vm02 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
vm03 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
All done! What rest is to write more tasks to install more softwares towards a kubernetes cluster. Thanks for your reading.