Ansible AT NWZ - Ansible Basics
Ansible is an open source automation tool for orchestration and general configuration and administration of computers (this and more at https://de.wikipedia.org/wiki/Ansible ). It offers a number of advantages over administration with your own script collection:
- Infrastructure is configured declaratively ('infrastructure as code'), which, especially in combination with the management of configuration files in git repositories, enables very reliable deployment: the correctness and security of configurations can be systematically and automatically checked and implemented before use, there is 'managed reporting' of all processes, so that the four-eyes principle is also easier to implement, etc.
-
This also makes it easier to treat client computers in a similar way to 'immutable infrastructure': Computers can be replaced or exchanged more easily because all relevant status data is stored in the configuration files. (The computer itself is transformed from an irreplaceable 'pet' into expendable 'livestock' ...)
-
Idempotency, i.e. the multiple use of instructions without consequences when the target state has already been reached, can be exploited further: Ideally, you should write your most important playbooks and roles in such a way that they are as free of side effects as possible (restarts only when really necessary, as much as possible solved via ansible-builtins and not shell commands, etc.). Then you can also see from the ansible output whether systems are still in a good state, as only a few changes are displayed.
To get started: (The following is based on an article: “Automatisierung mit Ansible”, c't Cloud 2021 special issue. Many thanks to Julian Jeggle for helpful additions).
Ansible is used for the automatic configuration of operating systems, applications, etc., as an alternative to e.g. CFEngine or Puppet. It is consistently modularized and uses standard communication paths (SSH or Windows Remote Management WinRM), i.e. no agents need to be installed on clients. Any system with the Ansible package including modules can be used as a control host, i.e. no dedicated machine is required. Only a directory for the configuration files is required on the control host; however, it is better to store these in a Git repository, at the University of MS at https://zivgitlab.uni-muenster.de/, see also https://confluence.uni-muenster.de/pages/viewpage.action?pageId=26673453
Ansible is then based on the following structural elements:
1. Inventory: directory of the target hosts, in the simplest case a text file in INI style (YAML is also possible if you want to use the same markup language as for the playbooks):
pc0.example.com
[group1] pc1.example.com pc2.example.com [group2] pc3.example.com pc4.example.com
Subgroups can also be formed, variables defined and parameters specified. An inventory can be generated dynamically with scripts. A configuration framework is created by Ansible under /etc/ansible/, user-specific .ansinble.cfg files in the home directory are also possible.
2. Tasks: Tasks for the target hosts
For communication, SSH keys without password request should be set up for an unprivileged user on the target hosts. (This account should only be used for Ansible.) The name (remote_user) and the private key (private_key_file) of this user can be entered in the [defaults] section of .ansible.cfg. Temporary root rights should be granted to this user via sudo.
Note 1: This is not necessary for systems with a central user administration such as the University of MS, as the ansible user on the target system will usually be identical to the ansible user on the source system. If it really is a separate user (e.g. on a server that is not in the domain), the user name is probably specific to the respective playbook and should then either be directly in the playbook or possibly in a project-specific ansible.cfg.
Note 2: In principle, it is also possible to work directly as root and without a key; if the ssh-askpass package is installed on the source system, you can log in with -k or --asp-pass using a password. Trying out Ansible requires almost no effort. If the local user has no (sudo) privileges, however, only corresponding tasks are of course possible. This could be useful if you want to roll out your dotfiles via Ansible in order to be able to quickly change the computer without having to reconfigure everything first.
You now tell Ansible which target hosts should execute which tasks (wildcards are allowed). This can be done either via ad-hoc commands or via playbooks, which can contain multi-step instructions. The following command lists which clients fall under a pattern such as all or group1:
ansible all|group1 -i inventory --list-hosts
The following command is used to execute the shell command on the host via the command module:
ansible-i inventory -m command -a "shellcommand" -o
(-m specifies the Ansible module, -a options for this module, -o the output on one line per host)
There are Ansible modules for all typical tasks. When executing Ansible commands, only the achievement of the defined (target) state counts. (It is not returned as a default what had to be done for this and whether any errors occurred on the way there that required detours - only whether the target state was reached or not). The first point of contact is the module index of the Ansible documentation, which is divided into so-called collections (there are many examples for the respective modules).
https://docs.ansible.com/ansible-core/devel/index.html -> https://docs.ansible.com/ansible/latest/collections/index.html
3. Playbooks: Structured collections of tasks in YAML format
The following playbook installs the packet on all hosts of the group group1 via the packet manager yum:
--- - name: Install <'Trivialname' des Packets> hosts: group1 become: yes tasks: - name: Install Latest Version ansible.build.yum: name:state: present
(--- -> start YAML; become -> execute tasks; multiple tasks possible)
Note 1: If sudo requires a password (usually the default), you have to call ansible-playbook with -K or --ask-become-pass.
Note 2: It is recommended to use complete module names, so this is not just yum (which would also work), but ansible.builtin.yum.
The following task activates and starts the service via the Ansible module service (see documentation for options):
---
- name: Enable and Startansible.builtin.service: name: enabled: true state: started
Playbooks are executed with ansible-playbook -i inventory .yml. (Possibly with -k and/or -K if you need to pass passwords.) The Ansible user can be defined in the playbook via remote_user.
Many playbooks will require variables. The content of variables is referenced via {{ variable }}. Variables can be defined on the command line, in the playbook, in the inventory or in special files. Best practice is the use of host and group variables: For this purpose, the directories host_vars and group_vars (possibly also common_vars) are created at the level of the inventory file. Files such as all, group1, etc. with corresponding variable definitions (again in YAML format) can then be stored in the group_vars directory. The following playbook is an example of the use of variables:
- name: create x file hosts: all become: yes tasks: -name: create file copy: content: text inklusive {{ variable }} dest:/
4. Facts: System informationen
ansible -m setup displays all facts. In playbooks, individual facts (e.g. default IP) can be referenced as variables (here from the debug module for an output):
- ansible.builtin.debug: msg: "{{ factname }}"
Facts can also be defined in /etc/ansible/facts.d/ and/or collected by modules, e.g. the module ansible.builtin.package_facts collects information about the installed packages.
The following page of the documentation is helpful for dealing with Facts: https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_vars_facts.html
In tasks, facts can also be used in when statements (the syntax of Jinja2 is used for this, as already mentioned above for the use of variables and also otherwise in Ansible - for control structures this largely corresponds to that of Python), e.g:
when: ansible_memtotal_mb > 3000
Or to check whether other tasks have run successfully.
Facts can also be used in loop statements (obsolete: “with”), e.g. to check whether certain services are running (item is replaced by the loop elements; the correct indentation is important):
task: -name: check for services ansible.builtin.service: name: "{{ item }}" state: started loop: - sshd - httpd
Test setup:
In connection with another article (“Schnellstart mit Ansible”, c't Cloud 2021 special issue) some 'bootstrap' scripts for the typical basic tasks were provided: ct.de/wmvu
debianstrap.sh - preparation target host
myroot.yml- set ssh pubkey for root, disable SSH password login
hostname.yml - set hostname, customize /etc/hosts
dockersetup.yml - set up Docker
You can then proceed as follows:
- install Ansible on the control host (desktop, notebook or Linux VM), create directory structure for Ansible files (ideally using Git): mkdir ~/ansible && mkdir ~/ansible/.ssh, possibly also ~/ansible/host_vars and ~/ansible/group_vars or ~/ansible/common_vars
Note: Ansible can be installed as a Python package without root rights: On old systems or in Venvs via pip install --include-deps ansible, on newer systems (after installing pipx) via pipx install --include-deps ansible.
- Create SSH key pair (ansible/ansible.pub) without passphrase in the ssh directory, create second key pair with passphrase for root access, possibly more for monitoring etc.
- debianstrap.yaml (executed on the target host) creates the user ansible on the target host, disables the login by password, stores the ansible.pub (the key must be specified as PUBKEY in debianstrap.yaml), and gives the ansible user the required sudo rights
- create the file inventory.ini in the ansible directory on the control host and enter “ ansible_host:” there (DNS names are also OK, for registered end devices the name or .uni-muenster.de is usually sufficient)
- Create the .ansible.cfg file on the control host in the home directory:
[defaults] remote_user = ansible private_key_file = ~/ansible/.ssh/ansible
to test with: ansible '*' -i inventory.ini -m ping)
- myroot.yml allows you to set the ssh pubkey for root and disable SSH password login, to be called with ansible-playbook -i inventory.ini myroot.yml (to be tested several times)
- hostname.yml allows you to set a hostname and then customize /etc/hosts (order is important!); the old hostname (as 'Ansible fact') is {{ ansible_hostname }}, the new one is taken from the variable {{ hostname }}, which can be specified on the control host in ansible/hosts_vars/ (---; hostame: ).
- dockersetup.yml automates the multi-step setup of Docker on the target hosts via the apt module.
Additional material:
Ansible tutorial: https://docs.ansible.com/ansible/latest/getting_started/index.html
YAML syntax: https://en.wikipedia.org/wiki/YAML