Como corrigir o erro 'UNREACHABLE!' no Ansible

AnsibleBeginner
Pratique Agora

Introdução

Ansible é uma ferramenta poderosa de automação de infraestrutura que simplifica o gerenciamento de ambientes de TI complexos. No entanto, os usuários frequentemente encontram o erro 'UNREACHABLE!', que pode interromper os fluxos de trabalho de automação. Este erro normalmente ocorre quando o Ansible não consegue estabelecer uma conexão com os hosts de destino. Neste laboratório, você aprenderá a identificar, solucionar problemas e prevenir o erro 'UNREACHABLE!' em suas implementações do Ansible.

Ao final deste laboratório, você entenderá as causas comuns de problemas de conectividade no Ansible e será capaz de implementar soluções eficazes para garantir que sua automação seja executada sem problemas.

Configurando o Ambiente Ansible

Nesta etapa, configuraremos um ambiente Ansible básico para trabalhar. Instalaremos o Ansible, configuraremos os arquivos essenciais e garantiremos que tudo esteja pronto para nossa experimentação.

Instalando o Ansible

Primeiro, vamos instalar o Ansible na VM do LabEx usando os seguintes comandos:

sudo apt update
sudo apt install -y ansible

Isso instalará a versão mais recente do Ansible disponível nos repositórios do Ubuntu. Após a conclusão da instalação, verifique a instalação verificando a versão do Ansible:

ansible --version

Você deve ver uma saída semelhante à seguinte, mostrando a versão do Ansible e os detalhes da configuração:

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

Criando um Diretório de Trabalho

Vamos criar um diretório dedicado para nosso trabalho com Ansible:

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

Criando o Arquivo de Configuração do Ansible

Agora, vamos criar um arquivo de configuração Ansible básico em nosso diretório do projeto:

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

Este arquivo de configuração:

  • Especifica o local do nosso arquivo de inventário
  • Desabilita a verificação da chave do host SSH (útil para ambientes de laboratório)
  • Define o usuário remoto padrão como 'labex'

Criando o Arquivo de Inventário

O arquivo de inventário define os hosts que o Ansible irá gerenciar. Vamos criar um arquivo de inventário simples:

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

[virtual]
virtual-host ansible_host=10.10.10.10
EOF

Este inventário contém dois grupos:

  • local: Contém apenas localhost, que usa uma conexão local
  • virtual: Contém um host virtual que usaremos para demonstrar o erro 'UNREACHABLE!'

O virtual-host é configurado com um endereço IP (10.10.10.10) que não existe em nosso ambiente, o que nos ajudará a gerar o erro 'UNREACHABLE!'.

Testando o Ansible

Vamos testar nossa configuração do Ansible executando um comando ping simples contra o host local:

ansible local -m ping

Você deve ver uma resposta bem-sucedida como:

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

Isso confirma que o Ansible está funcionando corretamente para a conexão local. Agora, vamos tentar fazer ping no host virtual, o que deve falhar:

ansible virtual -m ping

Isso produzirá o erro 'UNREACHABLE!' porque o host não 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
}

Você configurou com sucesso o Ansible e criou um cenário onde o erro 'UNREACHABLE!' ocorre, o qual investigaremos na próxima etapa.

Entendendo o Erro 'UNREACHABLE!'

Na etapa anterior, encontramos o erro 'UNREACHABLE!' ao tentar conectar a um host inexistente. Agora, vamos entender o erro com mais detalhes e explorar as causas comuns.

Analisando a Mensagem de Erro

Vamos analisar a mensagem de erro que recebemos:

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
}

A mensagem de erro fornece informações valiosas:

  • UNREACHABLE! indica que o Ansible não conseguiu estabelecer uma conexão com o host
  • O campo msg nos diz o porquê: "Failed to connect to the host via ssh" (Falha ao conectar ao host via ssh)
  • O erro específico é "Connection timed out" (Tempo limite da conexão), o que significa que o Ansible tentou conectar, mas não recebeu resposta

Causas Comuns de Erros 'UNREACHABLE!'

O erro 'UNREACHABLE!' pode ocorrer por vários motivos:

  1. Problemas de Rede: O host pode estar atrás de um firewall, ou pode haver problemas de conectividade de rede.
  2. Informações do Host Incorretas: O nome do host ou o endereço IP no inventário podem estar errados.
  3. Configuração SSH: O SSH pode não estar configurado corretamente no host de destino.
  4. Problemas de Autenticação: A chave SSH ou a senha podem estar incorretas.
  5. Indisponibilidade do Host: O host pode estar inativo ou inacessível.

