はじめに
この記事は、こちらの記事に影響されてネットワークのCI/CDを真面目に考えてみようということで書いています。
※この記事は2本立てのうちの2本目です。詳細が気になる方はまずこちらをご覧ください。
目的
前回の記事でcontainerlab, Ansible, NAPALM + pytestを使ってネットワークのテストを行う基盤を作成しました。これのおかげで、プログラミング言語からネットワーク状態の検証ができるようになりました。
つまり、ソフトウェア開発と同様にCI/CDの恩恵を受けられるようになるということです。
今回はGithub Actionsを使って一連のフローを自動化(CI)し、本番想定の環境にデプロイ(CD)するところまで作ってみます。
とはいえ、流石にネットワーク機器に全自動CI/CDフローを適用するのは怖いので、デプロイ前に手動での承認を必須にしようと思います。
最終形態としては以下のようになります。
mainブランチに PR作成 or push
↓
CI実行
- ユニットテスト
- containerlabデプロイ
- Ansibleで設定投入
- ネットワーク状態のテスト
↓
手動承認 (Environment Protection)
↓
CD実行
- 本番環境(想定)にデプロイ
リポジトリ
ディレクトリ構成(再掲)
network-cicd/
├── ansible/
├── containerlab/
├── config/
├── src/
├── tests/
└── desired_state.yml
Pythonのパッケージマネージャはuvを利用します。
Actions実行環境を作る
ネットワークのCDを行うということで、社内のネットワークにアクセスできる環境でActionsを動かす必要があります。しかし、通常ネットワーク機器はSaaSであるGithubからアクセスできないため、self-hosted runnerを利用します。
こちらの記事を参考に設定しました。
CIを作る
解説は後回しで、まずはいったん完成したものを貼ります。
name: CI Pipeline
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
test-fabric:
runs-on: self-hosted
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Install the project
run: |
uv sync --locked --all-extras --dev
uv pip install -e .
- name: Run unit tests
run: uv run pytest tests/unit/
- name: Install containerlab
run: curl -sL https://containerlab.dev/setup | sudo -E bash -s "all"
- name: Cleanup existing containerlab topology
run: |
cd containerlab/
containerlab destroy || true
- name: Deploy containerlab topology
run: |
cd containerlab/
containerlab deploy
- name: Deploy configurations
run: |
cd ansible/
uv run ansible-playbook playbooks/deploy.yml -i inventory.dev.yml
- name: Sleep for 180 seconds to allow the topology to stabilize
run: sleep 180
- name: Run fabric tests
run: uv run pytest tests/fabric/
- name: Cleanup current containerlab topology
run: |
cd containerlab/
containerlab destroy
CIで実現したいこと
実現したいのは以下のことです。
- ユニットテスト実行: Pythonコード自体の正常性チェック
- containerlab起動: 検証環境の立ち上げ
- Ansible実行: 設定投入
- ネットワーク状態のテスト実行: fabricの正常性チェック
解説
前半
jobs:
test-fabric:
runs-on: self-hosted
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Install the project
run: |
uv sync --locked --all-extras --dev
uv pip install -e .
- name: Run unit tests
run: uv run pytest tests/unit/
- name: Install containerlab
run: curl -sL https://containerlab.dev/setup | sudo -E bash -s "all"
ここでは利用するツールたちの準備をしています。
runs-on: self-hostedを指定することで、self-hosted runner上でこのパイプラインが動くようになります。
後半
- name: Cleanup existing containerlab topology
run: |
cd containerlab/
containerlab destroy || true
- name: Deploy containerlab topology
run: |
cd containerlab/
containerlab deploy
- name: Deploy configurations
run: |
cd ansible/
uv run ansible-playbook playbooks/deploy.yml -i inventory.dev.yml
- name: Sleep for 180 seconds to allow the topology to stabilize
run: sleep 180
- name: Run fabric tests
run: uv run pytest tests/fabric/
- name: Cleanup current containerlab topology
run: |
cd containerlab/
containerlab destroy
Cleanup existing containerlab topologyでは、前回のラボが残っていたら削除する処理を実行します。self-hosted runnerなので、もしパイプラインに失敗したらラボ環境が破棄されず残り続けます。なのでフローの中でお掃除してます。
Deploy containerlab topology、Deploy configurationsは環境のデプロイをしています。
Sleep for 180 seconds ...では、設定投入からBGP等が収束するまでの待機時間です。
Run fabric testsでネットワーク状態のテストを行います。
そして最後にCleanup current containerlab topologyで環境を消して終わりです。
承認の仕組みを作る
これはGithubの機能で実現可能です。この記事をもとに設定しました。
CDを作る
まずは成果物です。
CIのファイルと分けたほうが管理しやすいので別ファイルで作っています。
name: CD Pipeline
on:
workflow_run:
workflows:
- CI Pipeline
types:
- completed
jobs:
deploy-fabric:
if: >
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'push'
environment: Production
runs-on: self-hosted
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Install the project
run: |
uv sync --locked --all-extras --dev
uv pip install -e .
# using containerlab as production environment at this time
- name: Install containerlab
run: curl -sL https://containerlab.dev/setup | sudo -E bash -s "all"
- name: Cleanup existing production topology
run: |
cd containerlab-prod/
containerlab destroy || true
- name: Deploy production topology
run: |
cd containerlab-prod/
containerlab deploy
- name: Deploy configurations
run: |
cd ansible/
uv run ansible-playbook playbooks/deploy.yml -i inventory.prod.yml
- name: Cleanup current production topology
run: |
cd containerlab-prod/
containerlab destroy
解説
起動条件の定義
name: CD Pipeline
on:
workflow_run:
workflows:
- CI Pipeline
types:
- completed
ここでは、「CI Pipelineがcompletedになったら」という条件でパイプラインを起動することを宣言しています。
前半
jobs:
deploy-fabric:
if: >
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'push'
environment: Production
runs-on: self-hosted
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Install the project
run: |
uv sync --locked --all-extras --dev
uv pip install -e .
# using containerlab as production environment at this time
- name: Install containerlab
run: curl -sL https://containerlab.dev/setup | sudo -E bash -s "all"
ほぼCIと同じです。if: >ステートメントにて本当にmainブランチにプッシュされたときのみ実行するようにします。これがないと、PR作成時のCI実行でもCDが実行されてしまいます。
今回は本番環境想定のラボを用意して利用します。CDではユニットテストは不要なので書いていません。
後半
- name: Cleanup existing production topology
run: |
cd containerlab-prod/
containerlab destroy || true
- name: Deploy production topology
run: |
cd containerlab-prod/
containerlab deploy
- name: Deploy configurations
run: |
cd ansible/
uv run ansible-playbook playbooks/deploy.yml -i inventory.prod.yml
- name: Cleanup current production topology
run: |
cd containerlab-prod/
containerlab destroy
これも基本CIと同じです。本番想定のラボを展開している点、Ansibleのインベントリファイルが本番用になっている点が異なっています。
本番想定のラボなのでCI同様、パイプラインの最後に削除していますが実際はこんなことしないようにお願いします…(さすがにいないと思いますが...)
実際に動かしてみる
リポジトリに何らかの変更を加えます。今回はPRを作ってみます。

実行中...
数分経って検証が終わると画面が変わります。

マージすると承認を催促するメールが届きます

さいごに
これにて一通りの実装が全て終わりました。
過去Cisco DevNetでこういったネットワークのコード管理&CI/CDといった構成を見たことがありますが、実際に動くものを見たことがなかったので今回いい感じのものができて満足です。
反省点としてはAnsibleで設定を一括投入しており、差分がわからない点かなと思います。安定性のために、差分だけの投入によって冪等性を確保していきたいです。NAPALMに差分投入機能があるらしいので、色々使って遊んでみたいと思います。
