Introducción
Ansible es una poderosa herramienta de automatización de infraestructura que simplifica la gestión de entornos de TI complejos. Sin embargo, los usuarios a menudo se encuentran con el error 'UNREACHABLE!', que puede interrumpir los flujos de trabajo de automatización. Este error generalmente ocurre cuando Ansible no puede establecer una conexión con los hosts de destino. En este laboratorio, aprenderá a identificar, solucionar problemas y prevenir el error 'UNREACHABLE!' en sus implementaciones de Ansible.
Al final de este laboratorio, comprenderá las causas comunes de los problemas de conectividad en Ansible y podrá implementar soluciones efectivas para garantizar que su automatización se ejecute sin problemas.
Configuración del Entorno Ansible
En este paso, configuraremos un entorno Ansible básico para trabajar. Instalaremos Ansible, configuraremos los archivos esenciales y nos aseguraremos de que todo esté listo para nuestra experimentación.
Instalación de Ansible
Primero, instalemos Ansible en la máquina virtual (VM) de LabEx utilizando los siguientes comandos:
sudo apt update
sudo apt install -y ansible
Esto instalará la última versión de Ansible disponible en los repositorios de Ubuntu. Una vez que la instalación esté completa, verifique la instalación comprobando la versión de Ansible:
ansible --version
Debería ver una salida similar a la siguiente, que muestra la versión de Ansible y los detalles de configuración:
ansible [core 2.12.x]
config file = /etc/ansible/ansible.cfg
configured module search path = ['/home/labex/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python3/dist-packages/ansible
ansible collection location = /home/labex/.ansible/collections:/usr/share/ansible/collections
executable location = /usr/bin/ansible
python version = 3.10.x (default, Apr 8 2022, 09:04:19) [GCC 11.2.0]
jinja version = 3.0.3
libyaml = True
Creación de un Directorio de Trabajo
Creemos un directorio dedicado para nuestro trabajo con Ansible:
mkdir -p ~/project/ansible-lab
cd ~/project/ansible-lab
Creación del Archivo de Configuración de Ansible
Ahora, creemos un archivo de configuración básico de Ansible en nuestro directorio del proyecto:
cat > ansible.cfg << 'EOF'
[defaults]
inventory = ./inventory
host_key_checking = False
remote_user = labex
EOF
Este archivo de configuración:
- Especifica la ubicación de nuestro archivo de inventario
- Deshabilita la verificación de la clave del host SSH (útil para entornos de laboratorio)
- Establece el usuario remoto predeterminado en 'labex'
Creación del Archivo de Inventario
El archivo de inventario define los hosts que Ansible administrará. Creemos un archivo de inventario simple:
cat > inventory << 'EOF'
[local]
localhost ansible_connection=local
[virtual]
virtual-host ansible_host=10.10.10.10
EOF
Este inventario contiene dos grupos:
local: Contiene solo localhost, que utiliza una conexión localvirtual: Contiene un host virtual que usaremos para demostrar el error 'UNREACHABLE!'
El virtual-host está configurado con una dirección IP (10.10.10.10) que no existe en nuestro entorno, lo que nos ayudará a generar el error 'UNREACHABLE!'.
Prueba de Ansible
Probemos nuestra configuración de Ansible ejecutando un comando ping simple contra el host local:
ansible local -m ping
Debería ver una respuesta exitosa como:
localhost | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
Esto confirma que Ansible funciona correctamente para la conexión local. Ahora, intentemos hacer ping al host virtual, lo que debería fallar:
ansible virtual -m ping
Esto producirá el error 'UNREACHABLE!' porque el host no existe:
virtual-host | UNREACHABLE! => {
"changed": false,
"msg": "Failed to connect to the host via ssh: ssh: connect to host 10.10.10.10 port 22: Connection timed out",
"unreachable": true
}
Ahora ha configurado Ansible con éxito y ha creado un escenario donde ocurre el error 'UNREACHABLE!', que investigaremos en el siguiente paso.
Comprensión del Error 'UNREACHABLE!'
En el paso anterior, encontramos el error 'UNREACHABLE!' al intentar conectarnos a un host inexistente. Ahora, comprendamos el error con más detalle y exploremos las causas comunes.
Análisis del Mensaje de Error
Veamos el mensaje de error que recibimos:
virtual-host | UNREACHABLE! => {
"changed": false,
"msg": "Failed to connect to the host via ssh: ssh: connect to host 10.10.10.10 port 22: Connection timed out",
"unreachable": true
}
El mensaje de error proporciona información valiosa:
UNREACHABLE!indica que Ansible no pudo establecer una conexión con el host- El campo
msgnos dice por qué: "Failed to connect to the host via ssh" (Fallo al conectar con el host a través de ssh) - El error específico es "Connection timed out" (Tiempo de conexión agotado), lo que significa que Ansible intentó conectarse pero no recibió respuesta
Causas Comunes de los Errores 'UNREACHABLE!'
El error 'UNREACHABLE!' puede ocurrir por varias razones:
- Problemas de Red: El host podría estar detrás de un firewall, o podría haber problemas de conectividad de red.
- Información Incorrecta del Host: El nombre de host o la dirección IP en el inventario podrían ser incorrectos.
- Configuración de SSH: SSH podría no estar configurado correctamente en el host de destino.
- Problemas de Autenticación: La clave SSH o la contraseña podrían ser incorrectas.
- Indisponibilidad del Host: El host podría estar inactivo o inalcanzable.
Creación de un Playbook de Prueba
Creemos un playbook simple para demostrar aún más el error:
cat > test_playbook.yml << 'EOF'
---
- name: Test Connectivity
hosts: all
gather_facts: no
tasks:
- name: Ping the hosts
ping:
EOF
Este playbook intenta hacer ping a todos los hosts definidos en nuestro inventario. Ejecutémoslo:
ansible-playbook test_playbook.yml
Debería ver una salida similar a:
PLAY [Test Connectivity] ************************************************
TASK [Ping the hosts] ***************************************************
ok: [localhost]
fatal: [virtual-host]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: connect to host 10.10.10.10 port 22: Connection timed out", "unreachable": true}
PLAY RECAP *************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
virtual-host : ok=0 changed=0 unreachable=1 failed=0 skipped=0 rescued=0 ignored=0
El playbook tuvo éxito para localhost pero falló para virtual-host con el error 'UNREACHABLE!'.
Inspección de los Niveles de Verbose de Ansible
Ansible proporciona diferentes niveles de verbosidad para ayudar a diagnosticar problemas. Intentemos ejecutar el playbook con mayor verbosidad:
ansible-playbook test_playbook.yml -v
Para obtener una salida aún más detallada, use -vv o -vvv:
ansible-playbook test_playbook.yml -vvv
La opción -vvv proporciona la salida más detallada, mostrando los comandos SSH exactos que Ansible está intentando usar:
<virtual-host> SSH: EXEC ssh -vvv -C -o ControlMaster=auto -o ControlPersist=60s -o User=labex -o ConnectTimeout=10 -o ControlPath=/home/labex/.ansible/cp/ansible-ssh-%h-%p-%r 10.10.10.10 '/bin/sh -c '"'"'echo ~labex && sleep 0'"'"''
Este nivel de detalle puede ser invaluable para solucionar problemas de conexión SSH.
Uso de la Opción --limit
Cuando se trabaja con un inventario grande, puede limitar a Ansible a ejecutar comandos contra hosts o grupos específicos utilizando la opción --limit:
ansible-playbook test_playbook.yml --limit localhost
Este comando solo ejecutará el playbook contra localhost, evitando el error 'UNREACHABLE!' de virtual-host.
Ahora que entendemos mejor el error 'UNREACHABLE!', pasemos a la solución de problemas y la corrección de estos problemas en el siguiente paso.
Solución de Problemas y Corrección de Errores 'UNREACHABLE!'
Ahora que entendemos qué causa los errores 'UNREACHABLE!', aprendamos a solucionar y corregirlos. Utilizaremos una variedad de enfoques para diagnosticar y resolver problemas de conectividad.
Corrección de Problemas de Inventario
Una de las causas más comunes de los errores 'UNREACHABLE!' es la información incorrecta del inventario. Corrijamos nuestro archivo de inventario:
cd ~/project/ansible-lab
Primero, actualicemos nuestro archivo de inventario para incluir un host válido. En este entorno de laboratorio, nos centraremos en usar localhost con diferentes métodos de conexión para demostrar técnicas de solución de problemas:
cat > inventory << 'EOF'
[local]
localhost ansible_connection=local
[ssh_local]
local-ssh ansible_host=127.0.0.1 ansible_connection=ssh
[virtual]
virtual-host ansible_host=10.10.10.10
EOF
Hemos agregado un nuevo grupo ssh_local con un host que intentará conectarse a localhost a través de SSH en lugar del método de conexión local.
Prueba de Conectividad SSH Directamente
Antes de usar Ansible, siempre es una buena idea probar la conectividad SSH directamente:
ssh 127.0.0.1
Es posible que se le solicite una contraseña o vea un mensaje sobre la clave del host. Esta es una buena señal, ya que significa que la conectividad SSH está funcionando, pero es posible que deba configurar SSH correctamente para Ansible.
Presione Ctrl+C para salir si se atasca en el indicador de contraseña.
Configuración de Claves SSH para Autenticación sin Contraseña
Ansible normalmente usa claves SSH para la autenticación. Configuremos el acceso SSH sin contraseña a localhost:
## Generate an SSH key if you don't have one
ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa
## Add the key to authorized_keys
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
## Set proper permissions
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
Ahora intente conectarse a localhost a través de SSH:
ssh 127.0.0.1
Debería poder conectarse sin que se le solicite una contraseña. Escriba exit para volver a su sesión original.
Prueba de Ansible con Conexión SSH
Ahora, probemos Ansible con la conexión SSH a localhost:
ansible ssh_local -m ping
Si la configuración de SSH es correcta, debería ver una respuesta exitosa:
local-ssh | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
Si aún ve un error 'UNREACHABLE!', agreguemos más parámetros de conexión a nuestro archivo de inventario:
cat > inventory << 'EOF'
[local]
localhost ansible_connection=local
[ssh_local]
local-ssh ansible_host=127.0.0.1 ansible_connection=ssh ansible_user=labex ansible_ssh_private_key_file=~/.ssh/id_rsa
[virtual]
virtual-host ansible_host=10.10.10.10
EOF
Intente el comando ping de nuevo:
ansible ssh_local -m ping
Uso de Ansible con una Configuración SSH Personalizada
A veces, necesita configuraciones SSH más complejas. Creemos un archivo de configuración SSH personalizado:
mkdir -p ~/.ssh
cat > ~/.ssh/config << 'EOF'
Host local-ssh
HostName 127.0.0.1
User labex
IdentityFile ~/.ssh/id_rsa
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
EOF
## Set proper permissions
chmod 600 ~/.ssh/config
Actualice el inventario para usar la entrada de configuración SSH:
cat > inventory << 'EOF'
[local]
localhost ansible_connection=local
[ssh_local]
local-ssh
[virtual]
virtual-host ansible_host=10.10.10.10
EOF
Pruebe la conexión de nuevo:
ansible ssh_local -m ping
Creación de un Playbook para Probar Todas las Conexiones
Creemos un playbook completo para probar todas nuestras conexiones:
cat > connection_test.yml << 'EOF'
---
- name: Test Local Connection
hosts: local
gather_facts: no
tasks:
- name: Ping local
ping:
register: local_ping
- name: Display local ping result
debug:
var: local_ping
- name: Test SSH Connection
hosts: ssh_local
gather_facts: no
tasks:
- name: Ping via SSH
ping:
register: ssh_ping
- name: Display SSH ping result
debug:
var: ssh_ping
EOF
Ejecute el playbook:
ansible-playbook connection_test.yml
Debería ver conexiones exitosas tanto a los hosts local como SSH:
PLAY [Test Local Connection] ********************************************
TASK [Ping local] ******************************************************
ok: [localhost]
TASK [Display local ping result] ****************************************
ok: [localhost] => {
"local_ping": {
"changed": false,
"ping": "pong"
}
}
PLAY [Test SSH Connection] **********************************************
TASK [Ping via SSH] ****************************************************
ok: [local-ssh]
TASK [Display SSH ping result] *****************************************
ok: [local-ssh] => {
"ssh_ping": {
"changed": false,
"ping": "pong"
}
}
PLAY RECAP *************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
local-ssh : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
La salida exitosa confirma que hemos corregido los errores 'UNREACHABLE!' para nuestros hosts válidos. El único host que permanece inalcanzable es virtual-host, lo cual es intencional ya que no existe.
Ahora ha diagnosticado y corregido con éxito los errores 'UNREACHABLE!' al:
- Probar la conectividad SSH directa
- Configurar claves SSH para la autenticación sin contraseña
- Configurar el inventario de Ansible con los parámetros de conexión adecuados
- Usar una configuración SSH personalizada
- Verificar la conectividad con un playbook completo
Implementación de las Mejores Prácticas para Prevenir Errores 'UNREACHABLE!'
Ahora que hemos solucionado los errores 'UNREACHABLE!' inmediatos, centrémonos en las mejores prácticas para prevenirlos en el futuro. Esto implica una gestión adecuada del inventario, configuraciones de conexión y técnicas de manejo de errores.
Creación de una Estructura de Inventario Robusta
Un inventario bien organizado facilita la solución de problemas. Creemos un directorio de inventario más estructurado:
cd ~/project/ansible-lab
mkdir -p inventory/{group_vars,host_vars}
Ahora, creemos un archivo de inventario principal:
cat > inventory/hosts << 'EOF'
## Production Servers
[production]
## prod-server ansible_host=prod.example.com
## Development Servers
[development]
## dev-server ansible_host=dev.example.com
## Local Connections
[local]
localhost ansible_connection=local
## SSH Connections
[ssh_local]
local-ssh ansible_host=127.0.0.1
EOF
A continuación, creemos variables de grupo para el grupo de conexión SSH:
cat > inventory/group_vars/ssh_local.yml << 'EOF'
---
ansible_connection: ssh
ansible_user: labex
ansible_ssh_private_key_file: ~/.ssh/id_rsa
ansible_ssh_common_args: '-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null'
EOF
Actualice la configuración de Ansible para usar el nuevo directorio de inventario:
cat > ansible.cfg << 'EOF'
[defaults]
inventory = ./inventory/hosts
host_key_checking = False
retry_files_enabled = True
retry_files_save_path = ~/.ansible/retry-files
timeout = 30
connect_timeout = 30
command_timeout = 30
[ssh_connection]
pipelining = True
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
control_path_dir = ~/.ansible/cp
EOF
Creación de un Playbook de Prueba de Conexión con Lógica de Reintento
Ansible le permite reintentar tareas fallidas. Creemos un playbook con lógica de reintento:
cat > connection_test_with_retry.yml << 'EOF'
---
- name: Test All Connections
hosts: all
gather_facts: no
tasks:
- name: Ping hosts
ping:
register: ping_result
retries: 3
delay: 5
until: ping_result is not failed
ignore_unreachable: yes
- name: Display ping status
debug:
msg: "Connection to {{ inventory_hostname }} was successful"
when: ping_result is success
- name: Report unreachable hosts
debug:
msg: "Host {{ inventory_hostname }} is unreachable"
when: ping_result is unreachable
EOF
Ejecute el playbook con nuestra nueva estructura de inventario:
ansible-playbook connection_test_with_retry.yml
Debería ver una salida que muestre conexiones exitosas a localhost y local-ssh.
Manejo de Errores 'UNREACHABLE!' con Elegancia
Creemos un playbook más avanzado que maneje los errores 'UNREACHABLE!' con elegancia y genere un informe:
cat > connection_report.yml << 'EOF'
---
- name: Test Connections and Generate Report
hosts: all
gather_facts: no
tasks:
- name: Try to connect to hosts
ping:
register: ping_result
ignore_unreachable: yes
- name: Create reachable hosts list
set_fact:
reachable_hosts: "{{ (reachable_hosts | default([])) + [inventory_hostname] }}"
when: ping_result is success
delegate_to: localhost
delegate_facts: true
- name: Create unreachable hosts list
set_fact:
unreachable_hosts: "{{ (unreachable_hosts | default([])) + [inventory_hostname] }}"
when: ping_result is unreachable
delegate_to: localhost
delegate_facts: true
- name: Generate Connection Report
hosts: localhost
gather_facts: no
tasks:
- name: Display reachable hosts
debug:
msg: "Reachable hosts: {{ reachable_hosts | default([]) | join(', ') }}"
- name: Display unreachable hosts
debug:
msg: "Unreachable hosts: {{ unreachable_hosts | default([]) | join(', ') }}"
- name: Write report to file
copy:
content: |
Connection Report
-----------------
Reachable hosts: {{ reachable_hosts | default([]) | join(', ') }}
Unreachable hosts: {{ unreachable_hosts | default([]) | join(', ') }}
Generated on: {{ ansible_date_time.iso8601 }}
dest: ~/project/ansible-lab/connection_report.txt
register: report
- name: Show report location
debug:
msg: "Report saved to {{ report.dest }}"
EOF
Ejecute el playbook de informe:
ansible-playbook connection_report.yml
Comprobemos el informe:
cat ~/project/ansible-lab/connection_report.txt
Debería ver un informe que enumera los hosts alcanzables e inalcanzables.
Uso de Plugins de Inventario de Ansible
Ansible proporciona plugins de inventario para administrar hosts dinámicamente. Creemos un script simple para demostrar esto:
cat > inventory_script.py << 'EOF'
#!/usr/bin/env python3
import json
import socket
def is_host_reachable(host, port=22, timeout=1):
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(timeout)
result = sock.connect_ex((host, port))
sock.close()
return result == 0
except:
return False
## Define our hosts
hosts = {
'localhost': '127.0.0.1',
'local-ssh': '127.0.0.1',
'virtual-host': '10.10.10.10'
}
## Check reachability and build inventory
inventory = {
'all': {
'hosts': list(hosts.keys())
},
'reachable': {
'hosts': []
},
'unreachable': {
'hosts': []
},
'_meta': {
'hostvars': {}
}
}
for hostname, ip in hosts.items():
reachable = is_host_reachable(ip)
group = 'reachable' if reachable else 'unreachable'
inventory[group]['hosts'].append(hostname)
inventory['_meta']['hostvars'][hostname] = {
'ansible_host': ip,
'reachability_checked': True,
'is_reachable': reachable
}
print(json.dumps(inventory, indent=2))
EOF
chmod +x inventory_script.py
Pruebe el script de inventario dinámico:
./inventory_script.py
Debería ver una salida JSON que muestra los hosts categorizados como alcanzables o inalcanzables.
Ejecutemos un playbook usando este inventario dinámico:
ansible-playbook -i ./inventory_script.py connection_test.yml --limit reachable
Esto solo intentará conectarse a los hosts que el script ha determinado que son alcanzables, lo que le ayudará a evitar los errores 'UNREACHABLE!' por completo.
Estas mejores prácticas proporcionan un marco robusto para administrar la conectividad de Ansible y prevenir los errores 'UNREACHABLE!' en entornos de producción.
Resumen
En este laboratorio, aprendió a identificar, solucionar problemas y prevenir el error 'UNREACHABLE!' en Ansible. Ha:
Configurado un entorno básico de Ansible y encontró el error 'UNREACHABLE!' de primera mano.
Analizado el mensaje de error y comprendido las causas comunes de los problemas de conectividad.
Utilizado varias técnicas de solución de problemas para diagnosticar problemas de conexión.
Implementado soluciones para corregir los errores, incluyendo:
- Configuración de claves SSH para autenticación sin contraseña.
- Configuración de archivos de inventario adecuados.
- Uso de opciones de configuración SSH.
Aplicado las mejores prácticas para prevenir futuros errores 'UNREACHABLE!', tales como:
- Creación de una organización de inventario estructurada.
- Implementación de lógica de reintento.
- Desarrollo de estrategias de manejo de errores.
- Uso de scripts de inventario dinámico para verificar la accesibilidad del host.
Estas habilidades le ayudarán a mantener implementaciones de Ansible confiables y a resolver rápidamente cualquier problema de conectividad que surja. Al comprender las causas subyacentes de los errores 'UNREACHABLE!' e implementar las medidas preventivas adecuadas, puede asegurarse de que la automatización de su infraestructura se ejecute de manera fluida y eficiente.


