Ansible - let's try

  • Tutorial
Ansible  is a relatively young configuration management system with a history of just over three years. But despite this, he quickly and quickly burst into the world of configuration management systems, crowding out Chef, Puppet and SaltStack.

Let's look at him carefully to understand why he is so loved by techies.

So what is ansbile good for:
  • low entry threshold;
  • declarative configuration description language;
  • Managed nodes do not need to install any additional software;
  • just write an add-on module.


Low entry threshold


You can start using ansible in a couple of minutes. Suppose you are using OSX.
$ brew install ansible
$ ansible --version
ansible 1.8.4
  configured module search path = None


Now create a file hosts:
[test]
localhost ansible_connection=local


Go:
$ ansible -i hosts -m ping all
localhost | success >> {
    "changed": false,
    "ping": "pong"
}

What have we done? For all hosts (parameter all) from the hosts file, execute the ping module. Let's see something else.

ansible -i hosts -a 'ls -lah' all
$ ansible -i hosts -a "ls -lah" all
localhost | success | rc=0 >>
total 12K
drwxr-xr-x  5 brun staff  170 Apr  1 11:50 .
drwxr-xr-x 91 brun staff 3.1K Apr  1 11:37 ..
-rw-r--r--  1 brun staff  230 Apr  1 12:07 export.sh
-rw-r--r--  1 brun staff   42 Apr  3 14:48 hosts
-rw-r--r--  1 brun staff  376 Apr  1 12:49 playbook.yml



If the module (key -m) is not specified, then the command module is used . In fact, ansible can be used not only as a configuration management system, but also as a framework for distributed execution of commands.

Declarative configuration description language


In addition to the ansible utility, there is also the ansible-playbook utility, which you will use most often.

For further examples, I started the t2.micro machine on aws with Ubuntu 14.04 and registered it in hosts.
# hosts
[web]
111.111.111.111

Also, in order not to enter parameters every time in the command line, I created a file in the project directory ansible.cfg.
# ansible.cfg
[defaults]
hostfile       = hosts


After that, create our first script (playbook in ansible terminology) web.yml.

web.yml
# web.yml
---
- hosts: all
  user: ubuntu
  tasks:
    - name: Update apt cache
      apt: update_cache=yes
      sudo: yes
    - name: Install required packages
      apt: name={{ item }}
      sudo: yes
      with_items:
        - nginx
        - postgresql



We run our first script, which updates the apt cache, and then puts two packages: nginx and postgresql.
ansible-playbook web.yml
$ ansible-playbook web.yml
PLAY [all] ********************************************************************
GATHERING FACTS ***************************************************************
ok: [111.111.111.111]
TASK: [Update apt cache] ******************************************************
ok: [111.111.111.111]
TASK: [Install required packages] *********************************************
changed: [111.111.111.111] => (item=nginx,postgresql)
PLAY RECAP ********************************************************************
111.111.111.111               : ok=3    changed=1    unreachable=0    failed=0


In fact, we called the apt module twice , only with different parameters. The script file itself is a Yaml file interspersed with the Jinja2 template engine .

No additional software needs to be installed on managed nodes


Indeed, in order to control the machine, Python must be installed on it (and it is installed by default on all modern linux systems) and there must be ssh access. Compare this with other systems where you need to put a client who needs certain versions of different languages ​​and libraries. This fact, by the way, makes starting with ansible much easier than for other systems.

Just write an add-on module


The module can be written in any language, it should be able to accept input parameters and issue json in response. But why write a new module if there are already 242 modules for all occasions (in version 1.8.4). In case you are really missing something, there is a good description of how to write your module .

Something serious


I would not want to make a huge sheet of the script, so let's split the script into parts using the role mechanism.

For those who are too lazy to write something themselves, there are already thousands of ready-made roles on the ansible galaxy website , and they are of quite decent quality to use them in battle.

We will create a pure role.
ansible-galaxy init nginx -p roles
$ ansible-galaxy init nginx -p roles
- nginx was created successfully
$ tree
├── roles
│   └── nginx
│       ├── README.md
│       ├── defaults
│       │   └── main.yml
│       ├── files
│       ├── handlers
│       │   └── main.yml
│       ├── meta
│       │   └── main.yml
│       ├── tasks
│       │   └── main.yml
│       ├── templates
│       └── vars
│           └── main.yml



Let's modify the file web.yml.
web.yml
---
- hosts: all
  user: ubuntu
  sudo: yes
  roles:
    - nginx


Pay attention to the key sudo: yes. It allows you not to write this line in every task. Most administrative tasks should still be performed with rights root, so it makes sense to leave it here.

And in the file roles/nginx/tasks/main.ymlwe write the following:

roles / nginx / tasks / main.yml
---
- name: Update apt cache
  apt: update_cache=yes
- name: Install required packages
  apt: name=nginx



Using roles, you can reuse code (although whether Yaml is code is a philosophical question). Of course, the example is intentionally simplified, but from it it becomes clear how to organize projects on ansible with a large number of components.

Facts


Ansible has a built-in module setupthat runs first for all managed nodes. Let's see what he does.

ansible -m setup all -u ubuntu
$ ansible -m setup all -u ubuntu
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "172.31.7.80"
        ],
        "ansible_architecture": "x86_64",
        "ansible_bios_date": "12/03/2014",
        "ansible_bios_version": "4.2.amazon",
        "ansible_cmdline": {
            "BOOT_IMAGE": "/boot/vmlinuz-3.13.0-44-generic",
            "console": "ttyS0",
            "ro": true,
            "root": "UUID=fd803688-5c41-4188-8a06-382a65a520bf"
        },
        "ansible_default_ipv4": {
            "address": "172.31.7.80",
            "alias": "eth0",
            "gateway": "172.31.0.1",
            "interface": "eth0",
            "macaddress": "06:a8:07:41:47:a5",
            "mtu": 9001,
            "netmask": "255.255.240.0",
            "network": "172.31.0.0",
            "type": "ether"
        }
   ... и так далее


