第13章 监控Tornado
- 我们将在Kubernetes集群上部署一个名为Tornado的应用程序并对其进行监控。Tornado是一个简单的REST-ful HTTP API,用Clojure语言编写的,可在JVM上运行,具有 Redis数据存储和Mysql
- 监控如下内容
- Mysql
- Redis
- Tornado API应用程序
13.1 边车模式
- 边车为应用程序提供支持功能——例如,基础设施边车可能会收集日志或进行监控。边车还与父应用程序共享相同的生命周期,与父应用程序一起创建和删除
提示:边车有时也被称为sidekicks
13.2 Mysql
- 使用Prometheus监控Mysql是通过exporter完成的:Mysqld exporter。exporter使用提供的凭证连接到Mysql服务器并查询服务器状态。使用边车模式在部署到Kubernetes的Docker容器中运行exporter
- 代码清单:exporter容器
- 使用带有latest标签的
prom/mysqld-exporter
镜像,并将容器命名为tornado-db-exp。我们使用DATA_SOURCE_NAME环境变量指定了数据库连接的详细信息,此连接使用DSN格式配置Mysql服务器的连接和凭据的详细信息 - 代码清单:连接到Mysql容器
kubectl exec -ti <pod> -- /usr/bin/mysql -p
- 代码清单:创建Mysql用户
- 代码清单:额外的Mysql exporter收集器
- 从Mysql的performance_schema数据库中收集数据,跟踪特定查询和操作的性能
- 代码清单:tornado-db服务
- 使用了两个注解:
prometheus.io/scrape
告诉Prometheus抓取这个服务:prometheus.io/port
告诉要抓取的端口。我们指定这一点是希望Prometheus在端口9104上访问Mysql Exporter,而不是直接访问Mysql服务器 - 代码清单:Kubernetes端点作业的重新标记
relabel_configs:
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
action: keep
regex: true
- source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port]
action: replace
target_label: __address__
regex: ([^:] )(?::d )?;(d )
replacement: $1:$2
……
prometheus.io/port
注解将被注入__address__
标签中,以便被作业抓取。接下来的服务发现将开始收集这些Mysql指标
Mysql监控配置
- 我们将创建一个可能的规则样本,大致与Google的四个黄金指标一致,让你了解如何使用Mysql指标。将专注于
- 延迟
- 流量
- 错误
- 饱和度
警告:测量Mysql性能很难,尤其是在跟踪延迟等信号时,情况会因应用程序和服务器配置的不同而有很大差异。这些规则为你提供了起点,而不是明确的答案
- 代码清单:Mysql慢查询警报
- alert: MySQLHighSlowQuerysHigh
expr: rate(mysql_global_status_slow_queries[2m]) > 5
labels:
severity: warning
annotations:
summary: MySQL Slow query rate is exceeded on {{ $labels.instance }} for {{ $labels.kubernetes_name }}
- 如果两分钟内慢查询超过5个,则会生成一个警报
- 代码清单:Mysql请求速率记录mysql_rules.yml
groups:
- name: mysql_rules
rules:
- record: mysql:write_requests:rate2m
expr: sum(rate(mysql_global_status_commands_total{command=~"insert|update|delete"}[2m])) without (command)
- record: mysql:select_requests:rate2m
expr: sum(rate(mysql_global_status_commands_total{command="select"}[2m]))
- record: mysql:total_requests:rate2m
expr: rate(mysql_global_status_commands_total[2m])
- 我们使用mysql_global_status_commands_total指标并获取特定命令的所有写请求:insert、update和delete。然后,为这些请求计算两分钟的速率。我们对使用select命令的读取请求和总请求执行相同的操作。然后使用topk聚合运算符来获取最近五分钟内根据模式和速率分组的最常用语句,这有助于我们理解服务器正在做什么
- 代码清单:连接和中止的连接
- alert: MySQLAbortedConnectionsHigh
expr: rate(mysql_global_status_aborted_connects[2m]) > 5
labels:
severity: warning
annotations:
summary: MySQL Aborted connection rate is exceeded on {{ $labels.instance }} for {{ $labels.kubernetes_name }}
- 我们将警告中止连接的速率是否超过阀值,并创建一个记录规则以跟踪整体连接速率
- 最后,我们想知道Mysql服务何时不可用。这些警报使用服务状态和特定于exporter的up指标的组合:mysql_up。mysql_up指标在Mysql服务器上执行SELECT 1,如果查询成功,则将其设置为1。第一个警报检查mysql_up指标的值 是否为0,0表示查询失败。第二个警报在服务消失且指标过期时检查此指标的存在
- 代码清单:Mysql警报
- alert: TornadoDBServerDown
expr: mysql_up{kubernetes_name="tornado-db"} == 0
for: 10m
labels:
severity: critical
annotations:
summary: MySQL Server {{ $labels.instance }} is down!
13.3 Redis
- 像Mysql一样,Promethseus也有对应Redis的exporter。Redis Exporter将从INFO命令导出大部分条目,其中包含服务器、客户端、内存和CPU使用情况的详细信息。在每个数据库中,还有用于键总数、过期键和键的平均TTL的指标,你可以导出这些键的值
- 代码清单:Redis服务和边车
apiVersion: apps/v1beta2
kind: Deployment
- name: redis-exporter
image: oliver006/redis_exporter:latest
env:
- name: REDIS_ADDR
value: redis//tornado-redis:6379
- name: REDIS_PASSWORD
value: tornadoapi
ports:
- containerPort: 9121
- 我们正在运行一个名为redis-exporter的容器,该容器通过Docker镜像构建。指定了两个环境变量和端口
- 代码清单:通过Kubernetes暴露Redis服务端口
- 可以看到我们暴露了端口9121,并指定了两个注解:一个用于告诉Prometheus服务端点作业要抓取这个服务,另一个用于指示要抓取的端口。下一次Prometheus进行服务发现时,它将检测更新后的服务并开始收集redis指标
Redis监控配置
- 代码清单:Redis警报
- alert: TornadoRedisCacheMissesHigh
expr: redis_keyspace_hits_total / (redis_keyspace_hits_total redis_keyspace_misses_total) > 0.8
for: 10m
labels:
severity: warning
annotations:
summary: Redis Server {{ $labels.instance }} Cache Misses are high.
- 我们想知道Redis服务何时不可用。这些警报使用服务状态和特定于exporter的up指标的组合:redis_up。如果Redis服务器的抓取成功,则redis_up指标设置为1.第一个警报检查redis_up指标的值是否为0,0表示查询失败。第二个警报在服务消失且指标过期时检查此指标的存在
- 代码清单:Redis可用性警报
- alert: TornadoRedisServerDown
expr: redis_up{kubernetes_name="tornado-redis"} == 0
for: 10m
labels:
severity: critical
annotations:
summary: Redis Server {{ $labels.instance }} is down!
13.4 Tornado
- Tornado API是一个Clojure应用程序,它使用Ring并在JVM上运行。应用程序提供了一个API端点,可以购买和销售商品(https://github.com/ring-clojure/ring)
13.4.1 添加Clojure包装器
- 为了检测应用程序,我们使用了iapetos Clojure包装器(https://github.com/clj-commons/iapetos)。要启用iapetos包装器,需要将它添加到project.clj文件项目的依赖项中
- 代码清单:project.clj
(defproject tornado-api "0.1.0-SNAPSHOT"
:description "Example Clojure REST service for AoM"
:url "http://artofmonitoring.com"
:dependencies [[org.clojure/clojure "1.8.0"]
[compojure "1.1.1"]
[ring/ring-json "0.1.2"]
[ring/ring-jetty-adapter "1.3.1"]
[ring-logger-timbre "0.7.5"]
[com.taoensso/timbre "4.2.1"]
[c3p0/c3p0 "0.9.1.2"]
[org.clojure/java.jdbc "0.4.2"]
[mysql/mysql-connector-java "5.1.38"]
[com.taoensso/carmine "2.12.2"]
[cheshire "4.0.3"]
[clj-statsd "0.3.11"]]
:plugins [[lein-ring "0.7.3"]]
:main tornado-api.handler
:ring {:handler tornado-api.handler/app}
:profiles {
:dev {:dependencies [[ring-mock "0.1.3"]]}
:uberjar {:aot :all}})
13.4.2 添加注册表
- 我们需要定义一个注册表来保存所有指标
- 代码清单:定义注册表
- 我们创建了一个名为registry的注册表,并且已经初始化了Ring和JVM指标,这些指标将被自动收集或导出。然后我们定义了五个特定的指标,包括四个计数型指标和一个测量型指标
- 代码清单:添加标签
- 我们为item-bought计数器添加了description标签
13.4.3 添加指标
- 我们现在可以在应用程序上为每个API方法添加函数调用以递增计数器
- 代码清单:添加指标调用
(defn buy-item [item]
(let [id (uuid)]
(sql/db-do-commands db-config
(let [item (assoc item "id" id)]
(sql/insert! db-config :items item)
(statsd/gauge (str statsd-prefix "item.bought.total") (item "price"))))
(wcar* (car/ping)
(car/set id (item "title")))
(get-item id)))
- 我们调用inc函数,以便在购买商品时增加item-bought计数器
- 我们还添加了一个名为tornado_up的测量型指标,它将作为应用程序的up指标。当应用程序启动时,它会自动将值 设置为1
- 代码清单:测量型指标tornado_up
(prometheus/set (registry :tornado/up) 1)
13.4.4 导出指标
- 我们希望启用/metrics页面本身,在示例中是使用内置的Ring支持
- 代码清单:开始导出
(def app
(-> (handler/api app-routes)
(middleware/wrap-json-body)
(middleware/wrap-json-response)
(ring/wrap-metrics registry {:path "/metrics")))
- 代码清单:Tornado指标
13.4.5 Tornado监控配置
- 使用一个Ring HTTP指标创建的延迟记录规则
- 代码清单:Ring延时规则
- record: tornado:request_latency_seconds:avg
expr: http_request_latency_seconds_sum{status="200"} / http_request_latency_seconds_count{status="200"}
- 我们创建了一个新指标tornado:request_latency_seconds:avg,表示响应为200 HTTP代码的请求的平均请求延迟(秒)
- 还可以利用一个与Ring相关的历史记录来触发高延迟警报
- 代码清单:Ring高延时警报
- alert: TornadoRequestLatencyHigh
expr: histogram_quantile(0.9, rate(http_request_latency_seconds_bucket{kubernetes_name="tornado-api"}[5m])) > 0.05
for: 10m
labels:
severity: warning
annotations:
summary: API Server {{ $labels.instance }} latency is over 0.05.
- 这里,我们使用histogram_quantile函数(https://prometheus.io/docs/prometheus/latest/querying/functions/#histogram_quantile)在5分钟内生成请求延迟的第90百分位数。如果持续10分钟超过0.05,那么警报将被触发
- 还可以利用创建的up风格指标tornado_up来监控API服务的可用性
- 代码清单:监控Tornado API可用性
- alert: TornadoAPIServerDown
expr: tornado_up{kubernetes_name="tornado-api"} != 1
for: 10m
labels:
severity: critical
annotations:
summary: API Server {{ $labels.instance }} is down!
- alert: TornadoAPIServerGone
expr: absent(tornado_up{kubernetes_name="tornado-api"})
for: 10m
labels:
severity: critical
annotations:
summary: No Tornado API servers are reporting!
description: Werner Heisenberg says - there is no uncertainty about the Tornado API server being gone.
- 在这里,我们将检测tornado_up指标的值是否大于0,或者它是否从我们的指标中消失