GitlabのCI/CDによるネットワーク機器設定
こんちわ。
今回はGitlabのCI/CD機能を使って、ネットワーク機器の展開をやってみようかと。
①アーキテクチャ
ざっくりとこんな感じ:
管理下のネットワーク機器は物理と仮想のミックスですが、管理用のサービスは全てコンテナとしてローカルで運用してます。
②ビジネスオブジェクティブ
今回のセットアップの目的について:
■ネットワーク機器の初期設定以後のコンフィグデプロイをスタンダード化し、自動化する。
■今回のセットアップはバリューストリーム内のごく一部的な自動化である。(例えばPIQ内の案件内容の自動化などはない)
ワークフローのプロセス:
■まず、ネットワーク機器にごく一般的な初期設定が施される(SSHでのアクセスと、ローカルアカウント)
■Gitlabのリポで新たなデプロイがブランチとして設定される
■ブランチでデプロイ用のコンフィグが生成される
■ブランチでコミットする事によって、ステージングのPlaybookがCIパイプラインとして作動する
■ステージングがレビューされて、問題なければMainにマージ ▶ マージ後にMain上でCDのパイプラインが作動し、プロダクションネットワークにコンフィグ設定が施される
■CI/CDによるデプロイが完了した旨チャットに通知が来る
■デプロイしたネットワーク機器がOxidizedのリストについかされ、Rawコンフィグのバックアップが開始されるキャッシング
③Gitlab Runnerのセットアップ
前回のOxidizedのセットアップでGitlabのサーバーは設定されてます。
今回はGitlab上でのCI/CDパイプラインのワーカーとして作動するGitlab Runnerを設定していきます。
Runnerとサーバーを同じホストで運用するのはおすすめされてないのですが、今回は検証のみなので同じDockerホストでコンテナとして運用します。
Runner自体の設定はマニュアル通りやれば簡単です。https://docs.gitlab.com/runner/install/docker.html
コンテナが開始した後、Gitlab上でRunnerを登録する必要があります。
こちらもマニュアル通り行えば問題はありませんでした。
ただし、自分の場合はサーバーをFQDNとしてはなく、IPで登録を行ったので、SSL証明書関連で問題が発生しました。
TLS証明書のSAN設定
GitlabサーバーはデフォルトでSelf-signedの証明書を使用し、Runner登録自体のコネクションはSSLではなくてはだめ(昔は無効にできたみたいです)。
IPアドレスを使っているため証明書のSANにIPの登録がないとエラーになってしまいました。
ワークアラウンドとして、SANを含む、SSL証明書の再発行とGitlabサーバー上への再設定が必要でした。
登録の様子:
kazuma:~/gitlab-runner$ sudo docker run --rm -it -v gitlab-runner-config:/etc/gitlab-runner gitlab/gitlab-runner:latest register --tls-ca-file /etc/gitlab-runner/certs/gitlab.example.com.crt
Runtime platform arch=amd64 os=linux pid=7 revision=5316d4ac version=14.6.0
Running in system-mode.
Enter the GitLab instance URL (for example, https://gitlab.com/):
https://192.168.0.50
Enter the registration token:
hQVhneK6h_Eukhsyyuet
Enter a description for the runner:
[5744035213af]:
Enter tags for the runner (comma-separated):
Registering runner... succeeded runner=hQVhneK6
Enter an executor: ssh, virtualbox, docker-ssh, shell, parallels, docker+machine, docker-ssh+machine, kubernetes, custom, docker:
docker
Enter the default Docker image (for example, ruby:2.6):
python:latest
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!
登録は完了した後、Gitlab上でもRunnerの確認ができます。
次に、CI/CDパイプラインの設定です。
④CI/CDパイプラインの設定
こちらに関してはずぶの素人なりにやりはじめたんですが、色々な機能に使えそうです!
セオリー的に理解するよりも、サンプルコードなりで諸々実験したほうが頭に入ってきました。
CI/CDパイプラインってなんですか?
簡単にいうと、ソフトウェアデプロイメント観点でいうステージング、テスティング、デプロイの自動化及び効率化。
CIでデプロイ予定のコードでプロダクションアプリケーションに問題が出ないかのDevサーバ上での試験であったり、コードの事前チェックがフレームワークに沿って行われる。
CDでコードのデプロイ自体が自動化ツールによって行われる。
インフラやネットワークエンジニア観点でいうと、CIでシンタックスチェックや、コンフィグのステージングを行って、CDでAnsibleやSaltを使ってコンフィグをデプロイする、というなイメージです。
↑個人の解釈です。^^
Gitlab Runnerを使ってこういったワークフローを作っていきます。
やりかたとしては、リポ上の.gitlab-ci.yamlに指示を入れていくだけ。
GitlabのUIのエディターであれば、Lint機能もあるので、コミット前にエラー確認もできます。
CI/CDパイプラインのまとめ:
さて、今回のワークフローのおさらいです:
これをもとに.gitlab-ci.yamlをエディットしていきます。
適当にこんな感じにしました。
image: python:latest
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
ANSIBLE_HOST_KEY_CHECKING: 'false'
ANSIBLE_FORCE_COLOR: 'true'
cache:
paths:
- .cache/pip
- venv/
before_script:
- pip install virtualenv
- virtualenv venv
- source venv/bin/activate
- pip install -r requirements.txt
- ansible-galaxy collection install junipernetworks.junos
# Performs the sanity checks after every commit on non-main branch
sanity-check-job:
stage: test
script:
- echo "Performing sanity checks..."
- ansible --version
- ansible-galaxy collection list
- ansible-playbook prj001_homelabdeployment/stage.yaml -i prj001_homelabdeployment/inventory --syntax-check
- ansible-playbook prj001_homelabdeployment/main.yaml -i prj001_homelabdeployment/inventory --syntax-check
rules:
- if: '$CI_COMMIT_BRANCH != "main"'
# Performs the config staging once MR is created for non-main > main merge
config-stage-job:
stage: test
script:
- echo "Hello, $GITLAB_USER_LOGIN! The configuration is being tested"
- ansible --version
- ansible-playbook prj001_homelabdeployment/stage.yaml -i prj001_homelabdeployment/inventory
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main"'
# The config deployment is conducted once commit is made on main branch - syntax-check is there for accidental deployment
ansible-deployment-job:
stage: deploy
script:
- ansible-playbook prj001_homelabdeployment/main.yaml -i prj001_homelabdeployment/inventory --syntax-check
- ansible-playbook prj001_homelabdeployment/main.yaml -i prj001_homelabdeployment/inventory
- echo "Performing robot dance tests"
- robot prj001_homelabdeployment/test.robot
artifacts:
paths:
- /builds/$GITLAB_USER_LOGIN/ansible_cicd_test/report.html
- /builds/$GITLAB_USER_LOGIN/ansible_cicd_test/log.html
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
いくつか自分用のノーツ:
■Cache機能を使うことによってパッケージの再利用が可能。上例ではPypiのパッケージをキャッシングすることによってタスク実行を効率化する。
■Rules機能でパイプライン実行のコンディショナルをコントロールすることができる
■Runnerからサーバへの接続の際FQDNが使用される、今回はIPのみ使用したので、RunnerがDNS解決できずエラーになった。この場合、コンテナ自体のホストファイルに追記しても意味がないので、Runnerのコンフィグ上でホストの追記が必要
■Runnerからサーバへの接続の際、デフォルトでTLSが使用される。今回はTLSが確率できない(証明書のミスマッチによって)ので、Runnerコンフィグ上でTLSを無効化する必要があった
■Gitlab-Runnerコンフィグで問題が発生した場合、CI/CDパイプラインがフリーズする(エラーはないが、パイプラインがそもそも走らない)
Runner追記部分:
root@a347ecc11d4c:/# cat /etc/gitlab-runner/config.toml
concurrent = 1
check_interval = 0
[session_server]
session_timeout = 1800
[[runners]]
name = "471909eac0c1"
url = "https://192.168.0.50"
token = "VQw8pMJyVMzQi5zYZ_bP"
tls-ca-file = "/etc/gitlab-runner/certs/gitlab.example.com.crt"
executor = "docker"
** environment = ["GIT_SSL_NO_VERIFY=true"]**
[runners.custom_build_dir]
[runners.cache]
[runners.cache.s3]
[runners.cache.gcs]
[runners.cache.azure]
[runners.docker]
tls_verify = false
image = "python:latest"
privileged = false
disable_entrypoint_overwrite = false
oom_kill_disable = false
disable_cache = false
volumes = ["/cache"]
shm_size = 0
** extra_hosts = ["gitlab.example.com:192.168.0.50"]**
検証用のSRXに対して、この設定でCI/CDパイプラインにおけるコンフィグのデプロイが無事できました。
また、デプロイの実行後パイプラインの一環として、Robot Frameworkによるテスティングも行ってます。
Gitlab-ci.yaml上でのArtifacts部分でRobot Frameworkで実行試験結果をパイプラインからダウンロードできるようにもしてます。
試験結果のレポート一例
将来的な機能追加・疑問
★PIQからコンフィグテンプレへの自動展開(ユーザー向けフロントエンドとGitlabへのAPI)
★デプロイ後の機能的・コンプライアンス試験の自動化(の強化とスタンダード化)
★ネットワーク図面の自動化
★複数プロジェクトの場合、リポはどうスケールされる?(デザイン的に)
Discussion