The setup module collects various data for the node, this is an analog of ohai in chef (by the way, ansible can also use ohai to collect information). All this data can then be used in scripts and templates. For example, the default ip address can be obtained by accessing the variable ansible_default_ipv4.

# web.yml
tasks:
  - debug: msg={{ansible_default_ipv4}}


ansible-playbook web.yml
$ ansible-playbook web.yml
PLAY [all] ********************************************************************
GATHERING FACTS ***************************************************************
ok: [111.111.111.111]
TASK: [debug msg="{{ansible_default_ipv4}}"] **********************************
ok: [111.111.111.111] => {
    "msg": "{u'macaddress': u'06:a8:07:41:47:a5', u'network': u'172.31.0.0', u'mtu': 9001, u'alias': u'eth0', u'netmask': u'255.255.240.0', u'address': u'172.31.7.80', u'interface': u'eth0', u'type': u'ether', u'gateway': u'172.31.0.1'}"
}



Patterns


Ansible has templates that use the Jinja2 template engine. Let's make 2 patterns.

# roles/nginx/templates/ansible.conf.j2
server {
        listen 80 default_server;
        root /usr/share/nginx/html;
        index index.html index.htm;
}


# roles/nginx/templates/index.html.j2


{{ ansible_default_ipv4 }}
{{ ansible_env }}

Pay attention to the variables in double curly brackets (for example {{ ansible_env }}) - these are ansible variables that we collected using the module we already know setup.

Handlers


In order to work out some actions asynchronously, ansible has handlers (handlers) that can be called from tasks. Create your own handler that reloads nginx.

# roles/nginx/handlers/main.yml
---
# handlers file for nginx
- name: reload nginx
  service: name=nginx state=reloaded


We will now refine the task file for the nginx role.

roles / nginx / tasks / main.yml
# roles/nginx/tasks/main.yml
---
- name: Update apt cache
  apt: update_cache=yes
- name: Install required packages
  apt: name=nginx
- name: Start nginx service
  service: name=nginx state=started
- name: Delete default nginx site
  file: path=/etc/nginx/sites-enabled/default state=absent
  notify: reload nginx
- name: Create default nginx site
  template: src=ansible.conf.j2 dest=/etc/nginx/sites-enabled/ansible owner=www-data group=www-data
  notify: reload nginx
- name: Create index.html file
  template: src=index.html.j2 dest=/usr/share/nginx/html/index.html owner=www-data group=www-data


As is clear from the description, this task sets nginx, starts it, deletes the nginx configuration file by default, creates a configuration file from the template ansible.conf.j2that we wrote above, and generates a file index.htmlfrom the template that we described above. Please note that some tasks send a notification notify: reload nginx. Moreover, in this case 2 notifications should be sent to reboot nginx, but, in fact, they will merge into one, as will be seen below. Notifications are needed to restart nginx (or any other service) only if the template (or something else) has changed, so as not to do this every time ansible starts.

So, run the resulting script.

ansible-playbook web.yml
$ ansible-playbook web.yml
PLAY [all] ********************************************************************
GATHERING FACTS ***************************************************************
ok: [111.111.111.111]
TASK: [nginx | Update apt cache] **********************************************
ok: [111.111.111.111]
TASK: [nginx | Install required packages] *************************************
ok: [111.111.111.111]
TASK: [nginx | Start nginx service] *******************************************
ok: [111.111.111.111]
TASK: [nginx | Delete default nginx site] *************************************
changed: [111.111.111.111]
TASK: [nginx | Create default nginx site] *************************************
changed: [111.111.111.111]
TASK: [nginx | Create index.html file] ****************************************
changed: [111.111.111.111]
TASK: [debug msg="{{ansible_default_ipv4}}"] **********************************
ok: [111.111.111.111] => {
    "msg": "{u'macaddress': u'06:a8:07:41:47:a5', u'network': u'172.31.0.0', u'mtu': 9001, u'alias': u'eth0', u'netmask': u'255.255.240.0', u'address': u'172.31.7.80', u'interface': u'eth0', u'type': u'ether', u'gateway': u'172.31.0.1'}"
}
NOTIFIED: [nginx | reload nginx] **********************************************
changed: [111.111.111.111]
PLAY RECAP ********************************************************************
111.111.111.111               : ok=9    changed=4    unreachable=0    failed=0


As you can see, at the end nginx reloaded the configuration. Take a look at the browser.



Voila! We installed and started nginx with the configuration file we needed, generated a page with data from ansible for it, and reloaded nginx.

disadvantages


And so you do not think that ansible is an ideal product that has nowhere to develop, I will talk about its shortcomings.

  • Lack of dependency manager. Now all roles can be stored in the repository, but there is no clear answer to how to work with dependencies and update roles. Everyone uses this approach (it was the same with Chef, for example, 3 years ago), but when the project grows, the disadvantages of this approach are obvious. UPDATE There is some kind of dependency manager , it’s not clear why it is written about this “in small print below”.
  • Incomplete documentation. The answers to some obvious questions have to google, and often they are in private blogs and tickets on github.
  • Ansible-pull mode for large installations will require serious “file refinement”.
  • Inconvenient debug. Even when calling with the key, it is -vvvvnot always clear which command was executed on the server and what prevented it from executing.
  • Fast development. Of course, this drawback has a downside - ansible really is developing rapidly. But I came across a bug when version 1.8.1 worked, and 1.8.4 - broke.

Also popular now: