12

从kube-prometheus定制k8s监控(三) 定制的原则和准备

 3 years ago
source link: https://studygolang.com/articles/30455
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

上一篇介绍了如何借助 kube-prometheus 生成的通用配置文件,对k8s进行最基础的监控。 一般情况下这是不够的,我们仍旧需要配置告警的发送渠道、增加额外的告警条件、持久化历史数据、对系统应用(如mysql,kafka)的监控和指标收集,对自有应用的监控等等。 从这一篇开始,逐步分享笔者在定制过程中的经验。

定制的原则

kube-prometheus 这个项目开发的其实是一个jsonnet的库,核心代码在 jsonnet/kube-prometheus 目录下, 其他文件大多是说明和例子。 通过jsonnet解释执行 example.jsonnet 这个入口,在 manifests/ 目录下生成之前提到的配置文件。

这个项目用到了很多上游的jsonnet库,比如:

完整列表在jsonnet/kube-prometheus/jsonnetfile.json`。

由于项目本身处于很早期,变化大,问题也不少,所以我们要做好时不时更新上游库的准备。 笔者就遇到过几次莫须有的告警,其实是上游 kubernetes-mixin 的告警策略不合理造成的。 好在几次问题修复都比较快,看到PR被合并后,用命令更新上游库,重新编译配置文件,再应用到集群中解决问题。

所以,定制要利用jsonnet的mixin,不是简单地修改 manifests/ 下生成的配置,也不能去修改库中的代码。 否则,每次更新库之后都需要重做修改。

关于jsonnet

定制绕不开要了解 jsonnet

jsonnet是google开发的模板语言(data templating language),可以定义和生成json。 jsonnet支持变量、函数、条件、运算、包管理、错误处理等,用来更方便的维护配置文件。 创造一个新的语言来解决工程问题,算是西方工程师的文化。

jsonnet有c++和golang两个版本, jsonnet-builder是jsonnet的包管理工具。

jsonnet虽然资料很少,但语言本身不复杂。3-4个小时就能大致读完官网提供的Tutorial和Standard Library。不过笔者并非专业coder,对这个语言运用的理解足足花了一两周的时间。 我理解的几个要点:

  • 语言采用mixin,而非传统面向对象的继承。维基百科mixin( https://en.wikipedia.org/wiki/Mixin ) 的解释大概是,一个类不需要继承就可以访问另一个类的方法。对jsonnet来说,我就简单理解成两个对象相加 { a: 1, b: 2 } + { b: 3, c: 4 } ,保留两边不冲突的元素,冲突的元素以 + 右边为准,结果是 { a: 1, b: 3, c:4}
  • "late bound"机制,即对元素引用时,引用的并不是当时的值,而是该元素后续做完所有mixin操作后的值。 这就代表你可以后续使用 + 操作修改变量中的某一个元素,对全局都有效。
  • 用双冒号 :: 定义的对象是隐藏的,不会出现在最终生成的json里。 主要用来定义一些有结构的变量。
  • 一点感受,如果库作者没有提供接口(变量),后期对array的删改挺不好做的。

编译环境

照着readme里 Customizing Kube-Prometheus 去做就行了。

安装golang运行环境

不细说了。

安装jsonnet-builder, jsonnet和gojsontoyaml

$ go get github.com/jsonnet-bundler/jsonnet-bundler/cmd/jb
$ brew install jsonnet
$ go get github.com/brancz/gojsontoyaml

克隆kube-prometheus库

$ mkdir kube-prometheus; cd kube-prometheus
$ jb init
$ jb install github.com/prometheus-operator/kube-prometheus/jsonnet/[email protected]

更新自身和依赖的库

$ jb update

所有库文件都保存在 vender/ 文件夹下。

编译

example.jsonnet 是定制的起点,我们先复制这个文件到你自己命名的项目文件,比如我们是 wk-mixin.jsonnet ,然后用jsonnet执行。

$ cp example.jsonnet wk-mixin.jsonnet
$ ./build.sh wk-mixin.jsonnet
+ set -o pipefail
++ pwd
+ PATH=/Users/Roger/Documents/Program/git/kube-prometheus/tmp/bin:/usr/local/opt/mysql-client/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/go/bin:/usr/local/opt/mysql-client/bin:/Users/Roger/Documents/Program/golib/bin:/usr/local/go/bin:/Users/Roger/Documents/Program/golib/bin
+ rm -rf manifests
+ mkdir -p manifests/setup
+ jsonnet -J vendor -m manifests wk-mixin.jsonnet
+ xargs '-I{}' sh -c 'cat {} | gojsontoyaml > {}.yaml; rm -f {}' -- '{}'

如果运行成功, manifests/ 目录下应该会生成很多yaml文件。 这代表你把基本编译环境跑通了。

如果你对这个定制项目使用版本管理, vendor/manifests/ 都是不需要提交的。

对示例的理解

在自己动手定制之前,我们先要理解当前的配置文件是如何生成出来的。

build.sh

我们先来看一下编译脚本 build.sh 。 关键部分只有一行:

jsonnet -J vendor -m manifests "${1-example.jsonnet}" | xargs -I{} sh -c 'cat {} | gojsontoyaml > {}.yaml' -- {}
  • -J 指定库文件的位置,这里只扫描 vendor/ 文件夹里的内容。 前面我们也提到了, jb update 会把所有库文件都保存在 vendor 下。
  • -m 用到了jsonnet的一个特性,它会把生成的json的最顶层键名作为文件名,值作为内容,写入多个文件,并把文件名输出到标准输出。 目标文件夹是 manifests/
  • "${1-example.jsonnet}" bash的特性,如果没有传入 $1 ,则把 example.jsonnet 作为输入。
  • xargs 这一段从pipe读取jsonnet输出的文件名,逐个用 gojsontoyaml 转格式,并写入加了 .yaml 后缀的文件。

example.jsonnet

再看 example.jsonnet 的内容(以 release-04 为例):

7bIZ3uZ.png!mobile

example.jsonnet.png

整个文件其实分两个部分:

  1. L1-14 定义了变量 kp , 你可以不断用 + 去添加和覆盖。
  2. L2(import xxx.libsonnet) 就是简单地把libsonet的内容替换到这个位置。
  3. L10-14 实际是一个替换操作。代码里约定俗成,所有用到namspace的地方,都会从 $_config.namespace 获取。 这里利用 + 和"late bound",等同于替换了所有引用namespace的地方。 另外,我们注意到 L11 中的 _config+:: 用的是双引号,代表_config这个对象,不会出现在最后json的结果中。
  4. L16-28 控制实际的输出。以 L23 为例,上面提到 build.sh 会用最顶层的键名作为文件名,这行输出的文件名是 ['node-exporter-' + name] , 其中 name 是通过 std.objectFields() 这个标准函数,循环读取某个键下元素名,这个例子里是 kp.nodeExporter 代表 kp 变量中 nodeExporter 下的元素。

有个要点,各个库习惯把可配置的变量写在 {_config:: {<组件名>: { ... } } } , 定制的时候用 + 直接修改。那么如何知道哪些变量可以设置呢?一种方法是读项目里提供的例子,另一种就是读库的源码。 另外,对某些设置,可能库并没有提供 _config 下的变量, 一般来说,这是库作者不推荐修改的,但是你仍旧可以利用jsonnet的特性去做,通常更复杂些。

如何开始定制?

针对已有组件的配置修改

在动手之前,推荐先阅读所有的例子,如果恰巧有匹配的,就照例子修改,主要是对变量 kp 的修改。这些例子包括:

  • README.mdCustomization Example
  • example/ 下的文件
  • jsonnet/kube-prometheus/ 下的所有libsonnet
  • 项目"issue"和"discussion"

监控其他通用系统

如果是基于已有组件的配置修改,大部分在例子里都能找到。 但如果是要添加新的监控对象,比如 kafka ,则需要做这些工作:

  • 选择exporter: 用于获取目标的metrics。 参考 Prometheus的官网 ,一些常用系统的exporter。
  • 定制dashboard: 可以从grafana的社区中找模板,自己修改。
  • 编写jsonnet生成相关的配置。 包括exporter的deployment, 设计serviceMonitor,设计告警规则,把dashboard加入grafana。

监控自有应用

自有应用需要自己编写全部内容。

  • 开发exporter: 开发一个接口。这里可以重用k8s的liveness和readness,让它输出 Prometheus的metrics的格式 。 这样,在监控目标健康状况的同时,收集metrics。
  • 创建dashboard: 自己用PromQL设计指标,在grafana中用设计呈现,再把设计好的dashboard导出成json供整合用。
  • 编写jsonnet生成相关的配置。

测试环境和生产环境

最后提一下环境的切换。 从测试的角度,配置修改都需要在测试环境运行通过后,再应用到生产环境中。 这两个环境不完全相同,比如域名,告警邮件地址,告警的钉钉群等等。 如何能方便的切换生成这两个环境的配置?分享一下我的经验:

  • 创建两个文件,比如 wk-env-lab.jsonwk-env-production.json ,把各环境不同变量分别定义在里面,比如
{
    "domain": ".example.wukongbox.cn",
    "email": "[email protected]",
    "ding": "000000000000000000000000000000000000000000000000000000"
}
  • 修改 build.sh 中执行jsonnet那行,增加 --ext-str env="production" ,即从命令行传入变量,这是jsonnet的功能。
  • 在wk-mixin.jsonnet中根据 env 判断要引入的哪个json文件,这样后续就可以通过 $._config.wukongbox.变量名 ,引用json里的值了。
local kp =
  ...
  {
    _config+:: {
    ...
      wukongbox: 
      // read env type
      local env = std.extVar("env");

      // loading envs from external json
      if env == 'production' then 
        (import 'wk-env-production.json')
      else 
        (import 'wk-env-lab.json'),
      ...
  • 复制 build.sh 到一个新的文件比如 build-test.sh ,修改成 --ext-str env="lab"
  • 这样执行 ./build.sh wk-mixin.jsonnet./build-test.sh wk-mixin.jsonnet 会对应生产和测试环境的配置。

后面的文章,对我们自己项目里曾经做过的定制,我会一个个来举例说明,希望对其他人有帮助。

有疑问加站长微信联系

iiUfA3j.png!mobile

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK