はじめに
ネットワーク機器の構成管理に Ansible を使う場合、本番に近いステージング環境を用意するのは容易ではありません。機器の調達やライセンス、設置場所などの制約があるため、事前検証や運用の担保をどうするかは、導入を検討する際に気になる点の一つです。
筆者はこれまで Ansible を使ったことがありませんでした。以前の職場では運用部門が社内の検証環境で事前検証を行っており、筆者は業務上の関わりを通じて、その仕組みを知識として把握している程度でした。今回、手元の検証環境で稼働していた仮想ルーター Cisco CSR1000V と Ubuntu を使って初めて Ansible を導入し、仮想環境で事前検証をどこまで担保できるかを確かめました。本記事は、その実測結果をまとめたものです。
- CSR1000V と Ubuntu で Ansible の検証環境を作る構成
- 古い IOS XE へ SSH 接続する際につまずいたポイントと暫定対応
- ios_facts と ios_config による情報取得・バックアップの実測結果
- –check による変更予定コマンドの確認と冪等性の検証結果
- 仮想環境の検証で確認できたことと、確認していないこと
結論として、物理ルーターを用意しなくても、仮想環境で Ansible の接続、設定取得、変更予定コマンドの確認、設定投入、冪等性までを事前に検証できました。ただし、仮想環境での成功は、本番環境での動作や通信への影響を保証するものではありません。仮想環境は本番検証の代替ではなく、本番投入前の不確実性を減らすための有効な手段の一つとして位置付けるのが適切です。
ネットワーク機器の Ansible 事前検証は仮想環境でもできる?
仮想環境でも、Ansible の基本動作や設定変更の流れは事前に確認できます。一方で、仮想環境と実機では前提が異なる部分があるため、何を確認できるのかを切り分けておくことが重要です。
Ansible でネットワーク機器を管理する仕組み
Ansible は、管理対象にエージェントを導入せず、制御ノード(今回は Ubuntu)から接続して処理を実行します。ネットワーク機器では、ansible.netcommon.network_cli 接続プラグインが SSH で機器へ接続し、CLI コマンドを送受信します。
Cisco IOS / IOS XE 向けには cisco.ios コレクションが提供されており、今回は次の 2 つのモジュールを使用しました。
- cisco.ios.ios_facts は、ホスト名、OS バージョン、モデルなどの機器情報を取得します。
- cisco.ios.ios_config は、設定セクション単位での設定投入や、変更前の running-config のバックアップを行います。
実機検証と仮想環境検証の違い
仮想環境で何を確認しやすいかについて、Cisco は検証用プラットフォーム Cisco Modeling Labs(CML)の資料で考え方を説明しています。CML の仮想イメージは OS をハードウェア抽象化層で切り離して仮想マシン化したもので、ソフトウェアで実装された機能は同じバージョンの物理機器と同様に動作する一方、ASIC で処理される機能はソフトウェア実装がない限りサポートされない場合がある、という内容です。物理機器の設定をそのまま適用しても期待どおりに動作しない場合があることも記載されています。
参考: Cisco DevNet「VM Images for CML Labs」
“it’s not generally suitable for testing throughput or timing.”
(一般に、スループットやタイミングの検証には適していません。)
https://developer.cisco.com/docs/modeling-labs/vm-images-for-cml-labs/
今回は CML ではなく、VMware Workstation 上の CSR1000V を使用しています。そのため、この資料は今回の結果の直接の根拠ではなく、仮想ルーターで検証する際の考え方の参考です。この前提を踏まえると、設定内容や管理操作の確認は仮想環境に向いており、性能やハードウェアに依存する挙動は実機相当の環境で確認する、という切り分けが考えられます。
CSR1000V と Ubuntu で Ansible の検証環境を構築
検証は、VMware Workstation 上の Ubuntu と CSR1000V の 2 台で行いました。CSR1000V は、Azure Site-to-Site VPN の検証環境で稼働していた仮想ルーターを流用しています。

| 項目 | 構成 |
|---|---|
| 仮想化環境 | VMware Workstation |
| Ansible 実行環境 | Ubuntu 24.04 LTS |
| 対象機器 | Cisco CSR1000V |
| Cisco IOS XE | 03.15.00.S |
| Cisco IOS | 15.5(2)S |
| Python | 3.12.3 |
| Ansible Core | 2.21.5 |
| cisco.ios | 11.6.0 |
| ansible.netcommon | 8.7.1 |
| SSH 接続方式 | network_cli + Paramiko |
| Paramiko | 4.0.0 |
今回の Ansible 検証で変更対象にしたのは、検証用に作成した Loopback99 だけです。VPN で使用している既存インターフェースやルーティング設定は変更していません。
Ubuntu に Ansible と cisco.ios をインストール
Ansible を初めて導入する場合の最小限の手順です。検証時のバージョンをもとに記事用に整理したコマンド例で、実際の作業順や入力内容とは異なる場合があります。
Ubuntu 24.04 LTS の標準の Python 3 で、検証用ディレクトリに仮想環境を作成して有効化します。
sudo apt install -y python3-venv
mkdir -p ~/ansible-lab && cd ~/ansible-lab
python3 -m venv .venv
source .venv/bin/activate仮想環境内に ansible-core と Paramiko をインストールします。検証時と同じバージョンを指定した例です。
pip install "ansible-core==2.21.5" "paramiko==4.0.0"Cisco IOS 向けの cisco.ios と、network_cli を含む ansible.netcommon をインストールします。
ansible-galaxy collection install cisco.ios:==11.6.0 ansible.netcommon:==8.7.1ansible-core、コレクション、Paramiko のバージョンが想定どおりかを確認します。
ansible --version
ansible-galaxy collection list cisco.ios
ansible-galaxy collection list ansible.netcommon
pip show paramikoPython の仮想環境を使うと、OS のパッケージと分けて Ansible のバージョンを管理できます。なお、後述のとおり今回は libssh での接続も試しており、その際は libssh 用のライブラリ(ansible-pylibssh)を別途使用しています。
inventory.yml で接続先と接続方式を定義
検証用ディレクトリ /home/labuser/ansible-lab に inventory.yml を作成し、routers グループに csr-azure-lab を定義しました。次は、グループ名とホスト名を検証時の構成に合わせ、接続に関係する主な設定項目を整理した例です。
all:
children:
routers:
hosts:
csr-azure-lab:
ansible_host: 192.168.46.254
vars:
ansible_connection: ansible.netcommon.network_cli
ansible_network_os: cisco.ios.ios
ansible_user: labadmin
ansible_network_cli_ssh_type: paramikoパスワードは inventory.yml に記載せず、実行時に -k(--ask-pass)を指定して入力しています。ansible_network_cli_ssh_type: paramiko を指定している理由は、次のセクションで説明します。
古い IOS XE への SSH 接続でつまずいたポイント
Ubuntu から CSR1000V への ping は成功しましたが、SSH 接続でつまずきました。今回の CSR1000V は IOS XE 03.15.00.S と古いバージョンで、暗号アルゴリズムの不一致が発生しました。
| 接続方法 | 発生した事象 | 結果 |
|---|---|---|
| 手動 SSH(追加オプションなし) | 鍵交換アルゴリズム、ホスト鍵アルゴリズムの不一致 | 接続不可 |
| 手動 SSH(アルゴリズムを追加指定) | なし | 接続成功 |
| Ansible(libssh) | MAC アルゴリズムの不一致 | 接続不可 |
| Ansible(Paramiko 4.0.0) | なし | 接続成功 |
手動 SSH では、次のオプションを指定して接続できました。
ssh -o KexAlgorithms=+diffie-hellman-group14-sha1 -o HostKeyAlgorithms=+ssh-rsa labadmin@192.168.46.254Ansible では、libssh を利用した接続で MAC アルゴリズムの不一致が発生しました。最終的に、Paramiko 4.0.0 を使用し、inventory.yml で ansible_network_cli_ssh_type: paramiko を指定することで接続できました。
古い暗号アルゴリズムを許可する設定や Paramiko への切り替えは、今回の閉じた検証環境における暫定対応です。本番環境へ安易に適用せず、機器の OS 更新や機器更改を含めて対応を検討することを推奨します。
また、network_cli で Paramiko を使う方式そのものが、公式ドキュメントで非推奨とされています。
参考: Ansible Documentation「ansible.netcommon.network_cli connection」
“This option is deprecated and will be removed in a release after 2028-02-01.”
(このオプションは非推奨であり、2028-02-01 より後のリリースで削除されます。)
https://docs.ansible.com/projects/ansible/latest/collections/ansible/netcommon/network_cli_connection.html
Ansible の公式フォーラムの告知では、ansible.netcommon v8.5.0 で Paramiko が非推奨になり、libssh(ansible-pylibssh)へ移行するよう案内されています。今回使用した ansible.netcommon 8.7.1 も、この告知の対象となるバージョンです。Paramiko で接続できたことは、長期的に使える接続手段を確保できたことを意味しません。libssh で接続できる機器・OS への更新を前提に計画することを推奨します。
Ansible で機器情報の取得と running-config のバックアップを検証
接続できた後、まず設定を変更しない操作として、機器情報の取得と running-config のバックアップを試しました。
ios_facts で機器情報を取得
検証では、cisco.ios.ios_facts に gather_subset: min を指定し、最小限の機器情報を取得しました。取得した情報を ansible_net_hostname などの変数で参照したところ、非推奨の警告が表示されました。
Ansible の公式フォーラムの告知では、ファクトを変数として直接参照する従来の方式に対し、ansible-core 2.20 以降で警告が表示され、ansible_facts['...'] 経由で参照するよう案内されています。そのため、次の掲載例では表示部分を ansible_facts 経由の参照に変更しています。この参照形式に変更した掲載例は、再実行による動作確認をしていません。
- name: Gather facts from CSR1000V
hosts: routers
gather_facts: false
tasks:
- name: Collect device facts
cisco.ios.ios_facts:
gather_subset: min
- name: Show device information
ansible.builtin.debug:
msg:
- "Hostname: {{ ansible_facts['net_hostname'] }}"
- "Version: {{ ansible_facts['net_version'] }}"
- "Model: {{ ansible_facts['net_model'] }}"検証時(ansible_net_hostname などで参照した構成)の実行結果は次のとおりです。
| 項目 | 取得結果 |
|---|---|
| Hostname | CSR-AZURE-LAB |
| Version | 03.15.00.S |
| Model | CSR1000V |
| 実行結果 | ok=2、changed=0、failed=0 |
Ansible は変更なし(changed=0)と判定しており、情報取得の操作として期待どおりの結果です。
ios_config の backup で running-config を保存
cisco.ios.ios_config に backup: true を指定すると、変更前の running-config 全体を制御ノードへバックアップできます。公式ドキュメントでは、backup_options を指定しない場合、Playbook のルートディレクトリ配下の backup フォルダーへ保存される仕様と説明されています。
今回の検証では、この既定の backup フォルダーではなく、private-backups ディレクトリにバックアップを保存しました。保存先は backup_options の dir_path で指定できます。次は、保存先を private-backups にした場合の記事用の例です。
- name: Backup running-config
hosts: routers
gather_facts: false
tasks:
- name: Save running-config to the control node
cisco.ios.ios_config:
backup: true
backup_options:
dir_path: "{{ playbook_dir }}/private-backups"実行後、Ubuntu 側の private-backups ディレクトリにバックアップファイルが生成されていることを確認しました。
機器設定のバックアップには認証情報などが含まれる可能性があるため、本記事では内容を掲載していません。そのため、今回はバックアップ取得後に、保存先ディレクトリの権限を 700、バックアップファイルの権限を 600 に手動で変更しました。これらの権限は Ansible が自動で設定したものではありません。手動で変更する場合のコマンド例は次のとおりです。
chmod 700 ~/ansible-lab/private-backups
chmod 600 ~/ansible-lab/private-backups/*バックアップの取得とあわせて、保存先のアクセス権限と保管方法も設計しておくことを推奨します。
ドライラン(–check)による事前確認と設定変更を検証
次に、設定を変更する前の確認として --check を使い、その後に設定投入と冪等性を確認しました。
検証対象に Loopback99 を選んだ理由
変更対象には、既存設定に存在しない Loopback99 を選び、description を 1 行だけ設定する内容にしました。既存の VPN 設定に関係しないインターフェースを使い、検証による変更範囲を限定するためです。投入予定の設定は、interface Loopback99 と description Ansible-Lab-Only の 2 行です。
desc_check.yml の主な設定
Playbook では cisco.ios.ios_config を使い、次のパラメーターを指定しました。
- parents: interface Loopback99
-
設定を適用する階層(親コマンド)を指定します。
- lines: description Ansible-Lab-Only
-
親の配下で設定するコマンドです。公式ドキュメントでは、冪等性と正しい差分のために、running-config に表示されるものと同じコマンドで記述するよう説明されています。
- match: line
-
現在の設定とコマンドを 1 行ずつ照合します。
matchの既定値もlineです。 - save_when: never
-
running-config を startup-config へ保存しません。既定値も
neverですが、意図を明示するために指定しました。
次は、検証時の設定内容をもとに整理した Playbook です。検証時は 2 回目の実行で debug タスクが警告を出したため(後述)、掲載版では debug タスクに条件を追加しています。条件を追加した掲載版は、再実行による動作確認をしていません。
- name: Set description on Loopback99
hosts: routers
gather_facts: false
tasks:
- name: Configure Loopback99 description
cisco.ios.ios_config:
parents: interface Loopback99
lines:
- description Ansible-Lab-Only
match: line
save_when: never
register: preview_result
- name: Show commands to be sent
ansible.builtin.debug:
var: preview_result.commands
when: preview_result.commands | default([]) | length > 0Cisco IOS の設定は、省略形ではなく running-config と同じ表記で記述することが冪等性の前提になります。公式ドキュメントの例でも、この点が示されています。
参考: Ansible Documentation「cisco.ios.ios_config module」
“For idempotency, use full-form commands”
(冪等性のため、省略しない形式のコマンドを使用してください。)
https://docs.ansible.com/projects/ansible/latest/collections/cisco/ios/ios_config_module.html
–check で変更予定のコマンドを確認
まず、--check と --diff を付けて実行しました。
ansible-playbook -i inventory.yml desc_check.yml -k --check --diff実行結果は changed=1、failed=0 でした。preview_result.commands には次の 2 行が返り、投入予定のコマンドを事前に確認できました。
interface Loopback99description Ansible-Lab-Only
実行後に CSR1000V の running-config を確認したところ、Loopback99 は存在していませんでした。–check の実行では、CSR1000V の running-config が変更されていないことを確認しています。
なお、今回の環境では --diff を付けても具体的な差分表示は得られませんでした。ios_config では diff_against で差分の比較対象を指定する仕様が公式ドキュメントで説明されていますが、今回は指定しておらず、指定した場合の表示は未検証です。
また、check mode はあくまで Ansible の処理をシミュレーションする機能です。
参考: Ansible Documentation「Validating tasks: check mode and diff mode」
“Check mode is just a simulation.”
(check mode は、あくまでシミュレーションです。)
https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_checkmode.html
--check で確認できるのは、Ansible が機器へ送信する予定のコマンドです。そのコマンドを投入したときに通信やほかの機能へどのような影響が出るかまでは、--check だけでは確認できません。
設定投入と冪等性の確認
--check を外して Playbook を実行し、その後 CSR1000V で次のコマンドを実行して反映結果を確認しました。
show running-config | section ^interface Loopback99interface Loopback99
description Ansible-Lab-Only
no ip addressAnsible からの設定変更が running-config に反映されています。save_when: never を指定しているため、Playbook による startup-config への保存は行っていません。
続けて同じ Playbook を再実行したところ、設定変更タスクは ok となり、追加の設定変更は発生しませんでした。3 回の実行結果は次のとおりです。
| 実行 | オプション | 結果 | 対象機器の状態 |
|---|---|---|---|
| 1 回目 | --check --diff | changed=1、failed=0 | Loopback99 は未作成 |
| 2 回目 | --check なし | 設定を投入 | running-config に Loopback99 と description が反映 |
| 3 回目 | 2 回目と同じ | ok=2、changed=0、failed=0 | 追加の変更なし |
同じ Playbook を繰り返し実行しても、今回の設定については不要な変更が発生しないことを確認できました。
なお、3 回目は設定変更が不要だったため preview_result.commands が返されず、debug タスクで警告が表示されました。これは設定変更タスクの失敗ではありません。前掲の Playbook では、返されたコマンドがある場合だけ表示する条件を追加しています。この条件では変更がない場合に debug タスクが skipped になるため、実行結果の集計は検証時の ok=2 と異なる表示になると考えられます(未検証)。
仮想環境でどこまで運用の安全性を確認できるか
今回の検証で確認できたのは、主に Ansible の操作そのものが期待どおりに動くかどうかです。本番運用の安全性を判断するには、追加の確認が必要です。
今回確認できたことと確認していないこと
| 仮想環境で今回確認できたこと | 今回の検証だけでは担保できないこと |
|---|---|
| Ansible からの接続 | 本番機種との互換性 |
| 設定情報の取得 | 本番ネットワーク全体への影響 |
| 設定バックアップ | 障害発生時の復旧動作 |
| 変更前の予定コマンド確認 | 冗長構成・フェイルオーバー |
| 設定投入 | 実機固有のハードウェア動作 |
| 冪等性 | 性能や負荷に関する影響 |
右列は、仮想環境で一切検証できないという意味ではなく、今回の小規模な検証では確認していない項目です。
本番適用までの段階的な確認
本番への適用を検討する場合は、次のような段階的な確認が考えられます。今回実施したのは主に 1〜3 の範囲で、5 はバックアップ取得のみです。


| 段階 | 確認内容 | 今回の検証範囲 |
|---|---|---|
| 1. 構文確認 | Playbook の記述を確認する | 対象 |
| 2. ドライラン | 変更予定のコマンドを確認する | 対象 |
| 3. 仮想環境での動作確認 | 設定投入と冪等性を確認する | 対象 |
| 4. 本番に近い構成での影響確認 | 機種、OS バージョン、構成をそろえて影響を確認する | 対象外 |
| 5. 変更計画の作成 | バックアップと切り戻し手順を含めて計画する | 一部実施(バックアップ取得のみ) |
| 6. 本番投入後の確認 | 設定と通信を確認する | 対象外 |
本番環境に適用する際の注意点
今回の結果を本番の運用へ持ち込む場合は、次の点に注意が必要です。
- 検証した IOS XE 03.15.00.S は古いバージョンであり、現行の IOS XE やほかの機種で同じ結果になるとは限りません。
cisco.iosの動作は機種や OS バージョンによって異なる可能性があるため、本番と同じ機種・OS バージョンで改めて確認することを推奨します。- 古い SSH アルゴリズムの許可や Paramiko の利用は、本番環境の標準手順にしないことを推奨します。
save_whenの指定によって startup-config への保存の有無が変わるため、保存のタイミングを変更計画で明確にしておきます。- 切り戻しは、取得したバックアップや手動での戻し手順を含めて計画し、本番投入前に検証しておく必要があります。
仮想環境は本番検証の代替ではなく、本番投入前の不確実性を減らすための手段として、実機相当の検証や変更管理と組み合わせることが重要です。
まとめ
本記事では、CSR1000V と Ubuntu の仮想環境で、Ansible によるネットワーク機器の事前検証を試しました。接続から設定取得、ドライラン、設定投入、冪等性までは確認できた一方、本番環境での動作や通信への影響は別途確認が必要です。
- 物理ルーターなしでも、仮想環境で Ansible の基本動作を検証できました。
- 古い IOS XE では SSH の暗号アルゴリズム不一致への対応が必要でした。
- ios_facts と ios_config で情報取得とバックアップを確認できました。
- –check で予定コマンドを確認し、running-config の未変更も確かめました。
- 同じ Playbook の再実行で、追加の変更が発生しないことを確認しました。
- 性能、冗長構成、通信への影響は今回の検証範囲外です。
- 仮想環境の検証は、実機相当の確認や変更管理と組み合わせて活用します。
以上、最後までお読みいただきありがとうございました。


