监控能力接入

监控接入标准

ADP交付的云原生底座默认自带Prometheus-stack,指标暴露方式符合Prometheus的Metrics规范即可。

监控指标接入

接入流程图

接入Metrics监控报警服务,需要以下4个步骤:

业务指标

基础指标监控由拙知团队负责,无需各个业务方负责,基础指标主要包括:

  • 容器云机器节点基础指标监控(CPU/内存/磁盘/网络等)

  • 非容器云机器节点基础指标监控(CPU/内存/磁盘/网络等)

  • k8s资源监控(pod/service/node的基础指标)

  • 基础中间件指标监控(mysql/redis/ceph/zk)

各个业务方需要梳理各自业务的metrics指标,例如:核心API调用次数、RT等。

应用metrics暴露

前提条件:应用需要暴露metrics api,以java类应用为例,prometheus 提供了JVM Client:

Prometheus提供了一个client_java项目可以很方便的将JVM和自定义的指标暴露出来。

  • simpleclient_hotspot

  • simpleclient_spring_web

  • simpleclient_spring_boot

  • simpleclient_httpserver

使用Java可以开发以下两种服务:

第一种是使用spring-boot-starter-web开发的HTTP Restful API,这类服务要集成Prometheus十分简单,只需要在项目中依赖管理如引入simpleclient_hotspotsimpleclient_spring_boot

compile 'io.prometheus:simpleclient_spring_boot:0.1.0'
compile 'io.prometheus:simpleclient_hotspot:0.1.0'

simpleclient_spring_boot中的io.prometheus.client.spring.boot.PrometheusEndpointConfiguration会将prometheus exporter注册为Spring Boot Actuator的enpoint。启动这个配置只需在spring boot项目的Appication类上加上@EnablePrometheusEndpoint的注解,例如:

@EnablePrometheusEndpoint
@EnableSpringBootMetricsCollector
@SpringBootApplication
public class BootApplication {
  @PostConstruct
 public void init() {
  DefaultExports.initialize();
 }
    ......

当然spring boot的配置文件中需要配置如何暴露这个endpoint:

management:
  port: 8088
  security:
    enabled: false
  health:
    defaults:
      enabled: false
endpoints:
  enabled: false
  prometheus:
    enabled: true
    path: /prometheus
  health:
    enabled: true
    path: /health

此时http://:8088/prometheus这个端点就是这个Java应用暴露的监控指标。

第二种是使用最基本的spring-boot-starter和其他starter开发的rpc服务(thrift,gRPC等),本身不包含嵌入的tomcat,而Prometheus的exporter需要以HTTP暴露监控指标。需要在项目中依赖管理如引入simpleclient_hotspotsimpleclient_httpserver

compile 'io.prometheus:simpleclient_httpserver:0.1.0'
compile 'io.prometheus:simpleclient_hotspot:0.1.0'

simpleclient_httpserver库包含一个简单的httpserver,使用其完成监控指标的暴露:

DefaultExports.initialize();
new io.prometheus.client.exporter.HTTPServer.HTTPServer(8088);

http://:8088这个端点就是这个Java应用暴露的监控指标。

参考Demo:

1.新增maven依赖:

<!-- the client -->
<dependency>
   <groupId>io.prometheus</groupId>
   <artifactId>simpleclient_spring_boot</artifactId>
   <version>0.4.0</version>
</dependency>
<!-- Hotspot JVM metrics-->
<dependency>
   <groupId>io.prometheus</groupId>
   <artifactId>simpleclient_hotspot</artifactId>
   <version>0.4.0</version>
</dependency>
<!-- Exposition HTTPServer-->
<dependency>
   <groupId>io.prometheus</groupId>
   <artifactId>simpleclient_httpserver</artifactId>
   <version>0.4.0</version>
</dependency>

2.application.yml配置:

server:
  port: 8080

management:
  security:
    enabled: false #关掉安全认证
  port: 8082 #管理端口8082
  context-path: /monitor #actuator的访问路径
endpoints:
  shutdown:
    enabled: true

3.启动类注解:

@SpringBootApplication
@EnablePrometheusEndpoint
@EnableSpringBootMetricsCollector
public class MonitorDemoApplication {
    ...
}

集成Prometheus SDK后,actuator收集到的数据将转化为prometheus可识别的结构,运行结果示例:

# TYPE redis_db_keys gauge
redis_db_keys{addr="redis://localhost:6379",alias="",db="db0"} 80
redis_db_keys{addr="redis://localhost:6379",alias="",db="db1"} 0
redis_db_keys{addr="redis://localhost:6379",alias="",db="db10"} 0
redis_db_keys{addr="redis://localhost:6379",alias="",db="db11"} 0
redis_db_keys{addr="redis://localhost:6379",alias="",db="db12"} 0
redis_db_keys{addr="redis://localhost:6379",alias="",db="db13"} 0
redis_db_keys{addr="redis://localhost:6379",alias="",db="db14"} 0
redis_db_keys{addr="redis://localhost:6379",alias="",db="db15"} 0
redis_db_keys{addr="redis://localhost:6379",alias="",db="db2"} 0
redis_db_keys{addr="redis://localhost:6379",alias="",db="db3"} 0
redis_db_keys{addr="redis://localhost:6379",alias="",db="db4"} 0
redis_db_keys{addr="redis://localhost:6379",alias="",db="db5"} 0
redis_db_keys{addr="redis://localhost:6379",alias="",db="db6"} 0
redis_db_keys{addr="redis://localhost:6379",alias="",db="db7"} 0
redis_db_keys{addr="redis://localhost:6379",alias="",db="db8"} 0
redis_db_keys{addr="redis://localhost:6379",alias="",db="db9"} 0

敏捷PaaS 配置统一使用/prometheus 路径对外透出http接口

https://github.com/prometheus/client_python

