prometheus operator
导航:这里主要是列出一个prometheus一些系统的学习过程,最后按照章节顺序查看,由于写作该文档经历了不同时期,所以在文中有时出现 的云环境不统一,但是学习具体使用方法即可,在最后的篇章,有一个完整的腾讯云的实战案例。 8.kube-state-metrics 和 metrics-server 13.Grafana简单用法 参考: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config https://www.bookstack.cn/read/prometheus_practice/introduction-README.md |
1.Prometheus Operator 介绍
在各种方式部署要注意各方式不同的地方,比如二进制部署,那么metrics-server等服务一定要正常,以及二进制集群开启比如聚合层这样的配置,所以这一章在metrics-server章节的后面,这2章阅读的时候是相辅相成的,不然实验很容易失败.
之前为了在Kubernetes能够方便的管理和部署Prometheus,可使用ConfigMap了管理Prometheus配置文件。每次对Prometheus配置文件进行升级时,我们需要手动移除已经运行的Pod实例,从而让Kubernetes可以使用最新的配置文件创建Prometheus。 而如果当应用实例的数量更多时,通过手动的方式部署和升级Prometheus过程繁琐并且效率低下。
从本质上来讲Prometheus属于是典型的有状态应用,而其有包含了一些自身特有的运维管理和配置管理方式。而这些都无法通过Kubernetes原生提供的应用管理概念实现自动化。为了简化这类应用程序的管理复杂度,CoreOS率先引入了Operator的概念,并且首先推出了针对在Kubernetes下运行和管理Etcd的Etcd Operator。并随后推出了Prometheus Operator。
1.1 Prometheus Operator的工作原理
从概念上来讲Operator就是针对管理特定应用程序的,在Kubernetes基本的Resource和Controller的概念上,以扩展Kubernetes api的形式。帮助用户创建,配置和管理复杂的有状态应用程序。从而实现特定应用程序的常见操作以及运维自动化。
在Kubernetes中我们使用Deployment、DamenSet,StatefulSet来管理应用Workload等一些应用程序,使用Service,Ingress来管理应用的访问方式,使用ConfigMap和Secret来管理应用配置。我们在集群中对这些资源的创建,更新,删除的动作都会被转换为事件(Event),Kubernetes的Controller Manager负责监听这些事件并触发相应的任务来满足用户的期望。这种方式我们成为声明式,用户只需要关心应用程序的最终状态,其它的都通过Kubernetes来帮助我们完成,通过这种方式可以大大简化应用的配置管理复杂度。
而除了这些原生的Resource资源以外,Kubernetes还允许用户添加自己的自定义资源(Custom Resource)。并且通过实现自定义Controller来实现对Kubernetes的扩展。
如下所示,是Prometheus Operator的架构示意图:
Prometheus Operator架构
Prometheus的本职就是一组用户自定义的CRD资源以及Controller的实现,Prometheus Operator负责监听这些自定义资源的变化,并且根据这些资源的定义自动化的完成如Prometheus Server自身以及配置的自动化管理工作。
1.2 Prometheus Operator能做什么
要了解Prometheus Operator能做什么,其实就是要了解Prometheus Operator为我们提供了哪些自定义的Kubernetes资源,列出了Prometheus Operator目前提供的4类资源:
- Prometheus:声明式创建和管理Prometheus Server实例;
- ServiceMonitor:负责声明式的管理监控配置;
- PrometheusRule:负责声明式的管理告警配置;
- Alertmanager:声明式的创建和管理Alertmanager实例。
简言之,Prometheus Operator能够帮助用户自动化的创建以及管理Prometheus Server以及其相应的配置。
2.安装
最新的版本官方将资源:https://github.com/coreos/prometheus-operator/tree/master/contrib/kube-prometheus
迁移到了独立的git仓库中:https://github.com/coreos/kube-prometheus.git
之前是一个项目,后来官方发现无法进行版本控制,所以拆分出来了,prometheus-poerator里面现在是只有一个 opetator的yml,其余的crd都需要自己去自定义,非常不方便;kube-prometheus里面有全套的模版给我们使用.
我们这里直接通过 Prometheus-Operator 的源码来进行安装,当然也可以用 Helm 来进行一键安装,我们采用源码安装可以去了解更多的实现细节。
git clone https://github.com/coreos/kube-prometheus.git [root@ip-10-1-50-250 kube-prometheus]# cd manifests/ [root@ip-10-1-50-250 manifests]# pwd /k8s/kube-prometheus/manifests [root@ip-10-1-50-250 manifests]# ls 00namespace-namespace.yaml grafana-service.yaml prometheus-adapter-configMap.yaml 0prometheus-operator-0alertmanagerCustomResourceDefinition.yaml kube-controller-manager-service.yaml prometheus-adapter-deployment.yaml 0prometheus-operator-0podmonitorCustomResourceDefinition.yaml kube-scheduler-service.yaml prometheus-adapter-roleBindingAuthReader.yaml 0prometheus-operator-0prometheusCustomResourceDefinition.yaml kube-state-metrics-clusterRoleBinding.yaml prometheus-adapter-serviceAccount.yaml 0prometheus-operator-0prometheusruleCustomResourceDefinition.yaml kube-state-metrics-clusterRole.yaml prometheus-adapter-service.yaml 0prometheus-operator-0servicemonitorCustomResourceDefinition.yaml kube-state-metrics-deployment.yaml prometheus-additional.yaml 0prometheus-operator-clusterRoleBinding.yaml kube-state-metrics-roleBinding.yaml prometheus-clusterRoleBinding.yaml
如果这里kubelet的端口不是https的,那么进入到 manifests 目录下面,这个目录下面包含我们所有的资源清单文件,我们需要对其中的文件 prometheus-serviceMonitorKubelet.yaml 进行简单的修改,因为默认情况下,这个 ServiceMonitor 是关联的 kubelet 的10250端口去采集的节点数据,而我们前面说过为了安全,这个 metrics 数据已经迁移到10255这个只读端口上面去了,我们只需要将文件中的https-metrics更改成http-metrics即可,由于这里是aws eks环境,这一步不需要操作.
[root@ip-10-1-50-250 manifests]# pwd /k8s/kube-prometheus/manifests [root@ip-10-1-50-250 manifests]# kubectl apply -f .
部署完成后,会创建一个名为monitoring的 namespace,所以资源对象对将部署在改命名空间下面,此外 Operator 会自动创建4个 CRD 资源对象:
$ kubectl get crd |grep coreos
alertmanagers.monitoring.coreos.com 5d
prometheuses.monitoring.coreos.com 5d
prometheusrules.monitoring.coreos.com 5d
servicemonitors.monitoring.coreos.com 5d
可以在 monitoring 命名空间下面查看所有的 Pod,其中 alertmanager 和 prometheus 是用 StatefulSet 控制器管理的,其中还有一个比较核心的 prometheus-operator 的 Pod,用来控制其他资源对象和监听对象变化的:
$ kubectl get pods -n monitoring NAME READY STATUS RESTARTS AGE alertmanager-main-0 2/2 Running 0 21h alertmanager-main-1 2/2 Running 0 21h alertmanager-main-2 2/2 Running 0 21h grafana-df9bfd765-f4dvw 1/1 Running 0 22h kube-state-metrics-77c9658489-ntj66 4/4 Running 0 20h node-exporter-4sr7f 2/2 Running 0 21h node-exporter-9mh2r 2/2 Running 0 21h node-exporter-m2gkp 2/2 Running 0 21h prometheus-adapter-dc548cc6-r6lhb 1/1 Running 0 22h prometheus-k8s-0 3/3 Running 1 21h prometheus-k8s-1 3/3 Running 1 21h prometheus-operator-bdf79ff67-9dc48 1/1 Running 0 21h
查看创建的 Service:
$ kubectl get svc -n monitoring NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE alertmanager-main ClusterIP 172.20.71.161 <none> 9093/TCP 79m alertmanager-operated ClusterIP None <none> 9093/TCP,9094/TCP,9094/UDP 79m grafana ClusterIP 172.20.200.116 <none> 3000/TCP 79m kube-state-metrics ClusterIP None <none> 8443/TCP,9443/TCP 79m node-exporter ClusterIP None <none> 9100/TCP 79m prometheus-adapter ClusterIP 172.20.122.213 <none> 443/TCP 79m prometheus-k8s ClusterIP 172.20.32.47 <none> 9090/TCP 79m prometheus-operated ClusterIP None <none> 9090/TCP 79m
可以看到上面针对 grafana 和 prometheus 都创建了一个类型为 ClusterIP 的 Service,当然如果我们想要在外网访问这两个服务的话可以通过创建对应的 Ingress 对象或者使用 NodePort 类型的 Service,这里使用什么样的方式都可以,按照自己定义的环境,接下来看看2个服务.(这里使用的是nginx)
比如Prometheus targets界面
3.简单配置
这里做的就是监控kube-controller-manager和kube-scheduler的配置,这里区分的主要是2种方式;2个组件是以pod部署,还是以二进制部署,像在EKS上,全部托管了,而且不是以pod的方式部署的,那么我们就无法监控(具体看后面章节)
我们可以看到大部分的配置都是正常的,只有两三个没有管理到对应的监控目标,比如 kube-controller-manager 和 kube-scheduler 这两个系统组件,这就和 ServiceMonitor 的定义有关系了,我们先来查看下 kube-scheduler 组件对应的 ServiceMonitor 资源的定义:(prometheus-serviceMonitorKubeScheduler.yaml)
apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: labels: k8s-app: kube-scheduler name: kube-scheduler namespace: monitoring spec: endpoints: - interval: 30s # 每30s获取一次信息 port: http-metrics # 对应service的端口名 jobLabel: k8s-app namespaceSelector: # 表示去匹配某一命名空间中的service,如果想从所有的namespace中匹配用any: true matchNames: - kube-system selector: # 匹配的 Service 的labels,如果使用mathLabels,则下面的所有标签都匹配时才会匹配该service,如果使用matchExpressions,则至少匹配一个标签的service都会被选择 matchLabels: k8s-app: kube-scheduler
上面是一个典型的 ServiceMonitor 资源文件的声明方式,上面我们通过selector.matchLabels在 kube-system 这个命名空间下面匹配具有k8s-app=kube-scheduler这样的 Service,但是我们系统中根本就没有对应的 Service,所以我们需要手动创建一个 Service:(kube-scheduler-service.yaml)
apiVersion: v1 kind: Service metadata: namespace: kube-system name: kube-scheduler labels: k8s-app: kube-scheduler spec: selector: component: kube-scheduler ports: - name: http-metrics port: 10251 targetPort: 10251 protocol: TCP
10251是kube-scheduler组件 metrics 数据所在的端口,10252是kube-controller-manager组件的监控数据所在端口。
这里有2个分支,2种部署方式了。
3.1 Kube-scheduler以pod方式部署
其中最重要的是上面 labels 和 selector 部分,labels 区域的配置必须和我们上面的 ServiceMonitor 对象中的 selector 保持一致,selector下面配置的是component=kube-scheduler,为什么会是这个 label 标签呢,我们可以去 describe 下 kube-scheduelr 这个 Pod:(二进制部署没有这个步骤)
$ kubectl describe pod kube-scheduler-master -n kube-system Name: kube-scheduler-master Namespace: kube-system Node: master/10.151.30.57 Start Time: Sun, 05 Aug 2018 18:13:32 +0800 Labels: component=kube-scheduler tier=control-plane ......
我们可以看到这个 Pod 具有component=kube-scheduler和tier=control-plane这两个标签,而前面这个标签具有更唯一的特性,所以使用前面这个标签较好,这样上面创建的 Service 就可以和我们的 Pod 进行关联了,直接创建即可:
$ kubectl create -f prometheus-kubeSchedulerService.yaml $ kubectl get svc -n kube-system -l k8s-app=kube-scheduler NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kube-scheduler ClusterIP 10.102.119.231 <none> 10251/TCP 18m
创建完成后,隔一小会儿后去 prometheus 查看 targets 下面 kube-scheduler 的状态
我们可以看到现在已经发现了 target,但是抓取数据结果出错了,这个错误是因为我们集群是使用 kubeadm 搭建的,其中 kube-scheduler 默认是绑定在127.0.0.1上面的,而上面我们这个地方是想通过节点的 IP 去访问,所以访问被拒绝了,我们只要把 kube-scheduler 绑定的地址更改成0.0.0.0即可满足要求,由于 kube-scheduler 是以静态 Pod 的形式运行在集群中的,所以我们只需要更改静态 Pod 目录下面对应的 YAML 文件即可:
$ ls /etc/kubernetes/manifests/
etcd.yaml kube-apiserver.yaml kube-controller-manager.yaml kube-scheduler.yaml
将 kube-scheduler.yaml 文件中-command的--address地址更改成0.0.0.0:
containers: - command: - kube-scheduler - --leader-elect=true - --kubeconfig=/etc/kubernetes/scheduler.conf - --address=0.0.0.0
修改完成后我们将该文件从当前文件夹中移除,隔一会儿再移回该目录,就可以自动更新了,然后再去看 prometheus 中 kube-scheduler 这个 target 是否已经正常了
以上是kube-scheduler以pod模式部署配置正常监控的流程, kube-controller-manager和kube-scheudler方式相同
3.2 Kube-scheduler以二进制方式部署
首先二进制的方式不仅没有匹配到svc,甚至即使匹配到了svc,默认是没有endpoints的,所以在pod部署的方式里面创建svc就可以了;但是二进制,不仅要创建svc,还要创建关联的endpoints.这样监控才知道要监控的目标是什么。
下面还是列出完整的步骤;
[root@master01 manifests]# cat kube-controller-manager-service.yaml kube-scheduler-service.yaml --- apiVersion: v1 kind: Service metadata: labels: k8s-app: kube-controller-manager name: kube-controller-manager namespace: kube-system spec: type: ClusterIP clusterIP: None ports: - name: http-metrics port: 10252 protocol: TCP targetPort: 10252 --- apiVersion: v1 kind: Service metadata: namespace: kube-system name: kube-scheduler labels: k8s-app: kube-scheduler spec: type: ClusterIP clusterIP: None ports: - name: http-metrics port: 10251 targetPort: 10251 protocol: TCP
现在查看这这2个svc是没有endpoints,所以,这里我们还要创建关联的endpoints
[root@master01 manifests]# cat kube-scheduler-endpoint.yml kube-controller-manager-endpoint.yaml --- apiVersion: v1 kind: Endpoints metadata: labels: k8s-app: kube-scheduler name: kube-scheduler namespace: kube-system subsets: - addresses: - ip: 172.21.161.145 #这里是具体二进制部署组件的ip,我这里有2台 - ip: 172.21.161.146 ports: - name: http-metrics port: 10251 protocol: TCP apiVersion: v1 kind: Endpoints metadata: labels: k8s-app: kube-controller-manager name: kube-controller-manager namespace: kube-system subsets: - addresses: - ip: 172.21.161.145 - ip: 172.21.161.146 ports: - name: http-metrics port: 10252 protocol: TCP
创建完成之后可以看到一下endpoints,那么prometheus根据标签就可以得到要监控访问的地址了
到这里差不多就结束了,但是在prometheus 的targets界面的时候,还是显示连接超时,这是因为controller-manager的监听地址是127.0.0.1,那么就需要把127.0.0.1改为0.0.0.0,目前二进制只需要更改controller-manager即可,改完之后重启服务,然后登录网页查看.
4.高级配置
4.1 新增自动发现配置
以下是添加自定义刮擦的官方链接:https://github.com/coreos/prometheus-operator/blob/master/Documentation/additional-scrape-config.md
虽然prometheus operator有了很多的监控项,但是还是有很多监控无法覆盖完全,比如自建集群的etcd,和一些其它的监控,所以Prometheus Operator 为我们提供了一个额外的抓取配置的来解决这个问题,我们可以通过添加额外的配置来进行服务发现进行自动监控。和前面自定义的方式一样,我们想要在 Prometheus Operator 当中去自动发现并监控具有prometheus.io/scrape=true这个 annotations 的 Service,之前我们定义的 Prometheus 的配置如下:
- job_name: 'kubernetes-service-endpoints' kubernetes_sd_configs: - role: endpoints relabel_configs: - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape] action: keep regex: true - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme] action: replace target_label: __scheme__ regex: (https?) - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path] action: replace target_label: __metrics_path__ regex: (.+) - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port] action: replace target_label: __address__ regex: ([^:]+)(?::\d+)?;(\d+) replacement: $1:$2 - action: labelmap regex: __meta_kubernetes_service_label_(.+) - source_labels: [__meta_kubernetes_namespace] action: replace target_label: kubernetes_namespace - source_labels: [__meta_kubernetes_service_name] action: replace target_label: kubernetes_name
如果你对上面这个配置还不是很熟悉的话,建议去查看下前面关于监控prometheus集群章节,要想自动发现集群中的 Service,就需要我们在 Service 的annotation区域添加prometheus.io/scrape=true的声明,将上面文件直接保存为 prometheus-additional.yaml,然后通过这个文件创建一个对应的 Secret 对象:
$ kubectl create secret generic additional-configs --from-file=prometheus-additional.yaml -n monitoring secret "additional-configs" created
创建完成后,会将上面配置信息进行 base64 编码后作为 prometheus-additional.yaml 这个 key 对应的值存在:
$ kubectl get secret additional-configs -n monitoring -o yaml apiVersion: v1 data: prometheus-additional.yaml: LSBqb2JfbmFtZTogJ2t1YmVybmV0ZXMtc2VydmljZS1lbmRwb2ludHMnCiAga3ViZXJuZXRlc19zZF9jb25maWdzOgogIC0gcm9sZTogZW5kcG9pbnRzCiAgcmVsYWJlbF9jb25maWdzOgogIC0gc291cmNlX2xhYmVsczogW19fbWV0YV9rdWJlcm5ldGVzX3NlcnZpY2VfYW5ub3RhdGlvbl9wcm9tZXRoZXVzX2lvX3NjcmFwZV0KICAgIGFjdGlvbjoga2VlcAogICAgcmVnZXg6IHRydWUKICAtIHNvdXJjZV9sYWJlbHM6IFtfX21ldGFfa3ViZXJuZXRlc19zZXJ2aWNlX2Fubm90YXRpb25fcHJvbWV0aGV1c19pb19zY2hlbWVdCiAgICBhY3Rpb246IHJlcGxhY2UKICAgIHRhcmdldF9sYWJlbDogX19zY2hlbWVfXwogICAgcmVnZXg6IChodHRwcz8pCiAgLSBzb3VyY2VfbGFiZWxzOiBbX19tZXRhX2t1YmVybmV0ZXNfc2VydmljZV9hbm5vdGF0aW9uX3Byb21ldGhldXNfaW9fcGF0aF0KICAgIGFjdGlvbjogcmVwbGFjZQogICAgdGFyZ2V0X2xhYmVsOiBfX21ldHJpY3NfcGF0aF9fCiAgICByZWdleDogKC4rKQogIC0gc291cmNlX2xhYmVsczogW19fYWRkcmVzc19fLCBfX21ldGFfa3ViZXJuZXRlc19zZXJ2aWNlX2Fubm90YXRpb25fcHJvbWV0aGV1c19pb19wb3J0XQogICAgYWN0aW9uOiByZXBsYWNlCiAgICB0YXJnZXRfbGFiZWw6IF9fYWRkcmVzc19fCiAgICByZWdleDogKFteOl0rKSg/OjpcZCspPzsoXGQrKQogICAgcmVwbGFjZW1lbnQ6ICQxOiQyCiAgLSBhY3Rpb246IGxhYmVsbWFwCiAgICByZWdleDogX19tZXRhX2t1YmVybmV0ZXNfc2VydmljZV9sYWJlbF8oLispCiAgLSBzb3VyY2VfbGFiZWxzOiBbX19tZXRhX2t1YmVybmV0ZXNfbmFtZXNwYWNlXQogICAgYWN0aW9uOiByZXBsYWNlCiAgICB0YXJnZXRfbGFiZWw6IGt1YmVybmV0ZXNfbmFtZXNwYWNlCiAgLSBzb3VyY2VfbGFiZWxzOiBbX19tZXRhX2t1YmVybmV0ZXNfc2VydmljZV9uYW1lXQogICAgYWN0aW9uOiByZXBsYWNlCiAgICB0YXJnZXRfbGFiZWw6IGt1YmVybmV0ZXNfbmFtZQo= kind: Secret metadata: creationTimestamp: 2018-12-20T14:50:35Z name: additional-configs namespace: monitoring resourceVersion: "41814998" selfLink: /api/v1/namespaces/monitoring/secrets/additional-configs uid: 9bbe22c5-0466-11e9-a777-525400db4df7 type: Opaque
然后我们只需要在声明 prometheus 的资源对象文件中添加上这个额外的配置:(prometheus-prometheus.yaml)
apiVersion: monitoring.coreos.com/v1 kind: Prometheus metadata: labels: prometheus: k8s name: k8s namespace: monitoring spec: alerting: alertmanagers: - name: alertmanager-main namespace: monitoring port: web baseImage: quay.io/prometheus/prometheus nodeSelector: beta.kubernetes.io/os: linux replicas: 2 secrets: - etcd-certs resources: requests: memory: 400Mi ruleSelector: matchLabels: prometheus: k8s role: alert-rules securityContext: fsGroup: 2000 runAsNonRoot: true runAsUser: 1000 additionalScrapeConfigs: name: additional-configs key: prometheus-additional.yaml serviceAccountName: prometheus-k8s serviceMonitorNamespaceSelector: {} serviceMonitorSelector: {} version: v2.5.0
在Prometheus Dashboard 的配置页面下面我们可以看到已经有了对应的的配置信息了,但是我们切换到 targets 页面下面却并没有发现对应的监控任务,查看 Prometheus 的 Pod 日志:
$ kubectl logs -f prometheus-k8s-0 prometheus -n monitoring level=error ts=2018-12-20T15:14:06.772903214Z caller=main.go:240 component=k8s_client_runtime err="github.com/prometheus/prometheus/discovery/kubernetes/kubernetes.go:302: Failed to list *v1.Pod: pods is forbidden: User \"system:serviceaccount:monitoring:prometheus-k8s\" cannot list pods at the cluster scope" level=error ts=2018-12-20T15:14:06.773096875Z caller=main.go:240 component=k8s_client_runtime err="github.com/prometheus/prometheus/discovery/kubernetes/kubernetes.go:301: Failed to list *v1.Service: services is forbidden: User \"system:serviceaccount:monitoring:prometheus-k8s\" cannot list services at the cluster scope" level=error ts=2018-12-20T15:14:06.773212629Z caller=main.go:240 component=k8s_client_runtime err="github.com/prometheus/prometheus/discovery/kubernetes/kubernetes.go:300: Failed to list *v1.Endpoints: endpoints is forbidden: User \"system:serviceaccount:monitoring:prometheus-k8s\" cannot list endpoints at the cluster scope" ......
可以看到有很多错误日志出现,都是xxx is forbidden,这说明是 RBAC 权限的问题,通过 prometheus 资源对象的配置可以知道 Prometheus 绑定了一个名为 prometheus-k8s 的 ServiceAccount 对象,而这个对象绑定的是一个名为 prometheus-k8s 的 ClusterRole:(prometheus-clusterRole.yaml)
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: prometheus-k8s rules: - apiGroups: - "" resources: - nodes/metrics verbs: - get - nonResourceURLs: - /metrics verbs: - get
上面的权限规则中我们可以看到明显没有对 Service 或者 Pod 的 list 权限,所以报错了,要解决这个问题,我们只需要添加上需要的权限即可:
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: prometheus-k8s rules: - apiGroups: - "" resources: - nodes - services - endpoints - pods - nodes/proxy verbs: - get - list - watch - apiGroups: - "" resources: - configmaps - nodes/metrics verbs: - get - nonResourceURLs: - /metrics verbs: - get
更新上面的 ClusterRole 这个资源对象,然后重建下 Prometheus 的所有 Pod,正常就可以看到 targets 页面下面有 kubernetes-service-endpoints 这个监控任务了:
我们这里自动监控了两个 Service,这是默认的core dns ,Service 中有两个特殊的 annotations:
annotations: prometheus.io/scrape: "true" prometheus.io/port: "9121"
所以被自动发现了,当然我们也可以用同样的方式去配置 Pod、Ingress 这些资源对象的自动发现。
4.2 新增prometheus 配置
在上节中,配置了自定义的发现配置,但是如果想配置其它的一些配置,比如remote_write,remote_read就不是这种方法了,那么这里有官方文档的对应.
https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#remotewritespec
查看上一章节的自动发现配置
是以secret的方式导入进去的,而remote存储和其它一些配置却有自己的方式,如:
下面是官方文档对应的remote存储
additionalScrapeConfigs: name: additional-configs key: prometheus-additional.yaml remoteWrite: - url: 'http://influxdb.public:8086/api/v1/prom/write?db=prometheus' remoteRead: - url: 'http://influxdb.public:8086/api/v1/prom/read?db=prometheus'
像上面的导入到influxdb,因为配置简短,可以直接写入至 prometheus-prometheus.yaml
验证,查看influxdb是否有数据写入。
由于写入可以很明显的看出是否配置正确,但读取因为prometheus的特性,即使错误了在短期也无法判断,所以这里看prometheus没有报错,可以不准确的判断;
4.3 新增prometheus 持久化
由于这里是功能实体的类型,所以很难手动给pod加入pvc,所以本章主要是使用pv动态供给来完成的,如果对动态供给不太熟悉的,可以翻阅书籍然后在看这一章.
如果在没有remote_write和remote_read的情况下, 上面我们在修改完权限的时候,重启了 Prometheus 的 Pod,如果我们仔细观察的话会发现我们之前采集的数据已经没有了,这是因为我们通过 prometheus 这个 CRD 创建的 Prometheus 并没有做数据的持久化,我们可以直接查看生成的 Prometheus Pod 的挂载情况就清楚了:
我们可以看到 Prometheus 的数据目录 /prometheus 实际上是通过 emptyDir 进行挂载的,我们知道 emptyDir 挂载的数据的生命周期和 Pod 生命周期一致的,所以如果 Pod 挂掉了,数据也就丢失了,这也就是为什么我们重建 Pod 后之前的数据就没有了的原因,对应线上的监控数据肯定需要做数据的持久化的,同样的 prometheus 这个 CRD 资源也为我们提供了数据持久化的配置方法,由于我们的 Prometheus 最终是通过 Statefulset 控制器进行部署的,所以我们这里需要通过 storageclass 来做数据持久化,首先创建一个 StorageClass 对象:
(aws是gp2,默认不需要不需要创建,这一步根据不同的环境来确认是否需要创建)
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: prometheus-data-db provisioner: fuseim.pri/ifs
这里我们声明一个 StorageClass 对象,其中 provisioner=fuseim.pri/ifs,则是因为我们集群中使用的是 nfs 作为存储后端,而前面我们课程中创建的 nfs-client-provisioner 中指定的 PROVISIONER_NAME 就为 fuseim.pri/ifs,这个名字不能随便更改,将该文件保存为 prometheus-storageclass.yaml:
$ kubectl create -f prometheus-storageclass.yaml storageclass.storage.k8s.io "prometheus-data-db" created
然后在 prometheus 的 CRD 资源对象中添加如下配置:
storage: volumeClaimTemplate: spec: storageClassName: prometheus-data-db resources: requests: storage: 10Gi
注意这里的 storageClassName 名字为上面我们创建的 StorageClass 对象名称,然后更新 prometheus 这个 CRD 资源。更新完成后会自动生成两个 PVC 和 PV 资源对象:
$ kubectl get pvc -n monitoring NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE prometheus-k8s-db-prometheus-k8s-0 Bound pvc-0cc03d41-047a-11e9-a777-525400db4df7 10Gi RWO prometheus-data-db 8m prometheus-k8s-db-prometheus-k8s-1 Bound pvc-1938de6b-047b-11e9-a777-525400db4df7 10Gi RWO prometheus-data-db 1m $ kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE pvc-0cc03d41-047a-11e9-a777-525400db4df7 10Gi RWO Delete Bound monitoring/prometheus-k8s-db-prometheus-k8s-0 prometheus-data-db 2m pvc-1938de6b-047b-11e9-a777-525400db4df7 10Gi RWO Delete Bound monitoring/prometheus-k8s-db-prometheus-k8s-1 prometheus-data-db 1m
现在我们再去看 Prometheus Pod 的数据目录就可以看到是关联到一个 PVC 对象上了。
$ kubectl get pod prometheus-k8s-0 -n monitoring -o yaml ...... volumeMounts: - mountPath: /etc/prometheus/config_out name: config-out readOnly: true - mountPath: /prometheus name: prometheus-k8s-db ...... volumes: ...... - name: prometheus-k8s-db persistentVolumeClaim: claimName: prometheus-k8s-db-prometheus-k8s-0 ......
现在即使我们的 Pod 挂掉了,数据也不会丢失了,最后,下面是我们 Prometheus Operator 系列课程中最终的创建资源清单文件,更多的信息可以在https://github.com/cnych/kubernetes-learning下面查看。
apiVersion: monitoring.coreos.com/v1 kind: Prometheus metadata: labels: prometheus: k8s name: k8s namespace: monitoring spec: alerting: alertmanagers: - name: alertmanager-main namespace: monitoring port: web storage: volumeClaimTemplate: spec: storageClassName: prometheus-data-db resources: requests: storage: 10Gi baseImage: quay.io/prometheus/prometheus nodeSelector: beta.kubernetes.io/os: linux replicas: 2 secrets: - etcd-certs additionalScrapeConfigs: name: additional-configs key: prometheus-additional.yaml resources: requests: memory: 400Mi ruleSelector: matchLabels: prometheus: k8s role: alert-rules securityContext: fsGroup: 2000 runAsNonRoot: true runAsUser: 1000 serviceAccountName: prometheus-k8s serviceMonitorNamespaceSelector: {} serviceMonitorSelector: {} version: v2.5.0
5.Prometheus operator 自定义报警
5.1 配置 PrometheusRule
如果需要自定义一个报警规则的话呢?比如现在我们去查看 Prometheus Dashboard 的 Alert 页面下面就已经有一些报警规则了,还有一些是已经触发规则的了:
但是这些报警信息是哪里来的呢?他们应该用怎样的方式通知我们呢?我们知道之前我们使用自定义的方式可以在 Prometheus 的配置文件之中指定 AlertManager 实例和报警的 rules 文件,现在我们通过 Operator 部署的呢?我们可以在 Prometheus Dashboard 的 Config 页面下面查看关于 AlertManager 的配置:
上面 alertmanagers 实例的配置我们可以看到是通过角色为 endpoints 的 kubernetes 的服务发现机制获取的,匹配的是服务名为 alertmanager-main,端口名未 web 的 Service 服务,我们查看下 alertmanager-main 这个 Service:
kubectl describe svc alertmanager-main -n monitoring Name: alertmanager-main Namespace: monitoring Labels: alertmanager=main Annotations: kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"alertmanager":"main"},"name":"alertmanager-main","namespace":"... Selector: alertmanager=main,app=alertmanager Type: ClusterIP IP: 172.20.71.161 Port: web 9093/TCP TargetPort: web/TCP Endpoints: 10.1.53.79:9093,10.1.54.229:9093,10.1.55.141:9093 Session Affinity: ClientIP Events: <none>
可以看到服务名正是 alertmanager-main,Port 定义的名称也是 web,符合上面的规则,所以 Prometheus 和 AlertManager 组件就正确关联上了。而对应的报警规则文件位于:/etc/prometheus/rules/prometheus-k8s-rulefiles-0/目录下面所有的 YAML 文件。我们可以进入 Prometheus 的 Pod 中验证下该目录下面是否有 YAML 文件:
$ kubectl exec -it prometheus-k8s-0 /bin/sh -n monitoring Defaulting container name to prometheus. Use 'kubectl describe pod/prometheus-k8s-0 -n monitoring' to see all of the containers in this pod. /prometheus $ ls /etc/prometheus/rules/prometheus-k8s-rulefiles-0/ monitoring-prometheus-k8s-rules.yaml /prometheus $ cat /etc/prometheus/rules/prometheus-k8s-rulefiles-0/monitoring-pr ometheus-k8s-rules.yaml groups: - name: k8s.rules rules: - expr: | sum(rate(container_cpu_usage_seconds_total{job="kubelet", image!="", container_name!=""}[5m])) by (namespace) record: namespace:container_cpu_usage_seconds_total:sum_rate ......
这个 YAML 文件实际上就是我们之前创建的一个 PrometheusRule 文件包含的:
$ cat prometheus-rules.yaml apiVersion: monitoring.coreos.com/v1 kind: PrometheusRule metadata: labels: prometheus: k8s role: alert-rules name: prometheus-k8s-rules namespace: monitoring spec: groups: - name: k8s.rules rules: - expr: | sum(rate(container_cpu_usage_seconds_total{job="kubelet", image!="", container_name!=""}[5m])) by (namespace) record: namespace:container_cpu_usage_seconds_total:sum_rate
我们这里的 PrometheusRule 的 name 为 prometheus-k8s-rules,namespace 为 monitoring,我们可以猜想到我们创建一个 PrometheusRule 资源对象后,会自动在上面的 prometheus-k8s-rulefiles-0 目录下面生成一个对应的<namespace>-<name>.yaml文件,所以如果以后我们需要自定义一个报警选项的话,只需要定义一个 PrometheusRule 资源对象即可。至于为什么 Prometheus 能够识别这个 PrometheusRule 资源对象呢?这就需要查看我们创建的 prometheus 这个资源对象了,里面有非常重要的一个属性 ruleSelector,用来匹配 rule 规则的过滤器,要求匹配具有 prometheus=k8s 和 role=alert-rules 标签的 PrometheusRule 资源对象,现在明白了吧?
prometheus-prometheus.yaml
ruleSelector:
matchLabels:
prometheus: k8s
role: alert-rules
所以我们要想自定义一个报警规则,只需要创建一个具有 prometheus=k8s 和 role=alert-rules 标签的 PrometheusRule 对象就行了,比如现在我们添加一个 etcd 是否可用的报警,我们知道 etcd 整个集群有一半以上的节点可用的话集群就是可用的,所以我们判断如果不可用的 etcd 数量超过了一半那么就触发报警,创建文件 prometheus-etcdRules.yaml:
apiVersion: monitoring.coreos.com/v1 kind: PrometheusRule metadata: labels: prometheus: k8s role: alert-rules name: etcd-rules namespace: monitoring spec: groups: - name: etcd rules: - alert: EtcdClusterUnavailable annotations: summary: etcd cluster small description: If one more etcd peer goes down the cluster will be unavailable expr: | count(up{job="etcd"} == 0) > (count(up{job="etcd"}) / 2 - 1) for: 3m labels: severity: critical
注意 label 标签一定至少要有 prometheus=k8s 和 role=alert-rules,创建完成后,隔一会儿再去容器中查看下 rules 文件夹:
kubectl exec -it prometheus-k8s-0 /bin/sh -n monitoring Defaulting container name to prometheus. Use 'kubectl describe pod/prometheus-k8s-0 -n monitoring' to see all of the containers in this pod. /prometheus $ ls /etc/prometheus/rules/prometheus-k8s-rulefiles-0/ monitoring-etcd-rules.yaml monitoring-prometheus-k8s-rules.yaml
可以看到我们创建的 rule 文件已经被注入到了对应的 rulefiles 文件夹下面了,证明我们上面的设想是正确的。然后再去 Prometheus Dashboard 的 Alert 页面下面就可以查看到上面我们新建的报警规则了:
5.2 配置报警
我们知道了如何去添加一个报警规则配置项,但是这些报警信息用怎样的方式去发送呢?前面的课程中我们知道我们可以通过 AlertManager 的配置文件去配置各种报警接收器,现在我们是通过 Operator 提供的 alertmanager 资源对象创建的组件,应该怎样去修改配置呢?
首先我们将 alertmanager-main 这个 Service 改为 NodePort 类型的 Service,修改完成后我们可以在页面上的 status 路径下面查看 AlertManager 的配置信息:
这些配置信息实际上是来自于我们之前在prometheus-operator/contrib/kube-prometheus/manifests目录下面创建的 alertmanager-secret.yaml 文件:
apiVersion: v1 data: alertmanager.yaml: Imdsb2JhbCI6IAogICJyZXNvbHZlX3RpbWVvdXQiOiAiNW0iCiJyZWNlaXZlcnMiOiAKLSAibmFtZSI6ICJudWxsIgoicm91dGUiOiAKICAiZ3JvdXBfYnkiOiAKICAtICJqb2IiCiAgImdyb3VwX2ludGVydmFsIjogIjVtIgogICJncm91cF93YWl0IjogIjMwcyIKICAicmVjZWl2ZXIiOiAibnVsbCIKICAicmVwZWF0X2ludGVydmFsIjogIjEyaCIKICAicm91dGVzIjogCiAgLSAibWF0Y2giOiAKICAgICAgImFsZXJ0bmFtZSI6ICJEZWFkTWFuc1N3aXRjaCIKICAgICJyZWNlaXZlciI6ICJudWxsIg== kind: Secret metadata: name: alertmanager-main namespace: monitoring type: Opaque
可以将alertmanager.yaml 对应的 value 值做一个 base64 解码:
$ echo "Imdsb2JhbCI6IAogICJyZXNvbHZlX3RpbWVvdXQiOiAiNW0iCiJyZWNlaXZlcnMiOiAKLSAibmFtZSI6ICJudWxsIgoicm91dGUiOiAKICAiZ3JvdXBfYnkiOiAKICAtICJqb2IiCiAgImdyb3VwX2ludGVydmFsIjogIjVtIgogICJncm91cF93YWl0IjogIjMwcyIKICAicmVjZWl2ZXIiOiAibnVsbCIKICAicmVwZWF0X2ludGVydmFsIjogIjEyaCIKICAicm91dGVzIjogCiAgLSAibWF0Y2giOiAKICAgICAgImFsZXJ0bmFtZSI6ICJEZWFkTWFuc1N3aXRjaCIKICAgICJyZWNlaXZlciI6ICJudWxsIg==" | base64 -d "global": "resolve_timeout": "5m" "receivers": - "name": "null" "route": "group_by": - "job" "group_interval": "5m" "group_wait": "30s" "receiver": "null" "repeat_interval": "12h" "routes": - "match": "alertname": "DeadMansSwitch" "receiver": "null"
我们可以看到内容和上面查看的配置信息是一致的,所以如果我们想要添加自己的接收器,或者模板消息,我们就可以更改这个文件:
global: resolve_timeout: 5m # 告警自定义邮件 #smtp_smarthost: 'smtp.163.com:25' #smtp_from: 'lizexiongtest@163.com' #smtp_auth_username: 'lizexiongtest@163.com' #smtp_auth_password: 'lzx1231' smtp_smarthost: 'postfix.public:25' smtp_from: 'alert@dena.com' smtp_auth_username: 'alert' smtp_auth_password: 'renyi' smtp_require_tls: false receivers: - name: 'email' email_configs: - to: "zexiong.li@dena.com" send_resolved: true text: 'https://internal.myorg.net/wiki/alerts/{{ .GroupLabels.app }}/{{ .GroupLabels.alertname }}' route: group_by: ['alertname'] group_interval: 20s group_wait: 20s receiver: 'email' repeat_interval: 20s
将上面文件保存为 alertmanager.yaml,然后使用这个文件创建一个 Secret 对象:(注意,这个文件名一定要和alertmanager-alertmanager.yaml里面的对应上,包括yaml的后缀)
# 先将之前的 secret 对象删除 $ kubectl delete secret alertmanager-main -n monitoring secret "alertmanager-main" deleted $ kubectl create secret generic alertmanager-main --from-file=alertmanager.yaml -n monitoring secret "alertmanager-main" created
可以查看收到告警
我们再次查看 AlertManager 页面的 status 页面的配置信息可以看到已经变成上面我们的配置信息了:
5.3 告警模版
AlertManager 配置也可以使用模板(.tmpl文件),这些模板可以与 alertmanager.yaml 配置文件一起添加到 Secret 对象中,比如:
apiVersion:v1 kind:secret metadata: name:alertmanager-example data: alertmanager.yaml:{BASE64_CONFIG} #这里的模版只是一个变量,这里要替换成实际的 template_1.tmpl:{BASE64_TEMPLATE_1} template_2.tmpl:{BASE64_TEMPLATE_2} ...
模板会被放置到与配置文件相同的路径,当然要使用这些模板文件,还需要在 alertmanager.yaml 配置文件中指定:
templates: - '*.tmpl'
创建成功后,Secret 对象将会挂载到 AlertManager 对象创建的 AlertManager Pod 中去。