Как исправить ошибку 'UNREACHABLE!' в Ansible

AnsibleBeginner
Практиковаться сейчас

Введение

Ansible – это мощный инструмент автоматизации инфраструктуры, упрощающий управление сложными ИТ-средами. Однако пользователи часто сталкиваются с ошибкой 'UNREACHABLE!', которая может нарушить процессы автоматизации. Эта ошибка обычно возникает, когда Ansible не может установить соединение с целевыми хостами. В этой лабораторной работе вы узнаете, как идентифицировать, устранять неполадки и предотвращать ошибку 'UNREACHABLE!' в ваших развертываниях Ansible.

К концу этой лабораторной работы вы поймете распространенные причины проблем с подключением в Ansible и сможете реализовать эффективные решения для обеспечения бесперебойной работы вашей автоматизации.

Настройка среды Ansible

На этом шаге мы настроим базовую среду Ansible для работы. Мы установим Ansible, настроим необходимые файлы и убедимся, что все готово для наших экспериментов.

Установка Ansible

Сначала установим Ansible на виртуальную машину LabEx, используя следующие команды:

sudo apt update
sudo apt install -y ansible

Это установит последнюю версию Ansible, доступную в репозиториях Ubuntu. После завершения установки проверьте установку, проверив версию Ansible:

ansible --version

Вы должны увидеть вывод, аналогичный следующему, показывающий версию Ansible и сведения о конфигурации:

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

Создание рабочего каталога

Давайте создадим выделенный каталог для нашей работы с Ansible:

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

Создание файла конфигурации Ansible

Теперь давайте создадим базовый файл конфигурации Ansible в нашем каталоге проекта:

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

Этот файл конфигурации:

  • Указывает местоположение нашего файла инвентаризации
  • Отключает проверку ключей хоста SSH (полезно для лабораторных сред)
  • Устанавливает пользователя по умолчанию для удаленного доступа (remote user) как 'labex'

Создание файла инвентаризации

Файл инвентаризации определяет хосты, которыми будет управлять Ansible. Давайте создадим простой файл инвентаризации:

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

[virtual]
virtual-host ansible_host=10.10.10.10
EOF

Эта инвентаризация содержит две группы:

  • local: Содержит только localhost, который использует локальное подключение
  • virtual: Содержит виртуальный хост, который мы будем использовать для демонстрации ошибки 'UNREACHABLE!'

virtual-host настроен с IP-адресом (10.10.10.10), который не существует в нашей среде, что поможет нам сгенерировать ошибку 'UNREACHABLE!'.

Тестирование Ansible

Давайте протестируем нашу настройку Ansible, запустив простую команду ping против локального хоста:

ansible local -m ping

Вы должны увидеть успешный ответ, например:

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

Это подтверждает, что Ansible работает правильно для локального подключения. Теперь давайте попробуем пропинговать виртуальный хост, который должен завершиться неудачей:

ansible virtual -m ping

Это приведет к ошибке 'UNREACHABLE!', потому что хост не существует:

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
}

Теперь вы успешно настроили Ansible и создали сценарий, в котором возникает ошибка 'UNREACHABLE!', которую мы рассмотрим на следующем шаге.

Понимание ошибки 'UNREACHABLE!'

На предыдущем шаге мы столкнулись с ошибкой 'UNREACHABLE!' при попытке подключиться к несуществующему хосту. Теперь давайте подробнее разберем эту ошибку и рассмотрим распространенные причины.

Анализ сообщения об ошибке

Давайте посмотрим на полученное сообщение об ошибке:

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
}

Сообщение об ошибке предоставляет ценную информацию:

  • UNREACHABLE! указывает на то, что Ansible не смог установить соединение с хостом
  • Поле msg сообщает нам почему: "Failed to connect to the host via ssh" (Не удалось подключиться к хосту через ssh)
  • Конкретная ошибка - "Connection timed out" (Превышено время ожидания соединения), что означает, что Ansible пытался подключиться, но не получил ответа

Распространенные причины ошибок 'UNREACHABLE!'

Ошибка 'UNREACHABLE!' может возникнуть по нескольким причинам:

  1. Проблемы с сетью: Хост может находиться за брандмауэром, или могут быть проблемы с сетевым подключением.
  2. Неверная информация о хосте: Имя хоста или IP-адрес в инвентаризации могут быть неверными.
  3. Настройка SSH: SSH может быть настроен неправильно на целевом хосте.
  4. Проблемы аутентификации: SSH-ключ или пароль могут быть неверными.
  5. Недоступность хоста: Хост может быть выключен или недоступен.

