当使用jsonnet-builder自定义kube-prometheus时的处理备忘录
首先
kube-prometheus以便捷的Operator形式提供,用于在k8s集群中运行prometheus、grafana和alertmanager。单独使用Prometheus Operator需要同时运行Grafana等周边应用程序,因此能够一键部署它们确实非常方便。
但是网络策略被严格设置,基本上只允许部署的Pod之间进行通信。
不論是從外部網頁瀏覽器進行存取還是從指定的網頁瀏覽器進行通訊,都只記載了使用 port-forward 的方式。
我将详细记录经过Ingress访问Grafana的方式并进行了各种自定义的情况,重点是关于在jsonnet中的备忘录。
本文首先记录了一种关于通过更改运行时来访问Grafana的默认配置的临时方法。然后介绍了使用jsonnet-builder自定义配置信息并部署包含Grafana的其他更改的方法。
环境
本番系集群
-
- Kubernetes v1.25.6 (Kubespray v2.21.0)
- kube-prometheus v0.12.0
验证集群
-
- Kubernetes v1.27.5 (Kubespray v2.23.0)
- kube-prometheus v0.13.0
jsonnet、gojson2yaml和jb命令已被放置在~/go/bin/的@latest位置,并已注册到PATH环境变量中。
引入方式
按照README.md中的“Quick Start”部分进行操作。
没有特殊步骤,只需保留以供确认。
$ git clone https://github.com/prometheus-operator/kube-prometheus.git
$ cd kube-prometheus
## Compatibility Matrixを参照し、適切なtagをcheckoutする
$ git checkout refs/tags/v0.12.0 -b my_v0.12.0
$ sudo kubectl apply --server-side -f manifests/setup
$ sudo kubectl wait \
--for condition=Established \
--all CustomResourceDefinition \
--namespace=monitoring
$ sudo kubectl apply -f manifests/
探索如何应对课题和Grafana方面的配合。
由于Grafana的默认状态部署在顶层目录(‘/’)上,我不知道如何更改context-root以便在/subpath类似于/grafana/的路径中访问。
根据Grafana的指南,可以通过更改context-root来修改,但相关信息似乎很少。我将尝试根据Grafana指南,通过ConfigMap来进行修改。
- https://grafana.com/tutorials/run-grafana-behind-a-proxy/
为了寻找线索,首先会查看Pod中的配置文件情况等。
奇迹般地,Grafana的Pod似乎可以正常使用诸如bash和ls等实用程序命令。
在进行各种搜索后,可以发现/etc/grafana/grafana.ini存在,并且可以确认从定义中将secret/grafana-config映射进来。
## Podの中に移動 + 探索
$ sudo kubectl -n monitoring exec -it grafana-9f58f8675-4n957 -- bash
bash-5.1$ ls /etc/grafana/
grafana.ini provisioning
## /etc/grafana がどうやって設定されているかDeploymentの定義を確認
$ sudo kubectl -n monitoring get deploy grafana -o yaml
...
- mountPath: /etc/grafana
name: grafana-config
...
- name: grafana-config
secret:
defaultMode: 420
secretName: grafana-config
...
## Secretオブジェクトから grafana-config の内容を確認
$ sudo kubectl -n monitoring get secret grafana-config -o yaml
apiVersion: v1
data:
grafana.ini: W2RhdGVfZm9ybWF0c10KZGVmYXVsdF90aW1lem9uZSA9IFVUQwo=
kind: Secret
...
由于它似乎是Base64编码的, 所以我们将对其进行解码以确认内容。
$ echo "W2RhdGVfZm9ybWF0c10KZGVmYXVsdF90aW1lem9uZSA9IFVUQwo=" | base64 -d
[date_formats]
default_timezone = UTC
向现有的grafana.ini文件内容中添加[server]节的配置文件。
$ cat <<EOF | base64
[date_formats]
default_timezone = UTC
[server]
domain = grafana.example.com
root_url = %(protocol)s://%(domain)s:%(http_port)s/grafana/
serve_from_sub_path = true
EOF
W2RhdGVfZm9ybWF0c10KZGVmYXVsdF90aW1lem9uZSA9IFVUQwpbc2VydmVyXQpkb21haW4gPSBn
cmFmYW5hLmV4YW1wbGUuY29tCnJvb3RfdXJsID0gJShwcm90b2NvbClzOi8vJShkb21haW4pczol
KGh0dHBfcG9ydClzL2dyYWZhbmEvCnNlcnZlX2Zyb21fc3ViX3BhdGggPSB0cnVlCg==
在该域中,指定了一个启用了TLS和HTTP/2的Nginx代理服务器的名称。
特别是在不使用此类代理服务器的情况下,应指定用于Web浏览器的主机名(分配给Ingress的IP地址等)。
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
data:
grafana.ini: |
W2RhdGVfZm9ybWF0c10KZGVmYXVsdF90aW1lem9uZSA9IFVUQwpbc2VydmVyXQpkb21haW4gPSBn
cmFmYW5hLmV4YW1wbGUuY29tCnJvb3RfdXJsID0gJShwcm90b2NvbClzOi8vJShkb21haW4pczol
KGh0dHBfcG9ydClzL2dyYWZhbmEvCnNlcnZlX2Zyb21fc3ViX3BhdGggPSB0cnVlCg==
kind: Secret
...
編集后应该会以明文编辑,然后编码成Base64,但是由于更改了context-root,所以只重新启动grafana的Pod是无法通过ReadinessProbe的。我们需要修改Deployment对象而不重新启动,来编辑/api/health的部分。
$ sudo kubectl -n monitoring edit deploy grafana
更改正在访问的API路径。
...
readinessProbe:
failureThreshold: 3
httpGet:
path: /grafana/api/health
port: http
scheme: HTTP
...
当您保存并退出时,Pod 将自动重新启动。Reconsile 机制真的很方便。
从Ingress连接到Grafana
实际通过Web浏览器访问是通过具有全局IP的行指定的服务器名称的nginx进行HTTPS连接,并通过这个服务器进行代理到http://192.168.110.79/。
我将在这里总结有关Ingress的更改,包括初始设置。
Ingress的初始设置
如果已经使用Ingress,请只需添加后续的Ingress对象等即可,所以这一部分可以跳过。仅供参考,假设您已经使用kubespray等工具启用了Ingress Controller,我将提供Ingress的初始设置文件。
由于没有任何IngressClass,因此预先定义ingressclass/nginx以供稍后在Ingress对象的定义中使用。
---
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: nginx
spec:
controller: k8s.io/ingress-nginx
为了通过Web浏览器访问,需要分配一个静态地址。
k8s正在运行的192.168.110.0/24是内部网络,Web浏览器所在的网络。
---
apiVersion: v1
kind: Service
metadata:
name: ingress-nginx-controller
namespace: ingress-nginx
spec:
type: LoadBalancer
loadBalancerIP: "192.168.110.79"
ports:
- name: http
port: 80
targetPort: http
selector:
app.kubernetes.io/name: ingress-nginx
通过这些设置,您现在可以通过Web浏览器从192.168.110.79连接到Ingress。
创建Ingress对象
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
labels:
group: ingress-nginx
namespace: ingress-nginx
spec:
ingressClassName: nginx
rules:
- http:
paths:
- backend:
service:
name: monitoring-grafana-svc
port:
number: 3000
path: /grafana
pathType: Prefix
用名称“monitoring-grafana-svc”指定Service对象的名字进行定义。
---
apiVersion: v1
kind: Service
metadata:
name: monitoring-grafana-svc
labels:
group: ingress-nginx
namespace: ingress-nginx
spec:
type: ExternalName
externalName: grafana.monitoring.svc.cluster.local
网络策略的修改
尽管尝试从Ingress连接到Grafana,仍然会出现503错误。
正如前文所述,由于网络策略较严格,需要额外设置权限以允许从Ingress访问。
我会检查当前的网络策略。
在中国,原生地,有许多选项可以重新表述这段话。这是其中的一个选项:
以以下方式输出:$ sudo kubectl -n monitoring get networkpolicy grafana -o yaml。
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
spec:
egress:
- {}
ingress:
- from:
- podSelector:
matchLabels:
app.kubernetes.io/name: prometheus
ports:
- port: 3000
protocol: TCP
podSelector:
matchLabels:
app.kubernetes.io/component: grafana
app.kubernetes.io/name: grafana
app.kubernetes.io/part-of: kube-prometheus
policyTypes:
- Egress
- Ingress
Prometheus Pod和Grafana Pod之间的连接被允许,但其他访问被禁止。
我們將使用namespaceSelector來允許來自Ingress的連接。
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
spec:
egress:
- {}
ingress:
- from:
- podSelector:
matchLabels:
app.kubernetes.io/name: prometheus
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- port: 3000
protocol: TCP
podSelector:
matchLabels:
app.kubernetes.io/component: grafana
app.kubernetes.io/name: grafana
app.kubernetes.io/part-of: kube-prometheus
policyTypes:
- Egress
- Ingress
如果在podSelector和namespaceSelector这两个都加上’-‘并行排列,则成为”OR”条件。在这个例子中,如果不给第二个元素加上”-“,则会被合并为[{ podSelector: { matchLabels: … }, namespaceSelector: { matchLabels: … } }],结果会被处理为”AND”条件。
目前为止,您可以通过https://grafana.example.com/grafana/ 进入控制台。
另一种解决方案是网络策略的另一种解放。
通过增加表单块也可以获得相同的结果。
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
spec:
egress:
- {}
ingress:
- from:
- podSelector:
matchLabels:
app.kubernetes.io/name: prometheus
ports:
- port: 3000
protocol: TCP
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- port: 3000
protocol: TCP
podSelector:
matchLabels:
app.kubernetes.io/component: grafana
app.kubernetes.io/name: grafana
app.kubernetes.io/part-of: kube-prometheus
policyTypes:
- Egress
- Ingress
Grafana UI的默认密码是什么?
这个环境下运行的Grafana的默认账号(ID, 密码)是(admin, admin)。
这是按照Grafana官方文档所示。
到目前为止,我们已经成功连接到了Grafana的用户界面。
使用jsonnet-builder自定义kube-prometheus的准备工作。
在部署后编辑Secret和Deployment对象的方法很简单,作为初步步骤并没有问题,但是kube-prometheus似乎并不这样认为。
如果要使用kube-prometheus中预期的jsonnet-builder来生成自定义的定义文件,需要事先进行以下准备工作。
事先使用go命令引入jsonnet命令。
$ go install -a github.com/jsonnet-bundler/jsonnet-bundler/cmd/jb@latest
$ go install github.com/brancz/gojsontoyaml@latest
$ go install github.com/google/go-jsonnet/cmd/jsonnet@latest
安装的命令将位于~/go/bin/目录中。
添加到PATH环境变量的方法有很多种,但是如果在Ubuntu上,建议使用名为~/.bash_aliases的默认存在的别名配置文件来加载。
如果直接编辑~/.bashrc文件,并且/etc/skel/.bashrc在升级等操作中发生变化,会导致需要合并修改点,增加额外的工作量。而使用~/.bash_aliases,只需要从/etc/skel/复制即可。
在customizing.md文档中介绍了生成文件的步骤,但我们将在v0.12.0分支上进行操作。
允许来自Ingress的通信连接到Grafana的自定义示例1.
为了以声明性的方式执行刚刚直接更改运行时的内容,需要修改 examples/kustomize.jsonnet 文件,并生成已更改的 YAML 文件。
添加 namespaceSelector
我們會進行以下更改。
请注意值不是以并列的形式,而是以一个点的方式呈现。
local kp =
(import 'kube-prometheus/main.libsonnet') +
(import 'kube-prometheus/addons/managed-cluster.libsonnet') +
{
values+:: {
...
},
// 以下のブロックを追加
grafana+: {
networkPolicy+: {
spec+: {
ingress+: [{
from: [{
namespaceSelector: {
matchLabels: {
'name': 'ingress-nginx',
},
},
}],
ports: [{
port: 3000,
protocol: "TCP",
}],
}],
},
},
},
};
编辑grafana.ini文件
将context_root更改为”变更”,并将更改内容添加到grafana.ini以便使用子路径。
local kp =
(import 'kube-prometheus/main.libsonnet') +
(import 'kube-prometheus/addons/managed-cluster.libsonnet') +
{
values+:: {
common+: {
namespace: 'monitoring',
},
grafana+:: {
config+: {
sections+: {
server: { domain: 'grafana.examples.com', root_url: '%(protocol)s://%(domain)s:%(http_port)s/grafana/', serve_from_sub_pa
th: true },
},
},
},
},
};
更改ReadinessProbe的路径
我将按照以下方式进行编辑。
local kp =
(import 'kube-prometheus/main.libsonnet') +
(import 'kube-prometheus/addons/managed-cluster.libsonnet') +
{
values+:: {
...
},
grafana+: {
networkPolicy+: {
...
},
// 以下のブロックを追加
deployment+: {
spec+: {
template+: {
spec+: {
containers: std.map(
function(container)
if container.name == "grafana" then
container {
readinessProbe+: {
httpGet+: {
path: "/grafana/api/health"
},
},
}
else
container,
super.containers
),
},
},
},
},
},
};
请注意,事先在 vendor/grafana/grafana.libsonnet 中准备好的 local defaults = containers 变量可以用于添加新的附属容器等其他镜像的一套容器,但无法修改与现有 Grafana 容器相关的设置(例如 env)。
麻烦的是容器:由于containers是一个数组,所以不能使用类似containers+:的表示来更改对象的元素,必须使用std.map()和super.containers来将函数应用于各个元素。
如果我们假设数组只有一个元素的话,那么if-else语句就不再需要,代码会更加简洁。不过考虑到将来可能出现意外情况,使用这种写法会更加安全,避免出现未预料的结果。
如果要删除if〜else语句,则在super.containers之前需要一个}。在此处列出的else语句前的{没有逗号。仅仅简单地删除或添加if语句将导致错误。jsonnet是一种相当复杂的语言,难以掌握。
对ChatGPT的应用
由于我之前从未接触过Jsonnet,所以在这方面我进行了许多尝试和困惑,最终通过以下步骤,找到了前面提到的方法。
- https://chat.openai.com/share/85e6eee2-1b26-47fe-b879-b7e01872476a
尽管我认为如果没有最初的试错,可能就无法达到目标,但如果指示恰当的话,也确实有很多方便的情况。
允许来自adapter的通信的网络策略/prometheus-k8s。
在v0.12.0版本中,NetworkPolicy的设置不完整,使得适配器无法与prometheus主体进行通信并被阻断。
我将如下进行编辑。
local kp =
(import 'kube-prometheus/main.libsonnet') + {
values+:: {
common+: {
...
},
},
prometheus+: {
networkPolicy+: {
spec+: {
ingress+: [{
from: [{
podSelector: {
matchLabels: {
'app.kubernetes.io/name': 'prometheus-adapter',
},
},
}],
ports: [{
port: 9090,
protocol: "TCP",
}],
}],
},
},
},
};
生成Manifests和Manifests的差异
最后,请使用Makefile来生成YAML文件。
$ rm -rf manifests
$ make generate
生成的 manifests/grafana-networkPolicy.yaml 文件如下所示,与手动修改的文件相同。
diff --git a/manifests/grafana-networkPolicy.yaml b/manifests/grafana-networkPolicy.yaml
index cab676c8..a259b618 100644
--- a/manifests/grafana-networkPolicy.yaml
+++ b/manifests/grafana-networkPolicy.yaml
@@ -16,6 +16,9 @@ spec:
- podSelector:
matchLabels:
app.kubernetes.io/name: prometheus
+ - namespaceSelector:
+ matchLabels:
+ name: ingress-nginx
ports:
- port: 3000
protocol: TCP
以下是grafana.ini的内容。
diff --git a/manifests/grafana-config.yaml b/manifests/grafana-config.yaml
index 10d9c6a9..beab186a 100644
--- a/manifests/grafana-config.yaml
+++ b/manifests/grafana-config.yaml
@@ -12,4 +12,8 @@ stringData:
grafana.ini: |
[date_formats]
default_timezone = UTC
+ [server]
+ domain = grafana.example.com
+ root_url = %(protocol)s://%(domain)s:%(http_port)s/grafana/
+ serve_from_sub_path = true
type: Opaque
grafana-deployment.yaml已经被这样配置。
diff --git a/manifests/grafana-deployment.yaml b/manifests/grafana-deployment.yaml
index eca41b6e..60fa54a6 100644
--- a/manifests/grafana-deployment.yaml
+++ b/manifests/grafana-deployment.yaml
@@ -18,7 +18,7 @@ spec:
template:
metadata:
annotations:
- checksum/grafana-config: adbde4cde1aa3ca57c408943af53e6f7
+ checksum/grafana-config: c9018225d844e770a4c521083fe592b9
checksum/grafana-dashboardproviders: d8fb24844314114bed088b83042b1bdb
checksum/grafana-datasources: 0800bab7ea1e2d8ad5c09586d089e033
labels:
@@ -37,7 +37,7 @@ spec:
name: http
readinessProbe:
httpGet:
- path: /api/health
+ path: /grafana/api/health
port: http
resources:
limits:
在应用了之前的更改并更新了manifests/文件夹后,$ git status的输出将如下所示。
On branch my_v0.12.0
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: examples/kustomize.jsonnet
modified: jsonnet/kube-prometheus/components/grafana.libsonnet
modified: manifests/grafana-config.yaml
modified: manifests/grafana-deployment.yaml
modified: manifests/grafana-networkPolicy.yaml
no changes added to commit (use "git add" and/or "git commit -a")
升级的方法与常规安装方法相同。
【自定义示例2】删除所有NetworkPolicy
如果您指定 examples/networkpolicies-disabled.jsonnet 作为 examples/kustomize.jsonnet 的替代,则与 NetworkPolicy 相关的文件将不会输出到 manifests/,因为 examples/networkpolicies-disabled.jsonnet 已经准备好了。
diff --git a/Makefile b/Makefile
index 8a438b21..6cf226a0 100644
--- a/Makefile
+++ b/Makefile
@@ -39,7 +39,7 @@ check-docs: $(MDOX_BIN) $(shell find examples) build.sh example.jsonnet
.PHONY: generate
generate: manifests
-manifests: examples/kustomize.jsonnet $(GOJSONTOYAML_BIN) vendor
+manifests: examples/networkpolicies-disabled.jsonnet $(GOJSONTOYAML_BIN) vendor
./build.sh $<
vendor: $(JB_BIN) jsonnetfile.json jsonnetfile.lock.json
这种方法需要编辑文件,包括之前的grafana.ini的更改,所以有点麻烦。
毫無疑問地,我們可以利用 examples/kustomize.jsonnet,按照以下方式進行編輯以融入 networkpolicies-disabled 的功能。
diff --git a/examples/kustomize.jsonnet b/examples/kustomize.jsonnet
index c12f98b3..5186d9b6 100644
--- a/examples/kustomize.jsonnet
+++ b/examples/kustomize.jsonnet
@@ -1,9 +1,17 @@
local kp =
- (import 'kube-prometheus/main.libsonnet') + {
+ (import 'kube-prometheus/main.libsonnet') +
+ (import 'kube-prometheus/addons/networkpolicies-disabled.libsonnet') + {
values+:: {
common+: {
namespace: 'monitoring',
},
+ grafana+:: {
+ config+: {
+ sections+: {
+ "server": {domain: "grafana.example.com", root_url: "%(protocol)s://%(domain)s:%(http_port)s/grafana/", serve_from_sub_path:
true},
+ },
+ },
+ },
},
};
如果在方法2中反映之前的更改,则git status的输出将如下所示。
$ git status
On branch my_v0.12.0
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: examples/kustomize.jsonnet
modified: jsonnet/kube-prometheus/components/grafana.libsonnet
modified: kustomization.yaml
deleted: manifests/alertmanager-networkPolicy.yaml
deleted: manifests/blackboxExporter-networkPolicy.yaml
deleted: manifests/grafana-networkPolicy.yaml
deleted: manifests/kubeStateMetrics-networkPolicy.yaml
deleted: manifests/nodeExporter-networkPolicy.yaml
deleted: manifests/prometheus-networkPolicy.yaml
deleted: manifests/prometheusAdapter-networkPolicy.yaml
deleted: manifests/prometheusOperator-networkPolicy.yaml
no changes added to commit (use "git add" and/or "git commit -a")
指定发送警报邮件的目标。
在测试环境中没有必要发送电子邮件,但在生产环境中,我们尽可能地想要尽早发现异常情况。
根据严重程度发送电子邮件。
这个需求是自然而然可以预料到的,所以变更很容易完成。
local kp =
(import 'kube-prometheus/main.libsonnet') + {
values+:: {
common+: {
namespace: 'monitoring',
},
alertmanager+:: {
config+: {
global+: {
resolve_timeout: '5m',
smtp_smarthost: 'smtp.example.com:25',
smtp_from: 'grafana@example.com',
},
receivers: [
{
name: 'Critical',
email_configs: [{
to: 'admin@example.com',
}],
},
{ name: 'Default', },
{ name: 'Watchdog', },
{ name: 'null', },
],
},
},
},
},
};
如果发生了被分类为严重警报的情况,将发送电子邮件。
停止KubeSchedulerDown和KubeControllerManagerDown的警报。
在某些环境中,对于kube-scheduler和kube-controller-manager的监控可能不适当。
由于这些被分类为关键信息,一旦您发送电子邮件,您可能会定期收到警告邮件。
目前,在 jsonnet/kube-prometheus/addons 中有一个方便的定义来停止这个。所以我们需要在 examples/kustomize.jsonnet 中添加以下 import 语句。
local kp =
(import 'kube-prometheus/main.libsonnet') +
(import 'kube-prometheus/addons/managed-cluster.libsonnet') +
{
只需添加一行,但请注意{ 和 +。
根据diff的结果,如下所示。 diff de , .)
diff --git a/examples/kustomize.jsonnet b/examples/kustomize.jsonnet
index 04f151ad..ee33e27e 100644
--- a/examples/kustomize.jsonnet
+++ b/examples/kustomize.jsonnet
@@ -1,5 +1,7 @@
local kp =
- (import 'kube-prometheus/main.libsonnet') + {
+ (import 'kube-prometheus/main.libsonnet') +
+ (import 'kube-prometheus/addons/managed-cluster.libsonnet') +
+ {
values+:: {
common+: {
namespace: 'monitoring',
如果您将生成的YAML文件通过`kubectl apply -f manifests`应用后,警报将消失。但如果您还是担心,可能可以考虑删除不再需要的ServiceMonitor对象。
$ kubectl -n monitoring delete servicemonitor kube-controller-manager
$ kubectl -n monitoring delete servicemonitor kube-scheduler
希望停止node-exporter的CPUThrottlingHigh警报。
由于严重性为“info”,因此不会发送电子邮件,但如果您有任何关注的问题,可以进行以下自定义。
请根据具体环境的变化情况,将资源的指定值调整为原值的2倍、3倍等进行观察。
diff --git a/examples/kustomize.jsonnet b/examples/kustomize.jsonnet
index 0cfc8d3a..687c4d15 100644
--- a/examples/kustomize.jsonnet
+++ b/examples/kustomize.jsonnet
@@ -12,6 +14,32 @@ local kp =
},
},
},
+ nodeExporter+:: {
+ resources+: {
+ requests+: { cpu: "306m", },
+ limits+: { cpu: "750m", },
+ },
+ },
+ blackboxExporter+:: {
+ resources+: {
+ requests+: { cpu: "80m", },
+ limits+: { cpu: "160m", },
+ },
+ },
+ kubeStateMetrics+:: {
+ kubeRbacProxyMain+:: {
+ resources+: {
+ requests+: { cpu: "80m", },
+ limits+: { cpu: "160m", },
+ },
+ },
+ kubeRbacProxySelf+:: {
+ resources+: {
+ requests+: { cpu: "40m", },
+ limits+: { cpu: "80m", },
+ },
+ },
+ },
},
};
【自定义示例6】希望消除Rook/Ceph文件系统中的inode警报
Rook/Ceph所提供的storageclass/rook-cephfs是一种可通过ReadWriteMany方式访问的存储功能,但是尽管inodes被动态分配,一旦写入了一定数量的文件,就会达到上限。
由于这个原因,df -i命令的输出显示inode使用率达到100%,触发了警报。
为了解决KubePersistentVolumeInodesFillingUp规则的问题,我们可以通过对规则进行即席编辑来确定解决方案。
unless on(namespace, persistentvolumeclaim)
kube_persistentvolumeclaim_info{storageclass="rook-cephfs"} == 1
这个规则有两个上限,一个是每1米,另一个是每1小时,因此我们必须区分它们并分别更新规则。
为了实现这一变更,我们对 examples/kustomize.jsonnet 进行了如下修改。
diff --git a/examples/kustomize.jsonnet b/examples/kustomize.jsonnet
index c12f98b3..09141738 100644
--- a/examples/kustomize.jsonnet
+++ b/examples/kustomize.jsonnet
@@ -5,6 +5,34 @@ local kp =
namespace: 'monitoring',
},
},
+ kubernetesControlPlane+: {
+ prometheusRule+: {
+ spec+: {
+ groups: std.map(
+ function(group)
+ if group.name == 'kubernetes-storage' then
+ group {
+ rules: std.map(
+ function(rule)
+ if rule.alert == 'KubePersistentVolumeInodesFillingUp' then
+ rule {
+ expr+: |||
+ unless on(namespace, persistentvolumeclaim)
+ kube_persistentvolumeclaim_info{storageclass="rook-cephfs"} == 1
+ |||
+ }
+ else
+ rule,
+ super.rules
+ ),
+ }
+ else
+ group,
+ super.groups
+ ),
+ },
+ },
+ },
};
local manifests =
我们也会检查由make generate生成的YAML文件的差异。
diff --git a/manifests/kubernetesControlPlane-prometheusRule.yaml b/manifests/kubernetesControlPlane-prometheusRule.yaml
index ce0fefd6..0dd03e65 100644
--- a/manifests/kubernetesControlPlane-prometheusRule.yaml
+++ b/manifests/kubernetesControlPlane-prometheusRule.yaml
@@ -440,6 +440,8 @@ spec:
kube_persistentvolumeclaim_access_mode{ access_mode="ReadOnlyMany"} == 1
unless on(namespace, persistentvolumeclaim)
kube_persistentvolumeclaim_labels{label_excluded_from_alerts="true"} == 1
+ unless on(namespace, persistentvolumeclaim)
+ kube_persistentvolumeclaim_info{storageclass="rook-cephfs"} == 1
for: 1m
labels:
severity: critical
@@ -465,6 +467,8 @@ spec:
kube_persistentvolumeclaim_access_mode{ access_mode="ReadOnlyMany"} == 1
unless on(namespace, persistentvolumeclaim)
kube_persistentvolumeclaim_labels{label_excluded_from_alerts="true"} == 1
+ unless on(namespace, persistentvolumeclaim)
+ kube_persistentvolumeclaim_info{storageclass="rook-cephfs"} == 1
for: 1h
labels:
severity: warning
参考文献
kube-prometheus中的变更示例是添加了新规则,以及修改默认规则中的一部分,这需要一些巧妙的操作。以下资料可以作为参考。
- https://groups.google.com/g/prometheus-users/c/ha6wLqFlFTc
【个性化示例7】想要添加Rook/Ceph的度量指标和仪表板。
我们正在以下环境中进行验证。
- Rook/Ceph v1.11.10 (Ceph v17.2.6、namespace: rook-ceph)
在Rook/Ceph中的配置设置
rook的Git存储库包含了用于监控的YAML文件等。
-
- rook/deploy/examples/monitoring/service-monitor.yaml
- rook/deploy/examples/monitoring/localrules.yaml
这两个YAML文件应该设置在namespace: rook-ceph中。
由于YAML文件中已经指定了namespace: rook-ceph,因此不需要进行编辑。
$ sudo kubectl -n rook-ceph apply -f rook/deploy/examples/monitoring/service-monitor.yaml
$ sudo kubectl -n rook-ceph apply -f rook/deploy/examples/monitoring/localrules.yaml
这样就可以在Prometheus上收集指标了。
$ sudo kubectl -n rook-ceph get ServiceMonitor rook-ceph-mgr
$ sudo kubectl -n rook-ceph get PrometheusRule prometheus-ceph-rules
Prometheus的配置在这一侧
Grafana Dashboard的定义在以下官方网站上提供链接。
- https://rook.io/docs/rook/latest/Storage-Configuration/Monitoring/ceph-monitoring/#grafana-dashboards
我将使用您可以从这里下载的下一个文件。
-
- 2842_rev17.json (Ceph – Cluster)
-
- 5336_rev9.json (Ceph – OSD (Single))
- 5342_rev9.json (Ceph – Pools)
将这些文件作为名为 namespace: monitoring 的ConfigMap对象进行注册。
$ sudo kubectl -n monitoring create configmap grafana-dashboard-ceph-cluster --from-file=2842_rev17.json
$ sudo kubectl -n monitoring create configmap grafana-dashboard-ceph-osd --from-file=5336_rev9.json
$ sudo kubectl -n monitoring create configmap grafana-dashboard-ceph-pools --from-file=5342_rev9.json
在Kube-Prometheus的部分中,我们将在examples/kustomize.jsonnet中进行以下更改。
local kp =
(import 'kube-prometheus/main.libsonnet') +
(import 'kube-prometheus/addons/managed-cluster.libsonnet') +
{
values+:: {
common+: {
namespace: 'monitoring',
},
prometheus+: {
namespaces+: ['rook-ceph'],
},
},
grafana+: {
networkPolicy+: {
// 省略
},
deployment+: {
spec+: {
template+: {
spec+: {
containers: std.map(
function(container)
if container.name == "grafana" then
container {
readinessProbe+: {
httpGet+: {
path: "/grafana/api/health"
},
},
volumeMounts+: [
{
mountPath: "/grafana-dashboard-definitions/0/ceph-cluster",
name: "grafana-dashboard-ceph-cluster",
readOnly: false,
},
{
mountPath: "/grafana-dashboard-definitions/0/ceph-osd",
name: "grafana-dashboard-ceph-osd",
readOnly: false,
},
{
mountPath: "/grafana-dashboard-definitions/0/ceph-pool",
name: "grafana-dashboard-ceph-pools",
readOnly: false,
},
],
}
else
container,
super.containers
),
volumes+: [
{
configMap: {
name: "grafana-dashboard-ceph-cluster",
},
name: "grafana-dashboard-ceph-cluster",
},
{
configMap: {
name: "grafana-dashboard-ceph-osd",
},
name: "grafana-dashboard-ceph-osd",
},
{
configMap: {
name: "grafana-dashboard-ceph-pools",
},
name: "grafana-dashboard-ceph-pools",
},
],
},
},
},
},
},
};
编辑 examples/kustomize.jsonnet 后,通过执行 make generate 命令更新 manifests/,然后使用 kube apply 命令进行应用。
无需重新启动Pod,只需等待一段时间,然后访问Grafana的仪表盘界面,即可显示已添加的三个面板。
修改localrules.yaml文件
为了将以下警告变为无害,我修改了localrules.yaml文件中的severity并将其修正为info。
-
- CephNodeNetworkPacketDrops
-
- CephNodeNetworkPacketErrors
- CephNodeInconsistentMTU
修改 rook/deploy/examples/monitoring/localrules.yaml 并使用 kube apply 命令即可立即生效。
现在整个使用中的 examples/kustomize.jsonnet 文件
以下是我目前正在使用的 kustomize.jsonnet 文件供您参考。
local kp =
(import 'kube-prometheus/main.libsonnet') +
(import 'kube-prometheus/addons/managed-cluster.libsonnet') + {
values+:: {
common+: {
namespace: 'monitoring',
},
nodeExporter+:: {
resources+: {
requests+: { cpu: "500m", },
limits+: { cpu: "1000m", },
},
},
blackboxExporter+:: {
resources+: {
requests+: { cpu: "80m", },
limits+: { cpu: "160m", },
},
},
kubeStateMetrics+:: {
resources+: {
requests+: { cpu: "50m", },
limits+: { cpu: "300m", },
},
kubeRbacProxyMain+:: {
resources+: {
requests+: { cpu: "120m", },
limits+: { cpu: "320m", },
},
},
kubeRbacProxySelf+:: {
resources+: {
requests+: { cpu: "120m", },
limits+: { cpu: "320m", },
},
},
},
grafana+:: {
resources+: {
requests+: { cpu: "300m", },
limits+: { cpu: "600m", },
},
config+: {
sections+: {
server: { domain: 'admin.example.com', root_url: '%(protocol)s://%(domain)s:%(http_port)s/grafana/', serve_from_sub_path: true }
,
"auth.proxy": { enabled: true, auto_sign_up: true, header_name: "X-WEBAUTH-USER", header_property: "username" },
},
},
},
alertmanager+:: { [121/1857]
config+: {
global+: {
resolve_timeout: '5m',
smtp_smarthost: 'smarthost1.example.com:25',
smtp_from: 'admin-dev@example.com',
smtp_require_tls: false,
},
receivers: [
{
name: 'Critical',
email_configs: [{
to: 'admin-dev@example.com',
require_tls: false,
}],
},
{
name: 'Warning',
email_configs: [{
to: 'admin-dev@example.com',
require_tls: false,
}],
},
{ name: 'Default', },
{ name: 'Watchdog', },
{ name: 'null', },
],
},
},
prometheus+: {
namespaces+: ['rook-ceph'],
},
},
grafana+: {
networkPolicy+: {
spec+: {
ingress+: [{
from: [{
namespaceSelector: {
matchLabels: {
'name': 'ingress-nginx',
},
},
}],
ports: [{
port: 3000,
protocol: "TCP",
}],
}],
},
},
deployment+: { [70/1857]
spec+: {
template+: {
spec+: {
containers: std.map(
function(container)
if container.name == "grafana" then
container {
readinessProbe+: {
httpGet+: {
path: "/grafana/api/health"
},
},
volumeMounts+: [
{
mountPath: "/grafana-dashboard-definitions/0/ceph-cluster",
name: "grafana-dashboard-ceph-cluster",
readOnly: false,
},
{
mountPath: "/grafana-dashboard-definitions/0/ceph-osd",
name: "grafana-dashboard-ceph-osd",
readOnly: false,
},
{
mountPath: "/grafana-dashboard-definitions/0/ceph-pool",
name: "grafana-dashboard-ceph-pools",
readOnly: false,
},
],
}
else
container,
super.containers
),
volumes+: [
{
configMap: {
name: "grafana-dashboard-ceph-cluster",
},
name: "grafana-dashboard-ceph-cluster",
},
{
configMap: {
name: "grafana-dashboard-ceph-osd",
},
name: "grafana-dashboard-ceph-osd",
},
{
configMap: {
name: "grafana-dashboard-ceph-pools",
},
name: "grafana-dashboard-ceph-pools",
},
],
},
},
},
},
},
prometheus+: { [10/1857]
networkPolicy+: {
spec+: {
ingress+: [{
from: [{
podSelector: {
matchLabels: {
'app.kubernetes.io/name': 'prometheus-adapter',
},
},
}],
ports: [{
port: 9090,
protocol: "TCP",
}],
}],
},
},
},
kubernetesControlPlane+: {
prometheusRule+: {
spec+: {
groups: std.map(
function(group)
if group.name == 'kubernetes-storage' then
group {
rules: std.map(
function(rule)
if rule.alert == 'KubePersistentVolumeInodesFillingUp' then
rule {
expr+: |||
unless on(namespace, persistentvolumeclaim)
kube_persistentvolumeclaim_info{storageclass="rook-cephfs"} == 1
|||
}
else
rule,
super.rules
),
}
else
group,
super.groups
),
},
},
},
};
local manifests =
{
['setup/' + resource]: kp[component][resource]
for component in std.objectFields(kp)
for resource in std.filter(
function(resource)
kp[component][resource].kind == 'CustomResourceDefinition' || kp[component][resource].kind == 'Namespace', std.objectFields(kp[compo
nent])
)
} +
{
[component + '-' + resource]: kp[component][resource]
for component in std.objectFields(kp)
for resource in std.filter(
function(resource)
kp[component][resource].kind != 'CustomResourceDefinition' && kp[component][resource].kind != 'Namespace', std.objectFields(kp[compo
nent])
)
};
local kustomizationResourceFile(name) = './manifests/' + name + '.yaml';
local kustomization = {
apiVersion: 'kustomize.config.k8s.io/v1beta1',
kind: 'Kustomization',
resources: std.map(kustomizationResourceFile, std.objectFields(manifests)),
};
manifests {
'../kustomization': kustomization,
}
因为我们还没有解释require_tls:false的设置,但是即使没有这个设置,您也可以连接到smarthost的25号端口并发送电子邮件而无需身份验证,所以这是不必要的。
最后
使用jsonnet-builder对kube-prometheus进行定制是我第一次尝试,与后来进行更改相比确实非常困难。我仍然对选择的方法是否合适感到困惑。
考虑到之后发送的警报邮件以及删除KubeSchedulerDown警报,我认为使用jsonnet进行定制是合适的方法。
尽管我认为jsonnet是一种方便的工具,但与其固守于这项技术并使用jsonnet进行所有更改,不如灵活选择变更方式更好。
我还记得以前,在选择Linux的RHEL系发行版时,试图创建和注册自己制作的应用程序的RPM时遇到了很大的困难。即使技术本身很好,但我们也需要考虑在其中投入的时间与其目的和内容之间的平衡。
我认为jsonnet是一项值得学习的技术,但感觉它有一种像m4宏一样难以应付的氛围。
要熟练使用,需要阅读 https://jsonnet.org/ 的文档,并且需要一定程度上理解其内部处理的方法。
以上 – all the above