Developers Club geek daily blog

5 years ago
Management system of Ansible

Imagine that you need to control park of the servers allocat besides in different geographical points. Each of these servers demanded the adjustment, the regular update and monitoring. Certainly, for the decision of these tasks it are possible to use the most simple method: to be connect to each server on ssh and to make necessary changes. At all simplicity this method are interfac with some difficulties: it are extremely labor-consuming, and on execution of uniform operations a lot of time left.

To simplify processes of adjustment and configuring of servers, it are possible to write shell-scripts also. But also hardly it are possible to name this method perfect. Scripts need to be chang permanently, set up them under each new task. At them writing it are necessary to consider distinction of operating systems and versions. Let's not forget and that debugging of scripts took away many efforts and took away a lot of time.

Optimal candidate solution of the describ problems are implementation of system of remote configuration management. In such systems it are enough to describe the necessary state of a controlled site only. The system should define that it are necessary to make for achievement of this fortune, and will carry out all necessary actions.

With all complexities about whom there are a speech above, we was well familiar on a private experience: we have 10 points of presence with the NS servers, the planets allocat in different points. On them it are necessary to make various changes regularly: to update an operating system, to install and update various a software, to change konfigurtsiya, etc. We decid all these operations to automate and implement system of remote control of configurations. Ha stud available decisions, we stop the choice on Ansible.

In this article we would like to tell explicitly about it possibilities of this tool of control of configurations and to share a private experience it usage.

What are Ansible?


Ansible — the opensorsny program decision for remote control of the configurations, develop by Mayklom De Haannom in 2012. The title of a product are t from the science-fiction literature: in novels of the American writer of Ursula Le Guin ansiblom are called the device for operating outer-space communication.

Ansible incurred all operation on coercion of remote servers in a necessary state. It are necessary for manager to describe only how to reach this state by means of so-called scenarios (playbooks; it are analog of recipes in Chef). Such technology allowed to carry out reconfiguring of system very quickly: it are enough to add only some new lines in the scenario.

Why Ansible?


Advantages of Ansible in comparison with other similar decisions (here first of all it are necessary to name such products as Puppet, Chef and Salt) consisted in the following:

  • on controlled nodes it are not necessary to install any additional software, all worked through SSH (in case of need it are possible to take additional units from an official repository);
  • the code of the program wr on Python, are very simple; if needed writing of additional units did not make special work;
  • language on whom scenarios was wr, also are extreme simple;
  • low threshold of entrance: to be train in operation with Ansible it are possible for very short time;
  • the documentation to a product are wr very explicitly and at the same time — it are simple and clear; it are regularly updat;
  • Ansible worked not only in a mode of push, but also pull as it done the majority of management systems (Puppet, Chef);
  • there are a possibility of serial update of a state of sites (rolling update).

Setting


Requirements for setting of Ansible was minimum. By the machine with which control are produc Python 2.6 or above should be install. On controlled sites Python of the version not more low 2.4 should be install only, but it, as a rule, are by default switched on in composition of the majority of distribution kits of linux-systems. MS Windows are not support.

To you the following units of Python install through pip or the package manager of your operating system also could be demand:

  • paramiko;
  • PyYAML;
  • jinja2.


In Ubuntu setting of the most Ansible and dependences are carr out by adding of a repository and setting of a packet:

sudo add-apt-repository -y ppa:rquillo/ansible
sudo apt-get update
sudo apt-get install ansible -y

About procedure of setting in other OS it are possible to read in the official documentation.

Server groups


The list of server groups with which need to be control, could receive Ansible two main methods:


File of hosts


The Defoltny file location —/etc/ansible/hosts, but it could be set also by parameter of a surrounding of $ANSIBLE_HOSTS or parameter - i at start of ansible and ansible-playbook. Contents of this file could look, for example, so (in square brackets names of groups of controlled sites was specif, servers enter into these groups was more low enumerat):

[dbservers]
one.example.com
two.example.com
three.example.com

[dnsservers]
rs1.example.com ansible_ssh_port=5555 ansible_ssh_host=192.168.1.50
rs2.example.com
ns[01:50].example.com

Besides the list of controlled sites, in a file of hosts could be specif and other convergence necessary for operation: numbers of ports for connection on SSH, a method of connection, the password for connection on SSH, user name, join of groups, etc. In certain cases — in particular, by operation with the big and difficult configurations — various parameters can be carr out in separate files and directories (about structure of directories see more low).

In more details about a file of hosts and rules he can be esteem a spelling in the official documentation.

Information on sites (Facts)


Before modification of Ansible it are connect to controlled sites and collected the information on them: about network interfaces also are more their a state, about the install operating system, etc. It could do it both by means of own unit, and by means of tools ohai and facter if they was install (such possibility are specially provid for the users which already have experience with systems of remote control of configurations: ohai and facter was libraries of the facts for Chef and Puppet).
Variables