Создание тестового плейбука

Давайте создадим простой плейбук, чтобы дополнительно продемонстрировать ошибку:

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

Этот плейбук пытается пропинговать все хосты, определенные в нашей инвентаризации. Давайте запустим его:

ansible-playbook test_playbook.yml

Вы должны увидеть вывод, аналогичный следующему:

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

Плейбук успешно выполнился для localhost, но завершился неудачей для virtual-host с ошибкой 'UNREACHABLE!'.

Проверка уровней детализации Ansible

Ansible предоставляет различные уровни детализации (verbosity) для помощи в диагностике проблем. Давайте попробуем запустить плейбук с повышенной детализацией:

ansible-playbook test_playbook.yml -v

Для еще более подробного вывода используйте -vv или -vvv:

ansible-playbook test_playbook.yml -vvv

Опция -vvv предоставляет наиболее подробный вывод, показывая точные команды SSH, которые пытается использовать Ansible:

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

Этот уровень детализации может быть бесценным для устранения неполадок с подключением SSH.

Использование опции --limit

При работе с большой инвентаризацией вы можете ограничить Ansible для запуска команд против определенных хостов или групп, используя опцию --limit:

ansible-playbook test_playbook.yml --limit localhost

Эта команда запустит плейбук только против localhost, избегая ошибки 'UNREACHABLE!' от virtual-host.

Теперь, когда мы лучше понимаем ошибку 'UNREACHABLE!', давайте перейдем к устранению неполадок и исправлению этих проблем на следующем шаге.

Устранение неполадок и исправление ошибок 'UNREACHABLE!'

Теперь, когда мы понимаем, что вызывает ошибки 'UNREACHABLE!', давайте узнаем, как устранять неполадки и исправлять их. Мы будем использовать различные подходы для диагностики и решения проблем с подключением.

Исправление проблем с инвентаризацией

Одной из наиболее распространенных причин ошибок 'UNREACHABLE!' является неверная информация об инвентаризации. Давайте исправим наш файл инвентаризации:

cd ~/project/ansible-lab

Сначала обновим наш файл инвентаризации, чтобы включить действительный хост. В этой лабораторной среде мы сосредоточимся на использовании localhost с различными методами подключения, чтобы продемонстрировать методы устранения неполадок:

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

Мы добавили новую группу ssh_local с хостом, который попытается подключиться к localhost через SSH вместо метода локального подключения.

Прямое тестирование подключения SSH

Прежде чем использовать Ansible, всегда рекомендуется напрямую протестировать подключение SSH:

ssh 127.0.0.1

Вам может быть предложено ввести пароль или вы увидите сообщение о ключе хоста. Это хороший знак, так как это означает, что подключение SSH работает, но вам может потребоваться правильно настроить SSH для Ansible.

Нажмите Ctrl+C, чтобы выйти, если вы застряли в запросе пароля.

Настройка SSH-ключей для аутентификации без пароля

Ansible обычно использует SSH-ключи для аутентификации. Давайте настроим доступ к 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

Теперь попробуйте подключиться к localhost через SSH:

ssh 127.0.0.1

Вы должны иметь возможность подключиться без запроса пароля. Введите exit, чтобы вернуться в исходную сессию.

Тестирование Ansible с подключением SSH

Теперь давайте протестируем Ansible с подключением SSH к localhost:

ansible ssh_local -m ping

Если настройка SSH правильная, вы должны увидеть успешный ответ:

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

Если вы все еще видите ошибку 'UNREACHABLE!', давайте добавим больше параметров подключения в наш файл инвентаризации:

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

Попробуйте команду ping еще раз:

ansible ssh_local -m ping

Использование Ansible с пользовательской конфигурацией SSH

Иногда вам нужны более сложные конфигурации SSH. Давайте создадим пользовательский файл конфигурации SSH:

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

Обновите инвентаризацию, чтобы использовать запись конфигурации SSH:

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

[ssh_local]
local-ssh

[virtual]
virtual-host ansible_host=10.10.10.10
EOF

Снова протестируйте подключение:

ansible ssh_local -m ping

Создание плейбука для тестирования всех подключений

