Comment corriger l'erreur 'UNREACHABLE!' dans Ansible

AnsibleBeginner
Pratiquer maintenant

Introduction

Ansible est un outil puissant d'automatisation de l'infrastructure qui simplifie la gestion des environnements informatiques complexes. Cependant, les utilisateurs rencontrent souvent l'erreur 'UNREACHABLE!', ce qui peut perturber les flux de travail d'automatisation. Cette erreur se produit généralement lorsque Ansible ne peut pas établir de connexion avec les hôtes cibles. Dans ce lab, vous apprendrez à identifier, dépanner et prévenir l'erreur 'UNREACHABLE!' dans vos déploiements Ansible.

À la fin de ce lab, vous comprendrez les causes courantes des problèmes de connectivité dans Ansible et serez capable de mettre en œuvre des solutions efficaces pour garantir le bon fonctionnement de votre automatisation.

Configuration de l'environnement Ansible

Dans cette étape, nous allons configurer un environnement Ansible de base pour travailler. Nous installerons Ansible, configurerons les fichiers essentiels et nous assurerons que tout est prêt pour notre expérimentation.

Installation d'Ansible

Tout d'abord, installons Ansible sur la VM LabEx en utilisant les commandes suivantes :

sudo apt update
sudo apt install -y ansible

Cela installera la dernière version d'Ansible disponible dans les référentiels Ubuntu. Une fois l'installation terminée, vérifiez l'installation en vérifiant la version d'Ansible :

ansible --version

Vous devriez voir une sortie similaire à la suivante, montrant la version d'Ansible et les détails de configuration :

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

Création d'un répertoire de travail

Créons un répertoire dédié à notre travail Ansible :

mkdir -p ~/project/ansible-lab
cd ~/project/ansible-lab

Création du fichier de configuration Ansible

Maintenant, créons un fichier de configuration Ansible de base dans notre répertoire de projet :

cat > ansible.cfg << 'EOF'
[defaults]
inventory = ./inventory
host_key_checking = False
remote_user = labex
EOF

Ce fichier de configuration :

  • Spécifie l'emplacement de notre fichier d'inventaire
  • Désactive la vérification des clés d'hôte SSH (utile pour les environnements de lab)
  • Définit l'utilisateur distant par défaut sur 'labex'

Création du fichier d'inventaire

Le fichier d'inventaire définit les hôtes qu'Ansible gérera. Créons un fichier d'inventaire simple :

cat > inventory << 'EOF'
[local]
localhost ansible_connection=local

[virtual]
virtual-host ansible_host=10.10.10.10
EOF

Cet inventaire contient deux groupes :

  • local : Contient uniquement localhost, qui utilise une connexion locale
  • virtual : Contient un hôte virtuel que nous utiliserons pour démontrer l'erreur 'UNREACHABLE!'

Le virtual-host est configuré avec une adresse IP (10.10.10.10) qui n'existe pas dans notre environnement, ce qui nous aidera à générer l'erreur 'UNREACHABLE!'.

Test d'Ansible

Testons notre configuration Ansible en exécutant une simple commande ping contre l'hôte local :

ansible local -m ping

Vous devriez voir une réponse réussie comme :

localhost | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}

Cela confirme qu'Ansible fonctionne correctement pour la connexion locale. Maintenant, essayons de pinger l'hôte virtuel, ce qui devrait échouer :

ansible virtual -m ping

Cela produira l'erreur 'UNREACHABLE!' car l'hôte n'existe pas :

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
}

Vous avez maintenant configuré avec succès Ansible et créé un scénario où l'erreur 'UNREACHABLE!' se produit, que nous étudierons à l'étape suivante.

Comprendre l'erreur 'UNREACHABLE!'

Dans l'étape précédente, nous avons rencontré l'erreur 'UNREACHABLE!' en essayant de nous connecter à un hôte inexistant. Maintenant, comprenons l'erreur plus en détail et explorons les causes courantes.

Analyse du message d'erreur

Examinons le message d'erreur que nous avons reçu :

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
}