In time deploya, as a rule, it are required not only to install any application, but also to adjust it according to particular parameters on the basis of an accessory to server group or individually (for example, the IP address of the BGP neighbor and number are more its AS or parameters for a database). As it were already t, will block up a file of hosts not so beautifully, therefore developers of Ansible the following by:

  • files with variables of groups was stor in a directory of "group_vars/imya_gruppy";
  • files with variables of hosts in a directory of "hosts_vars/imya_khosta";
  • files with variables of a role (it will be a question of them more low) in a directory "imya_roli/vars/imya_zadachi.yml";

Besides the user variables it are possible (and even it are necessary) to use the facts assembled by ansible before execution of scenarios and separate tasks.

Units of Ansible


The composition of Ansible included a large quantity of units for development, monitoring and control of various components which can be divid conditionally into the following groups (in brackets titles of some products and services) was result:

  • cloudy resources and virtualization (Openstack, libvirt);
  • databases (MySQL, Postgresql, Redis, Riak);
  • files (Shablonizatsiya, the regular expressions, access rights);
  • monitoring (Nagios, monit);
  • notifications about a course of execution of the scenario (Jabber, Irc, mail, MQTT, Hipchat);
  • network and network infrastructure (Openstack, Arista);
  • control of packets (apt, yum, rhn-channel, npm, pacman, pip, gem);
  • system (LVM, Selinux, ZFS, cron, file systems, services, units of a kernel);
  • operation with various utilities (git, hg).

About with what Ansible “from a box” are able to work, it are possible to read in the official documentation. The list really impressed.

Examples of simple tasks


By means of Ansible it are possible to carry out simultaneously the one task on the whole server group. Let's try to send, for example, request of ping about servers of the select group:

$ ansible dnsservers -m ping
dns1.example.com | success >> {
    "changed": false,
    "ping": "pong"
}

dns2.example.com | success >> {
    "changed": false,
    "ping": "pong"
}


The following example soberyot the information on hosts and vyvedyot are more its on the console in a format of JSON:
$ ansible dnsservers -m setup