  1. 将 Pod 的 metrics 的 port 透出:

# POD yaml
...
metadata:
  annotations:
    # The default configuration will scrape all pods and, if set to false, this annotation will exclude the pod from the scraping process.
    # prometheus.io/scrape: 'false'
    # If the metrics are exposed on a different port to the service then set this appropriately
    # prometheus.io/port: 'metrics'
    ## If the metrics path is not /metrics, define it with this annotation.
    # prometheus.io/path: '/metrics'
    # If the metrics endpoint is secured then you will need
    # to set this to `https` & most likely set the `tls_config` of the scrape config.
    # prometheus.io/scheme: 'https'
spec:
  containers:
    - ports:
      # port 命名包含 'metrics', 建议: 'http-metrics'
      - name: http-metrics
        containerPort: 8081
        protocol: TCP
...

2.在Service资源里面配置metrics的port(name一定要是包含 'metrics',targetPort根据pod的实际情况来定):

...
spec:
  # port 命名包含 'metrics', 建议: 'http-metrics'
  - name: http-metrics
    port: 8081
    protocol: TCP
    targetPort: http-metrics
...

3.在Service 资源里面加以下标签(prometheus会使用这个标签来选择service,达到服务发现的目的):

...
metadata:
  labels:
    product: emas
...

  • Pod 级别监控、比较适合有状态的的 replica pods, i.e., MySQL Master&Slave Pods

apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
  name: myapp
spec:
  podMetricsEndpoints:
  - interval: 15s
    port: http-metrics
  selector:
    matchLabels:
      app.kubernetes.io/component: myapp
  namespaceSelector:
    any: true

