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 localvirtual: 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
msgnos 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:
- Problemas de Rede: O host pode estar atrás de um firewall, ou pode haver problemas de conectividade de rede.
- Informações do Host Incorretas: O nome do host ou o endereço IP no inventário podem estar errados.
- Configuração SSH: O SSH pode não estar configurado corretamente no host de destino.
- Problemas de Autenticação: A chave SSH ou a senha podem estar incorretas.
- 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:
- Testar a conectividade SSH direta
- Configurar chaves SSH para autenticação sem senha
- Configurar o inventário do Ansible com os parâmetros de conexão corretos
- Usar uma configuração SSH personalizada
- 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ê:
Configurou um ambiente Ansible básico e encontrou o erro 'UNREACHABLE!' em primeira mão
Analisou a mensagem de erro e compreendeu as causas comuns de problemas de conectividade
Usou várias técnicas de solução de problemas para diagnosticar problemas de conexão
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
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.