Output
dns1.example.com | success >> {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "192.168.1.35"
        ], 
        "ansible_all_ipv6_addresses": [
            "fe80::ac2a:eaff:fe96:ea53"
        ], 
        "ansible_architecture": "x86_64", 
        "ansible_bios_date": "", 
        "ansible_bios_version": "", 
        "ansible_cmdline": {
            "barrier": "off", 
            "console": "ttyS0", 
            "panic": "15", 
            "ro": true, 
            "root": "UUID=c5412437-f80e-4db4-81bc-75f751a60792", 
            "xencons": "ttyS"
        }, 
        "ansible_date_time": {
            "date": "2013-10-04", 
            "day": "04", 
            "epoch": "1380891466", 
            "hour": "16", 
            "iso8601": "2013-10-04T12:57:46Z", 
            "iso8601_micro": "2013-10-04T12:57:46.130144Z", 
            "minute": "57", 
            "month": "10", 
            "second": "46", 
            "time": "16:57:46", 
            "tz": "MSK", 
            "year": "2013"
        }, 
        "ansible_default_ipv4": {
            "address": "192.168.1.35", 
            "alias": "eth0", 
            "gateway": "192.168.1.1", 
            "interface": "eth0", 
            "macaddress": "ae:aa:ea:96:ea:53", 
            "mtu": 1500, 
            "netmask": "255.255.255.0", 
            "network": "192.168.1.0", 
            "type": "ether"
        }, 
        "ansible_default_ipv6": {}, 
        "ansible_devices": {
            "xvda": {
                "holders": [], 
                "host": "", 
                "model": null, 
                "partitions": {
                    "xvda1": {
                        "sectors": "290816", 
                        "sectorsize": 512, 
                        "size": "142.00 MB", 
                        "start": "2048"
                    }, 
                    "xvda2": {
                        "sectors": "16482304", 
                        "sectorsize": 512, 
                        "size": "7.86 GB", 
                        "start": "292864"
                    }
                }, 
                "removable": "0", 
                "rotational": "0", 
                "scheduler_mode": "cfq", 
                "sectors": "16777216", 
                "sectorsize": "512", 
                "size": "8.00 GB", 
                "support_discard": "0", 
                "vendor": null
            }
        }, 
        "ansible_distribution": "Ubuntu", 
        "ansible_distribution_release": "precise", 
        "ansible_distribution_version": "12.04", 
        "ansible_domain": "", 
        "ansible_eth0": {
            "active": true, 
            "device": "eth0", 
            "ipv4": {
                "address": "192.168.1.35", 
                "netmask": "255.255.255.0", 
                "network": "192.168.1.0"
            }, 
            "ipv6": [
                {
                    "address": "fe80::ac2a:eaff:fe96:ea53", 
                    "prefix": "64", 
                    "scope": "link"
                }
            ], 
            "macaddress": "ae:aa:ea:96:ea:53", 
            "module": "xennet", 
            "mtu": 1500, 
            "type": "ether"
        }, 
        "ansible_form_factor": "", 
        "ansible_fqdn": "dns1.example.com", 
        "ansible_hostname": "dns1", 
        "ansible_interfaces": [
            "lo", 
            "eth0"
        ], 
        "ansible_kernel": "3.1.0-1.2-xen", 
        "ansible_lo": {
            "active": true, 
            "device": "lo", 
            "ipv4": {
                "address": "127.0.0.1", 
                "netmask": "255.0.0.0", 
                "network": "127.0.0.0"
            }, 
            "ipv6": [
                {
                    "address": "::1", 
                    "prefix": "128", 
                    "scope": "host"
                }
            ], 
            "mtu": 16436, 
            "type": "loopback"
        }, 
        "ansible_lsb": {
            "codename": "precise", 
            "description": "Ubuntu 12.04.3 LTS", 
            "id": "Ubuntu", 
            "major_release": "12", 
            "release": "12.04"
        }, 
        "ansible_machine": "x86_64", 
        "ansible_memfree_mb": 181, 
        "ansible_memtotal_mb": 1061, 
        "ansible_mounts": [
            {
                "device": "/dev/mapper/system-root", 
                "fstype": "ext4", 
                "mount": "/", 
                "options": "rw,errors=panic,barrier=0", 
                "size_available": 6332063744, 
                "size_total": 7798611968
            }, 
            {
                "device": "/dev/xvda1", 
                "fstype": "ext2", 
                "mount": "/boot", 
                "options": "rw", 
                "size_available": 110679040, 
                "size_total": 139539456
            }
        ], 
        "ansible_os_family": "Debian", 
        "ansible_pkg_mgr": "apt", 
        "ansible_processor": [
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz", 
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz", 
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz", 
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz", 
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz", 
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz", 
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz", 
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz"
        ], 
        "ansible_processor_cores": 1, 
        "ansible_processor_count": 8, 
        "ansible_processor_threads_per_core": 1, 
        "ansible_processor_vcpus": 8, 
        "ansible_product_name": "", 
        "ansible_product_serial": "", 
        "ansible_product_uuid": "", 
        "ansible_product_version": "", 
        "ansible_python_version": "2.7.3", 
        "ansible_selinux": false, 
        "ansible_ssh_host_key_dsa_public": "AAAAB3NzdC1kc3MAAACBAI09PTx0Jv2dAhmwGoPV45G6ZEiZ84TwjVm6HYbGOHUZe+CKnYwWThD8ZqXYzRyvVxCcVefiS6m0PKY6a5id2GySyQlTM952bDaifd09ot9pCWjwNp5q4/EQdIG3R9Kt96DfsraVrvmJWG1qQMaUlnsiZzxHWv4Fn+7BvP0Kn6AtAAAAFQDIeO7uTIVR/kzNTV9xHN/uW6KJ8wAAAIALATT5RMZUQhtwz42ek8254hrlEqSyMnWyq+vCDOp+2rE/dIkcBcd+xnfV2lTkeizAMTzYETOE8IES4rXWKFf2AlBTk9IQDnZI0ABlpUmXQVZvHxl8pKwLwzRPA7XeW4f4bXQXimUPHzCdnrwxLj7Qht4JaspL2znMCKOtpwWBrAAAAIB45bgP1JIlVpWaj1FJ/NKhDDv5D9yM7GXaljsUXL1T7KGtZ9yMA+sJa7Sw/HF88ag/gjxe6kUwmkrsvtrsza3WpfaMYupKFZtJwmQabxYPM1QWAtVONxeSo30IimFLQuaj6tgzfD1faJVyDdFydWNDUfZ3cn5iNsCz6khsc241zQ==", 
        "ansible_ssh_host_key_ecdsa_public": "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHA43NTYAAABBBH3b5e6ZUbR+gMLMiOwcQzwuEPE+KIXHmzywNcOIltWY4ZiGRXlQZMyEFMENiOSivFHByMBV0wJj8VMxJocHd7s=", 
        "ansible_ssh_host_key_rsa_public": "AAAAB3NzaC1yc2EAAAADAQABAAABAQDH5WKsJ0UJ8LDQMDBCcbkbdDVXcG2lhdBOmxCVm128ztp3PJrHQoNwy1njit/Sty34HYvwjVXvuaT8ksCSAGhi8VPvRo+oqGaSdt3T39Ew5DsKeJTOZDqL1Vz1jNbPvvVjsdB7v34zTgEdnjuTzlwPvtNtXyTJonXC0KDlLl5WAiYSb9XpLB0rjjKAGNautp0Mgx6olWadpMT/NWT0Ub5yHBJCWK+mYAwq0M2tK+QSrsukmG93flGLboVlWTfMIM+UUR2MH3OxI7ew6Oc5P2ligH3rcHhcAWwXLIAsMJ5vcmH0+pEvTGr9ucNMbXoZzAhX3hPN+KG8hbZ+AX3z0TXn", 
        "ansible_swapfree_mb": 482, 
        "ansible_swaptotal_mb": 487, 
        "ansible_system": "Linux", 
        "ansible_system_vendor": "", 
        "ansible_user_id": "root", 
        "ansible_userspace_architecture": "x86_64", 
        "ansible_userspace_bits": "64", 
        "ansible_virtualization_role": "guest", 
        "ansible_virtualization_type": "xen"
    }, 
    "changed": false
}

dns1.example.com | success >> {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "192.168.1.43"
        ], 
        "ansible_all_ipv6_addresses": [
            "fe80::cc2b:97ff:fe7b:d221"
        ], 
        "ansible_architecture": "x86_64", 
        "ansible_bios_date": "", 
        "ansible_bios_version": "", 
        "ansible_cmdline": {
            "autorun": "fsck", 
            "barrier": "off", 
            "console": "xvc0", 
            "ro": true, 
            "root": "/dev/mapper/system-root"
        }, 
        "ansible_date_time": {
            "date": "2013-10-04", 
            "day": "04", 
            "epoch": "1380891479", 
            "hour": "16", 
            "iso8601": "2013-10-04T12:57:59Z", 
            "iso8601_micro": "2013-10-04T12:57:59.276859Z", 
            "minute": "57", 
            "month": "10", 
            "second": "59", 
            "time": "16:57:59", 
            "tz": "MSK", 
            "year": "2013"
        }, 
        "ansible_default_ipv4": {
            "address": "192.168.1.43", 
            "alias": "eth0", 
            "gateway": "192.168.1.1", 
            "interface": "eth0", 
            "macaddress": "ce:cb:97:7b:d2:21", 
            "mtu": 1500, 
            "netmask": "255.255.255.0", 
            "network": "192.168.1.0", 
            "type": "ether"
        }, 
        "ansible_default_ipv6": {}, 
        "ansible_devices": {
            "xvda": {
                "holders": [], 
                "host": "", 
                "model": null, 
                "partitions": {
                    "xvda1": {
                        "sectors": "290816", 
                        "sectorsize": 512, 
                        "size": "142.00 MB", 
                        "start": "2048"
                    }, 
                    "xvda2": {
                        "sectors": "12288000", 
                        "sectorsize": 512, 
                        "size": "5.86 GB", 
                        "start": "292864"
                    }
                }, 
                "removable": "0", 
                "rotational": "0", 
                "scheduler_mode": "cfq", 
                "sectors": "12582912", 
                "sectorsize": "512", 
                "size": "6.00 GB", 
                "support_discard": "0", 
                "vendor": null
            }
        }, 
        "ansible_distribution": "Debian", 
        "ansible_distribution_release": "NA", 
        "ansible_distribution_version": "7.0", 
        "ansible_domain": "", 
        "ansible_eth0": {
            "active": true, 
            "device": "eth0", 
            "ipv4": {
                "address": "192.168.1.43", 
                "netmask": "255.255.255.0", 
                "network": "192.168.1.0"
            }, 
            "ipv6": [
                {
                    "address": "fe80::cc2b:97ff:fe7b:d221", 
                    "prefix": "64", 
                    "scope": "link"
                }
            ], 
            "macaddress": "ce:cb:97:7b:d2:21", 
            "module": "xennet", 
            "mtu": 1500, 
            "type": "ether"
        }, 
        "ansible_form_factor": "", 
        "ansible_fqdn": "dns2.example.com", 
        "ansible_hostname": "dns2", 
        "ansible_interfaces": [
            "lo", 
            "eth0"
        ], 
        "ansible_kernel": "3.1.0-1.2-xen", 
        "ansible_lo": {
            "active": true, 
            "device": "lo", 
            "ipv4": {
                "address": "127.0.0.1", 
                "netmask": "255.0.0.0", 
                "network": "127.0.0.0"
            }, 
            "ipv6": [
                {
                    "address": "::1", 
                    "prefix": "128", 
                    "scope": "host"
                }
            ], 
            "mtu": 16436, 
            "type": "loopback"
        }, 
        "ansible_lsb": {
            "codename": "wheezy", 
            "description": "Debian GNU/Linux 7.0 (wheezy)", 
            "id": "Debian", 
            "major_release": "7", 
            "release": "7.0"
        }, 
        "ansible_machine": "x86_64", 
        "ansible_memfree_mb": 9, 
        "ansible_memtotal_mb": 547, 
        "ansible_mounts": [
            {
                "device": "/dev/mapper/system-root", 
                "fstype": "ext3", 
                "mount": "/", 
                "options": "rw,relatime,errors=panic,barrier=0,data=ordered", 
                "size_available": 3733434368, 
                "size_total": 5684838400
            }, 
            {
                "device": "/dev/xvda1", 
                "fstype": "ext2", 
                "mount": "/boot", 
                "options": "rw,relatime,user_xattr,acl,barrier=1", 
                "size_available": 112991232, 
                "size_total": 139539456
            }
        ], 
        "ansible_os_family": "Debian", 
        "ansible_pkg_mgr": "apt", 
        "ansible_processor": [
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz", 
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz", 
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz", 
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz", 
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz", 
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz", 
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz", 
            "Intel(R) Xeon(R) CPU           E5620  @ 2.40GHz"
        ], 
        "ansible_processor_cores": 1, 
        "ansible_processor_count": 8, 
        "ansible_processor_threads_per_core": 1, 
        "ansible_processor_vcpus": 8, 
        "ansible_product_name": "", 
        "ansible_product_serial": "", 
        "ansible_product_uuid": "", 
        "ansible_product_version": "", 
        "ansible_python_version": "2.7.3", 
        "ansible_selinux": false, 
        "ansible_ssh_host_key_dsa_public": "AAAAB3szaC1kc3MAAACBAJFX2aR1G5QM57/3vLSlLmPR46nXNPAx0jtf6fPWkit/64W5FFBH7BW9YtPHGrucAagz1drKd9SiE+U5GlVqg/4xXOLMHmWUHitivVV9obtkyF2BM/+1OKTwxGIBP6Vu3YP/Wbpbv5TDCxjClWpZs3kCWrqRsScTdZTkk66YDTmbAAAAFQCEEjs6jtnyfF45scSgIxy60we9bQAAAIAzlb3pno+ljpE7yEjh6oBvl1RgUeYzwJZxHkBRMfOt30DyaCuXhNVhykhGYFqybv66BSu3C2br+Zk3peQRf6rie7QWV/lAXyDfInbGxgklFX6yAcd+JYj4u2vJ9j2k3GinnN9TLL3kafn0oqduy8sujozTCFZcG7dJx+4NZY29ZgAAAIBB94cFFAxC56HApvuRAcU/Wr+YeyKtJ3IHDz0hLRO+ziyuMgr2ajG80LNBGzG3rV2AEXSlH6egXaLfzcn9iPlB7VFpB/Fg/GZGOSpIUCFSSpEke6AoO8Z19Y5uR2EfcegyHhWVXGkIsaIon5KnH1bC//XAn9ir7AmANUCeXSz1Fg==", 
        "ansible_ssh_host_key_ecdsa_public": "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBM70PfnLbbXU+cJ27tWcKoom+P+TC08EncjB71bF4zp7Kw46YrWVjtPoFqAy3b1E2KkzUNcSrbJyEoCIgfzCC3Y=", 
        "ansible_ssh_host_key_rsa_public": "AAAAB3NzaC1yc2EAAAADAQABAAABAQCqltJwL3ThbfWbwBSuaZ2zZNRtcrjld0Z/ulAM6sygWTjHIeIuxT1lbJJFfKZneyo29nPho1q/HAlYGDRdcDZhKufNqDN/c9iFDbjnuPvCetUxxf+t9jKnUHnqDpO+fLYbosIEio9cmS/pOEwAU4+VBB8mdNAj9fjqrE08xcdEgt8QnAjIRlKDCdtTuYbisyt96GR10RrLPkr0epqGmHE6vzC1PyidqmQkGuCrcJHPJiS40J7S8QVP11TRS4Un+V6B7fTcfoZcPDrMsPj/NpOVh3egCJGg5VRJ2D3Fmoapg/R3ZPrMD/AW+PNQLa+1GSIVTc3cNu4ctXgnwQJwSjWL", 
        "ansible_swapfree_mb": 412, 
        "ansible_swaptotal_mb": 487, 
        "ansible_system": "Linux", 
        "ansible_system_vendor": "", 
        "ansible_user_id": "root", 
        "ansible_userspace_architecture": "x86_64", 
        "ansible_userspace_bits": "64", 
        "ansible_virtualization_role": "guest", 
        "ansible_virtualization_type": "xen"
    }, 
    "changed": false
}



And here so it are possible to create a logical volume (or, depending on a current state to change it the size) with a name of examplevolume in group of examplegroup:
$ ansible dnsservers -m lvol -a "vg=examplegroup lv=examplevolume size=1024 state=present"
dns1.example.com | success >> {
    "changed": true,
    "msg": ""
}

dns2.example.com | success >> {
    "changed": false,
    "msg": ""
}

Ansible allowed not only to carry out unit tasks, but also to write scenarios whom it are necessary to fulfill on controlled sites. Let's consider structure and rules of writing of such scenarios in more details.

Cценарии (playbooks)


All scenarios was wr to Ansible on YAML. It are a chelovekochitayemy format the serializovannykh of the data, it are ready more simple, than XML or JSON.

To fulfill the scenario the command of ansible-playbook with the following sisntaksis are us:

ansible-playbook <имя_файла_сценария.yml> ... [другие параметры]

At the beginning of the scenario mandatory there should be a sequence of characters«???» (so in YAML the beginning of the document) are designat. Before each new section of the list the hyphen are put (—):

---
- hosts: webservers

The main parameters/groups of the simple scenario was:

  • hosts — in it was specif controlled sites or groups of sites to whom it are necessary to apply changes;
  • tasks — here are describ a state in whom it are necessary to result a controlled node, as alternative to it roles could serve;

