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

SongRijie · · 1425 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

上一篇介绍了如何借助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/kube-prometheus@release-0.4

更新自身和依赖的库

$ 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为例):

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": "example@wukongbox.com",
    "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会对应生产和测试环境的配置。

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


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:SongRijie

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

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1425 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传