Ansible での 'UNREACHABLE!' エラーの修正方法

AnsibleBeginner
オンラインで実践に進む

はじめに

Ansible は、複雑な IT 環境の管理を簡素化する強力なインフラ自動化ツールです。しかし、ユーザーはしばしば「UNREACHABLE!」エラーに遭遇し、これが自動化ワークフローを中断させる可能性があります。このエラーは通常、Ansible がターゲットホストとの接続を確立できない場合に発生します。この実験(Lab)では、Ansible デプロイメントで「UNREACHABLE!」エラーを特定、トラブルシューティング、および防止する方法を学びます。

この実験(Lab)の終了時には、Ansible における接続問題の一般的な原因を理解し、自動化がスムーズに実行されるようにするための効果的な解決策を実装できるようになります。

Ansible 環境のセットアップ

このステップでは、使用する基本的な Ansible 環境をセットアップします。Ansible をインストールし、重要なファイルを構成し、すべてが実験(experimentation)の準備ができていることを確認します。

Ansible のインストール

まず、次のコマンドを使用して、LabEx VM に Ansible をインストールしましょう。

sudo apt update
sudo apt install -y ansible

これにより、Ubuntu リポジトリで利用可能な最新バージョンの Ansible がインストールされます。インストールが完了したら、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 ホストキーの確認を無効にします(実験環境に役立ちます)。
  • デフォルトのリモートユーザーを 'labex' に設定します。

インベントリファイルの作成

インベントリファイルは、Ansible が管理するホストを定義します。簡単なインベントリファイルを作成しましょう。

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

[virtual]
virtual-host ansible_host=10.10.10.10
EOF

このインベントリには、2 つのグループが含まれています。

  • local: ローカル接続を使用する localhost のみを含みます。
  • virtual: 「UNREACHABLE!」エラーをデモンストレーションするために使用する仮想ホストを含みます。

virtual-host は、環境に存在しない IP アドレス (10.10.10.10) で構成されており、これにより「UNREACHABLE!」エラーを生成できます。

Ansible のテスト

ローカルホストに対して簡単な ping コマンドを実行して、Ansible のセットアップをテストしましょう。

ansible local -m ping

次のような成功した応答が表示されるはずです。

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

これにより、Ansible がローカル接続で正しく動作していることが確認されます。次に、仮想ホストに ping を試してみましょう。これは失敗するはずです。

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

このプレイブックは、インベントリで定義されているすべてのホストに ping を試みます。実行してみましょう。

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 は、問題を診断するのに役立つさまざまな詳細レベルを提供します。プレイブックを詳細度を上げて実行してみましょう。

ansible-playbook test_playbook.yml -v

さらに詳細な出力については、-vv または -vvv を使用します。

ansible-playbook test_playbook.yml -vvv

-vvv オプションは、Ansible が使用しようとしている正確な SSH コマンドを表示する、最も詳細な出力を提供します。

<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 オプションの使用

大規模なインベントリを操作する場合、--limit オプションを使用して、特定のホストまたはグループに対して Ansible がコマンドを実行するように制限できます。

ansible-playbook test_playbook.yml --limit localhost

このコマンドは、localhost に対してのみプレイブックを実行し、virtual-host からの「UNREACHABLE!」エラーを回避します。

これで、「UNREACHABLE!」エラーをよりよく理解できたので、次のステップでこれらの問題のトラブルシューティングと修正に進みましょう。

「UNREACHABLE!」エラーのトラブルシューティングと修正

「UNREACHABLE!」エラーの原因を理解したので、それらのトラブルシューティングと修正方法を学びましょう。さまざまなアプローチを使用して、接続の問題を診断し、解決します。

インベントリの問題の修正

「UNREACHABLE!」エラーの最も一般的な原因の 1 つは、誤ったインベントリ情報です。インベントリファイルを修正しましょう。

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 経由で localhost に接続しようとするホストを持つ新しいグループ ssh_local を追加しました。

SSH 接続の直接テスト

Ansible を使用する前に、常に SSH 接続を直接テストすることをお勧めします。

ssh 127.0.0.1

パスワードの入力を求められたり、ホストキーに関するメッセージが表示される場合があります。これは、SSH 接続が機能していることを意味する良い兆候ですが、Ansible 用に SSH を適切に構成する必要がある場合があります。

パスワードプロンプトでスタックした場合は、Ctrl+C を押して終了します。

パスワードなし認証のための SSH キーの設定

Ansible は通常、認証に SSH キーを使用します。localhost へのパスワードなし SSH アクセスを設定しましょう。

## まだ SSH キーがない場合は、SSH キーを生成します
ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa

## キーを authorized_keys に追加します
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys

## 適切なアクセス許可を設定します
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

次に、SSH 経由で localhost に接続してみましょう。

ssh 127.0.0.1

パスワードを求められることなく接続できるはずです。「exit」と入力して、元のセッションに戻ります。

SSH 接続を使用した Ansible のテスト

次に、SSH 接続を使用して Ansible を 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

カスタム SSH 設定を使用した Ansible の使用

場合によっては、より複雑な 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

## 適切なアクセス許可を設定します
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!」エラーを防止するための堅牢なフレームワークを提供します。

まとめ

この実験では、Ansible で「UNREACHABLE!」エラーを特定、トラブルシューティング、および防止する方法を学びました。以下のことを行いました。

  1. 基本的な Ansible 環境をセットアップし、「UNREACHABLE!」エラーを直接体験しました。

  2. エラーメッセージを分析し、接続問題の一般的な原因を理解しました。

  3. さまざまなトラブルシューティング技術を使用して、接続の問題を診断しました。

  4. 次のようなエラーを修正するためのソリューションを実装しました。

    • パスワードなし認証のための SSH キーの設定
    • 適切なインベントリファイルの設定
    • SSH 設定オプションの使用
  5. 今後の「UNREACHABLE!」エラーを防止するために、次のようなベストプラクティスを適用しました。

    • 構造化されたインベントリ組織の作成
    • リトライロジックの実装
    • エラー処理戦略の開発
    • ホストの到達可能性を確認するための動的インベントリスクリプトの使用

これらのスキルは、信頼性の高い Ansible デプロイメントを維持し、発生する可能性のある接続問題を迅速に解決するのに役立ちます。「UNREACHABLE!」エラーの根本原因を理解し、適切な予防措置を講じることで、インフラストラクチャの自動化がスムーズかつ効率的に実行されるようにすることができます。