Le message d'erreur fournit des informations précieuses :

  • UNREACHABLE! indique qu'Ansible n'a pas pu établir de connexion avec l'hôte
  • Le champ msg nous dit pourquoi : "Failed to connect to the host via ssh" (Échec de la connexion à l'hôte via ssh)
  • L'erreur spécifique est "Connection timed out" (Délai de connexion dépassé), ce qui signifie qu'Ansible a essayé de se connecter mais n'a reçu aucune réponse

Causes courantes des erreurs 'UNREACHABLE!'

L'erreur 'UNREACHABLE!' peut se produire pour plusieurs raisons :

  1. Problèmes de réseau : L'hôte peut être derrière un pare-feu, ou il peut y avoir des problèmes de connectivité réseau.
  2. Informations d'hôte incorrectes : Le nom d'hôte ou l'adresse IP dans l'inventaire peuvent être incorrects.
  3. Configuration SSH : SSH peut ne pas être configuré correctement sur l'hôte cible.
  4. Problèmes d'authentification : La clé SSH ou le mot de passe peuvent être incorrects.
  5. Indisponibilité de l'hôte : L'hôte peut être hors service ou inaccessible.

Création d'un playbook de test

Créons un playbook simple pour démontrer davantage l'erreur :

cat > test_playbook.yml << 'EOF'
---
- name: Test Connectivity
  hosts: all
  gather_facts: no
  tasks:
    - name: Ping the hosts
      ping:
EOF

Ce playbook tente de pinger tous les hôtes définis dans notre inventaire. Exécutons-le :

ansible-playbook test_playbook.yml

Vous devriez voir une sortie similaire à :

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

Le playbook a réussi pour localhost mais a échoué pour virtual-host avec l'erreur 'UNREACHABLE!'.

Inspection des niveaux de verbosité Ansible

Ansible fournit différents niveaux de verbosité pour aider à diagnostiquer les problèmes. Essayons d'exécuter le playbook avec une verbosité accrue :

ansible-playbook test_playbook.yml -v

Pour une sortie encore plus détaillée, utilisez -vv ou -vvv :

ansible-playbook test_playbook.yml -vvv

L'option -vvv fournit la sortie la plus détaillée, montrant les commandes SSH exactes qu'Ansible essaie d'utiliser :

<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'"'"''

Ce niveau de détail peut être inestimable pour le dépannage des problèmes de connexion SSH.

Utilisation de l'option --limit

Lorsque vous travaillez avec un grand inventaire, vous pouvez limiter Ansible à l'exécution de commandes contre des hôtes ou des groupes spécifiques en utilisant l'option --limit :

ansible-playbook test_playbook.yml --limit localhost

Cette commande n'exécutera le playbook que contre localhost, évitant l'erreur 'UNREACHABLE!' de virtual-host.

Maintenant que nous comprenons mieux l'erreur 'UNREACHABLE!', passons au dépannage et à la résolution de ces problèmes à l'étape suivante.

Dépannage et résolution des erreurs 'UNREACHABLE!'

Maintenant que nous comprenons ce qui cause les erreurs 'UNREACHABLE!', apprenons à les dépanner et à les corriger. Nous utiliserons diverses approches pour diagnostiquer et résoudre les problèmes de connectivité.

Correction des problèmes d'inventaire

L'une des causes les plus courantes des erreurs 'UNREACHABLE!' est une information d'inventaire incorrecte. Corrigeons notre fichier d'inventaire :

cd ~/project/ansible-lab

Tout d'abord, mettons à jour notre fichier d'inventaire pour inclure un hôte valide. Dans cet environnement de lab, nous nous concentrerons sur l'utilisation de localhost avec différentes méthodes de connexion pour démontrer les techniques de dépannage :

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

Nous avons ajouté un nouveau groupe ssh_local avec un hôte qui tentera de se connecter à localhost via SSH au lieu de la méthode de connexion locale.

Test de la connectivité SSH directement

Avant d'utiliser Ansible, il est toujours bon de tester la connectivité SSH directement :

ssh 127.0.0.1

Vous pouvez être invité à entrer un mot de passe ou voir un message concernant la clé d'hôte. C'est bon signe car cela signifie que la connectivité SSH fonctionne, mais vous devrez peut-être configurer SSH correctement pour Ansible.

Appuyez sur Ctrl+C pour quitter si vous êtes bloqué à l'invite de mot de passe.

Configuration des clés SSH pour l'authentification sans mot de passe

Ansible utilise généralement des clés SSH pour l'authentification. Configurer l'accès SSH sans mot de passe à localhost :

## Générer une clé SSH si vous n'en avez pas
ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa

## Ajouter la clé à authorized_keys
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys

## Définir les permissions appropriées
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

Maintenant, essayez de vous connecter à localhost via SSH :

ssh 127.0.0.1

Vous devriez pouvoir vous connecter sans être invité à entrer un mot de passe. Tapez exit pour revenir à votre session d'origine.

Test d'Ansible avec la connexion SSH

Maintenant, testons Ansible avec la connexion SSH à localhost :

ansible ssh_local -m ping

Si la configuration SSH est correcte, vous devriez voir une réponse réussie :

local-ssh | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}

Si vous voyez toujours une erreur 'UNREACHABLE!', ajoutons plus de paramètres de connexion à notre fichier d'inventaire :

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

Essayez à nouveau la commande ping :

ansible ssh_local -m ping

Utilisation d'Ansible avec une configuration SSH personnalisée

Parfois, vous avez besoin de configurations SSH plus complexes. Créons un fichier de configuration SSH personnalisé :

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

## Définir les permissions appropriées
chmod 600 ~/.ssh/config

Mettez à jour l'inventaire pour utiliser l'entrée de configuration SSH :

cat > inventory << 'EOF'
[local]
localhost ansible_connection=local

[ssh_local]
local-ssh

[virtual]
virtual-host ansible_host=10.10.10.10
EOF

