[TOC]
参考
- https://github.com/helm/charts/tree/master/stable/prometheus-operator
- https://github.com/coreos/prometheus-operator
- https://www.servicemesher.com/blog/prometheus-operator-manual/
- https://blog.csdn.net/travellersY/article/details/84632679
Kubernetes Operator 介绍
Operator
是由CoreOS公司开发的,用来扩展 Kubernetes API,特定的应用程序控制器,它用来创建、配置和管理复杂的有状态应用,如数据库、缓存和监控系统。Operator
基于 Kubernetes 的资源和控制器概念之上构建,但同时又包含了应用程序特定的一些专业知识,比如创建一个数据库的Operator
,则必须对创建的数据库的各种运维方式非常了解,创建Operator
的关键是CRD
(自定义资源)的设计。
CRD
是对 Kubernetes API 的扩展,Kubernetes 中的每个资源都是一个 API 对象的集合,例如我们在YAML文件里定义的那些spec
都是对 Kubernetes 中的资源对象的定义,所有的自定义资源可以跟 Kubernetes 中内建的资源一样使用 kubectl 操作。
Operator
是将运维人员对软件操作的知识给代码化,同时利用 Kubernetes 强大的抽象来管理大规模的软件应用。目前CoreOS
官方提供了几种Operator
的实现,其中就包括我们今天的主角:Prometheus Operator
,Operator
的核心实现就是基于 Kubernetes 的以下两个概念:
- 资源:对象的状态定义
- 控制器:观测、分析和行动,以调节资源的分布
当然我们如果有对应的需求也完全可以自己去实现一个Operator
,接下来我们就来给大家详细介绍下Prometheus-Operator
的使用方法。
Prometheus Operator介绍
Kubernetes的Prometheus Operator为Kubernetes服务和Prometheus实例的部署和管理提供了简单的监控定义。
安装完毕后,Prometheus Operator提供了以下功能:
- 创建/毁坏: 在Kubernetes namespace中更容易启动一个Prometheus实例,一个特定的应用程序或团队更容易使用Operator。
- 简单配置: 配置Prometheus的基础东西,比如在Kubernetes的本地资源versions, persistence, retention policies, 和replicas。
- Target Services通过标签: 基于常见的Kubernetes label查询,自动生成监控target 配置;不需要学习普罗米修斯特定的配置语言。
Prometheus Operator 架构图如下:
以上架构中的各组成部分以不同的资源方式运行在 Kubernetes 集群中,它们各自有不同的作用:
- Operator: Operator 资源会根据自定义资源(Custom Resource Definition / CRDs)来部署和管理 Prometheus Server,同时监控这些自定义资源事件的变化来做相应的处理,是整个系统的控制中心。
- Prometheus: Prometheus 资源是声明性地描述 Prometheus 部署的期望状态。
- Prometheus Server: Operator 根据自定义资源 Prometheus 类型中定义的内容而部署的 Prometheus Server 集群,这些自定义资源可以看作是用来管理 Prometheus Server 集群的 StatefulSets 资源。
- ServiceMonitor: ServiceMonitor 也是一个自定义资源,它描述了一组被 Prometheus 监控的 targets 列表。该资源通过 Labels 来选取对应的 Service Endpoint,让 Prometheus Server 通过选取的 Service 来获取 Metrics 信息。
- Service: Service 资源主要用来对应 Kubernetes 集群中的 Metrics Server Pod,来提供给 ServiceMonitor 选取让 Prometheus Server 来获取信息。简单的说就是 Prometheus 监控的对象,例如 Node Exporter Service、Mysql Exporter Service 等等。
- Alertmanager: Alertmanager 也是一个自定义资源类型,由 Operator 根据资源描述内容来部署 Alertmanager 集群。
为什么需要prometheus-operator
因为是prometheus主动去拉取的,所以在k8s里pod因为调度的原因导致pod的ip会发生变化,人工不可能去维持,自动发现有基于DNS的,但是新增还是有点麻烦。
Prometheus-operator的本职就是一组用户自定义的CRD资源以及Controller的实现,Prometheus Operator这个controller有BRAC权限下去负责监听这些自定义资源的变化,并且根据这些资源的定义自动化的完成如Prometheus Server自身以及配置的自动化管理工作。
在Kubernetes中我们使用Deployment、DamenSet、StatefulSet来管理应用Workload,使用Service、Ingress来管理应用的访问方式,使用ConfigMap和Secret来管理应用配置。我们在集群中对这些资源的创建,更新,删除的动作都会被转换为事件(Event),Kubernetes的Controller Manager负责监听这些事件并触发相应的任务来满足用户的期望。这种方式我们成为声明式,用户只需要关心应用程序的最终状态,其它的都通过Kubernetes来帮助我们完成,通过这种方式可以大大简化应用的配置管理复杂度。
而除了这些原生的Resource资源以外,Kubernetes还允许用户添加自己的自定义资源(Custom Resource)。并且通过实现自定义Controller来实现对Kubernetes的扩展,不需要用户去二开k8s也能达到给k8s添加功能和对象。
因为svc的负载均衡,所以在K8S里监控metrics基本最小单位都是一个svc背后的pod为target,所以prometheus-operator创建了对应的CRD: kind: ServiceMonitor
,创建的ServiceMonitor
里声明需要监控选中的svc的label以及metrics的url路径的和namespaces即可。
什么是metrics
例如我们要查看etcd的metrics,先查看etcd的运行参数找到相关的值,这里我是所有参数写在一个yml文件里,非yml自行查看systemd文件或者运行参数找到相关参数和值即可。
1 | [root@k8s-m1 ~]# ps aux | grep -P '/etc[d] ' |
我们需要两部分信息:
- listen-client-urls的httpsurl,我这里是
https://172.16.0.2:2379
- 允许客户端证书信息
然后使用下面的curl,带上各自证书路径访问https的url执行
1 | curl --cacert /etc/etcd/ssl/etcd-ca.pem --cert /etc/etcd/ssl/etcd.pem --key /etc/etcd/ssl/etcd-key.pem https://172.16.0.2:2379/metrics |
也可以etcd用选项和值--listen-metrics-urls http://interface_IP:port
设置成非https的metrics端口可以不用证书即可访问,我们会看到etcd的metrics输出信息如下:
1 | .... |
同理kube-apiserver也有metrics信息
1 | kubectl get --raw /metrics |
这种就是prometheus的定义的metrics格式规范,缺省是在http(s)的url的/metrics输出。 而metrics要么程序定义输出(模块或者自定义开发),要么用官方的各种exporter(node-exporter,mysqld-exporter,memcached_exporter…)采集要监控的信息占用一个web端口然后输出成metrics格式的信息,prometheus server去收集各个target的metrics存储起来(tsdb)。 用户可以在prometheus的http页面上用promQL(prometheus的查询语言)或者(grafana数据来源就是用)api去查询一些信息,也可以利用pushgateway去统一采集然后prometheus从pushgateway采集(所以pushgateway类似于zabbix的proxy)
部署
使用helm安装部署
拉取安装包
1 | helm search prometheus-operator |
解压之后修改values文件(请参考官方github)
1 | helm install --name prometheus-operator --namespace monitoring -f values.yaml ./ |
源码部署
先获取相关文件后面跟着文件来讲,直接用git客户端拉取即可,不过文件大概30多M,没梯子基本拉不下来。
1 | git clone https://github.com/coreos/prometheus-operator.git |
拉取不下来可以在katacoda的网页上随便一个课程的机器都有docker客户端,可以git clone下来后把文件构建进一个alpine镜像然后推到dockerhub上,再在自己的机器docker run这个镜像的时候docker cp到宿主机上。
Prometheus Operator引入的自定义资源包括:
- Prometheus
- ServiceMonitor
- Alertmanager
用户创建了prometheus-operator(也就是上面监听三个CRD的各种事件的controller)后,用户可以利用kind: Prometheus
这种声明式创建对应的资源。 下面我们部署简单的例子学习prometheus-operator
创建prometheus-operator的pod
拉取到文件后我们先创建prometheus-operator:
1 | $ cd prometheus-operator |
确认pod运行,以及我们可以发现operator的pod在有RBAC下创建了一个APIService:
1 | $ kubectl get pod |
查看这个APISerivce
1 | $ kubectl get --raw /apis/monitoring.coreos.com/v1 |
这个是因为bundle.yml里有如下的CLusterRole
和对应的ClusterRoleBinding
来让prometheus-operator有权限对monitoring.coreos.com
这个apiGroup里的这些CRD进行所有操作
1 | apiVersion: rbac.authorization.k8s.io/v1 |
同时我们查看到pod里的log发现operator也在集群里创建了对应的CRD
1 | $ kubectl logs prometheus-operator-6db8dbb7dd-dkhxc |
相关CRD介绍
这四个CRD作用如下
- Prometheus: 由 Operator 依据一个自定义资源
kind: Prometheus
类型中,所描述的内容而部署的 Prometheus Server 集群,可以将这个自定义资源看作是一种特别用来管理Prometheus Server的StatefulSets资源。 - ServiceMonitor: 一个Kubernetes自定义资源(和
kind: Prometheus
一样是CRD),该资源描述了Prometheus Server的Target列表,Operator 会监听这个资源的变化来动态的更新Prometheus Server的Scrape targets并让prometheus server去reload配置(prometheus有对应reload的http接口/-/reload
)。而该资源主要通过Selector来依据 Labels 选取对应的Service的endpoints,并让 Prometheus Server 通过 Service 进行拉取(拉)指标资料(也就是metrics信息),metrics信息要在http的url输出符合metrics格式的信息,ServiceMonitor也可以定义目标的metrics的url。 - Alertmanager:Prometheus Operator 不只是提供 Prometheus Server 管理与部署,也包含了 AlertManager,并且一样通过一个
kind: Alertmanager
自定义资源来描述信息,再由 Operator 依据描述内容部署 Alertmanager 集群。 - PrometheusRule:对于Prometheus而言,在原生的管理方式上,我们需要手动创建Prometheus的告警文件,并且通过在Prometheus配置中声明式的加载。而在Prometheus Operator模式中,告警规则也编程一个通过Kubernetes API 声明式创建的一个资源.告警规则创建成功后,通过在Prometheus中使用想servicemonitor那样用
ruleSelector
通过label匹配选择需要关联的PrometheusRule即可。
部署kind: Prometheus
现在我们有了prometheus这个CRD,我们部署一个prometheus server只需要如下声明即可。
1 | $ cat<<EOF | kubectl apply -f - |
因为负载均衡,一个svc下的一组pod是监控的最小单位,要监控一个svc的metrics就声明创建一个servicemonitors
即可。
部署一组pod及其svc
首先,我们部署一个带metrics输出的简单程序的deploy,该镜像里的主进程会在8080端口上输出metrics信息。
1 | $ cat<<EOF | kubectl apply -f - |
创建对应的svc。
1 | $ cat<<EOF | kubectl apply -f - |
部署kind: ServiceMonitor
现在创建一个ServiceMonitor
来告诉prometheus server需要监控带有label app: example-app
的svc背后的一组pod的metrics。
1 | $ cat<<EOF | kubectl apply -f - |
默认情况下ServiceMonitor
和监控对象必须是在相同Namespace下的,如果要关联非同ns下需要下面这样设置值。
1 | spec: |
如果希望ServiceMonitor可以关联任意命名空间下的标签,则通过以下方式定义:
1 | spec: |
如果需要监控的Target对象启用了BasicAuth认证,那在定义ServiceMonitor对象时,可以使用endpoints配置中定义basicAuth如下所示basicAuth中的password
和username
值来源于同ns下的一个名为basic-auth
的Secret。
1 | spec |
上面要注意的是我创建prometheus server的时候有如下值。
1 | serviceMonitorSelector: |
该值字面意思可以知道就是指定prometheus server去选择哪些ServiceMonitor
,这个概念和svc去选择pod一样,可能一个集群跑很多prometheus server来监控各自选中的ServiceMonitor
,如果想一个prometheus server监控所有的则spec.serviceMonitorSelector: {}
为空即可,而namespaces的范围同样的设置spec.serviceMonitorSelector: {}
,后面官方的prometheus实例里我们可以看到设置了这两个值。
给prometheus server设置相关的RBAC权限。
1 | $ cat<<EOF | kubectl apply -f - |
创建svc使用NodePort
方便我们访问prometheus的web页面,生产环境不建议使用NodePort
。
1 | $ cat<<EOF | kubectl apply -f - |
打开浏览器访问ip:30900
进入target发现已经监听起来了,对应的config里也有配置生成和导入。
先清理掉上面的,然后我们使用官方提供的全套yaml正式部署prometheus-operator
。
1 | kubectl delete svc prometheus example-app |
部署官方的prometheus-operator
官方把所有文件都放在一起,这里我分类下。
1 | cd contrib/kube-prometheus/manifests/ |
部署operator
先创建ns和operator,quay.io仓库拉取慢,可以使用我脚本拉取,其他镜像也可以这样去拉,不过在apply之前才能拉,一旦被docker接手拉取就只能漫长等。
1 | kubectl apply -f . |
确认状态运行正常再往后执行,这里镜像是quay.io仓库的可能会很慢耐心等待或者自行修改成能拉取到的。
1 | $ kubectl -n monitoring get pod |
部署整套CRD
创建相关的CRD,这里镜像可能也要很久。
1 | kubectl apply -f adapter/ |
可以通过get查看整体状态,这里镜像原因会等待很久,我们可以先往后看几个坑的地方。
1 | kubectl -n monitoring get all |
踩坑
坑一
这里要注意有一个坑,二进制部署k8s管理组件和新版本kubeadm部署的都会发现在prometheus server的页面上发现kube-controller
和kube-schedule
的target为0/0也就是上图所示。这是因为serviceMonitor是根据label去选取svc的,我们可以看到对应的serviceMonitor
是选取的ns范围是kube-system
。
1 | $ grep -2 selector serviceMonitor/prometheus-serviceMonitorKube* |
而kube-system里默认只有这俩svc,且没有符合上面的label。
1 | $ kubectl -n kube-system get svc |
但是却有对应的ep(没有带任何label)被创建,这点想不通官方什么鬼操作,另外这里没有kubelet的ep,我博客部署的二进制的话会有。
1 | $ kubectl get ep -n kube-system |
解决办法
所以这里我们创建两个管理组建的svc,名字无所谓,关键是svc的label要能被servicemonitor选中,svc的选择器的label是因为kubeadm的staticPod的label是这样,如果是二进制部署的这俩svc的selector部分不能要。
1 | apiVersion: v1 |
二进制的话需要我们手动填入svc对应的ep的属性,我集群是HA的,所有有三个,仅供参考,别傻傻得照抄,另外这个ep的名字得和上面的svc的名字和属性对应上:
1 | apiVersion: v1 |
这里不知道为啥kubeadm部署的没有kubelet这个ep,我博客二进制部署后是会有kubelet这个ep的(好像metrics server创建的),下面仅供参考,IP根据实际写。另外kubeadm部署下kubelet的readonly的metrics端口(默认是10255)不会开放可以删掉ep的那部分port:
1 | apiVersion: v1 |
至于prometheus server的服务访问,别再用效率不行的NodePort
了,上ingress controller吧,怎么部署参照我博客IngressController。
1 | apiVersion: extensions/v1beta1 |
坑二
访问prometheus server的web页面我们发现即使创建了svc和注入对应ep的信息在target页面发现prometheus server请求被拒绝。
在宿主机上我们发现127.0.0.1才能访问,网卡ip不能访问(这里是另一个环境找的,所以ip是192不是前面的172)
1 | $ hostname -i |
解决办法
修改管理组件bind的ip。
如果使用kubeadm启动的集群,初始化时的config.yml里可以加入如下参数
1 | controllerManagerExtraArgs: |
已经启动后的使用下面命令更改就会滚动更新
1 | sed -ri '/--address/s#=.+#=0.0.0.0#' /etc/kubernetes/manifests/kube-* |
二进制的话查看是不是bind的0.0.0.0如果不是就修改成0.0.0.0,多块网卡如果只想bind一个网卡就写对应的主机上的网卡ip,写0.0.0.0就会监听所有网卡的对应端口。
监控mysql
曾经
想象一下,我们以传统的方式去监控一个mysql服务,首先需要安装mysql-exporter,获取mysql metrics,并且暴露一个端口,等待prometheus服务来拉取监控信息,然后去Prometheus Server的prometheus.yaml文件中在scarpe_config中添加mysql-exporter的job,配置mysql-exporter的地址和端口等信息,再然后,需要重启Prometheus服务,就完成添加一个mysql监控的任务
现在
现在我们以Prometheus-Operator的方式来部署Prometheus,当我们需要添加一个mysql监控我们会怎么做,首先第一步和传统方式一样,部署一个mysql-exporter来获取mysql监控项,然后编写一个ServiceMonitor通过labelSelector选择刚才部署的mysql-exporter,由于Operator在部署Prometheus的时候默认指定了Prometheus选择label为:prometheus: kube-prometheus
的ServiceMonitor,所以只需要在ServiceMonitor上打上prometheus: kube-prometheus
标签就可以被Prometheus选择了,完成以上两步就完成了对mysql的监控,不需要改Prometheus配置文件,也不需要重启Prometheus服务,是不是很方便,Operator观察到ServiceMonitor发生变化,会动态生成Prometheus配置文件,并保证配置文件实时生效
安装mysql_exporter
获取安装包
1 | helm fetch stable/prometheus-mysql-exporter |
解压
1 | tar xf prometheus-mysql-exporter-0.5.2.tgz |
进入解压出来的文件夹修改values文件
1 | # Default values for prometheus-mysql-exporter. |
安装
1 | helm install --name me-release -f values.yaml . |
如果values文件中的serviceMonitor.enabled为false,则需要我们自己创建serviceMonitor
创建监控
接下来让我看看自己如何编写servicemonitor.yaml
接下来编写ServiceMonitor文件,执行命令vim servicemonitor.yaml
1 | apiVersion: monitoring.coreos.com/v1 |
保存并退出文件,然后执行命令:kubectl create -f servicemonitor.yaml
,创建成功之后执行命令kubectl get serviceMonitor
查看是否有刚才创建的serviceMonitor:
1 | root@k8s1:~/mysql-exporter# kubectl create -f servicemonitor.yaml |
可以看到Prometheus-exporter-mysql已经存在了,表示创建成功了,过1分钟左右,在prometheus的界面中查看Targets,可以看到已经成功添加了mysql监控。
上面提到prometheus通过标签prometheus: kube-prometheus选择ServiceMonitor,该配置写在这里, 当然,你可以通过在values.yaml中配置serviceMonitorsSelector来指定按照自己的规则选择serviceMonitor,关于如何配置serviceMonitorsSelector将放在后文统一讲解
动态添加告警规则
当我们动态添加了监控对象,一般会对该对象配置告警规则,采用prometheus-operator的架构模式下,当我们需要动态配置告警规则的时候,可以使用另一种自定义资源(CRD)PrometheusRule,PrometheusRule和ServiceMonitor都是一种自定义资源,ServiceMonitor用于动态添加监控实例,而PrometheusRule则用于动态添加告警规则,下面依然通过动态添加mysql的告警规则为例来演示如何使用PrometheusRule资源。
执行命令vim mysql-rule.yaml
,输入以下内容
1 | apiVersion: monitoring.coreos.com/v1 #这和ServiceMonitor一样 |
Prometheus选择PrometheusRule资源是通过ruleSelector来选择,默认也是通过标签:prometheus: kube-prometheus来选择,在这里可以看到,ruleSelector和ServiceMonitorsSelector都是可以配置的,如何配置将放在后文的配置统一讲解
保存以上文件之后执行kubectl create -f mysql-rule.yaml
,创建成功之后执行命令kubectl get prometheusRule
可以看到刚才创建的PrometheusRule资源prometheus-rule-mysql:
1 | root@k8s1:~/mysql-exporter# kubectl create -f mysql-rule.yaml |
等待1分钟左右,在prometheus图形界面中可以找到刚才添加的mysql.rule的内容了
如何动态更新Alertmanager配置
原理
Operator部署Alertmanager的时候会生成一个statefulset类型对象,通过命令kubectl get statefulset –all-namespaces可以找到这个statefulset,可以看到name是alertmanager-kube-prometheus
1 | root@k8s1:~/prometheus-operator/helm/alertmanager/templates# kubectl get statefulset --all-namespaces |
然后执行命令kubectl describe statefulset alertmanager-kube-prometheus -n monitoring
可以看到该statefulset的详细信息:
1 | root@k8s1:~/prometheus-operator/helm/alertmanager/templates# kubectl describe statefulset alertmanager-kube-prometheus -n monitoring |
该statefulset挂载了一个名为alertmanager-kube-prometheus的secret资源到alertmanager容器内部的/etc/alertmanager/config/alertmanager.yaml
,上面的Volumes:
下面的config-volume:
标签下可以看到,Type:字段的值为Secret表示挂载一个secret资源,secrect的name是alertmanager-kube-prometheus
,通过一下命令查看该secret: kubectl describe secrets alertmanager-kube-prometheus -n monitoring
1 | root@k8s1:~# kubectl describe secrets alertmanager-kube-prometheus -n monitoring |
可以看到该secert的Data项里面有一个key为alertmanager.yaml
的属性,其value包含567bytes,而这个alertmanager.yaml的值其实就是alertmanager容器的/etc/alertmanager/config/alertmanager.yaml
中的内容,statefulset通过挂载的方式将/etc/alertmanager/config
挂载成一个secret,执行kubectl edit secrets -n monitoring alertmanager-kube-prometheus
可以看到该secret的内容:
1 | apiVersion: v1 |
其中data:
下面的alertmanager.yaml
这个key对应的值是一串base64编码过后的字符串,将这段字符串复制出来通过base64反编码之后内容如下:
1 | global: |
这其实就是alertmanager的config配置,上面说到,该内容会被挂载到alertmanager容器的/etc/alertmanager/config/alertmanager.yaml
,我们进入alertmanager容器去看看该文件,执行命令kubectl exec -it alertmanager-kube-prometheus-0 -n monitoring sh
进入到容器(可能你的容器名和我的不同,可以通过kubectl get pods –all-namespaces命令查看所有的容器),然后进入目录/etc/alertmanager/config
,然后ls
可以看到该目录下有一个叫alertmanager.yaml
的文件,而该文件的内容就是上面base64反编译之后的内容,我们通过修改名为alertmanager-kube-prometheus的secret的data属性中的alertmanager.yaml字段对应的值就相当于修改了该文件中的内容,所以现在问题就变简单了,在alertmanager的pod中还有另一个container叫做config-reloader,它会监听/etc/alertmanager/config
目录,当该目录下的文件发生变化的时候,config-reloader会向alertmanager发起http://localhost:9093/-/reloadPOST请求,alertmanager会重新加载该目录下的配置文件,从而实现了动态配置更新
如何操作
在理解了alertmanager动态配置的原理之后,问题就很清晰了,我们需要动态配置alertmanager只需要更新名为alertmanager-kube-prometheus(你的secret名不一定为这个名字,但一定是alertmanager-{*}格式)的secret的data属性中的alertmanager.yaml字段的值就可以了,更新secret有两种方法,一是通过kubectl edit secret
的方式,一种是通过kubectl patch secret
的方式,但是两种方式更新secret都需要输入base64编码之后的字符串,这里通过linux下的base64命令进行编码:
首先修改上面base64反编译后的文件,比如smtp_from改成另一个邮箱发送,修改完成之后保存文件,然后通过命令base64 file > test.txt
的方式将配置通过base64编码并将编码结果输出到test.txt文件中,然后进入test.txt文件中复制编码之后的字符串,如果通过第一种方式更新secret,执行命令
kubectl edit secrets -n monitoring alertmanager-kube-prometheus
然后data下面的alertmanager.yaml的值为刚才复制的字符串,保存并退出就可以了。如果通过第二种方式更新secert,执行命令
kubectl patch secert alertmanager-kube-prometheus -n -n monitoring -p '{"data":{"alertmanager.yaml":"此处填写刚才复制的base64编码之后的配置字符串"}'
即可完成更新,该命令中 -p参数后面跟的是一个JSON字符串,将刚才复制的base64编码后的字符串填入正确的位置可以了
在完成更新之后可以访问alertmanager的界面http://192.168.11.178:30903/#/status,查看配置已经生效了
通过上面的操作我们已经实现了监控对象的动态发现,监控告警规则的动态添加,告警配置(发送邮件)的动态配置,基本上已经实现了所有配置的动态配置
Prometheus-Operator配置
配置Prometheus一般情况下只需要配置kube-prometheus下的values.yaml就能实现对alertmanager、promethes的配置,该文件该如何配置在配置项上一般都有说明,这里主要讲解上文提到的几个配置以及其他比较常用的几个配置,避免占用太多篇幅,我已经将比较简单或不常用的配置以省略号代替:
1 | ... |