Getting Started

Kuestaを動かしてみよう

このチュートリアルでは、以下のステップでKuestaを構築し、動作確認を行います。

  1. ローカルのKubernetesクラスターを作成する
  2. Kuestaをインストールする
  3. 装置エミュレータを立ち上げる
  4. 新しいServiceコンフィグを作成して、GitOpsを介して装置エミュレータに設定を投入する

事前準備

  1. golang (v1.20.4+)) をインストールしてください。カスタムコントローラーのツール類を使用するために必要です。
  2. docker (v20.10.24+) をインストールしてください。kindの実行に必要です。
  3. kind (v0.12.0+) をインストールしてください。ローカルのKubernetesクラスタの構築に使用します。
  4. kubectl (v1.25.4+) をインストールしてください。Kubernetesクラスタに対してコマンドを実行する際に使用します。
  5. cue (v0.4.x) をインストールしてください。Kuestaのインストールスクリプトの実行に必要です。
  6. gnmic (v0.26.0+) をインストールしてください。Kuestaサーバに対してgNMIリクエストを行うために必要です。

ローカルKubernetesクラスタの立ち上げ

以下のコマンドを実行して、ローカルのKubernetesクラスタを立ち上げます。

kind create cluster

実行が完了したら、以下のコマンドを実行して、Kubernetesクラスタが正常に作成されたことを確認します。

kubectl cluster-info

以下のような出力が表示されたら、ローカルクラスタは正常に動作しています。

Kubernetes control plane is running at https://127.0.0.1:52096
CoreDNS is running at https://127.0.0.1:52096/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

検証用のGithubレポジトリの作成

KuestaのGitOpsを用いてネットワーク装置に設定を行うには、2つのGitHubレポジトリを作成する必要があります。 1つ目はGitOpsの起点となるコンフィグ管理レポジトリで、2つ目はネットワーク装置の実際のコンフィグの変更履歴を管理するレポジトリです。 Kuesta Example レポジトリをテンプレートとして使用することで、これらのレポジトリを簡単に作成できます。 レポジトリ名は、小文字の英数字、’-‘または’.‘で構成され、英数字で始まり、英数字で終わる必要があります。

以下を2回実行して、2つのGitHubレポジトリを作成してください。

  1. Kuesta Example レポジトリの Use this template ボタンをクリックしてください。

  2. レポジトリ名を入力して、 Create repository from template ボタンをクリックしてください。レポジトリの公開・非公開については、好きな方を選択してください。

Kuestaと検証用サンプルリソースのインストール

Kuestaと、検証用のドライバオペレータや装置エミュレータなどのサンプルリソースをインストールするには、以下のコマンドを実行してください。

git clone https://github.com/nttcom/kuesta.git
cd kuesta/tool/install
cue install

プロンプトが表示されますので、指示に従ってパラメータを入力してください。 GitHub repository for config GitHub repository for statusは前項で作成した2つの検証用のGithubレポジトリをそれぞれ指定してください。

GitHub repository for config: https://github.com/<your_org>/<your_config_repo>
GitHub repository for status: https://github.com/<your_org>/<your_status_repo>
GitHub username: <your_username>
GitHub private access token: <your_private_access_token>
Are these git repositories private? (yes|no): no
Do you need sample driver and emulator for trial?: (yes|no) yes

---
GitHub Config Repository: https://github.com/<your_org>/<your_config_repo>
GitHub Status Repository: https://github.com/<your_org>/<your_status_repo>
GitHub Username: <your_username>
GitHub Access Token: ***
Use Private Repo: true

Image Registry: ghcr.io/nttcom/kuesta
Version: latest
Deploy sample driver and emulator: true
---

Continue? (yes|no) yes

KuestaはGitへのプッシュやプルリクエストの作成を行うため、GitHubの personal access token (PAT) が必要です(classic推奨)。 ここで入力されたPATは、ローカルのKubernetesクラスタ内のSecretリソースにのみ保存されます。ローカルのKubernetesクラスタを削除することで、どこにもデータを残さずに完全に削除できます。

インストールスクリプトの実行が完了したら、 kubectl コマンドを実行することで何がインストールされたかを確認できます。

1: Custom Resource Definitions(CRDs):

kubectl get customresourcedefinitions.apiextensions.k8s.io
NAME                                        CREATED AT
buckets.source.toolkit.fluxcd.io            2023-01-10T06:31:20Z
certificaterequests.cert-manager.io         2023-01-10T06:31:19Z
certificates.cert-manager.io                2023-01-10T06:31:19Z
challenges.acme.cert-manager.io             2023-01-10T06:31:19Z
clusterissuers.cert-manager.io              2023-01-10T06:31:19Z
devicerollouts.kuesta.hrk091.dev            2023-01-10T06:43:30Z
gitrepositories.source.toolkit.fluxcd.io    2023-01-10T06:31:20Z
helmcharts.source.toolkit.fluxcd.io         2023-01-10T06:31:20Z
helmrepositories.source.toolkit.fluxcd.io   2023-01-10T06:31:20Z
issuers.cert-manager.io                     2023-01-10T06:31:19Z
ocdemoes.kuesta.hrk091.dev                  2023-01-10T06:43:36Z
ocirepositories.source.toolkit.fluxcd.io    2023-01-10T06:31:20Z
orders.acme.cert-manager.io                 2023-01-10T06:31:19Z