Давайте создадим всеобъемлющий плейбук для тестирования всех наших подключений:

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

Запустите плейбук:

ansible-playbook connection_test.yml

Вы должны увидеть успешные подключения как к локальному, так и к 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

Успешный вывод подтверждает, что мы исправили ошибки 'UNREACHABLE!' для наших действительных хостов. Единственный хост, который остается недоступным, - это virtual-host, что сделано намеренно, поскольку он не существует.

Теперь вы успешно диагностировали и исправили ошибки 'UNREACHABLE!' путем:

  1. Тестирования прямого подключения SSH
  2. Настройки SSH-ключей для аутентификации без пароля
  3. Настройки инвентаризации Ansible с правильными параметрами подключения
  4. Использования пользовательской конфигурации SSH
  5. Проверки подключения с помощью всеобъемлющего плейбука

Внедрение лучших практик для предотвращения ошибок 'UNREACHABLE!'

Теперь, когда мы исправили непосредственные ошибки 'UNREACHABLE!', давайте сосредоточимся на лучших практиках, чтобы предотвратить их в будущем. Это включает в себя надлежащее управление инвентаризацией, конфигурации подключений и методы обработки ошибок.

Создание надежной структуры инвентаризации

Хорошо организованная инвентаризация упрощает устранение неполадок. Давайте создадим более структурированную директорию инвентаризации:

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

Теперь давайте создадим основной файл инвентаризации:

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

Далее давайте создадим переменные группы для группы подключений 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

Обновите конфигурацию Ansible, чтобы использовать новую директорию инвентаризации:

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

Создание плейбука для тестирования подключений с логикой повторных попыток

Ansible позволяет повторно выполнять неудачные задачи. Давайте создадим плейбук с логикой повторных попыток:

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

Запустите плейбук с нашей новой структурой инвентаризации:

ansible-playbook connection_test_with_retry.yml

Вы должны увидеть вывод, показывающий успешные подключения к localhost и local-ssh.

Аккуратная обработка ошибок 'UNREACHABLE!'

Давайте создадим более продвинутый плейбук, который аккуратно обрабатывает ошибки 'UNREACHABLE!' и генерирует отчет:

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

Запустите плейбук отчета:

ansible-playbook connection_report.yml

Давайте проверим отчет:

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

Вы должны увидеть отчет со списком доступных и недоступных хостов.

Использование плагинов инвентаризации Ansible

Ansible предоставляет плагины инвентаризации для динамического управления хостами. Давайте создадим простой скрипт, чтобы продемонстрировать это:

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

Протестируйте скрипт динамической инвентаризации:

./inventory_script.py

Вы должны увидеть вывод JSON, показывающий хосты, классифицированные как доступные или недоступные.

Давайте запустим плейбук, используя эту динамическую инвентаризацию:

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

Это приведет к попытке подключения только к хостам, которые скрипт определил как доступные, что поможет вам полностью избежать ошибок 'UNREACHABLE!'.

Эти лучшие практики обеспечивают надежную основу для управления подключением Ansible и предотвращения ошибок 'UNREACHABLE!' в производственных средах.

Резюме

В этой лабораторной работе вы узнали, как идентифицировать, устранять неполадки и предотвращать ошибки 'UNREACHABLE!' в Ansible. Вы:

  1. Настроили базовую среду Ansible и столкнулись с ошибкой 'UNREACHABLE!' на практике.

  2. Проанализировали сообщение об ошибке и поняли распространенные причины проблем с подключением.

  3. Использовали различные методы устранения неполадок для диагностики проблем с подключением.

  4. Реализовали решения для исправления ошибок, включая:

    • Настройку SSH-ключей для аутентификации без пароля
    • Настройку правильных файлов инвентаризации
    • Использование параметров конфигурации SSH
  5. Применили лучшие практики для предотвращения будущих ошибок 'UNREACHABLE!', такие как:

    • Создание структурированной организации инвентаризации
    • Реализация логики повторных попыток
    • Разработка стратегий обработки ошибок
    • Использование скриптов динамической инвентаризации для проверки доступности хостов

Эти навыки помогут вам поддерживать надежные развертывания Ansible и быстро решать любые возникающие проблемы с подключением. Понимая основные причины ошибок 'UNREACHABLE!' и применяя надлежащие профилактические меры, вы можете обеспечить бесперебойную и эффективную работу автоматизации вашей инфраструктуры.