Criando um Playbook de Teste

Vamos criar um playbook simples para demonstrar ainda mais o erro:

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

Este playbook tenta fazer ping em todos os hosts definidos em nosso inventário. Vamos executá-lo:

ansible-playbook test_playbook.yml

Você deve ver uma saída semelhante 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

O playbook foi bem-sucedido para localhost, mas falhou para virtual-host com o erro 'UNREACHABLE!'.

Inspecionando os Níveis de Verbose do Ansible

O Ansible fornece diferentes níveis de verbosidade para ajudar a diagnosticar problemas. Vamos tentar executar o playbook com verbosidade aumentada:

ansible-playbook test_playbook.yml -v

Para uma saída ainda mais detalhada, use -vv ou -vvv:

ansible-playbook test_playbook.yml -vvv

A opção -vvv fornece a saída mais detalhada, mostrando os comandos SSH exatos que o Ansible está tentando 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 nível de detalhe pode ser inestimável para solucionar problemas de conexão SSH.

Usando a Opção --limit

Ao trabalhar com um inventário grande, você pode limitar o Ansible a executar comandos contra hosts ou grupos específicos usando a opção --limit:

ansible-playbook test_playbook.yml --limit localhost

Este comando executará o playbook apenas contra localhost, evitando o erro 'UNREACHABLE!' de virtual-host.

Agora que entendemos melhor o erro 'UNREACHABLE!', vamos passar para a solução de problemas e correção desses problemas na próxima etapa.

Solucionando Problemas e Corrigindo Erros 'UNREACHABLE!'

Agora que entendemos o que causa os erros 'UNREACHABLE!', vamos aprender como solucionar problemas e corrigi-los. Usaremos uma variedade de abordagens para diagnosticar e resolver problemas de conectividade.

Corrigindo Problemas de Inventário

Uma das causas mais comuns de erros 'UNREACHABLE!' são informações incorretas no inventário. Vamos corrigir nosso arquivo de inventário:

cd ~/project/ansible-lab

Primeiro, vamos atualizar nosso arquivo de inventário para incluir um host válido. Neste ambiente de laboratório, focaremos em usar localhost com diferentes métodos de conexão para demonstrar técnicas de solução 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

Adicionamos um novo grupo ssh_local com um host que tentará se conectar ao localhost via SSH em vez do método de conexão local.

Testando a Conectividade SSH Diretamente

Antes de usar o Ansible, é sempre uma boa ideia testar a conectividade SSH diretamente:

ssh 127.0.0.1

Você pode ser solicitado a inserir uma senha ou ver uma mensagem sobre a chave do host. Este é um bom sinal, pois significa que a conectividade SSH está funcionando, mas pode ser necessário configurar o SSH corretamente para o Ansible.

Pressione Ctrl+C para sair se você ficar preso na solicitação de senha.

Configurando Chaves SSH para Autenticação Sem Senha

O Ansible normalmente usa chaves SSH para autenticação. Vamos configurar o acesso SSH sem senha ao 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

Agora, tente se conectar ao localhost via SSH:

ssh 127.0.0.1

Você deve ser capaz de se conectar sem ser solicitado a inserir uma senha. Digite exit para retornar à sua sessão original.

Testando o Ansible com Conexão SSH

Agora, vamos testar o Ansible com a conexão SSH ao localhost:

ansible ssh_local -m ping

Se a configuração do SSH estiver correta, você deverá ver uma resposta bem-sucedida:

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

Se você ainda vir um erro 'UNREACHABLE!', vamos adicionar mais parâmetros de conexão ao nosso arquivo de inventário:

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

Tente o comando ping novamente:

ansible ssh_local -m ping

Usando o Ansible com uma Configuração SSH Personalizada

Às vezes, você precisa de configurações SSH mais complexas. Vamos criar um arquivo de configuração 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

Atualize o inventário para usar a entrada de configuração SSH:

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

[ssh_local]
local-ssh

[virtual]
virtual-host ansible_host=10.10.10.10
EOF

Teste a conexão novamente:

ansible ssh_local -m ping

Criando um Playbook para Testar Todas as Conexões