kuesta.hrk091.devsource.toolkit.fluxcd.iocert-manager.io に属するCRDが追加されていることが確認できます。

2: Pods:

kubectl get pods -A | grep -v 'kube-system'
NAMESPACE                NAME                                                  READY   STATUS    RESTARTS   AGE
cert-manager             cert-manager-7475574-t7qlm                            1/1     Running   0          12m
cert-manager             cert-manager-cainjector-d5dc6cd7f-wbh6r               1/1     Running   0          12m
cert-manager             cert-manager-webhook-6868bd8b7-kc85s                  1/1     Running   0          12m
device-operator-system   device-operator-controller-manager-5d8648469b-57x4l   2/2     Running   0          11s
flux-system              source-controller-7ff779586b-wsgf7                    1/1     Running   0          12m
kuesta-getting-started   gnmi-fake-oc01-79d4d679b4-tvm78                       1/1     Running   0          10s
kuesta-getting-started   gnmi-fake-oc02-69d9cc664d-56bsz                       1/1     Running   0          10s
kuesta-getting-started   subscriber-oc01                                       1/1     Running   0          9s
kuesta-getting-started   subscriber-oc02                                       1/1     Running   0          9s
kuesta-system            kuesta-aggregator-7586999c47-jj7m4                    1/1     Running   0          27s
kuesta-system            kuesta-server-85fc4f8646-kq72c                        1/1     Running   0          27s
local-path-storage       local-path-provisioner-9cd9bd544-lk9w9                1/1     Running   0          16m
provisioner-system       provisioner-controller-manager-7675d487c8-w6f7x       1/2     Running   0          17s

全てのPodがRunningのステータスになっていたら、準備完了です。

以下の各namespaceに、Kuestaのコア機能として必要なサービスがデプロイされています。

  • kuesta-system
    • Kuestaのコア機能やAPIを提供するサーバ
  • provisioner-system
    • KuestaのKubernetesカスタムオペレータ。GitOpsやトランザクション管理に使用
  • flux-system
    • FluxCDのカスタムコントローラ
  • cert-manager
    • プライベートCAの認証局、およびmTLS用の証明書発行

また、以下のnamespaceには、本チュートリアル用のリソースがデプロイされています。

  • device-operator-system
    • チュートリアルで使用する装置エミュレータに対し設定投入を行うためのドライバとして振る舞うKubernetesカスタムオペレータ
  • kuesta-getting-started
    • チュートリアルで使用する装置エミュレータやエミュレータに設定を行うためのKubernetesカスタムリソースなど

以下の2つは、OpenConfig over gNMIで設定が可能な装置エミュレータです。本チュートリアルでは、 oc01oc02 の2つのエミュレータがデプロイされています。

kuesta-getting-started   gnmi-fake-oc01-79d4d679b4-tvm78                       1/1     Running   0          10s
kuesta-getting-started   gnmi-fake-oc02-69d9cc664d-56bsz                       1/1     Running   0          10s

ポートフォワーディングして gnmic からリクエストを行うことで設定されているコンフィグを確認できますので、必要に応じて実施してください。 以下のコマンドで、装置エミュレータ内のコンフィグの確認ができます。

# oc01のコンフィグを取得
kubectl -n kuesta-getting-started port-forward svc/gnmi-fake-oc01 9339:9339
gnmic -a :9339 -u admin -p admin get --path "" --encoding JSON --skip-verify

Serviceコンフィグを作成し、GitOpsを走らせる

Service は、ネットワークコンフィグを高レベルに抽象化したモデルです。Serviceを作成/変更することで、関連する複数のネットワーク装置にまとめて設定を投入することができます。

本チュートリアル用のサンプルレポジトリの中には、 oc-circuit という名のServiceが設定されていますので、これを用いて動作確認を行います。 ServiceはCUEで記述されます。例として、 oc-circuit Serviceの定義を以下に示します。

package oc_circuit

import (
	ocdemo "github.com/hrk091/kuesta-testdata/pkg/ocdemo"
)

#Input: {
	// kuesta:"key=1"
	vlanID: uint32
	endpoints: [...{
		device: string
		port:   uint32
	}]
}

