Esta publicación cubre los siguientes módulos de bucle de Ansible: with_items, with_nested, with_subelements, with_dict.
Todos estos con * ya están obsoletos y se recomienda utilizar loop.
Uno de mis roles en Chromatic es como miembro del equipo de DevOps. Entre otras cosas, esto incluye trabajar con nuestros servidores y los servidores de nuestros clientes. Esto, a su vez, significa que dedico mucho tiempo a trabajar con Ansible , una herramienta popular para aprovisionar, configurar e implementar servidores y aplicaciones.
En pocas palabras, la máquina que ejecuta Ansible ejecuta comandos en otra máquina a través de SSH. Estos comandos se especifican de forma declarativa (opcional) utilizando pequeñas secciones de YAML llamadas tareas. Estas TAREAS llaman a los módulos de Ansible que se especializan en ejecutar opciones en ciertos componentes como archivos, bases de datos, etc.
Por ejemplo, la siguiente tarea usa el módulo Archivo ( documentación , código ) para crear un directorio específico si aún no existe, y modifica sus atributos si aún no están configurados correctamente:
- file:
path: /home/jenkins/.ssh
state: directory
owner: jenkins
group: jenkins
mode: 700
Varias tareas relacionadas con una tarea se agrupan en roles y se pueden agrupar múltiples roles en libros de jugadas. Luego, puede usar el libro de jugadas para realizar exactamente los mismos pasos de configuración en cualquier número de servidores al mismo tiempo.
¿Ansible es declarativo?
TASKS Ansible , , TASKS. , , , . , Ansible Copy, . Ansible , :
- name: Copy SSH config file into Alice’s .ssh directory.
copy:
src: files/config
dest: /home/alice/.ssh/config
owner: alice
group: alice
mode: 0600
, , bash, scp
, chown
chmod. Ansible , .
, , — , , .
, Ansible, — TASKS . , Ansible , , — PHP, .
, Ansible. Loops , «loops _ + lookup(), ». (Lookups) — Ansible, « Ansible », Loops Ansible Github, .
Ansible « », , . Ansible, , , .
Ansible
TASKS, , , , , ( : , , , , !)
:
, :
alice
,bob
,carol
dan
.
, :
.ssh/
loops
.
, . , alice :
/home/alice/
├── .ssh/
├── bob/
├── carol/
├── dan/
└── loops/
1. WITH_ITEMS
Ansible , chuck
, :
- name: Remove user ‘Chuck’ from the system.
user:
name: chuck
state: absent
remove: yes
— , Chuck
Craig
— with_items
. with_items ( ), ( ):
- name: Remove users ‘Chuck’ and ‘Craig’ from the system.
user:
name: "{{ item }}"
state: absent
remove: yes
with_items:
- chuck
- craig
, with_items , alice
bob
:
users_with_items:
- name: "alice"
personal_directories:
- "bob"
- "carol"
- "dan"
- name: "bob"
personal_directories:
- "alice"
- "carol"
- "dan"
TASKS
- name: "Loop 1: create users using 'with_items'."
user:
name: "{{ item.name }}"
with_items: "{{ users_with_items }}"
Ansible User users_with_items. , , , , personal_directories ( , personal_directories — ).
Ansible ( Ansible ): TASKS , . , , personal_directories
TASKS (. . User, File).
with_items
, PHP:
<?php
foreach ($users_with_items as $user) {
// Do something with $user...
}
, , :
• item.name
.
• with_items
, .
, Ansible item
, item.property
.
/home/
├── alice/
└── bob/
2: , WITH_NESTED
: 2 , 1. chown failed: failed to look up user
, users_with_items
1, , common_directories
, , . , ( PHP), -, :
<?php
foreach ($users_with_items as $user) {
foreach ($common_directories as $directory) {
// Create $directory for $user...
}
}
Ansible with_nested
. with_nested
, :
users_with_items:
- name: "alice"
personal_directories:
- "bob"
- "carol"
- "dan"
- name: "bob"
personal_directories:
- "alice"
- "carol"
- "dan"
common_directories:
- ".ssh"
- "loops
TASKS
# Note that this does not set correct permissions on /home/{{ item.x.name }}/.ssh!
- name: "Loop 2: create common users' directories using 'with_nested'."
file:
dest: "/home/{{ item.0.name }}/{{ item.1 }}"
owner: "{{ item.0.name }}"
group: "{{ item.0.name }}"
state: directory
with_nested:
- "{{ users_with_items }}"
- "{{ common_directories }}"
, with_nested , item.0
( users_with_items
) item.1
( common_directories
) . , , /home/alice/.ssh
.
/home/
├── alice/
│ ├── .ssh/
│ └── loops/
└── bob/
├── .ssh/
└── loops/
3: , WITH_SUBELEMENTS
: 3 , 1. chown failed: failed to look up user
with_subelements
, users_with_items
1. PHP :
<?php
foreach ($users_with_items as $user) {
foreach ($user['personal_directories'] as $directory) {
// Create $directory for $user...
}
}
, $users_with_items
$user['personal_directories']
.
users_with_items:
- name: "alice"
personal_directories:
- "bob"
- "carol"
- "dan"
- name: "bob"
personal_directories:
- "alice"
- "carol"
- "dan"
TASKS
- name: "Loop 3: create personal users' directories using 'with_subelements'."
file:
dest: "/home/{{ item.0.name }}/{{ item.1 }}"
owner: "{{ item.0.name }}"
group: "{{ item.0.name }}"
state: directory
with_subelements:
- "{{ users_with_items }}"
- personal_directories
with_subelements
, with_nested
, , , — personal_directories. 2, ( ) /home/alice/bob
.
/home/
├── alice/
│ ├── .ssh/
│ ├── bob/
│ ├── carol/
│ ├── dan/
│ └── loops/
└── bob/
├── .ssh/
├── alice/
├── carol/
├── dan/
└── loops/
4: WITH_DICT
3 , alice
bob
, , , carol
dan
. users_with_dict
Ansible with_dict
.
, (dict
dictionary
— Python ); with_dict
, . , Ansible, PHP :
<?php
foreach ($users_with_dict as $user => $properties) {
// Create a user named $user...
}
users_with_dict:
carol:
common_directories: "{{ common_directories }}"
dan:
common_directories: "{{ common_directories }}"
TASKS
- name: "Loop 4: create users using 'with_dict'."
user:
name: "{{ item.key }}"
with_dict: "{{ users_with_dict }}"
with_dict
. , , , dict
with_dict
(, , with_dict
).
/home/
├── alice/
│ ├── .ssh/
│ ├── bob/
│ ├── carol/
│ ├── dan/
│ └── loops/
├── bob/
│ ├── .ssh/
│ ├── alice/
│ ├── carol/
│ ├── dan/
│ └── loops/
├── carol/
└── dan/
5: ,
users_with_dict
, Ansible, -. alice
, bob
, carol
dan
, with_nested
/home/
. , , , TASKS:
- Ansible
- Ansible
- Jinja2 ()
- Jinja2 ()
common_directories:
- ".ssh"
- "loops"
TASKS
- name: "Get list of extant users."
shell: "find * -type d -prune | sort"
args:
chdir: "/home"
register: "home_directories"
changed_when: false
- name: "Loop 5: create personal user directories if they don't exist."
file:
dest: "/home/{{ item.0 }}/{{ item.1 }}"
owner: "{{ item.0 }}"
group: "{{ item.0 }}"
state: directory
with_nested:
- "{{ home_directories.stdout_lines }}"
- "{{ home_directories.stdout_lines | union(common_directories) }}"
when: "'{{ item.0 }}' != '{{ item.1 }}'"
TASKS: shell
find
, file .
/home
find \ -type d -prune | sort
( shell
) , /home
, , , .
home_directories
register: "home_directories"
. , , :
"stdout_lines": [
"alice",
"bob",
"carol",
"dan",
],
( ) with_nested
, :
-
with_nested
:
- "{{ home_directories.stdout_lines | union(common_directories) }}"
,
when
TASKS:
when: "'{{ item.0 }}' != '{{ item.1 }}'"
. with_nested
Jinja2 TASKS ( home_directories.stdout_lines
). Jinja:
- (
home_directories.stdout_lines
) - (
|
) - , (
union (common_directories)
)
, home_directories.stdout_lines
common_directories
:
item:
- .ssh
- alice
- bob
- carol
- dan
- loops
, with_nested
home_directories.stdout_lines
( with_nested
) , .
, — , , , ! (, /home/alice/alice
, /home/bob/bob
. .) Ansible — when
— :
when: "'{{ item.0 }}' != '{{ item.1 }}'"
, home_directories.stdout_lines
home_directories.stdout_lines
( Ansible Loops, «… when
with_items
( ), when »). PHP , , :
<?php
$users = ['alice', 'bob', 'carol', 'dan'];
$common_directories = ['.ssh', 'loops'];
$directories = $user + $common_directories;
foreach ($users as $user) {
foreach ($directories as $directory) {
if ($directory != $user) {
// Create the directory…
}
}
}
, , .
/home/
├── alice/
│ ├── .ssh/
│ ├── bob/
│ ├── carol/
│ ├── dan/
│ └── loops/
├── bob/
│ ├── .ssh/
│ ├── alice/
│ ├── carol/
│ ├── dan/
│ └── loops/
├── carol/
│ ├── .ssh/
│ ├── alice/
│ ├── bob/
│ ├── dan/
│ └── loops/
└── dan/
├── .ssh/
├── alice/
├── bob/
├── carol/
└── loops/
Ansible . ( Ansible), , (with_nested
? with_subitems
?) .
, , TASKS, ( , array_filter
, array_reduce
, array_map
, ). , , — — .
Espero que esta publicación te ayude a deshacerte de mi dificultad inicial. Con este fin, construí una máquina virtual Vagrant (Vagrant admite de forma nativa el uso de Ansible para el aprovisionamiento) y un libro de jugadas de Ansible que usé para crear y probar estos ejemplos). Simplemente siga las instrucciones del archivo README para ejecutar los ejemplos de esta publicación o pruebe los suyos propios. Si tiene alguna pregunta o comentario, envíe un tweet a @chromaticHQ.