Vamos criar um playbook abrangente para testar todas as nossas conexões:

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

Execute o playbook:

ansible-playbook connection_test.yml

Você deve ver conexões bem-sucedidas para os hosts local e 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

A saída bem-sucedida confirma que corrigimos os erros 'UNREACHABLE!' para nossos hosts válidos. O único host que permanece inacessível é virtual-host, o que é intencional, pois ele não existe.

Você agora diagnosticou e corrigiu com sucesso os erros 'UNREACHABLE!' por meio de:

  1. Testar a conectividade SSH direta
  2. Configurar chaves SSH para autenticação sem senha
  3. Configurar o inventário do Ansible com os parâmetros de conexão corretos
  4. Usar uma configuração SSH personalizada
  5. Verificar a conectividade com um playbook abrangente

Implementando as Melhores Práticas para Prevenir Erros 'UNREACHABLE!'

Agora que corrigimos os erros 'UNREACHABLE!' imediatos, vamos nos concentrar nas melhores práticas para evitá-los no futuro. Isso envolve o gerenciamento adequado do inventário, configurações de conexão e técnicas de tratamento de erros.

Criando uma Estrutura de Inventário Robusta

Um inventário bem organizado facilita a solução de problemas. Vamos criar um diretório de inventário mais estruturado:

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

Agora, vamos criar um arquivo de inventário 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

Em seguida, vamos criar variáveis de grupo para o grupo de conexão 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

Atualize a configuração do Ansible para usar o novo diretório de inventário:

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

Criando um Playbook de Teste de Conexão com Lógica de Retentativa

O Ansible permite que você tente novamente as tarefas com falha. Vamos criar um playbook com lógica de retentativa:

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

Execute o playbook com nossa nova estrutura de inventário:

ansible-playbook connection_test_with_retry.yml

Você deve ver a saída mostrando conexões bem-sucedidas para localhost e local-ssh.

Lidando com Erros 'UNREACHABLE!' de Forma Elegante

Vamos criar um playbook mais avançado que lida com erros 'UNREACHABLE!' de forma elegante e gera um relatório:

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

Execute o playbook de relatório:

ansible-playbook connection_report.yml

Vamos verificar o relatório:

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

Você deve ver um relatório listando os hosts acessíveis e inacessíveis.

Usando Plugins de Inventário do Ansible

O Ansible fornece plugins de inventário para gerenciar hosts dinamicamente. Vamos criar um script simples para demonstrar isso:

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

Teste o script de inventário dinâmico:

./inventory_script.py

Você deve ver a saída JSON mostrando os hosts categorizados como acessíveis ou inacessíveis.

Vamos executar um playbook usando este inventário dinâmico:

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

Isso tentará se conectar apenas aos hosts que o script determinou serem acessíveis, ajudando você a evitar erros 'UNREACHABLE!' completamente.

Essas melhores práticas fornecem uma estrutura robusta para gerenciar a conectividade do Ansible e prevenir erros 'UNREACHABLE!' em ambientes de produção.

Resumo

Neste laboratório, você aprendeu a identificar, solucionar problemas e prevenir o erro 'UNREACHABLE!' no Ansible. Você:

  1. Configurou um ambiente Ansible básico e encontrou o erro 'UNREACHABLE!' em primeira mão

  2. Analisou a mensagem de erro e compreendeu as causas comuns de problemas de conectividade

  3. Usou várias técnicas de solução de problemas para diagnosticar problemas de conexão

  4. Implementou soluções para corrigir os erros, incluindo:

    • Configurar chaves SSH para autenticação sem senha
    • Configurar arquivos de inventário adequados
    • Usar opções de configuração SSH
  5. Aplicou as melhores práticas para prevenir futuros erros 'UNREACHABLE!', como:

    • Criar uma organização de inventário estruturada
    • Implementar lógica de retentativa
    • Desenvolver estratégias de tratamento de erros
    • Usar scripts de inventário dinâmicos para verificar a acessibilidade do host

Essas habilidades ajudarão você a manter implantações Ansible confiáveis e a resolver rapidamente quaisquer problemas de conectividade que surgirem. Ao entender as causas subjacentes dos erros 'UNREACHABLE!' e implementar medidas preventivas adequadas, você pode garantir que a automação de sua infraestrutura seja executada de forma suave e eficiente.