1 Basics of Ansible
1.1 What is Ansible?
Ansible is a modern and open-source IT automation orchestration and provisioning tool which makes your work easier. It manages servers serviceless. You just need to define a list of all instructions you are interested and Ansible will go and do it for you. Ansible is always ready to manage your servers, no matter if it is to install a package, update a configuration, or even restart a service. Ansible uses playbook files to describe automation jobs, and playbooks are written in a very simple language called YAML. YAML is a human-readable data serialization language and is commonly used for configuration files, but could be used in many applications where data is being stored. It is very easy for humans to understand, read and write. Hence the advantage is that even the non-IT support guys can read and understand the playbook and debug if needed.
After connecting to your servers, Ansible pushes small programs called “Ansible Modules”. Ansible runs that module on your servers and removes them when finished. Information for all servers, such as IP, ssh credentials, etc. is stored in inventory. This is one or a couple of text files. The content could be written in ini or YAML format. Ansible uses the host’s file where you can group the hosts and can control the actions of a specific group in the playbooks.
1.2 Why do we need Ansible?
Managing a server is easy. Managing 10 is possible. Managing hundreds or more is a painful task without automation. Ansible is designed to be simple and effective. You can create identical, replicable servers and clusters of servers. Ansible is designed for multi-tier deployment. Ansible does not manage one system at a time, it models IT infrastructure by describing all of your systems are related and is run against multiple hosts at a time. Ansible is completely agent-less which means it could work by connecting your servers through ssh(by default). But there are other methods for connection like Kerberos
1.3 What are the advantages of using Ansible?
Ansible manages machines in an agentless manner. You do not need to have anything installed on the client’s end. However, both push and pull modes are supported. Ansible is a security-focused tool. It uses OpenSSH as the transport protocol. Ansible playbooks are written in YAML and are easy to read. If needed, Ansible can easily connect with Kerberos, LDAP, and other centralized authentication management systems.
1.4 How to install Ansible?
Package managers like dnf, yum, zypper or apt can be used.
• On Fedora machines:
dnf install ansible
• On CentOS machines
yum install epel-release
yum install ansible
• On openSuse machines
zypper install ansible
2 Inventory Setup
Inventory defines the groups of hosts which are alike in any way. For example, if we would want to group our web servers in one group and backend servers in another. A group can have multiple servers and one server can be a part of multiple groups. The name of the group is enclosed in square brackets “[]”. Server names can be their DNS names or IP addresses.
Example 2.0.1:
[localhost]
localhost
[Backend]
192.168.1.10
192.168.1.11
By default, Ansible looks for the inventory file at /etc/ansible/hosts, but that can be modified by passing a “-i” {{path_to_inventory}} to the Ansible command line. We can modify the way Ansible connects to our hosts by supplying additional information in the inventory file.
Example 2.0.2:
[localhost]
LC1 ansible_ssh_host=localhost ansible_ssh_host=8022
[Backend]
BK1 ansible_ssh_host=192.168.1.10
BK2 ansible_ssh_host=192.168.1.11
[loadbalancers]
LB1 ansible_ssh_host=192.168.1.2
LB2 ansible_ssh_host=192.168.1.3
2.1 Adding ranges of hosts
We can skip a couple of entries by adding a range. Here we add 5 hosts with only one entry.
Example 2.1.1
[Backend]
192.168.1.[06:10]
2.2 Adding Variables to inventory
Basically, a variable represents a value. A variable can include letters, numerals, and underscores and always begin with a letter. Variables are used when instructions vary from one server to another.
There are a couple of variable types:
- Playbook variables. Defined in the current playbook. Can be global or local, based on where they are defined. Global variables are defined for the entire playbook and can be accessed from every task. Local variables are available only for the scope of the currently running task.
- Inventory variables. Only defined in the inventory file.
Example 2.2.1
[Backend]
192.168.1.10:8022 http_port=80 maxConnections=200
192.168.1.10:8022 http_port=443 maxConnections=400
- Special variables. These are special variables used by Ansible and cannot be set directly by the user. It will always override them to reflect internal state.
- Facts. These are variables that contain information pertinent to the current host (inventory_hostname). They are only available if gathered first.
- Connection variables. Connection variables are normally used to set the specifics on how to execute actions on a target. Most of them correspond to connection plugins, but not all are specific to them. Other plugins like shell, terminal and become are normally involved.
- Group variables. Defined for specific inventory group. Can be accessed only for hosts that are part of these groups.
2.3 Alias
We can also define aliases in your inventory:
Example 2.3.1:
[localhost]
LC1 ansible_ssh_host=localhost
[Backend]
BK1 ansible_ssh_host=192.168.1.10
BK2 ansible_ssh_host=192.168.1.11
[loadbalancers]
LB1 ansible_ssh_host=192.168.1.2
LB2 ansible_ssh_host=192.168.1.3
In the above example, running Ansible against the host alias “BK2” will connect to 192.168.1.11 on port 22.
2.4 Assigning a variable to many machines: group variables
Group variables are a convenient way to apply variables to multiple hosts at once. Before executing, however, Ansible always flattens variables, including inventory variables, to the host level. If a host is a member of multiple groups, Ansible reads variable values from all of those groups. If you assign different values to the same variable in different groups, Ansible chooses which value to use based on internal rules for merging.
Example 2.4.1:
[localhost]
LC1 ansible_ssh_host=localhost
[Backend]
BK1 ansible_ssh_host=192.168.1.10
BK2 ansible_ssh_host=192.168.1.11
[loadbalancers]
LB1 ansible_ssh_host=192.168.1.2
LB2 ansible_ssh_host=192.168.1.3
[Backend:vars]
services_folder=/opt/apps
logs_folder=/var/log
2.5 Inheriting variable values: group variables for groups of groups
We can make groups of groups using the :children suffix in INI or the children: entry in YAML format files. We can apply variables to these groups of groups using :vars or vars:
Example 2.4.1:
[localhost]
LC1 ansible_ssh_host=localhost
[Backend]
BK1 ansible_ssh_host=192.168.1.10
BK2 ansible_ssh_host=192.168.1.11
[loadbalancers]
LB1 ansible_ssh_host=192.168.1.2
LB2 ansible_ssh_host=192.168.1.3
[nfs:children]
Backend
[nfs:vars]
src=/usr/local/fileshare
dest=fileshare
user=test
Child groups have a couple of properties to note:
- Any host that is member of a child group is automatically a member of the parent group.
- A child group’s variables will have higher precedence (override) a parent group’s variables.
- Groups can have multiple parents and children, but not circular relationships.
- Hosts can also be in multiple groups, but there will only be one instance of a host, merging the data from the multiple groups.
2.6 Using multiple inventory sources
We can target multiple inventory sources (directories, dynamic inventory scripts or files supported by inventory plugins) at the same time by giving multiple inventory parameters from the command line or by configuring Ansible Inventory files. This can be useful when we want to target normally separate environments, like QA and production, at the same time for a specific action.
We can target two sources from the command line:
Example 2.4.1:
ansible-playbook test.yml -i backend -i localhost
Keep in mind that if there are variable conflicts in the inventories, they are resolved according to the rules. The emerging order is controlled by the order of the inventory source parameters. If [all:vars] in staging inventory defines myvar = 1, but production inventory defines myvar = 2, the playbook will be run with myvar = 2. The result would be reversed if the playbook was run with -i production -i qa.
[localhost]
LC1 ansible_ssh_host=localhost
[Backend]
BK1 ansible_ssh_host=192.168.1.10
BK2 ansible_ssh_host=192.168.1.11
[all:vars]
myvar=1
[Backend:vars]
myvar=2
2.7 Connecting to hosts: behavioral inventory parameters
As described above, setting the following variables control how Ansible interacts with remote hosts.
Host connection:
Ansible does not expose a channel to allow communication between the user and the ssh process to accept a password manually to decrypt a ssh key when using the ssh connection plugin. The use of ssh-agent is highly recommended. So we must provide passwords and passphrases before executing ansible.
- ansible_connection: Connection type to the host. This can be the name of any of ansible’s connection plugins. SSH protocol types are:
- smart
- ssh
- paramiko
The default is smart. There is a way for Non-SSH based types, too.
General for all connections:
- ansible_host: The name of the host to connect to, if different from the alias you wish to give to it.
- ansible_password: The password to use to authenticate to the host (never store this variable in plain text; always use a vault. See Variables and Vaults)
- ansible_port: The connection port number, if not the default (22 for ssh)
- ansible_user: The user name to use when connecting to the host
Specific to the SSH connection:
- ansible_ssh_private_key_file: Private key file used by ssh. Useful if using multiple keys and you don’t want to use SSH agent.
- ansible_ssh_common_args: This setting is always appended to the default command line for sftp, scp, and ssh. Useful to configure a ProxyCommand for a certain host (or group).
- ansible_sftp_extra_args: This setting is always appended to the default sftp command line.
- ansible_scp_extra_args: This setting is always appended to the default scp command line.
- ansible_ssh_extra_args: This setting is always appended to the default ssh command line.
- ansible_ssh_pipelining: Determines whether or not to use SSH pipelining. This can override the pipelining setting in ansible.cfg.
- ansible_ssh_executable (added in version 2.2): This setting overrides the default behavior to use the system ssh. This can override the ssh_executable setting in ansible.cfg.
Privilege escalation (see Ansible Privilege Escalation for further details):
- ansible_become: Equivalent to ansible_sudo or ansible_su, allows to force privilege escalation
- ansible_become_method: Allows to set privilege escalation method
- ansible_become_user: Equivalent to ansible_sudo_user or ansible_su_user, allows to set the user you become through privilege escalation
- ansible_become_password: Equivalent to ansible_sudo_password or ansible_su_password, allows you to set the privilege escalation password (never store this variable in plain text; always use a vault. See Variables and Vaults)
- ansible_become_exe: Equivalent to ansible_sudo_exe or ansible_su_exe, allows you to set the executable for the escalation method selected
- ansible_become_flags: Equivalent to ansible_sudo_flags or ansible_su_flags, allows you to set the flags passed to the selected escalation method. This can be also set globally in ansible.cfg in the sudo_flags option
Remote host environment parameters:
- · ansible_shell_type: The shell type of the target system. You should not use this setting unless you have set the ansible_shell_executable to a non-Bourne (sh) compatible shell. By default commands are formatted using sh-style syntax. Setting this to csh or fish will cause commands executed on target systems to follow those shell’s syntax instead.
- · ansible_python_interpreter: The target host python path. This is useful for systems with more than one Python or not located at /usr/bin/python such as *BSD, or where /usr/bin/python is not a 2.X series Python. We do not use the /usr/bin/env mechanism as that requires the remote user’s path to be set right and also assumes the python executable is named python, where the executable might be named something like python2.6.
- ansible_*_interpreter: Works for anything such as ruby or perl and works just like ansible_python_interpreter. This replaces the shebang of modules that will run on that host.
New in version 2.1.
- · ansible_shell_executable: This sets the shell that Ansible controller will use on the target machine, overrides value in ansible.cfg. Defaults is /bin/sh. You should only change it if it is not possible to use /bin/sh. Not installed or no writes to run it.
Examples from an Ansible-INI host file:
[localhost]
LC1 ansible_ssh_host=localhost
[Backend]
BK1 ansible_ssh_host=192.168.1.10 ansible_port=2222 ansible_user=manager ansible_python_interpreter=/usr/local/bin/python
BK2 ansible_ssh_host=192.168.1.11 ansible_ssh_private_key_file=/home/example/.ssh/aws.pem
2.8 Inventory setup
If you need to manage multiple environments, it is safer to have only hosts of a single environment defined per inventory file. This way, it is harder to accidentally change the state of nodes inside a QA environment when you actually wanted to update some “in production” servers.
For the example mentioned above you could have a production inventory file:
To apply a playbook called deploy.yml to all the backend servers in the production environment, use the following command:
ansible-playbook deploy.yml -i inventory/AFD_hosts_2.8.1_production -l backend
Morе on the topic coming soon!
3. Ad-hoc commands 4. YAML Basics 5. Playbooks 6. Variables 7. Roles