Ansibleで到達不能なホストを無視する方法
Ansibleの到達不能とタスク失敗の違いを解説します。ignore_unreachable、serial、max_fail_percentageの使い分けと、処理されなかったホストをrecapで確認する方法がわかります。
到達不能なホストは失敗したタスクではありません
Ansible で到達不能なホストを無視するには ignore_unreachable: true を設定します。この指定は機能します。重要なのは、いつ使用するかを理解することです。Ansible はこの2つの問題を異なる方法で処理します。ホスト上で実行され、エラーを返したタスクは failure です。Ansible がまったく接続できなかったホストは unreachable です。ignore_errors は前者だけを対象にします。ignore_unreachable は後者だけを対象にします。
play の recap では、次のように違いが表示されます。
PLAY RECAP *********************************************************************
web1 : ok=7 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
web2 : ok=0 changed=0 unreachable=1 failed=0 skipped=0 rescued=0 ignored=0Ansible は web1 に接続し、7個のタスクを実行しました。web2 には unreachable=1 と failed=0 が表示されています。これは、そのホストでは何も実行されなかったことを意味します。Ansible は接続を確立できなかったため、そのホストを play から除外し、残りの処理を続行しました。その play がセキュリティ更新をインストールするものだった場合、サーバーの1台には更新が適用されていません。
到達不能になる原因
到達不能とは、いずれのモジュールもホスト上で実行される前に接続が失敗した状態です。読めるのはモジュールの出力ではなく接続エラーだけで、マシンに初めてアクセスするタスクに表示されます。
fatal: [web2]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: connect to host 203.0.113.20 port 22: Connection refused", "unreachable": true}msgフィールドに実際の原因が示されます。次のエラーを確認することになります。
Connection refused: TCP 接続が拒否されたため、そのポートで待ち受けているものがありません。sshd が停止しているか、SSH が別のポートに移動したにもかかわらず、inventory には 22 と記載されています。Connection timed out: 何も応答しませんでした。ファイアウォールがパケットを破棄しているか、サーバーが停止しています。各試行で、デフォルトでは 10 秒に設定されている接続タイムアウト全体を消費します。Host key verification failed.:~/.ssh/known_hosts内の鍵が、サーバーから提示された鍵と一致しません。再構築した VPS は IP アドレスを維持したまま新しいホスト鍵を取得するため、再インストール後であれば想定される状態ですが、それ以外では重大な問題です。Permission denied (publickey): SSH は応答しましたが、鍵を拒否しました。ポートには問題がないため、これは認証の問題です。通常はansible_userが誤っているか、鍵が読み込まれていません。Timeout (12s) waiting for privilege escalation prompt: 接続には成功しましたが、becomeは成功しませんでした。sudo が、入力されることのないパスワードを待っています。
Python インタープリターがないことを、この一覧に含まれる原因だと考える人がいます。しかし、これは該当しません。SSH 接続には成功しているため、ホストには到達できます。その後、モジュールを実行する環境がないだけです。
fatal: [db1]: FAILED! => {"changed": false, "module_stdout": "/bin/sh: 1: /usr/bin/python3: not found\r\n", "msg": "The module failed to execute correctly, you probably need to set the interpreter", "rc": 127}この行は FAILED! と示しており、recap では failed として集計されるため、ignore_unreachable はこのホストに対して実行されません。このホストに ansible_python_interpreter を設定するか、ホストに python3 をインストールしてください。
到達不能なホストを play で無視する方法
タスクレベルでは、このキーワードをモジュールの横に記述します。
- name: Read the package list, and do not stop if the host is down
ansible.builtin.command: dpkg -l
register: packages
changed_when: false
ignore_unreachable: trueplay レベルでは、play 内のすべてのタスクに対するデフォルト値になります。個別のタスクで元に戻すこともできます。
- name: Opportunistic fleet maintenance
hosts: all
ignore_unreachable: true
tasks:
- name: This runs, cannot connect, and the play carries on
ansible.builtin.ping:
- name: This one still ends the play for a host that is down
ansible.builtin.ping:
ignore_unreachable: false内部で変わる動作を理解しておくことが重要です。ignore_unreachable を設定すると、そのホストは play から削除されません。そのため、後続のすべてのタスクが再接続を試み、同じように失敗します。各試行では接続タイムアウトまで待機します。ansible.cfg の timeout を変更しない場合、タイムアウトは 10 秒です。停止した 1 台のサーバーに対して 20 個のタスクを実行する play では、実行時間が約 200 秒延び、ログに 20 行のエラーが追加されます。
そのため、1 回だけ確認して、そのホストを正常に処理から外します。
- name: Opportunistic fleet maintenance
hosts: all
gather_facts: false
tasks:
- name: Check that the host answers before doing any work
ansible.builtin.ping:
register: reachable
ignore_unreachable: true
- name: End the play for this host if it never answered
ansible.builtin.meta: end_host
when: reachable.unreachable | default(false)
- name: Gather facts now that the connection is known good
ansible.builtin.setup:
- name: Refresh the package index
ansible.builtin.apt:
update_cache: true
become: true停止した各ホストに対する接続試行を、タスクごとに 1 回ではなく 1 回だけにできます。Ansible 2.8 で追加された end_host は、現在のホストに対する play を終了しますが、そのホストを failed とはマークしません。unreachable キーは、接続に失敗した場合にだけ登録済みの結果に存在します。そのため、default(false) により、応答したすべてのホストでも条件が有効になります。play レベルで fact gathering を無効にしているのは、暗黙的な Gathering Facts タスクが壊れた接続に遭遇するタスクになってしまうためです。ここでは、その確認を独自の ping で行います。
ignore_unreachable は play キーワードであると同時にタスクキーワードでもあります。実行時にどのホストを対象外にできるかを決める設定なので、role の内部ではなく、読者が確認できる playbook に記述してください。playbook と role の分担では、このような設定をどの層で管理すべきかを説明しています。
ここで ignore_errors を使うべきでない理由
Ansible のドキュメントは、この制限を明確に説明しています。ignore_errors「タスクを実行でき、'failed' という値を返す場合にのみ機能します。未定義変数エラー、接続失敗、実行上の問題(パッケージが見つからない場合など)、構文エラーを Ansible に無視させることはできません。」
接続失敗が failed: true を持つタスク結果になることはありません。接続失敗は別のフラグとして通知され、Ansible はまずそのフラグを処理します。そのため、ホストは unreachable リストに追加され、play の対象から外れます。play の 12 個すべてのタスクに ignore_errors: true を指定しても、SSH ポートが閉じているホストは最初のタスクで停止します。これは、この領域で最もよくある誤解です。特に VPS に対する最初の playbook の書き方を学んでいた頃に作成した古い playbook は、grep で確認するとよいでしょう。
抑制する前にデバッグします
恒久的な抑制は、サーバー群の構成にずれが生じる原因になります。誰も接続できないホストは、誰もパッチを適用しないホストでもあるためです。まず、次の順序で確認します。ここで示すコマンドはすべて読み取り専用です。
ansible web2 -i inventory.ini -m ansible.builtin.ping -oは、1 台のホストに対して 1 つのモジュールを実行し、1 行を出力します。- 同じコマンドに
-vvvvを追加します。Ansible は、対象ユーザー、ポート、秘密鍵、渡すオプションを含む、組み立てた完全な ssh コマンドを出力します。 -vを付けて、その ssh コマンドを自分で実行します。通常の ssh で接続できない場合、問題は Ansible より下の層にあり、playbook のキーワードでは解決できません。msg文字列を読み、上記の一覧と照合します。Connection refusedとConnection timed outは異なる場所を指します。一方は SSH サービス、もう一方はネットワーク経路です。Host key verification failed.では、ssh-keygen -F web2.example.comで保存済みの内容を確認します。サーバーを再構築した場合は、ssh-keygen -R web2.example.comで古いエントリを削除し、プロバイダーのコンソールで新しい鍵を確認してから受け入れます。ansible.cfgでhost_key_checking = Falseを設定するとエラーは解消しますが、そのアドレスで別のマシンが応答していることを検出するチェックも無効になります。Permission denied (publickey)では、Ansible が使用すべき設定を確認します。ansible-inventory -i inventory.ini --host web2は、適用中の変数を出力します。ansible_userとansible_portも含まれます。- SSH は機能するがモジュールが機能しない場合は、
ansible web2 -m ansible.builtin.raw -a 'command -v python3 || echo none'でインタープリターを確認します。rawモジュールは shell 経由でコマンドを実行するため、対象ホストに python は必要ありません。
この確認を終えて初めて、ホストを無視することが習慣ではなく判断になります。
到達不能は個別に集計され、CI では通常見落とされます
ansible-playbook は成功時に 0、少なくとも 1 台のホストで失敗した場合に 2、少なくとも 1 台のホストに到達できなかった場合に 4 を返します。これら 2 つの値はソース内でビットフラグとして定義されているため、失敗したホストと到達不能なホストがそれぞれ 1 台以上ある実行では終了コードが 6 になります。ansible コマンドも同じコードを返します。これらの動作は、2026 年 8 月に ansible-core のソースで確認しました。
ここで ignore_unreachable: true を設定し、同じ到達不能なホストに対して、同じ 7 タスクの play を実行します。
PLAY RECAP *********************************************************************
web1 : ok=7 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
web2 : ok=7 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=7web2 は unreachable=0 と 7 tasks ok を報告し、実行は 0 で終了します。このキーワードを設定すると、Ansible はそのホストについて、dark と呼ばれるカウンターではなく、ok と ignored のカウンターを増やします。dark は unreachable 列に表示される値です。赤色の UNREACHABLE! 行は引き続き出力されるため、ログは正確ですが、recap と終了コードは正確ではありません。
playbook を実行して $? だけを確認する CI ジョブは、その実行を成功として扱います。サマリーにも、マシンに一度も接続できなかったことは表示されません。到達性の確認を独立したステップにし、play の前に実行します。
ansible all -i inventory.ini -m ansible.builtin.ping -oこれはホストごとに 1 行を出力し、到達できないホストが 1 台でもあれば 4 で終了します。そのため、パイプラインで失敗させる条件にでき、ログから該当するホスト名も確認できます。ping には対象ホスト上で動作する Python インタープリターが必要です。そのため、通常の接続確認よりも多くの条件を検証できます。続いて ignore_unreachable を指定して playbook を実行すると、稼働しているホストには変更を適用できます。
バッチ全体に対する any_errors_fatal と max_fail_percentage
これら2つの play キーワードは、ホスト群の一部で問題が発生した後の動作を決定します。ただし、到達不能なホストの扱いは互いに異なります。
any_errors_fatal: true は、到達不能なホストにも反応します。Ansible はバッチ内の残りのホストで現在のタスクを完了した後、バッチ内のすべてのホストに対する play を停止します。協調してスキーマを変更する場合など、実行を全体成功または全体失敗として扱う必要があるときに使用します。
max_fail_percentage: 30 は、到達不能なホストには反応しません。この判定では、失敗したホスト数をバッチのサイズで割ります。到達不能なホストは別のリストで管理されるため、その数には含まれません。10台のホストのうち4台が到達不能でも、max_fail_percentage: 10 では処理が続行されます。一方、2台のホストでタスクが失敗すると play は停止します。ドキュメントには、もう1つ注意点があります。「設定した割合を超える必要があり、同じ割合では停止しません」。serial: 4 で4台中2台の失敗後に停止するには、50ではなく49を指定します。
到達不能なホストだけで実行が停止するケースも1つあります。バッチ内のすべてのホストが失敗または到達不能になると、Ansible は処理できるホストがなくなるため、NO MORE HOSTS LEFT で play を終了します。
フリート全体への変更の段階的な適用
- name: Rolling nginx config update
hosts: webservers
serial: 2
max_fail_percentage: 25
tasks:
- name: Deploy the site config
ansible.builtin.template:
src: site.conf.j2
dest: /etc/nginx/conf.d/site.conf
owner: root
mode: "0644"
become: true
notify: Reload nginx
handlers:
- name: Reload nginx
ansible.builtin.service:
name: nginx
state: reloaded
become: trueserial: 2 は 2 台のホストに対してプレイブック全体を実行し、完了してから次の 2 台を開始します。serial: "25%" はグループの規模に応じて拡張できます。リスト形式の serial: [1, 5, 10] はカナリア構成です。最初に 1 台、次に 5 台、その後 10 台で実行し、残りのホストは最後のバッチサイズで実行します。max_fail_percentage はバッチ単位で計測されるため、この 2 つを組み合わせて使用します。最初のマシンで障害が発生すると、40 台目に影響が及ぶ前に実行が停止します。これにより、1 台の制御マシンから Linux サーバーのフリートを管理することを、1 つのコマンドで安全に実行できます。
到達不能なホストを無視する場合と、無視してはいけない場合
一時的な作業では、到達不能なホストを無視します。ファクト収集や毎時のドリフトチェックでは、停止中のホストをスキップしても問題ありません。次回の実行で処理できるためです。この場合は、play レベルの ignore_unreachable: true が適しています。ping ステップと組み合わせ、スキップされたホスト名を担当者が確認できる場所に記録します。
セキュリティパッチの適用では、決して無視してはいけません。この実行の価値は、すべてのホストに修正が適用されたことを保証する点にあります。到達不能状態を抑制すると、「1 台のサーバーがまだ脆弱である」という事実が、正常終了したように見える要約に変わります。2 週間にわたって到達不能なホストは、対応が大幅に遅れている可能性が最も高いホストです。この実行は終了コード 4 で終了させ、担当者が確認できるようにします。
どちらの場合にも共通するルールがあります。停止は抑制しても、記録は抑制しないことです。ホストをスキップした場合は、要約、CI ログ、監視アラートのいずれかに、その事実を記録する必要があります。Ansible がホストの存在を認識できるのは、play がそのホストに対して実行されている数秒間だけです。そのため、火曜日からサーバーが停止していることを Ansible で把握するのは困難です。この役割は監視システムが担います。Zabbix をインストールする Ansible playbookを使えば、午後のうちにフリート全体の状況を確認できるようになります。
FAQ
Ansible の ignore_errors と ignore_unreachable の違いは何ですか?
ignore_errors: true は、ホスト上で実行されたタスクが失敗を返した場合に適用されます。たとえば、コマンドが 0 以外の終了コードで終了した場合です。ignore_unreachable: true は、Ansible が接続できず、モジュールが一度も実行されなかったホストに適用されます。これらはタスク結果の異なるフィールドを参照するため、一方で他方のケースを処理することはできません。Ansible のドキュメントには、ignore_errors は「未定義変数エラー、接続失敗、実行上の問題(パッケージ不足など)、構文エラーを Ansible に無視させるものではない」と記載されています。閉じた SSH ポートは接続失敗に該当します。
ignore_unreachable を設定すると、ホストは play recap から隠れますか?
実質的には隠れます。このキーワードを設定すると、Ansible はそのホストを unreachable として数えるのをやめ、タスクごとに ok と ignored として 1 回ずつ数えます。その後、実行は 0 で終了します。fatal: [host]: UNREACHABLE! の行は引き続き出力されるため、recap と終了コードが実際の状態を反映しなくても、ログは正確です。ignored 列を確認するか、ansible all -m ansible.builtin.ping -o を別のステップとして実行してください。そうすれば、到達不能なホストがどこかで 0 以外の終了コードを返します。
ホストに到達できない場合、ansible-playbook はどの終了コードを返しますか?
4 を返します。少なくとも 1 台のホストが失敗した実行では 2 を返します。これら 2 つの値はビットフラグであるため、失敗したホストと到達不能なホストの両方がある実行では 6 を返します。正常な実行では 0 を返します。これらのコードは、2026 年 8 月に ansible-core のソースコードで確認されています。ignore_unreachable: true を設定すると 4 のビットがなくなるため、終了コードだけを検査するパイプラインでは、処理されなかったマシンを検出できません。
応答しなかったホストについて、play の残りをスキップするにはどうすればよいですか?
最初のタスクを ansible.builtin.ping にし、ignore_unreachable: true と register: reachable を指定します。その後、when: reachable.unreachable | default(false) の条件で ansible.builtin.meta: end_host を実行します。end_host により、そのホストについて play を終了できます。失敗としては扱われません。play に gather_facts: false を設定し、接続が壊れていることを検出するタスクを ping にします。このパターンを使わない場合、停止したホストは play に残り、後続の各タスクで再び接続タイムアウトまで待つことになります。
セキュリティパッチの実行中に、到達不能なホストを無視すべきですか?
いいえ。パッチ適用は、すべてのホストに更新が適用されたことを保証するために実行します。到達不能なホストを無視すると、その保証が正常を示す recap に置き換わります。実行を 4 で終了させ、応答しなかったホスト名を確認して対応してください。抑制は、次回の実行で未処理分を回収できる、反復的な機会実行に限定すべきです。