Ansible quickly stumbles when trying to do something a little different from the sample in the documentation. I suspect that this Ansible wall is because you can't see the whole picture. As you use it, you will gradually see the whole picture, but it is often the case that you need to know it sooner. I will leave it as my own memo, such as the structure for a bird's-eye view of the whole picture, but I hope it will be helpful for other people as much as possible.
At a minimum, if you understand 1. Ansible's configuration notation, 2. config file ansible.cfg, 3. Inventory, 4. Playbook, you should be able to use ansible. Ansible. (There are prerequisites such as SSH settings.)
As you use it, you will be able to manage multiple tasks collectively and reuse them more often. Section 5 describes how to use ansible using the directory structure.
Finally, I will describe how to use it with Raspberry Pi3 as a bonus.
In Ansible settings, YAML format and INI format You can use E3% 82% A1% E3% 82% A4% E3% 83% AB) (extended INI notation that allows #
in comments).
Being able to describe both allows for flexible writing, but it is confusing when used for the first time because it is not uniform.
It's a good idea to keep in mind the following rules.
* .cfg
and files without extension
are in INI format.* .yml
and * .yaml
are in YAML formatIt consists of a "section name" enclosed in []
and a "parameter" described by =
.
You can put spaces, TAB, etc. before and after =
.
ini:./ansible.cfg
[defaults]
host_key_checking = True
#You can write comments sharply
[privilege_escalation]
become_ask_pass = True
;You can write a comment with a semicolon
[ssh_connection]
scp_if_ssh = False
After =
, except for the Booleans True / False and yes / no, it is treated as a "character string" until the end of the line.
If you enclose it in "
etc., "
itself may be interpreted as part of the string.
Separate "lists" (arrays) with commas (,
).
An enumeration of "names" separated by :
is called a "scalar".
Scalar
name1:
name2:
:
nameN:
If you enumerate without prefixing -
, it becomes" series "(simple enumeration," scalar ").
You can write the value after :
. Also known as a dictionary type, this is called a "mapping" in YAML.
mapping
name1: aaaa
name2: bbbb
:
nameN: cccc
If you prefix it with -
, it becomes a list (array). In YAML, this is called a "sequence."
sequence
- name1:
- name2:
- nameN:
Indentation represents a hierarchy, which is called a "collection".
Use spaces for indentation. Please note that if you use TAB as it is, an error will occur.
(If you are using vi / vim, setting it with set expandtab
will replace TAB with blanks)
Example-playbook-samle1.yml
- name: the 1st Example
hosts: 172.17.0.2
tasks:
- name: Hello, World!
debug:
- name: Next debug
debug: msg="Hello, Ansible!"
In this example, name: the 1st Example
, hosts: 172.17.0.2
, tasks:
are the same level of scalar (parallel (sequential)) settings, -name: Hello, ansible
, -name: Next. The debug
part is the sequence (list (array)). It is a mapping, each with a value.
tasks
is a collection.
I often see YAML format with ---
(three hyphens) at the beginning.
This is because the YAML structure is supposed to start one definition block with ---
and end with ...
(three dots).
You can write multiple blocks in one file with ---
, but in general, ansible does not use such a structure.
You can think that it is safe to add ---
. If you rewrite the above playbook-samle1.yml
, it will look like the one below.
playbook-samle1.yml
---
- name: the 1st Example
hosts: 172.17.0.2
tasks:
- name: Hello, World!
debug:
- name: Next debug
debug: msg="Hello, Ansible!"
See also: Ansible Configuration Settings — Ansible Documentation (https://docs.ansible.com/ansible/latest/reference_appendices/config.html)
Since the extension of the config file is .cfg
, it is described in INI format.
Ansible-related commands search for config files in the following order, and the first config file found is used.
(
./ansible.cfg`) in the current directory.ansible.cfg
(~ / .ansible.cfg
or `$ HOME / .ansible.cfg)ansible.cfg
[defaults]
inventory = ./inventory
interpreter_python = /usr/bin/python3
#deprecation_warnings = False
[privilege_escalation]
become_ask_pass = yes
[ssh_connection]
scp_if_ssh = False
See: How to build your inventory — Ansible Documentation In order to execute the ansible command and operate the playbook described later, it is necessary to prepare a file called Inventory that describes the host to be operated. In a nutshell, it's like a collection of / etc / hosts files.
-i
. You can omit the option by setting it in the config file. (ʻinventory parameter in [
defaults`] section)* .py
and * .sh
can be specified and dynamically configured at run time (must be returned in the correct format).myhosts1
filemyhost1
172.17.0.2
The smallest Inventory file contains only one IP address (host).
myhosts2
filemyhost2
172.17.0.2
172.17.0.3
[group1]
172.17.0.2
172.17.0.3
[group2]
172.17.0.[3:7]
[my_all:children]
group1
group2
[]
specifies a group. children
is a ** reserved word ** that is used to specify a group of children.[group name: vars]
describes the variables that apply to the group. vars
is a ** reserved word **.Instead of the ad hoc (= one-shot) module specification and operation of ʻansible -m ping host pattern`, specify a configuration file called a playbook to perform a series of operations. The format of the playbook is generally YAML format.
playbook-sample1.yml
- name: the 1st Example
hosts: 172.17.0.2
tasks:
#The task is a list(sequence)Specify with.
#Display the default message in debug.
- name: Hello, World
debug:
#Display the specified message in debug.
- name: Next debug
debug: msg="Hello, Ansible!"
In order to run the playbook, you need to specify an inventory file.
myhosts1
172.17.0.2
$ ansible-playbook -i myhosts1 playbook-sample1.yml
PLAY [the 1st Example] *************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************
ok: [172.17.0.2]
TASK [Hello, World] ****************************************************************************************************
ok: [172.17.0.2] => {
"msg": "Hello world!"
}
TASK [Next debug] ******************************************************************************************************
ok: [172.17.0.2] => {
"msg": "Hello, Ansible!"
}
PLAY RECAP *************************************************************************************************************
172.17.0.2 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
See also: Roles — Ansible Documentation By using the following directory structure, it is easy to specify files, tasks, etc.
./
├─ ansible.cfg ……………………………… (1)Configuration file. Environment variable ANSIBLE_In CONFIG
│ Fixed name unless explicitly specified.
├─ playbook.yml ……………………………… (2)Playbook Any file name. YAML format is common.
│
├─ inventory/ …………………………………… (3)Inventory directory. Must be specified,
│ │ Any name. Specify as an option.
│ ├─ hosts …………………………………… (4)Any name. If there is no extension, INI format,
│ ├─ hosts2.yml extension.YAML format for yml.
│ ├─ :
│ └─ hostsN
├─ group_vars/ ………………………………… (5)Variables that apply to the "target host group" of the specified task
│ │ The directory to store. Fixed name.
│ ├─ groupname1 ……………………… (6)Set the "Target Host Group" name to the file name. If there is no extension, INI format,
│ ├─ groupname2.If it is in yml YAML format, add the extension.Make it yml.
│ ├─ :
│ └─ groupnameN
├─ host_vars/ …………………………………… (7)Variables that apply to the "target host" of the specified task
│ │ The directory to store. Fixed name.
│ ├─ hostname1 ………………………… (8)Set the "target host" name to the file name. If there is no extension, INI format,
│ ├─ hostname2.If it is in yml YAML format, add the extension.Make it yml.
│ ├─ :
│ └─ hostnameN
├─ files/ ……………………………………………… (9)File storage used for copying and transferring. Fixed name.
│ ├─ file1 ……………………………………(10)Any file name.
│ ├─ file1
│ ├─ :
│ └─ fileN
└─ roles/ ………………………………………………(11)Settings directory for using roles in playbooks. Fixed name.
├─ role1/ ……………………………………(12)Set the "target role" name to the directory name.
├─ role2/
├─ :
└─ roleN/
├─ defaults …………………(13)The directory that stores the default variable settings. Fixed name.
│ └── main.yml ……(14)The variable configuration file that is loaded first. Fixed name.
│ import_Like vars, it is loaded when the playbook is parsed.
├─ vars ……………………………(15)A directory that stores variable settings. Fixed name.
│ │ Base directory when a file is specified with a relative path.
│ ├─ varfile1 ……(16)Any file name. If there is no extension, INI format,
│ ├─ varfile2.If it is in yml YAML format, add the extension.Make it yml.
│ ├─ :
│ └─ varfileN
└─ tasks …………………………(17)Directory for storing tasks. Fixed name.
└── main.yml ……(18)The first task configuration file to be executed. Fixed name.
If you want to change the process depending on the OS of the target host,
main.import from yml/Read with include.
There are other roles in directories such as handlers /
, templates /
, and meta /
, but for the time being, if you understand the above directory structure, it will be easier to understand other directories as well. ..
See "Roles — Ansible Documentation" for more information. There is also an example in "Best Practices: Directory Layout — Ansible Documentation".
See also: Including and Importing — Ansible Documentation Both import * and include * make no difference in reading a file, but at different times.
- All import* statements are pre-processed at the time playbooks are parsed.
- All include* statements are processed as they are encountered during the execution of the playbook.
Of particular importance is when variables are processed using the JINJA2 template (for example, the template {{samplevarname}}) and when they are assigned to variables.
If you use import *, it will be expanded before the task is executed, so the embedded template will be replaced with the actual value and the task will be executed after the assignment to the variable is finished.
On the other hand, if you use include *, it will be expanded when the task is being executed, so when it is loaded and executed, the template will be replaced with the actual value and the assignment will be made.
Ansible is used in a Python environment, but if you install OS-based ansibule (apt / apt-get install) on Ubuntu (stretch 9) of Raspberry Pi3, Version 2.2 of Ansible will be installed (written January 13, 2020). As of the date).
The latest features are not available in v2.2, which is a bit inconvenient, so you can use the latest Ansible (latest v2.9 at the time of writing) by installing (pip install) in a virtual environment of Python. (The version of Python3 for Raspberry Pi3 / Ubuntu is 3.5)
$ cat /etc/os-release
PRETTY_NAME="Raspbian GNU/Linux 9 (stretch)"
NAME="Raspbian GNU/Linux"
VERSION_ID="9"
VERSION="9 (stretch)"
VERSION_CODENAME=stretch
ID=raspbian
ID_LIKE=debian
HOME_URL="http://www.raspbian.org/"
SUPPORT_URL="http://www.raspbian.org/RaspbianForums"
BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs"
$ python3 --version
Python 3.5.3
The installation in the Python virtual environment is as follows.
$ python3 -m venv ansible
$ source ansible/bin/activate
$ pip install -U pip
$ pip install ansible
The * ansible * after python3 -m venv
and the * ansible * specified in the source
command are the same, and you can decide for yourself.
Since ʻansiblespecified in
pip install` is the package to be installed, execute it as it is without changing it.
If you are using bash, it is convenient to make the following settings in ~ / .bashrc
.
bash:~/.bashrc Or ~/.bash_aliases Such
alias ansibleenv='type deactivate > /dev/null 2>&1 && deactivate; cd ~/work/Ansible.d/; source ~/work/Python.d/envs/ansible/bin/activate'
In the type deactivate> / dev / null 2> & 1 && deactivate
part, deactivate if it was activated in another environment.
Change to your working directory with cd ~ / work / Ansible.d
.
source ~ / work / Python.d / envs / ansible / bin / activate
activates the ansible environment.
Once set, you can call it with ʻansibleenv`.
On Raspberry Pi, even if ansible itself is executed successfully, the following error may occur.
:
PLAY RECAP *************************************************************************************************************
172.17.0.2 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Exception ignored in: <function WeakValueDictionary.__init__.<locals>.remove at 0x75c7fdf8>
Traceback (most recent call last):
File "/usr/lib/python3.5/weakref.py", line 117, in remove
TypeError: 'NoneType' object is not callable
Exception ignored in: <function WeakValueDictionary.__init__.<locals>.remove at 0x75c7fdf8>
Traceback (most recent call last):
File "/usr/lib/python3.5/weakref.py", line 117, in remove
TypeError: 'NoneType' object is not callable
:
:
Exception ignored in: <function WeakValueDictionary.__init__.<locals>.remove at 0x75c7fdf8>
Traceback (most recent call last):
File "/usr/lib/python3.5/weakref.py", line 117, in remove
TypeError: 'NoneType' object is not callable
This is an error that occurs because the variable `` `/usr/lib/python3.5/weakref.py``` is not defined (type "None"). The underlying solution should be considered by the caller, but can be tentatively avoided by applying the following patches. (Although it is quite rough)
diff:weakref.py.patch
*** weakref.py.bak 2018-09-28 02:25:39.000000000 +0900
--- weakref.py 2020-01-03 18:44:49.190027705 +0900
***************
*** 114,120 ****
else:
# Atomic removal is necessary since this function
# can be called asynchronously by the GC
! _remove_dead_weakref(d, wr.key)
self._remove = remove
# A list of keys to be removed
self._pending_removals = []
--- 114,121 ----
else:
# Atomic removal is necessary since this function
# can be called asynchronously by the GC
! if type(_remove_dead_weakref) is not type(None) and type(d) is not type(None) and type(wr) is not type(None):
! _remove_dead_weakref(d, wr.key)
self._remove = remove
# A list of keys to be removed
self._pending_removals = []
Apply patch:
$ sudo patch -c -b /usr/lib/python3.5/weakref.py weakref.py.patch
-c
is the context type patching option and -b
is the backup creation option.