#Template: {
	input: #Input

	output: devices: {
		for _, ep in input.endpoints {
			"\(ep.device)": config: {
				ocdemo.#Device

				let _portName = "Ethernet\(ep.port)"
				Interface: "\(_portName)": SubInterface: "\(input.vlanID)": {
					Ifindex:     ep.port
					Index:       input.vlanID
					Name:        "\(_portName).\(input.vlanID)"
					AdminStatus: 1
					OperStatus:  1
				}

				Vlan: "\(input.vlanID)": {
					VlanId: input.vlanID
					Name:   "VLAN\(input.vlanID)"
					Status: ocdemo.#Vlan_Status_ACTIVE
					Tpid:   ocdemo.#OpenconfigVlanTypes_TPID_TYPES_UNSET
				}
			}
		}
	}
}

oc-circuit Serviceは、#Template フィールドに定義された内容に従い、受け取った #Template.input(Serviceコンフィグ)を #Template.output (複数のネットワーク装置のコンフィグ)に変換します。 #Input の定義されているServiceのインターフェーススキーマを見ると、oc-circuit Serviceを使用するためには、 VLAN ID と複数のネットワーク装置のポートが必要なことがわかります。 これらを指定して oc-circuit Serviceを作成すると、 #Template のマッピング定義に従い、VLAN定義と指定したポートのVLANサブインターフェースが作成されます。

以下の手順にしたがって、oc-circuit Serviceを作成してください。

1: oc-circuit Serviceの作成をリクエストするために、以下のJSONファイルを oc-circuit-vlan100.json という名前で保存してください。 このServiceでは、oc01とoc02の両エミュレータに対して、ポート番号1のインターフェイスにVLAN ID=100のVLANサブインターフェースを作成することをリクエストしています。

{
    "vlanID": 100,
    "endpoints": [
        {
            "device": "oc01",
            "port": 1
        },
        {
            "device": "oc02",
            "port": 1
        }
    ]
}

httpインターフェースを利用する場合、上に代わって以下JSONを作成してください。(pathにはServiceのパス、valueには投入するServiceの値を指定)

{
    "path": "/services/oc_circuit/100",
    "value": {
        "vlanID": 100,
        "endpoints": [
            {
                "device": "oc01",
                "port": 1
            },
            {
                "device": "oc02",
                "port": 1
            }
        ]
    }
}

2: ローカルのKubernetesクラスタで動作しているKuestaサーバのPodに対してポートフォワーディングしてください。

kubectl -n kuesta-system port-forward svc/kuesta-server 9339:9339

httpインターフェースを利用する場合は以下です。

kubectl -n kuesta-system port-forward svc/kuesta-server 8080:8080

3: gnmicを用いて、Kuestaサーバに対してgNMI SetRequestを送ってください。

gnmic -a :9339 -u dummy -p dummy set --replace-path "/services/service[kind=oc_circuit][vlanID=100]" --encoding JSON --insecure --replace-file oc-circuit-vlan100.json

httpインターフェースを利用する場合は、curlを用いてKuestaサーバに対して以下POST Requestを送ってください。

curl -X POST -H "Content-Type: application/json" -d @oc-circuit-vlan100.json http://localhost:8080/set

4: GitHubのWebコンソールを用いて、本チュートリアル向けに作成したコンフィグ用のGitHubレポジトリのプルリクエスト(PullRequest: PR)を確認してください。 PR一覧 にアクセスすると、 [kuesta] Automated PR というタイトルのPRが確認できます。 PRのコメントを見ると、どのServiceとどのネットワーク装置が変更されたのかがわかりますし、 Files changed タブを確認すると詳細な変更点が確認できます。

5: PRブランチをマージする前に、ローカルのKubernetesクラスターで DeviceRollout リソースをモニターしてください。 DeviceRollout リソースは、Gitレポジトリからプルしたコンフィグを各ネットワーク装置に設定する際の状態管理を行っています。以下のコマンドを実行してください。

kubectl -n kuesta-getting-started get devicerollouts.kuesta.hrk091.dev -w

DeviceRollout リソースが一つ表示され、その STATUSCompleted になっていれば、GitOpsによるデリバリプロセスを流す準備は完了です。

NAME              PHASE     STATUS
kuesta-testdata   Healthy   Completed

6: GitHubのWebコンソール上で、PRをマージしてください。その後、 DeviceRollout をモニターしているターミナル画面に移動してください。 1分以内に、 DeviceRolloutSTATUSRunning に変わり、その後 Completed に変わることを確認してください。

NAME              PHASE     STATUS
kuesta-testdata   Healthy   Completed
kuesta-testdata   Healthy   Completed
kuesta-testdata   Healthy   Running
kuesta-testdata   Healthy   Running
kuesta-testdata   Healthy   Running
kuesta-testdata   Healthy   Completed