Testez à nouveau la connexion :

ansible ssh_local -m ping

Création d'un playbook pour tester toutes les connexions

Créons un playbook complet pour tester toutes nos connexions :

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

Exécutez le playbook :

ansible-playbook connection_test.yml

Vous devriez voir des connexions réussies aux hôtes local et 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 sortie réussie confirme que nous avons corrigé les erreurs 'UNREACHABLE!' pour nos hôtes valides. Le seul hôte qui reste inaccessible est virtual-host, ce qui est intentionnel puisqu'il n'existe pas.

Vous avez maintenant réussi à diagnostiquer et à corriger les erreurs 'UNREACHABLE!' en :

  1. Testant la connectivité SSH directe
  2. Configurant les clés SSH pour l'authentification sans mot de passe
  3. Configurant l'inventaire Ansible avec les paramètres de connexion appropriés
  4. Utilisant une configuration SSH personnalisée
  5. Vérifiant la connectivité avec un playbook complet

Mise en œuvre des meilleures pratiques pour prévenir les erreurs 'UNREACHABLE!'

Maintenant que nous avons corrigé les erreurs 'UNREACHABLE!' immédiates, concentrons-nous sur les meilleures pratiques pour les prévenir à l'avenir. Cela implique une gestion appropriée de l'inventaire, des configurations de connexion et des techniques de gestion des erreurs.

Création d'une structure d'inventaire robuste

Un inventaire bien organisé facilite le dépannage. Créons un répertoire d'inventaire plus structuré :

cd ~/project/ansible-lab
mkdir -p inventory/{group_vars,host_vars}

Maintenant, créons un fichier d'inventaire 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

Ensuite, créons des variables de groupe pour le groupe de connexion 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

Mettez à jour la configuration Ansible pour utiliser le nouveau répertoire d'inventaire :

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

Création d'un playbook de test de connexion avec une logique de nouvelle tentative

Ansible vous permet de réessayer les tâches ayant échoué. Créons un playbook avec une logique de nouvelle tentative :

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

Exécutez le playbook avec notre nouvelle structure d'inventaire :

ansible-playbook connection_test_with_retry.yml

Vous devriez voir une sortie montrant les connexions réussies à localhost et local-ssh.

Gestion des erreurs 'UNREACHABLE!' avec élégance

Créons un playbook plus avancé qui gère les erreurs 'UNREACHABLE!' avec élégance et génère un rapport :

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

Exécutez le playbook de rapport :

ansible-playbook connection_report.yml

Vérifions le rapport :

cat ~/project/ansible-lab/connection_report.txt

Vous devriez voir un rapport listant les hôtes accessibles et inaccessibles.

Utilisation des plugins d'inventaire Ansible

Ansible fournit des plugins d'inventaire pour gérer dynamiquement les hôtes. Créons un script simple pour le démontrer :

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

Testez le script d'inventaire dynamique :

./inventory_script.py

Vous devriez voir une sortie JSON montrant les hôtes classés comme accessibles ou inaccessibles.

Exécutons un playbook en utilisant cet inventaire dynamique :

ansible-playbook -i ./inventory_script.py connection_test.yml --limit reachable

Cela tentera uniquement de se connecter aux hôtes que le script a déterminés comme étant accessibles, ce qui vous aidera à éviter complètement les erreurs 'UNREACHABLE!'.

Ces meilleures pratiques fournissent un cadre robuste pour gérer la connectivité Ansible et prévenir les erreurs 'UNREACHABLE!' dans les environnements de production.

Résumé

Dans ce lab, vous avez appris à identifier, dépanner et prévenir l'erreur 'UNREACHABLE!' dans Ansible. Vous avez :

  1. Mis en place un environnement Ansible de base et rencontré l'erreur 'UNREACHABLE!' directement

  2. Analysé le message d'erreur et compris les causes courantes des problèmes de connectivité

  3. Utilisé diverses techniques de dépannage pour diagnostiquer les problèmes de connexion

  4. Mis en œuvre des solutions pour corriger les erreurs, notamment :

    • Configuration des clés SSH pour l'authentification sans mot de passe
    • Configuration de fichiers d'inventaire appropriés
    • Utilisation des options de configuration SSH
  5. Appliqué les meilleures pratiques pour prévenir les futures erreurs 'UNREACHABLE!', telles que :

    • Création d'une organisation d'inventaire structurée
    • Mise en œuvre d'une logique de nouvelle tentative
    • Développement de stratégies de gestion des erreurs
    • Utilisation de scripts d'inventaire dynamiques pour vérifier l'accessibilité des hôtes

Ces compétences vous aideront à maintenir des déploiements Ansible fiables et à résoudre rapidement tous les problèmes de connectivité qui se présentent. En comprenant les causes sous-jacentes des erreurs 'UNREACHABLE!' et en mettant en œuvre des mesures préventives appropriées, vous pouvez vous assurer que l'automatisation de votre infrastructure fonctionne de manière fluide et efficace.