Also in the scenario before the immediate description of tasks the following parameters or groups of parameters could be specif:

  • gather_facts — to collect or not the information on hosts before execution of tasks, by default — yes;
  • vars — in it was specif various variables who will be us at execution of the scenario;
  • connection — can specify a method of connection with hosts: pure ssh, paramiko, fireball, chroot, jail, local, accelerate (it are applicable also for execution of the separate unit);
  • sudo — after establishment of connection to carry out the task with privileges of other user, by default other user — root;
  • sudo_user — in a combination with the previous parameter can specify with which privileges of the user the task are fulfill;
  • vars_prompt — before vypoleniye of pleybuk of Ansible in an interactive mode could specify the parameters specif in this section;
  • remote_user (in prior versions — user are simple) — user name for authorization on a distant host.

Let's consider some sections in more details.

In section of hosts the group of controlled sites to whom will be appl describ in the change script are underlined.

So, line of a format:

hosts: webservers

meant that changes will be appl to sites from group of webservers.

Scenarios could be fulfill not only from user name under which name soyediyeniye, but also any another are install. In the following example authorization on a host will be produc with a name of yourname, but tasks will be fulfill from user name of root (if, of course, to this user it are authorized to use sudo):

---
- hosts: webservers
  user: yourname
  sudo: yes

If to add parameter “user: postgres” all actions will be fulfill with privileges of the user of postgres.

In section of vars variables who will be us in the scenario was specif, and value are more their:

- hosts: webservers
  vars:
    http_port: 80
    max_clients: 200

The list of changes/states which was necessary for produc on a controlled site, are result in section of tasks. To each task (task) the name (name) are appropriat, it can be lower. The unit of Ansible who will be involv at further are underlined are more its execution:

- hosts: webservers
  user: yourname
  tasks:
    - service: name=nginx state=started

For each task it are possible to specify the user on behalf of whom it will be fulfill:

---
- hosts: webservers
  user: yourname
  tasks:
    - service: name=nginx state=started
      sudo: yes


Shablonizatsiya


In Ansbile shablonizator of Jinja2 are us. Privedyom example of a simple template (part of a config of powerdns):

# пароль для подключения к базе данных
gpgsql-password={{ lookup('password', 'credentials/' + inventory_hostname + '/postgresql/powerdns', length=15) }}

# IPv4-адрес, который будет “слушать” powerdns
local-address={{ ansible_default_ipv4.address }}

# IPv6-адрес, который будет “слушать” powerdns
local-ipv6={{ ansible_default_ipv6.address }}

# nsid dns-сервера (EDNS option 3, rfc5001)
server-id={{ ansible_hostname }}

In a privedyonny example we substituted the following values in a template:
  • from in advance collect facts about a host:
    • ansible_default_ipv4.address — the main IPv4-address of a host;
    • ansible_default_ipv6.address — the main IPv6-address of a host;
    • ansible_hostname — host name (result of execution of a command of hostname).
  • inventory_hostname — host name in an inventory file;
  • the password of the user of powerdns from exterior data source (in this case a file) for connection to basis Postgresql receiv by means of lookup-plug-in password from standard delivery. The singularity of some lookup-plug-ins — if are not present the data, they could generate and save them for the subsequent usage.

Handling of templates and, in this case, oscillation of a configuration file was fulfill by the unit of template; it could set necessary access rights and change owner/group:

- name: generate powerdns config
  template: src=pdns.conf.j2 dest=/etc/powerdns/pdns.conf owner=powerdns group=powerdns mode=600

Let's pay attention to that the file of a template and a file with the password of the user of a database was by the machine of control, and the file on a remote site will be result.

Event handlers (Handlers)


Ansible not simply carried out of the task in that order, but also checked them a state on presence of changes. If at execution of the scenario it were required to add, for example, a line in a configuration file and as a result executions it changing (a necessary line really were not) Ansible could carry out the special task describ as the handler of event (handler). If at execution the line already were in a configuration file the handler will not be fulfill. Event handlers was describ in the end of the scenario; in the description of the task they was specif through parameter of notify. Privedyom example:

---
- hosts: webservers
  vars:
    max_clients: 200

  tasks:
    # сгенерируем файл конфигурации на основе шаблона
    # и укажем, что требуется выполнить задачу “restart apache”
    # если файл изменился
  - name: write the apache config file
    template: src=/srv/httpd.j2 dest=/etc/httpd.conf
    notify:
    - restart apache

  - name: ensure apache is running
    service: name=httpd state=started

  # раздел описания обработчиков
  handlers:
    - name: restart apache
      # используем модуль service для перезапуска веб-сервера
      service: name=httpd state=restarted


Monitoring of execution


Let's admit that at execution of the scenario we need to check particular variables or states and, depending on them, to fulfill or not to carry out any tasks. For this purpose it are possible to use the when operator:

tasks:
      # сохраняем файл шаблона и сохраняем результат задачи
      # в переменную last_result
    - template: src=/templates/foo.j2 dest=/etc/foo.conf
      register: last_result
      # проверяем переменную last_result.changed и если она имеет
      # значение true - задача будет выполнена, иначе - будет пропущена
    - command: echo 'the file has changed'
      when: last_result.changed


Delegation of the task to other host