7: 装置コンフィグが本当に更新されたのかを確認します。 gnmic を用いて、gNMI GetRequestをKuestaサーバに対して送り、装置コンフィグ全体を取得してください。

gnmic -a :9339 -u admin -p admin get --path "/devices/device[name=oc01]" --path "/devices/device[name=oc02]" --encoding JSON --insecure

httpインターフェースを利用する場合は、curlを用いてKuestaサーバに対して以下POST Requestを送ってください。

curl -X POST -H "Content-Type: application/json" -d '{"paths": ["/devices/oc01", "/devices/oc02"]}'  http://localhost:8080/get

以下のようなレスポンスが表示されます。

[
  {
    "source": ":9339",
    "timestamp": 1673360105000000000,
    "time": "2023-01-10T23:15:05+09:00",
    "updates": [
      {
        "Path": "devices/device[name=oc01]",
        "values": {
          "devices/device": {
            "Interface": {
              "Ethernet1": {
                "AdminStatus": 1,
                "Description": "awesome interface!!",
                "Enabled": false,
                "Mtu": 9000,
                "Name": "Ethernet1",
                "OperStatus": 1,
                "Subinterface": {
                  "100": {
                    "AdminStatus": 1,
                    "Ifindex": 1,
                    "Index": 100,
                    "Name": "Ethernet1.100",
                    "OperStatus": 1
                  },
                  ...
                },
                ...
              },
              ...
            },
            "Vlan": {
              "100": {
                "Member": [],
                "Name": "VLAN100",
                "Status": 1,
                "Tpid": 0,
                "VlanId": 100
              },
              ...

httpインターフェースを使用した場合のレスポンスは以下です。

[
    {
        "Interface": {
            "Ethernet1": {
                "AdminStatus": 1,
                "Description": "",
                "Enabled": false,
                "Mtu": 9000,
                "Name": "Ethernet1",
                "OperStatus": 1,
                "SubInterface": {
                    "100": {
                        "AdminStatus": 1,
                        "Ifindex": 1,
                        "Index": 100,
                        "Name": "Ethernet1.100",
                        "OperStatus": 1
                    }
                },
                "Subinterface": {},
                "Type": 80
            },
            "Ethernet2": {
                "AdminStatus": 1,
                "Description": "",
                "Enabled": false,
                "Mtu": 9000,
                "Name": "Ethernet2",
                "OperStatus": 1,
                "Subinterface": {},
                "Type": 80
            },
            "Ethernet3": {
                "AdminStatus": 1,
                "Description": "",
                "Enabled": false,
                "Mtu": 9000,
                "Name": "Ethernet3",
                "OperStatus": 1,
                "Subinterface": {},
                "Type": 80
            }
        },
        "Vlan": {
            "100": {
                "Member": [],
                "Name": "VLAN100",
                "Status": 1,
                "Tpid": 0,
                "VlanId": 100
            }
        }
    },
    {
        "Interface": {
            "Ethernet1": {
                "AdminStatus": 1,
                "Description": "",
                "Enabled": false,
                "Mtu": 9000,
                "Name": "Ethernet1",
                "OperStatus": 1,
                "SubInterface": {
                    "100": {
                        "AdminStatus": 1,
                        "Ifindex": 1,
                        "Index": 100,
                        "Name": "Ethernet1.100",
                        "OperStatus": 1
                    }
                },
                "Subinterface": {},
                "Type": 80
            },
            "Ethernet2": {
                "AdminStatus": 1,
                "Description": "",
                "Enabled": false,
                "Mtu": 9000,
                "Name": "Ethernet2",
                "OperStatus": 1,
                "Subinterface": {},
                "Type": 80
            },
            "Ethernet3": {
                "AdminStatus": 1,
                "Description": "",
                "Enabled": false,
                "Mtu": 9000,
                "Name": "Ethernet3",
                "OperStatus": 1,
                "Subinterface": {},
                "Type": 80
            }
        },
        "Vlan": {
            "100": {
                "Member": [],
                "Name": "VLAN100",
                "Status": 1,
                "Tpid": 0,
                "VlanId": 100
            }
        }
    }
]

レスポンスを確認すると、oc01とoc02の両方の装置エミュレータに対して、VLAN ID=100のVLAN定義とVLANサブインターフェースが設定されていることが分かります。 また、これらの装置コンフィグの変更内容はチュートリアル向けに作成したステータス用のGitレポジトリに対してコミット・プッシュされています。ステータス用のGitレポジトリのメインブランチのHEADを見ると、変更が保存されていることが確認できます。

ローカルKubernetesクラスタの削除

このチュートリアルで作成したローカルのKubernetesクラスタを削除するには、以下のコマンドを実行してください。

kind delete cluster

プライベートレポジトリのアクセス用に作成したPATは、不要であれば忘れずに削除しましょう。