前回はQuickstartに従ってkube-prometheusを動かすところまでやってみました。今回はjsonnetを使ったカスタマイズをやってみたいと思います。
前回はこちら。
目次
jsonnet環境の構築
jsonnetは、JSONを出力するためのテンプレート言語およびツールです。
そしてjsonnetを使うためのgo製のパッケージマネージャーとしてjsonnet-bundlerがあります。
kube-prometheusでmanifestのカスタマイズを行うにはGo+Jsonnet+jsonnet-bundlerが必要になりますので、まずはこれをインストールしましょう。
まずGo。
$ wget https://golang.org/dl/go1.15.7.linux-amd64.tar.gz $ sudo tar -C /usr/local -zxf go1.15.7.linux-amd64.tar.gz $ cat <<'EOF' >> ~/.bashrc export GOPATH=$HOME/go PATH=$PATH:/usr/local/go/bin:$HOME/go/bin EOF $ source .bashrc
次にJsonnet+jsonnet-bundler。ついでにjsonをyamlに変換するgojsontoyamlも入れておきましょう。
$ go get github.com/google/go-jsonnet/cmd/jsonnet $ GO111MODULE="on" go get github.com/jsonnet-bundler/jsonnet-bundler/cmd/jb $ go get github.com/brancz/gojsontoyaml
jsonnetことはじめ
少しだけjsonnetに慣れておきましょう。jsonnetが動作することを確認しておいてください。
$ jsonnet --version Jsonnet commandline interpreter (Go implementation) v0.17.0
先ほども言いましたが、jsonnetは、JSONを出力するためのテンプレート言語およびツールであり、JSONでめんどくさいところを改善しつつ、より柔軟な記述が可能となっています。
以下のようなファイルを"fabfour.jsonnet"として作成します。
// description of fabfour { fabfour: [ { id: 1, name: "John", role: "Backing Guitar", }, { id: 2, name: "Paul", role: "Bass Guitar", }, { id: 3, name: "George", role: "Lead Guitar", }, { id: 4, name: "Ringo", role: "Drums", }, ] }
jsonnetコマンドで実行します。
$ jsonnet fabfour.jsonnet { "fabfour": [ { "id": 1, "name": "John", "role": "Backing Guitar" }, { "id": 2, "name": "Paul", "role": "Bass Guitar" }, { "id": 3, "name": "George", "role": "Lead Guitar" }, { "id": 4, "name": "Ringo", "role": "Drums" } ] }
JSONで怒られるコメントやケツカンマも気にすることなく、またキーをクォートせずに書けるなど、便利になっているのがわかるでしょうか。
さらに以下のように書くこともできます。
// description of fabfour local member = ["John", "Paul", "George", "Ringo"]; local role = ["Backing Guitar", "Bass Guitar", "Lead Guitar", "Drums"]; local concat(m,r) = m + " is " + r; { fabfour: [ { id: i + 1, name: member[i], role: role[i], comment: concat(member[i], role[i]) } for i in std.range(0, std.length(member)-1) ] }
実行するとこういう感じになります。
$ jsonnet fabfour.jsonnet { "fabfour": [ { "comment": "John is Backing Guitar", "id": 1, "name": "John", "role": "Backing Guitar" }, { "comment": "Paul is Bass Guitar", "id": 2, "name": "Paul", "role": "Bass Guitar" }, { "comment": "George is Lead Guitar", "id": 3, "name": "George", "role": "Lead Guitar" }, { "comment": "Ringo is Drums", "id": 4, "name": "Ringo", "role": "Drums" } ] }
変数や関数を定義したり、数値や文字列の操作、制御構文も可能ということですね!
Jsonnet+jsonnet-bundlerを使ったkube-prometheusプロジェクトのカスタマイズ
ではJsonnet+jsonnet-bundlerを使って、カスタマイズされたkube-prometheusプロジェクトを作成していきましょう。
ディレクトリを作成します。
$ mkdir my-kube-prometheus; cd my-kube-prometheus
jsonnet-bundlerのjbコマンドでプロジェクトを初期化します。
$ jb init
これにより、jsonnetfile.jsonが作成されます。中身はこんな感じです。
{ "version": 1, "dependencies": [], "legacyImports": true }
jb installコマンドでkube-prometheusパッケージを追加します。ここでもリリース番号を指定している点に注意してください。以降の説明の中でレポジトリを参照する箇所が複数回出てきますが、全てrelease-0.6のレポジトリを参照しています。適宜読み替えてください。
$ jb install github.com/coreos/kube-prometheus/jsonnet/kube-prometheus@release-0.6 GET https://github.com/coreos/kube-prometheus/archive/f69ff3d63de17f3f52b955c3b7e0d7aff0372873.tar.gz 200 GET https://github.com/coreos/prometheus-operator/archive/cd331ce9bb58bb926e391c6ae807621cb12cc29e.tar.gz 200 GET https://github.com/ksonnet/ksonnet-lib/archive/0d2f82676817bbf9e4acf6495b2090205f323b9f.tar.gz 200 GET https://github.com/kubernetes-monitoring/kubernetes-mixin/archive/a161500608ac2ca8908f2c318bd929ecd5e20415.tar.gz 200 GET https://github.com/kubernetes/kube-state-metrics/archive/89aaf6c524ee891140c4c8f2a05b1b16f5847309.tar.gz 200 GET https://github.com/prometheus/prometheus/archive/983ebb4a513302315a8117932ab832815f85e3d2.tar.gz 200 GET https://github.com/brancz/kubernetes-grafana/archive/57b4365eacda291b82e0d55ba7eec573a8198dda.tar.gz 200 GET https://github.com/coreos/etcd/archive/92458228e1268e9b78e11abeeb255824b44d0b2f.tar.gz 200 GET https://github.com/kubernetes/kube-state-metrics/archive/89aaf6c524ee891140c4c8f2a05b1b16f5847309.tar.gz 200 GET https://github.com/prometheus/node_exporter/archive/88ee42742e1a947c91a328dfe47e6ea3c3fbd5da.tar.gz 200 GET https://github.com/grafana/grafonnet-lib/archive/b0d72d6ed0e9fcab83fc2dd954b3bd57113e768c.tar.gz 200 GET https://github.com/grafana/jsonnet-libs/archive/e03ce6c81f2dc4b147d138737e7cced476c966cd.tar.gz 200 GET https://github.com/kubernetes-monitoring/kubernetes-mixin/archive/3d4f68cead695d2717d606f80246ffcfd7b1f08b.tar.gz 200
これにより、プロジェクトフォルダ内にパッケージが追加され、jsonnet.lock.jsonおよびjsonnet.lock.jsonが更新されます。npmっぽい感じですね。
$ ls jsonnetfile.json jsonnetfile.lock.json vendor $ ls vendor etcd-mixin ksonnet node-mixin github.com kube-prometheus prometheus grafana kube-state-metrics prometheus-operator grafana-builder kube-state-metrics-mixin promgrafonnet grafonnet kubernetes-mixin $ cat jsonnetfile.json { "version": 1, "dependencies": [ { "source": { "git": { "remote": "https://github.com/coreos/kube-prometheus.git", "subdir": "jsonnet/kube-prometheus" } }, "version": "release-0.6" } ], "legacyImports": true } $ cat jsonnetfile.lock.json { "version": 1, "dependencies": [ { "source": { "git": { "remote": "https://github.com/brancz/kubernetes-grafana.git", "subdir": "grafana" } }, "version": "57b4365eacda291b82e0d55ba7eec573a8198dda", "sum": "92DWADwGjnCfpZaL7Q07C0GZayxBziGla/O03qWea34=" }, (snip)
基本となるbase.jsonnetファイルを作成します。サンプルのjsonnetファイルから作成するのがよいようです。https://github.com/prometheus-operator/kube-prometheus/blob/release-0.6/example.jsonnet
local kp = (import 'kube-prometheus/kube-prometheus.libsonnet') + // Uncomment the following imports to enable its patches // (import 'kube-prometheus/kube-prometheus-anti-affinity.libsonnet') + // (import 'kube-prometheus/kube-prometheus-managed-cluster.libsonnet') + // (import 'kube-prometheus/kube-prometheus-node-ports.libsonnet') + // (import 'kube-prometheus/kube-prometheus-static-etcd.libsonnet') + // (import 'kube-prometheus/kube-prometheus-thanos-sidecar.libsonnet') + // (import 'kube-prometheus/kube-prometheus-custom-metrics.libsonnet') + { _config+:: { namespace: 'monitoring', }, }; { ['setup/0namespace-' + name]: kp.kubePrometheus[name] for name in std.objectFields(kp.kubePrometheus) } + { ['setup/prometheus-operator-' + name]: kp.prometheusOperator[name] for name in std.filter((function(name) name != 'serviceMonitor'), std.objectFields(kp.prometheusOperator)) } + // serviceMonitor is separated so that it can be created after the CRDs are ready { 'prometheus-operator-serviceMonitor': kp.prometheusOperator.serviceMonitor } + { ['node-exporter-' + name]: kp.nodeExporter[name] for name in std.objectFields(kp.nodeExporter) } + { ['kube-state-metrics-' + name]: kp.kubeStateMetrics[name] for name in std.objectFields(kp.kubeStateMetrics) } + { ['alertmanager-' + name]: kp.alertmanager[name] for name in std.objectFields(kp.alertmanager) } + { ['prometheus-' + name]: kp.prometheus[name] for name in std.objectFields(kp.prometheus) } + { ['prometheus-adapter-' + name]: kp.prometheusAdapter[name] for name in std.objectFields(kp.prometheusAdapter) } + { ['grafana-' + name]: kp.grafana[name] for name in std.objectFields(kp.grafana) }
これを環境に合わせてカスタマイズしていきます。カスタマイズの内容については以下を参考に。
https://github.com/prometheus-operator/kube-prometheus/tree/release-0.6#configuration
https://github.com/prometheus-operator/kube-prometheus/tree/release-0.6#customization-examples
とりあえず冒頭の箇所だけ軽く修正して試してみましょう。kubeadm+各ダッシュボードをnodeportに変更+prometheusとalertmanagerのpodのnode分離を有効にしました。
local kp = (import 'kube-prometheus/kube-prometheus.libsonnet') + (import 'kube-prometheus/kube-prometheus-kubeadm.libsonnet') + (import 'kube-prometheus/kube-prometheus-node-ports.libsonnet') + (import 'kube-prometheus/kube-prometheus-anti-affinity.libsonnet') + (snip)
ビルド用のスクリプト(https://github.com/prometheus-operator/kube-prometheus/blob/release-0.6/build.sh)を使ってビルドします。
#!/usr/bin/env bash # This script uses arg $1 (name of *.jsonnet file to use) to generate the manifests/*.yaml files. set -e set -x # only exit with zero if all commands of the pipeline exit successfully set -o pipefail # Make sure to use project tooling PATH="$(pwd)/tmp/bin:${PATH}" # Make sure to start with a clean 'manifests' dir rm -rf manifests mkdir -p manifests/setup # Calling gojsontoyaml is optional, but we would like to generate yaml, not json jsonnet -J vendor -m manifests "${1-example.jsonnet}" | xargs -I{} sh -c 'cat {} | gojsontoyaml > {}.yaml' -- {} # Make sure to remove json files find manifests -type f ! -name '*.yaml' -delete rm -f kustomization
$ ./build.sh base.libsonnet
ビルドするとmanifestディレクト以下にマニフェストが出力されます。
$ tree manifests manifests ├── alertmanager-alertmanager.yaml ├── alertmanager-secret.yaml ├── alertmanager-service.yaml ├── alertmanager-serviceAccount.yaml ├── alertmanager-serviceMonitor.yaml ├── grafana-dashboardDatasources.yaml ├── grafana-dashboardDefinitions.yaml ├── grafana-dashboardSources.yaml ├── grafana-deployment.yaml ├── grafana-service.yaml ├── grafana-serviceAccount.yaml ├── grafana-serviceMonitor.yaml ├── kube-state-metrics-clusterRole.yaml ├── kube-state-metrics-clusterRoleBinding.yaml ├── kube-state-metrics-deployment.yaml ├── kube-state-metrics-service.yaml ├── kube-state-metrics-serviceAccount.yaml ├── kube-state-metrics-serviceMonitor.yaml ├── node-exporter-clusterRole.yaml ├── node-exporter-clusterRoleBinding.yaml ├── node-exporter-daemonset.yaml ├── node-exporter-service.yaml ├── node-exporter-serviceAccount.yaml ├── node-exporter-serviceMonitor.yaml ├── prometheus-adapter-apiService.yaml ├── prometheus-adapter-clusterRole.yaml ├── prometheus-adapter-clusterRoleAggregatedMetricsReader.yaml ├── prometheus-adapter-clusterRoleBinding.yaml ├── prometheus-adapter-clusterRoleBindingDelegator.yaml ├── prometheus-adapter-clusterRoleServerResources.yaml ├── prometheus-adapter-configMap.yaml ├── prometheus-adapter-deployment.yaml ├── prometheus-adapter-roleBindingAuthReader.yaml ├── prometheus-adapter-service.yaml ├── prometheus-adapter-serviceAccount.yaml ├── prometheus-adapter-serviceMonitor.yaml ├── prometheus-clusterRole.yaml ├── prometheus-clusterRoleBinding.yaml ├── prometheus-kubeControllerManagerPrometheusDiscoveryService.yaml ├── prometheus-kubeSchedulerPrometheusDiscoveryService.yaml ├── prometheus-operator-serviceMonitor.yaml ├── prometheus-prometheus.yaml ├── prometheus-roleBindingConfig.yaml ├── prometheus-roleBindingSpecificNamespaces.yaml ├── prometheus-roleConfig.yaml ├── prometheus-roleSpecificNamespaces.yaml ├── prometheus-rules.yaml ├── prometheus-service.yaml ├── prometheus-serviceAccount.yaml ├── prometheus-serviceMonitor.yaml ├── prometheus-serviceMonitorApiserver.yaml ├── prometheus-serviceMonitorCoreDNS.yaml ├── prometheus-serviceMonitorKubeControllerManager.yaml ├── prometheus-serviceMonitorKubeScheduler.yaml ├── prometheus-serviceMonitorKubelet.yaml └── setup ├── 0namespace-namespace.yaml ├── prometheus-operator-0alertmanagerCustomResourceDefinition.yaml ├── prometheus-operator-0podmonitorCustomResourceDefinition.yaml ├── prometheus-operator-0probeCustomResourceDefinition.yaml ├── prometheus-operator-0prometheusCustomResourceDefinition.yaml ├── prometheus-operator-0prometheusruleCustomResourceDefinition.yaml ├── prometheus-operator-0servicemonitorCustomResourceDefinition.yaml ├── prometheus-operator-0thanosrulerCustomResourceDefinition.yaml ├── prometheus-operator-clusterRole.yaml ├── prometheus-operator-clusterRoleBinding.yaml ├── prometheus-operator-deployment.yaml ├── prometheus-operator-service.yaml └── prometheus-operator-serviceAccount.yaml 1 directory, 68 files
あとはこれを適用するだけです。
$ kubectl apply -f manifests/setup $ kubectl apply -f manifests/
確認してみましょう。
$ kubectl get all -n monitoring NAME READY STATUS RESTARTS AGE pod/alertmanager-main-0 2/2 Running 0 4m37s pod/alertmanager-main-1 2/2 Running 0 4m37s pod/alertmanager-main-2 2/2 Running 0 4m37s pod/grafana-67dfc5f687-qsqlq 1/1 Running 0 4m37s pod/kube-state-metrics-69d4c7c69d-gf945 3/3 Running 0 4m37s pod/node-exporter-j7r9d 2/2 Running 0 4m37s pod/node-exporter-jcbqv 2/2 Running 0 4m37s pod/node-exporter-kxzgq 2/2 Running 0 4m37s pod/node-exporter-qjhs8 2/2 Running 0 4m37s pod/prometheus-adapter-66b855f564-ss446 1/1 Running 0 4m37s pod/prometheus-k8s-0 3/3 Running 1 4m36s pod/prometheus-k8s-1 3/3 Running 1 4m36s pod/prometheus-operator-75c98bcfd7-d242v 2/2 Running 0 4m47s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/alertmanager-main NodePort 10.106.156.38 <none> 9093:30903/TCP 4m40s service/alertmanager-operated ClusterIP None <none> 9093/TCP,9094/TCP,9094/UDP 4m37s service/grafana NodePort 10.103.252.223 <none> 3000:30902/TCP 4m39s service/kube-state-metrics ClusterIP None <none> 8443/TCP,9443/TCP 4m38s service/node-exporter ClusterIP None <none> 9100/TCP 4m38s service/prometheus-adapter ClusterIP 10.111.27.139 <none> 443/TCP 4m38s service/prometheus-k8s NodePort 10.105.218.239 <none> 9090:30900/TCP 4m36s service/prometheus-operated ClusterIP None <none> 9090/TCP 4m36s service/prometheus-operator ClusterIP None <none> 8443/TCP 4m48s NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE daemonset.apps/node-exporter 4 4 4 4 4 kubernetes.io/os=linux 4m38s NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/grafana 1/1 1 1 4m40s deployment.apps/kube-state-metrics 1/1 1 1 4m39s deployment.apps/prometheus-adapter 1/1 1 1 4m39s deployment.apps/prometheus-operator 1/1 1 1 4m49s NAME DESIRED CURRENT READY AGE replicaset.apps/grafana-67dfc5f687 1 1 1 4m40s replicaset.apps/kube-state-metrics-69d4c7c69d 1 1 1 4m39s replicaset.apps/prometheus-adapter-66b855f564 1 1 1 4m39s replicaset.apps/prometheus-operator-75c98bcfd7 1 1 1 4m49s NAME READY AGE statefulset.apps/alertmanager-main 3/3 4m38s statefulset.apps/prometheus-k8s 2/2 4m37s
nodeportで上がっているので、それぞれアクセスしてみます。
とりあえず動いているようですね。
まとめ
Jsonnetの基本的な使い方と、jsonnet-bundlerを使ったkube-prometheusのカスタマイズの流れを一通り抑えました。kube-prometheusのjsonnetファイル、見てるとちょっと避けたい感があったのですがw、ちょっと和らいできたかな?という感じです。
とはいえ、ほとんどカスタマイズできてないし、また環境ごとに出力を分けたい、とかもあるので、次回以降はそのあたりをやっていきたいと思います。