Cisco IOSスイッチ200台へのACL一括適用におけるNetmikoタイムアウト対策とpyATS検証自動化
2026年5月31日の本番デプロイにおいて、200台のCisco IOSスイッチに対する一括ACL適用中に発生したNetmikoのSSHタイムアウトエラー(NetmikoTimeoutException)およびそれに伴う構成ドリフトの解決手順を記録します。この問題は、制御ノードの並行処理セマフォ制御の導入と、Netmikoの接続パラメータ(global_delay_factorおよびread_timeout_override)の最適化、そしてpyATSによる事後検証自動化によって解決されました。
本システムは、Gitを信頼の唯一の情報源(Source of Truth)とするNetDevOpsのアーキテクチャを採用しています。

大規模デプロイ時に発生したSSH接続切断と部分適用の検知
GitLab CI/CDパイプライン経由でAnsibleプレイブックを実行した際、特定のレガシースイッチ群においてタスクが中断し、以下のエラーログが出力されました。これにより、一部のデバイスにのみ設定が適用され、ネットワーク全体で構成の不整合(構成ドリフト)が発生しました。
netmiko.exceptions.NetmikoTimeoutException: Connection to device timed-out: cisco_ios 192.168.10.15:22
このエラーにより、パイプラインは異常終了し、デプロイ対象の200台中15台のスイッチが中間状態で放置される事態となりました。
CPUリソース飽和とコマンド応答遅延の相乗効果
事後解析の結果、タイムアウトの原因は以下の2点に集約されました。
制御ノードにおける並行処理数の過多 Ansibleの
forksパラメータがデフォルトのままであったため、制御ノードが同時に多数のSSHセッションを確立しようとし、CPU使用率が100%に達しました。これにより、SSHハンドシェイクの遅延が発生しました。レガシーハードウェアのコマンド処理遅延 対象のCisco IOSスイッチ(Catalyst 2960シリーズ等)は、大規模なACL(100行以上)のコンパイル時にCPU負荷が上昇し、コマンド応答に通常以上の時間を要します。Netmikoのデフォルトの読み取りタイムアウト(100秒)を超過したため、接続が切断されました。
タイムアウト値の動的調整とセマフォによる流量制御
この問題を解決するため、接続パラメータの最適化と、並行処理数を制限するセマフォ制御を導入しました。
1. Netmiko接続スクリプトのパラメータチューニング 🛠️
Pythonによる並行実行スクリプトにおいて、global_delay_factorを2.0に引き上げ、さらにread_timeout_overrideを300秒に設定しました。これにより、低速なデバイスからの応答を十分に待機できるようになります。
from netmiko import ConnectHandler
device_params = {
'device_type': 'cisco_ios',
'host': '192.168.10.15',
'username': 'admin',
'password': 'secure_password',
'global_delay_factor': 2.0,
'read_timeout_override': 300,
}
with ConnectHandler(**device_params) as net_connect:
output = net_connect.send_config_set(config_commands)
print(output)
2. Ansibleにおける接続設定の最適化 💡
Ansibleプレイブック側でも、ansible.cfgおよびインベントリ変数に変数を追加し、SSHのキープアライブとタイムアウトを制御しました。
# ansible.cfg
[defaults]
forks = 10
timeout = 300
[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o ServerAliveInterval=30 -o ServerAliveCountMax=3
pyATSによる状態検証とデプロイ時間の測定
修正適用後、テスト環境および本番環境において以下の検証手順を実施しました。
1. パイプラインの再実行と実行ログの確認 ⚠️
並行数を10に制限した状態でスクリプトを実行し、CPU使用率が安定していることを確認しました。
$ ansible-playbook -i inventory.ini deploy_acl.yml --forks=10
PLAY [Deploy ACL to Cisco IOS Switches] <b>TASK [Gathering Facts]</b>
ok: [switch-01]
ok: [switch-02]
TASK [Apply ACL Configuration] <b></b>
changed: [switch-01]
changed: [switch-02]
PLAY RECAP <b></b>
switch-01 : ok=2 changed=1 unreachable=0 failed=0
switch-02 : ok=2 changed=1 unreachable=0 failed=0
2. pyATSを用いた構成整合性検証
デプロイ完了後、pyATSを使用して全デバイスのACL適用状態をパースし、未適用または不整合な設定が存在しないかを自動検証しました。
from genie.testbed import load
testbed = load('testbed.yaml')
device = testbed.devices['switch-01']
device.connect()
parsed_output = device.parse('show ip access-lists')
assert 'MY_SECURE_ACL' in parsed_output
print("ACL verification passed successfully.")
検証の結果、タイムアウトによる切断は0件となり、全200台のスイッチに対して意図したACLが正常に適用されていることが確認されました。全体の処理時間は、タイムアウト再試行による遅延を含めた従来の1200秒から、安定した並行処理により45秒へと短縮されました。