🦊

GitlabのCI/CDによるネットワーク機器設定

2022/01/04に公開

こんちわ。
今回は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を登録する必要があります。
こちらもマニュアル通り行えば問題はありませんでした。
https://docs.gitlab.com/runner/register/index.html#docker

ただし、自分の場合はサーバーをFQDNとしてはなく、IPで登録を行ったので、SSL証明書関連で問題が発生しました。

TLS証明書のSAN設定
GitlabサーバーはデフォルトでSelf-signedの証明書を使用し、Runner登録自体のコネクションはSSLではなくてはだめ(昔は無効にできたみたいです)。
IPアドレスを使っているため証明書のSANにIPの登録がないとエラーになってしまいました。

ワークアラウンドとして、SANを含む、SSL証明書の再発行とGitlabサーバー上への再設定が必要でした。
https://nodeployfriday.com/posts/self-signed-cert/
https://stackoverflow.com/questions/65444621/gitlab-runner-ip-sans-issue-during-registration

https://stackoverflow.com/questions/65444621/gitlab-runner-ip-sans-issue-during-registration

登録の様子:

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パイプラインがフリーズする(エラーはないが、パイプラインがそもそも走らない)

https://gitlab.com/gitlab-org/gitlab-runner/-/issues/305
https://docs.gitlab.com/runner/configuration/advanced-configuration.html
https://gitlab.com/gitlab-org/gitlab-runner/-/issues/3661

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