Ansible: quick intro

Ansible is a solution for remote server management. It’s pure Python and basically the only thing  it requires is SSH connection. Why you might want to start using it? Well..

  • For starters, it’s very simple. It’s possible to start using it after 10 minutes introduction. It doesn’t require to know anything special. All configuration is plain YAML. Don’t worry if you don’t know what YAML is. It’s so simple you don’t even imagine.
  • It’s very handy and flexible. You can set up / update / remove / manage packages. You can deploy applications. You can deploy admin scripts. You can prepare an environment. You can fix security issues. You can check security updates.
  • It supports all common Linux OS distributions (Centos, Debian, Ubuntu).
  • It doesn’t require any host agents.
  • There’s great dynamic community. On Github  you can find almost all typical tasks prepared for you. Like LAMP setup, WordPress  / MySQL / nodejs deployment and so on.

So let’s get started. We will use Centos servers, but it’s possible to use other ones.

Obviously we need to install ansible on some server you can use as management one. We will run all commands from there. It’s possible to install it from the package (rpm or deb) or with use of pip. For Centos you might need to enable EPEL repo.

yum install ansible

Ok, our next step is creating of the inventory file. We will specify all our servers we want to manage. Let’s call it ‘hosts’.

[webservers]
web1.example.com
web2.example.com

At this point we are ready to run our first command. It’s supposed that you log in as root. It’s insecure  since direct root login should be disabled, and you should never do that. In this case we assume it’s freshly built cloud servers and the root password is the same on both servers. Later we will be able use different ones or SSH keys (better way).

[root@jump ~]# ansible webservers -i hosts -m ping -k
SSH password:
web1.example.com | success >> {
"changed": false,
"ping": "pong"
}

web2.example.com | success >> {
"changed": false,
"ping": "pong"
}
[root@jump ~]#

Quick tip. If you run

export ANSIBLE_HOSTS=hosts

you will not have to specify the inventory file each time you run ansible.


Let’s consider our command. Here we force ansible to use our inventory file (‘hosts’). We also use module ping. It’s the simplest one. If ‘-k’ is specified ansible asks for the password. Pretty simple, isn’t it?

One mode useful ansilbe module is shell:

[root@jump ~]# ansible webservers -i hosts -m shell -a 'hostname ' -k
SSH password:
web1.example.com | success | rc=0 >>
web1.example.com

web2.examle.com | success | rc=0 >>
web2.example.com

[root@jump ~]#

Useless, but you can specify basically any command you want.

Let’s do something more complex now. We will create a new user and add our pre-generated SSH keys. For this task we can write our first ansible playbook.:

- hosts: webservers

 tasks:
 - name: add deploy user 'devops'
 user: name=devops group=wheel
 - name: Add RSA key to the remote host
 authorized_key: user='devops' key="{{ lookup('file', '~/.ssh/id_rsa.pub') }}"

 - lineinfile: "dest=/etc/sudoers state=present regexp='^%wheel' line='%wheel ALL=(ALL) NOPASSWD: ALL'"

Yep, it’s that easy. I believe it’s pretty self-descriptive but let’s go through each line.

At the very beginning we declare tasks section.

We use name   line just to comment each action.

user  line is for users creating, and authorized_key  adds our SSH key on the servers.

lineinefile  makes sure we will be able to run command as root using our devops account with no password (although technically it’s possible to ask ansible to enter the password for you). We ommit name  line here, but of course you can always use it.

This is how to check our playbook.

ansible-playbook  ssh.yml --check -k

Once everything is ok, run:

[root@jump ~]# ansible-playbook ssh.yml -k

SSH password:

PLAY [webservers] *************************************************************

GATHERING FACTS ***************************************************************
ok: [web1.example.com]
ok: [web2.example.com]

TASK: [add deploy user 'devops'] **********************************************
changed: [web1.example.com]
changed: [web2.example.com]

TASK: [Add RSA key to the remote host] ****************************************
changed: [web1.example.com]
changed: [web1.example.com]

TASK: [lineinfile dest=/etc/sudoers state=present regexp='^%wheel' line='%wheel ALL=(ALL) NOPASSWD: ALL'] ***
ok: [web1.example.com]
ok: [web2.example.com]

PLAY RECAP ********************************************************************

web1.example.com : ok=3 changed=2 unreachable=0 failed=0
web1.example.com : ok=3 changed=2 unreachable=0 failed=0

[root@jump ~]#

You can check: user ‘devops’ was created on both servers and it’s able to run commands as root (via sudo). Let’s check:

ansible-playbook ssh.yml -u devops -s --check

-s means ‘use sudo’. So far so good. Now let’s suppose we want to update some package on all servers. It’s easy:

[root@jump ~]# ansible webservers -m yum -a "name=rsyslog state=installed" -u devops -s
web1.example.com | success >> {
"changed": false,
"msg": "",
"rc": 0,
"results": [
"rsyslog-5.8.10-8.el6.x86_64 providing rsyslog is already installed"
]
}

web1.example.com | success >> {
"changed": false,
"msg": "",
"rc": 0,
"results": [
"rsyslog-5.8.10-8.el6.x86_64 providing rsyslog is already installed"
]
}

[root@jump ~]#

Important note. Ansible tries to be idempotent. It means that ansible will not make any changes if there is already what you need to get, i. e. it won’t overwrite config files every time, but only it a change is required.


Now we can check if there are some security updates available (requires yum-security  installed):

ansible webservers -a 'yum check-update --security ' -s -u devops

Or if you need to install some gem, you can do it this way:

- name: install ruby gem bundler
gem: name=bundler state=present user_install=no

So if you still log in to servers to apply security updates or install some software, try ansible. It’s really great.

Leave a Reply

Your email address will not be published. Required fields are marked *