The knowledge bar for DevOps is set pretty high, good DevOps engineers need to acquire skills from a combination of many different technology categories. These may include programming (Java, Python, etc), virtualisation/container tech, shell scripting, configuration management tools (Which sometimes may require learning a Domain Specific Language or DSL in short) or even release management, etc. In Part #2 of the guide, I will briefly talk about the use of a convenient configuration management tool (Ansible) that can help you on your DevOps journey.
In my last guide (Part #1), we talked about setting up a DevOps learning environment. I am going to use that environment to jump straight into a pretty cool configuration management tool, Ansible.
Prerequisites
- Access to multiple Linux machines/VMs through ssh or a multi-instance DevOps environment.
- Python installed
Installing Ansible on Ubuntu
For the purpose of this guide, I will refer to the machine you are working on is the control node (I’m using Ubuntu 18.04), and the docker instances as the worker nodes. The first step is to include the PPA in your system’s resources list. Do run an update after to ensure the resource list is updated.
$ sudo apt-add-repository ppa:ansible/ansible $ sudo apt-get update
Simply run the apt-get install command to install Ansible. Verify the installation by viewing the ansible version installed on your machine.
$ sudo apt-get install ansible $ ansible --version ansible 2.9.10
You will get a bunch of other information (Such as python version, not listed above).
Setting up inventory hosts
Start up your favorite text editor(my favorite is vi) and edit the ansible hosts file (create one if it doesn’t exist).
$ sudo vi /etc/ansible/hosts
If a file was created by installation, it will contain some instructions, fill in the following to connect to the multi-cluster DevOps environment previously created.
[servers] server1 ansible_host=172.10.0.1 server2 ansible_host=172.10.0.2 server3 ansible_host=172.10.0.3
You can check whether the connection to your servers will succeed through the following command.
$ ansible all -m ping -u root server1 | UNREACHABLE! => { "changed": false, "msg": "Failed to connect to the host via ssh: root@172.10.0.1: Permission denied (publickey,password).", "unreachable": true } server2 | UNREACHABLE! => { "changed": false, "msg": "Failed to connect to the host via ssh: root@172.10.0.2: Permission denied (publickey,password).", "unreachable": true } server3 | UNREACHABLE! => { "changed": false, "msg": "Failed to connect to the host via ssh: root@172.10.0.3: Permission denied (publickey,password).", "unreachable": true }
However, your request will most likely fail as we are still missing one step. I usually go through some possible basic failures so it helps with knowing what to expect and how to troubleshoot.
For the above, the reason we cannot connect to all our instances, is because we are still relying on password authentications to ssh into our containers. What we need to do is to first generate ssh keys on our control node.
$ ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/home/work/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/work/.ssh/id_rsa. Your public key has been saved in /home/work/.ssh/id_rsa.pub. The key fingerprint is: SHA256:LY5Eqsz9vSMmTE6eI+ZQN7xHtTRyRBRvYF7fo76Ux3g work@control-node The key's randomart image is: +---[RSA 2048]----+ | oB.. | | + + . . | | o * o . o | | . o = = . . | | . = o S . . | | + +o= o . . + | |. +*o.o . = E | | .o Boo.. . + | | o.. +..oo . | +----[SHA256]-----+
Next thing we need to do is to copy the public key into our worker nodes, this eliminates the need for password authentication (Note this useful command if your job requires maintaining lots of servers).
$ ssh-copy-id -i ~/.ssh/id_rsa.pub root@172.10.0.1 /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/work/.ssh/id_rsa.pub" /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed /usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys root@172.10.0.1's password: Number of key(s) added: 1 Now try logging into the machine, with: "ssh 'root@172.10.0.1'" and check to make sure that only the key(s) you wanted were added.
Now try to ssh into the server where the key was copied, you should no longer require a password to log in.
$ ssh root@172.10.0.1 Welcome to Ubuntu 16.04.6 LTS (GNU/Linux 4.15.0-106-generic x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage root@5ff00d540729:~#
Now do the same for the other two instances.
$ ssh-copy-id -i ~/.ssh/id_rsa.pub root@172.10.0.2 $ ssh-copy-id -i ~/.ssh/id_rsa.pub root@172.10.0.3
Your ansible ping should now return successes, yay!
$ ansible all -m ping -u root server1 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python3" }, "changed": false, "ping": "pong" } server3 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python3" }, "changed": false, "ping": "pong" } server2 | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python3" }, "changed": false, "ping": "pong" }
Useful ad-hoc commands to try
The ansible ping example is actually one of the ad-hoc commands you can execute on the fly to obtain information from all your servers listed in the ansible configuration host file. More detailed introduction to ad-hoc commands can be found on the official ansible documentation here. https://docs.ansible.com/ansible/latest/user_guide/intro_adhoc.html
Lets say I wish to know what are all the python versions on each of my worker_nodes.
$ ansible all -m setup -a "filter=ansible_python_version" -u root server3 | SUCCESS => { "ansible_facts": { "ansible_python_version": "3.5.2", "discovered_interpreter_python": "/usr/bin/python3" }, "changed": false } server2 | SUCCESS => { "ansible_facts": { "ansible_python_version": "3.5.2", "discovered_interpreter_python": "/usr/bin/python3" }, "changed": false } server1 | SUCCESS => { "ansible_facts": { "ansible_python_version": "3.5.2", "discovered_interpreter_python": "/usr/bin/python3" }, "changed": false }
Or I want to to have a quick look at how much memory each of my container instances are using.
$ ansible all -m command -a "free -m" -u root server1 | CHANGED | rc=0 >> total used free shared buff/cache available Mem: 7896 3114 607 1054 4174 3263 Swap: 2047 29 2018 server3 | CHANGED | rc=0 >> total used free shared buff/cache available Mem: 7896 3094 627 1054 4174 3283 Swap: 2047 29 2018 server2 | CHANGED | rc=0 >> total used free shared buff/cache available Mem: 7896 3093 628 1054 4174 3284 Swap: 2047 29 2018
These are just some of the possibilities you can explore for the command line fanboys out there. Honestly, you shouldn’t need to do much of these, therefore I shall not go too much in detail. In my subsequent post, we shall look into some built in server configuration tricks that ansible provides to DevOps junkies like you and me.