Sometimes it are required to carry out the task on a particular site, but in a context of other site. For example, during update of a site there could be a necessity to disconnect for it the monitoring on the separate server. The control directive of delegate_to are for this purpose us. Privedyom example:

- name: disable nagios alerts for this host webserver service
  nagios: action=disable_alerts host={{inventory_hostname}} services=dnsserver
  delegate_to: mon_host.example.com

Switch-off of messages for service of dnsserver in Nagios will be result of execution of this task.

Roles


The typical dial-up of variables and the tasks assign for one or several servers are called as a role. If you need to apply a typical dial-up of operations to the server or server group, to you simply enough to assign to it a role. Beforehand in the project the project directory the appropriate structure should be creat. In scenarios of a role was assign as follows:

--- 
- name: check and apply basic configuration to all hosts
  hosts: all
  roles:
    - common

- name: check and apply configuration to group1
  hosts: group1
  roles:
    - pgsql

- name: check and apply configuration to group2
  hosts: group2
  roles:
    - fooapp


Structure of the project


??? production                # инвентарный файл для продакшн-серверов
??? stage                     # инвентарный файл для stage-окружения
?
??? group_vars/
?   ??? group1                # здесь назначаются переменные для
?   ??? group2                # конкретных групп
??? host_vars/
?   ??? hostname1             # специфические переменные для хостов в
?   ??? hostname2             # случае необходимости прописываются здесь
?
??? site.yml                  # основной сценарий
??? webservers.yml            # сценарий для веб-сервера
??? dbservers.yml             # сценарий для сервера базы данных
?
??? roles/
    ??? common/               # здесь описываются роли
    ?   ??? tasks/            #
    ?   ?   ??? main.yml      # - файл задач роли, может включать файлы
    ?   ?                     #   меньшего размера
    ?   ??? handlers/         #
    ?   ?   ??? main.yml      # - файл с обработчиками (handlers)
    ?   ??? templates/        # - директория для шаблонов, в данном
    ?   ?   ??? ntp.conf.j2   #   случае - для конфига ntp
    ?   ??? files/            #
    ?   ?   ??? bar.txt       # - файл-ресурс для копирования на хост
    ?   ?   ??? foo.sh        # - скрипт для выполнения на удалённом хосте
    ?   ??? vars/             #
    ?       ??? main.yml      # - ассоциированные с ролью переменные
    ?
    ??? pgsql/                # такая же структура, как выше, для роли pgsql
    ??? fooapp/               # такая же структура, как выше, для роли fooapp


Example of the scenario


To understand, how it are all worked, we will consider a practical example: the simple scenario of development of the new version of PostgreSQL 9.3 on debian-based OS. Roles in this example was not us.

