πŸ“œ ⬆️ ⬇️

Ansible - let's try

Ansible is a relatively young configuration management system, its history goes back a little over three years. But despite this, he quickly and quickly broke into the world of configuration management systems, displacing Chef, Puppet and SaltStack.

Let's look at him closely to see why he is so loved by techies.

So, what is good ansbile:


Low entry threshold


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

')
Now create the 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 ( all parameter) 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 (the -m key) is not specified, 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 command execution.

Declarative configuration language


In addition to the ansible utility, there is the ansible-playbook utility that 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 into the command line every time, I created a file, ansible.cfg , in the project directory.
 # 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 start our first script, which updates the apt cache, and then installs 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 language file interspersed with the Jinja2 template engine.

No additional software needs to be installed on managed nodes.


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

Just write an extra module.


A 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 really lack 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 mechanism of roles.

For those who are too lazy to write something themselves, there are already thousands of ready-made roles on the ansible galaxy site, and they are of quite decent quality in order 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 



web.yml finish the web.yml file.
web.yml
 --- - hosts: all user: ubuntu sudo: yes roles: - nginx 


Note the sudo: yes key. He allows not to write this line in each task. Most administrative tasks should still run as root , so it makes sense to leave it here.

And in the roles/nginx/tasks/main.yml we 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 code is a philosophical question). Of course, the example is intentionally simplified, but it becomes clear from it how to organize projects with a large number of components into ansible.

Data


In ansible there is a built-in setup module that is run 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, it is an analogue 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 referring to 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'}" } 



Templates


In ansible there are templates that use the template Jinja2. Let's make 2 templates.

 # 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 <html> <body> <pre> {{ ansible_default_ipv4 }} {{ ansible_env }} </pre> </body> </html> 

Notice the variables in double curly braces (for example, {{ ansible_env }} ) are the ansible variables that we collected using the setup module we already know.

Handlers


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

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


Now we will complete 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, runs it, deletes the default nginx configuration file, creates the configuration file from the template ansible.conf.j2 , which we wrote above, and generates the index.html file from the template that we described above . Please note that some tasks send notify: reload nginx notifications notify: reload nginx . Moreover, in this case, 2 notifications should be sent to reboot nginx, but, in fact, they will be merged into one, as will be seen below. Notifications are needed to reboot nginx (or any other service) only in case the template (or something else) has changed, so as not to do it every time you start ansible.

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. Let's look at the browser.



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

disadvantages


And so that you do not think that ansible is the ideal product, which has no place to develop, I will tell about its shortcomings.

Source: https://habr.com/ru/post/254959/


All Articles