  • 服务级别的监控,比较适合无状态或多主架构有状态的服务

piVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: myapp-svc
spec:
  endpoints:
  - interval: 15s
    port: http-metrics
    scheme: http
  selector:
    matchLabels:
      app: myapp

配置问题汇总

1.新增/修改service里的port不生效

原因分析:很可能是因为port冲突,一个service里一个port只能定义一次。

解决办法:更换port。

最终采集到的数据可以在prometheus控制台查询到:

1

配置报警

平台团队负责,报警通道目前暂只支持邮件和钉钉机器人报警。

各个业务方需要根据透出的metrics指标来梳理报警阀值和报警规则创建 PrometheusRule CR。Rules 内容参考官方文档 -https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/

配置示例:

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  labels:
    prometheus: k8s
    role: alert-rules
  name: prometheus-k8s-rules
  namespace: acs-system
spec:
  groups:
  - name: k8s.rules
    rules:
    - expr: |
        sum(container_memory_usage_bytes{job="kubelet", image!=""}) by (namespace)
      record: namespace:container_memory_usage_bytes:sum

# 确保 curl & yq 工具已安装 (`yum install -y curl jq`)

ALERTMANAGER_EP="$(kubectl -n acs-system get svc kube-prometheus-stack-alertmanager -o jsonpath='{.spec.clusterIP}:{.spec.ports[0].port}')"

## Alert Manager OpenAPI Spec ref: https://github.com/prometheus/alertmanager/blob/master/api/v2/openapi.yaml
# 查询当前报警
curl -g "${ALERTMANAGER_EP}/api/v2/alerts" | jq

# 查询当前报警按照 groups 区分
curl -g "${ALERTMANAGER_EP}/api/v2/alerts/group" | jq

# 查询 alertmanager 服务状态及配置信息
curl -g "${ALERTMANAGER_EP}/api/v2/status" | jq

# 查询当前 silence 报警列表
curl -g "${ALERTMANAGER_EP}/api/v2/silences" | jq

# 查询 silence ID
curl -g "${ALERTMANAGER_EP}/api/v2/silence/{silenceID}" | jq

# 删除 silence ID
curl -g -X DELETE "${ALERTMANAGER_EP}/api/v2/silence/{silenceID}" | jq

# silence 报警,根据 label key & value, e.g., label=alertname=etcdInsufficientMembers
curl -g -X POST --header "Content-Type: application/json" \
--data '{"matchers":[{"name":"alertname","value":"etcdInsufficientMembers","isRegex":false}],"startsAt":"2021-03-08T08:07:51.625Z","endsAt":"2021-03-09T03:43:21.625Z","createdBy":"admin","comment":"silence"}' \ "${ALERTMANAGER_EP}/api/v2/silences"

配置图表

各个业务方还需负责 Grafana (v7.2) 展示图表的配置,包括使用 PromQL 从 prometheus查询数据、数据聚合、数据计算、图表样式配置

kind: ConfigMap
metadata:
  name: <dashboard-name>
  labels:
    # Grafana Dashbaord JSON 发现标签
    grafana_dashboard: "1" 
apiVersion: v1
data:
  <dashboard-name>.json: |-
    {
      ...
    }

dashboard配置参考:https://play.grafana.org/d/000000003/big-dashboard?orgId=1

常用组件dashboard:https://grafana.com/dashboards

示例:

存储空间需求

参考OpenShift Prometheus DB Storage给出的评估

1
# 确保 curl & yq 工具已安装 (`yum install -y curl jq`)

PROMETHEUS_EP="$(kubectl -n acs-system get svc kube-prometheus-stack-prometheus -o jsonpath='{.spec.clusterIP}:{.spec.ports[0].port}')"

# Number of Time Series
curl --data-urlencode 'query=max_over_time(prometheus_tsdb_head_series[1d])' "${PROMETHEUS_EP}/api/v1/query?" | jq .

# Average bytes per sample
curl --data-urlencode 'query=rate(prometheus_tsdb_compaction_chunk_size_bytes_sum[1d])/rate(prometheus_tsdb_compaction_chunk_samples_sum[1d])' "${PROMETHEUS_EP}/api/v1/query?" | jq .

Prometheus 内存计算器:https://www.robustperception.io/how-much-ram-does-prometheus-2-x-need-for-cardinality-and-ingestion

Prometheus 高内存消耗排查

# 确保 curl & yq 工具已安装 (`yum install -y curl jq`)

PROMETHEUS_EP="$(kubectl -n acs-system get svc kube-prometheus-stack-prometheus -o jsonpath='{.spec.clusterIP}:{.spec.ports[0].port}')"


# 定位出 top 20 biggest time series by metric name and job
curl --data-urlencode 'query=topk(20,count by(__name__,job)({__name__=~".+"}))' "${PROMETHEUS_EP}/api/v1/query?" | jq .

评估是否可以在 scrape job 里的 'metric_relabel_configs' 指定 action=drop 或 ServiceMonitor 里的spec.endpoints[].metricRelabelings[].action=drop来忽略 metric 指标,e.g., 有个指标my_bad_metric:

## scrap job configs
scrape_configs:
 - job_name: 'my_job'
   # ...
   metric_relabel_configs:
   - : [ __name__ ]
     regex: 'my_bad_metric'
     action: drop

## servicemonitors.monitoring.coreos.com yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
...
spec:
  endpoints:
  - ...
    metricRelabelings:
    - ...
      action: drop

监控大盘展示

运维控制台会自动分析组件加载的GrafanaDashboard,并在组件的【组件监控】内进行嵌入,如果组件有多个GrafanaDashboard,可以在Tab进行切换。

1dashboard发现规则:通过helmChart引入监控大盘配置的configmap。prometheus-stack会自动加载dashboard资源,自定义的dashboard通过configmap保存,并使用label: grafana_dashboard=1,就能被grafana及adp-local自动发现

查看社区样例

非K8S集群内服务接入

可能有部分服务由于特殊性质并没有部署在Kubernetes集群内,但也可能会想接入同一套监控体系中。可在节点不多的情况下使用,手动维护ServiceMonitor内的endpoints信息。在Prometheus内进行consul_sd_config配置即可。

若已制作带业务+Consul(已配置)的虚拟机镜像,扩容会很方便,无需额外配置,在节点多的场景减少维护成本,若业务本身就依赖Consul,额外配置成本非常低,多了一个中间配置,可能会引入额外的不确定性和运维工作。若没有一体化的镜像,新增节点依然要进行一些手动配置的(如Consul的配置),其工作复杂度和手动维护ServiceMonitor类似。