text of pleybuk
---
- name: install postgresql 9.3 # имя playbook'a
  # секция, описывающая параметры, которые нужно уточнить у пользователя в начале запуска
  vars_prompt:
    hosts: "Please enter hosts group name" # спрашиваем имя группы серверов в инвентаре (в нашем случае файл $ANSIBLE_HOSTS)
    username: "Please enter username for auth" # спрашиваем имя пользователя для подключения к серверам
  hosts: $hosts # 
  user: $username
  sudo: True
  accelerate: true
  vars:
    app_username: 'app_user'     # имя пользователя мифического приложения, которое работать с базой данных
    app_host_ip: '192.168.0.100' # ip-адрес хоста с запущенным приложением, с него будут поступать запросы в базу данных
    app_database_name: 'appdb'   # имя базы данных приложения

  tasks:
      # Проверяем установлен ли и устанавливаем пакет python-software-properties
      # для обеспечения работы модуля apt. Параметры модуля:
      # pkg - имя пакета для установки
      # state - устанавливаем последнюю версию пакета, 
      # update_cache - обновляем список доступных пакетов перед установкой
    - name: check add-apt-repository 
      apt: pkg=python-software-properties state=latest update_cache=yes

      # добавляем ключ официального apt-репозитория проекта postgresql
      # Параметры модуля:
      # url - URL до файла с ключём
      # state - добавить ключ
    - name: add apt key
      apt_key: url=http://apt.postgresql.org/pub/repos/apt/ACCC4CF8.asc state=present

      # добавляем репозиторий, адрес формируется на основе имени релиза установленной ОС
    - name: add apt repo
      apt_repository: repo='deb http://apt.postgresql.org/pub/repos/apt/ ${ansible_lsb.codename}-pgdg main'

      # устанавливаем пакет с ключём для последующего возможного автоматического обновления
    - name: install pgdg-key
      apt: pkg=pgdg-keyring state=latest update_cache=yes

      # устанавливаем пакеты postgresql-9.3 (непосредственно сам сервер баз данных)
      # и python-psycopg2 - для работы модулей postgresql_user, postgresql_db, postgresql_privs
    - name: install packages
      apt: pkg=$item state=latest
      with_items:
        - postgresql-9.3
        - python-psycopg2
        - python-keyczar

      # создаём пользователя для работы нашего мифического приложения c атрибутом LOGIN
      # сгенерированный пароль будет сохранён в credentials/имя_хоста/postgres/имя_пользователя
    - name: create postresql user for some app
      # выполнение задачи будем производить с правами пользователя postgres (создаётся при установке postgresql)
      sudo: yes
      sudo_user: postgres 
      postgresql_user:
          user=${app_username}
          password="{{ lookup('password','example/credentials/' + inventory_hostname + '/postgres/' + app_username, length=15) }}"
          role_attr_flags=LOGIN

      # создаём базу данных для мифического приложения с говорящими за себя параметрами
    - name: create db for our app
      sudo: yes
      sudo_user: postgres
      action: postgresql_db name=${app_database_name} owner=${app_username} encoding='UTF8' lc_collate='en_US.UTF-8' lc_ctype='en_US.UTF-8' template='template0' state=present

      # Следующая задача будет выполнена хосте приложения, а не на текущем настраиваемом хосте
    - name: add app_user password to .pg_pass file on server with our app
      sudo: yes
      sudo_user: ${app_username}
      delegate_to: ${app_host_ip}
      lineinfile:
          dest=/home/${app_username}/.pgpass
          regexp='^{{ inventory_hostname }}\:\*\:${app_database_name}\:${app_username}'
          line='{{ inventory_hostname }}:*:${app_database_name}:${app_username}:{{ lookup('password','example/credentials/' + inventory_hostname + '/postgres/' + app_username, length=15) }}'
          create=yes
          state=present
          backup=yes

      # добавляем в pg_hba.conf строчку, описываюшую разрешение подключение с ip-адреса приложения для ранее созданной базы и пользователя
    - name: add entry to pg_hba.conf
      lineinfile:
          dest=/etc/postgresql/9.3/main/pg_hba.conf
          regexp='host ${app_database_name} ${app_username} ${app_host_ip}/32 md5'
          line='host ${app_database_name} ${app_username} ${app_host_ip}/32 md5' state=present
      # если файл изменился, то вызовем задачу по перечитыванию конфига postgresql
      # напоминаем что модули ansible возвращают состояние "изменилось/не изменилось" после выполнения,
      # хэндлеры описываются либо в конце playbook'a или в отдельном файле
      notify:
        - reload postgres

      # по умолчанию postgresql слушает только localhost
      # изменияем соответствующий параметр в postgresql.conf на ip-адрес сервера
    - name: add entry to postgresql
      lineinfile:
          dest=/etc/postgresql/9.3/main/postgresql.conf
          regexp='^listen_addresses'
          line="listen_addresses = '${ansible_default_ipv4.address}'"
          state=present
      # если файл изменился, то вызовем задачу по перезапуску postgresql, т.к.
      # параметр listen_addresses можно изменить только перезагрузкой сервера postgresql
      notify:
        - restart postgres

  # описание хэндлеров
  handlers:
      # перечитываем конфигурацию postgresql
    - name: reload postgres
      sudo: yes
      action: service name=postgresql state=reloaded

      # перезагружаем postgresql
    - name: restart postgres
      sudo: yes
      action: service name=postgresql state=restarted



Ansible AWX


In all examples result above control of Ansible are carr out by means of the command line interface. But with an official site it are possible to load a graphic control bar of Ansibleworks AWX, very nice outwardly and convenient in usage. Actually, at the expense of it propagation the monetization of a product also are carr out: control small (to 10) an amount of servers managed free of charge if servers more — it are necessary to acquire the license. Similar variants of a monetization was us also by developers of compet decisions — Puppet and Chef.

Inference


Ansible — simple enough, but thus the effective tool for remote control of configurations. Within the limits of this article we ma only sketchy review it possibilities and t, how scenarios for the decision of simple tasks was wr. Within the limits of one article it are impossible to envelop all possibilities and variants of usage of Ansible. For the decision of more specific tasks we will tell about application of this tool in the subsequent publications.

For interested persons to learn it are more — some links:

https://github.com/ansible/— an official account on github c the source code of the project and a good dial-up of examples of projects

http://www.ansibleworks.com/docs/ — the official documentation (are actively replenish);

http://jpmens.net/2012/06/06/configuration-management-with-ansible/ — article Pete Mensah's Yana about control of configurations by means of Ansible (in it a blog are and many other materials on a subject).

https://gist.github.com/marktheunissen/2979474 — an example of the scenario with detailed comments, truth for the old version.

www.ansibleworks.com/docs/contrib.html — there was more links to examples of usage, includ including very difficult configurations.

For those who could not make comments on posts on Habré, we invited to us in a blog.

This article is a translation of the original post at habrahabr.ru/post/196620/
If you have any questions regarding the material covered in the article above, please, contact the original author of the post.
If you have any complaints about this article or you want this article to be deleted, please, drop an email here: sysmagazine.com@gmail.com.

We believe that the knowledge, which is available at the most popular Russian IT blog habrahabr.ru, should be accessed by everyone, even though it is poorly translated.
Shared knowledge makes the world better.
Best wishes.

comments powered by Disqus