0%

什么是Prometheus

Prometheus是由SoundCloud开发的开源监控报警系统和时序列数据库(TSDB)。Prometheus使用Go语言开发。

2016年由Google发起Linux基金会旗下的原生云基金会(Cloud Native Computing Foundation), 将Prometheus纳入其下第二大开源项目。

Prometheus特点

多维数据模型:由度量名称和键值对标识的时间序列数据

1
node_cpu_seconds_total{cpu="0",endpoint="https",instance="k8s-master",job="node-exporter",mode="idle",namespace="monitoring",pod="node-exporter-t9ljw",service="node-exporter"}

PromSQL是一种灵活的查询语言,可以利用多维数据完成复杂的查询

不依赖分布式存储,单个服务器节点可直接工作

基于HTTP的pull方式采集时间序列数据

推送时间序列数据通过PushGateway组件支持

通过服务发现或静态配置发现目标

多种图形模式及仪表盘支持

Prometheus架构

Prometheus 由多个组件组成,但是其中许多组件是可选的:

Prometheus Server

用于收集指标和存储时间序列数据,并提供查询接口(http api)

Client Library

客户端库(例如Go,Python,Java等),为需要监控的服务产生相应的/metrics并暴露给Prometheus Server。目前已经有很多的软件原生就支持Prometheus,提供/metrics,可以直接使用。对于像操作系统已经不提供/metrics,可以使用exporter,或者自己开发exporter来提供/metrics服务。

Push Gateway

主要用于临时性的jobs。由于这类jobs存在时间较短,可能在Prometheus pull之前就消失了。对此Jobs定时将指标push到pushgateway,再由Prometheus
Server从Pushgateway上pull。这种方式主要用于服务层面的metrics

exporter

用于暴露已有的第三方服务的 metrics 给 Prometheus。

alertmanager

从 Prometheus server 端接收到 alerts 后,会进行去除重复数据,分组,并路由到对应的接收方式,发出报警。常见的接收方式有:电子邮件,pagerduty,OpsGenie, webhook 等。

Web UI

Prometheus内置一个简单的Web控制台,可以查询指标,查看配置信息或者Service Discovery等,实际工作中,查看指标或者创建仪表盘通常使用Grafana,Prometheus作为Grafana的数据源。

基本原理

Prometheus的基本原理是通过HTTP协议周期性抓取被监控组件的状态,任意组件只要提供对应的HTTP接口就可以接入监控。不需要任何SDK或者其他的集成过程。这样做非常适合做虚拟化环境监控系统,比如VM、Docker、Kubernetes等。输出被监控组件信息的HTTP接口被叫做exporter 。目前互联网公司常用的组件大部分都有exporter可以直接使用,比如Varnish、Haproxy、Nginx、MySQL、Linux系统信息(包括磁盘、内存、CPU、网络等等)。

服务过程

Prometheus Daemon负责定时去目标上抓取metrics(指标)数据,每个抓取目标需要暴露一个http服务的接口给它定时抓取。Prometheus支持通过配置文件、文本文件、Zookeeper、Consul、DNS SRV Lookup等方式指定抓取目标。Prometheus采用PULL的方式进行监控,即服务器可以直接通过目标PULL数据或者间接地通过中间网关来Push数据。

Prometheus在本地存储抓取的所有数据,并通过一定规则进行清理和整理数据,并把得到的结果存储到新的时间序列中。

Prometheus通过PromQL和其他API可视化地展示收集的数据。Prometheus支持很多方式的图表可视化,例如Grafana、自带的Promdash以及自身提供的模版引擎等等。Prometheus还提供HTTP API的查询方式,自定义所需要的输出。

PushGateway支持Client主动推送metrics到PushGateway,而Prometheus只是定时去Gateway上抓取数据。

Alertmanager是独立于Prometheus的一个组件,可以支持Prometheus的查询语句,提供十分灵活的报警方式。

三大套件

Prometheus Server

主要负责数据采集和存储,提供PromQL查询语言的支持。

Alertmanager

警告管理器,用来进行报警。

Push Gateway

支持临时性Job主动推送指标的中间网关。

数据模型

Prometheus将所有数据存储为时间序列;具有相同度量名称以及标签属于同一个指标。

每个时间序列都由度量标准名称和一组键值对(也成为标签)唯一标识。

时间序列格式:

<metric name>{<label name>=<label value>, ...}

指标类型

Counter

递增的计数器,如CPU使用时间。

Gauge

可以任意变化的数值:cup使用率

Histogram

对一段时间范围内数据进行采样,并对所有数值求和与统计数量

Summary

与Histogram类似,带有分位数。

抓取实例

1
2
3
4
5
6
7
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090','192.168.1.100:9090']
- job_name: 'node'
static_configs:
- targets: ['192.168.1.10:9090']

k8s集群三个节点,一个master node, 两个worker node。

我们可以看到prometheus以NodePort方式把服务暴露出来。

1
2
3
kubectl get svc -n monitoring
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
prometheus-k8s NodePort 10.105.212.163 <none> 9090:30090/TCP 45d

我们通过<worker-node-ip>:30090的方式,可以正确访问到页面,但通过<master-node-ip>:30090,却不能正常访问页面。

随即,我查看了calico网络插件的运行情况。

1
2
3
4
5
kubectl get po  -n kube-system
NAME READY STATUS RESTARTS AGE
calico-node-jt4fz 0/1 Running 4 45d
calico-node-mv4ht 1/1 Running 5 45d
calico-node-nqbkl 1/1 Running 5 45d

其中一个pod,并未通过健康检查。

查看一下这个pod的详情信息。

1
2
3
4
5
6
7
8
kubectl describe po calico-node-jt4fz -n kube-system
Name: calico-node-jt4fz
Namespace: kube-system
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning Unhealthy 6m22s (x499 over 89m) kubelet, k8s-master (combined from similar events): Readiness probe failed: calico/node is not ready: BIRD is not ready: BGP not established with 172.16.64.232,172.16.64.2352020-02-12 12:36:36.591 [INFO][12943] health.go 156: Number of node(s) with BGP peering established = 0

错误的原因是:

Readiness probe failed: calico/node is not ready: BIRD is not ready: BGP not established with 172.16.64.232,172.16.64.2352020-02-12 12:36:36.591 [INFO][12943] health.go 156: Number of node(s) with BGP peering established = 0

解决方案:

调整calico网络插件的网卡发现机制,修改IP_AUTODETECTION_METHOD对应的value值。

官方提供的yaml文件中,ip识别策略(IPDETECTMETHOD)没有配置,即默认为first-found,这会导致一个网络异常的ip作为nodeIP被注册,从而影响node-to-node mesh。

我们可以修改成can-reach或者interface的策略,尝试连接某一个Ready的node的IP,以此选择出正确的IP。

calico.yaml文件中添加如下两行内容

1
2
- name: IP_AUTODETECTION_METHOD
value: "interface=ens.*" # ens 根据实际网卡开头配置

配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
# Cluster type to identify the deployment type
- name: CLUSTER_TYPE
value: "k8s,bgp"
# Specify interface
- name: IP_AUTODETECTION_METHOD
value: "interface=ens.*"
# Auto-detect the BGP IP address.
- name: IP
value: "autodetect"
# Enable IPIP
- name: CALICO_IPV4POOL_IPIP
value: "Always"

修改后重新应用一下calico.yaml文件。

1
kubectl apply -f calico.yaml

我们发现calico所有pod都已经成功启动了。

1
2
3
4
5
kubectl get po -n kube-system
NAME READY STATUS RESTARTS AGE
calico-node-jtvh8 1/1 Running 0 15s
calico-node-k6m8t 1/1 Running 0 45s
calico-node-rb9qx 1/1 Running 0 29s

此时,通过<master-node-ip>:30090,即可以成功访问到页面了。

这个实验一定要在Linux环境下做,docker for mac, docker for win是不行的。

docker网络类型

docker会给我们创建三种网络类型:bridge, host, none

1
2
3
4
5
docker network ls
NETWORK ID NAME DRIVER SCOPE
a1681b4a3bc9 bridge bridge local
d724eb42948a host host local
c9381cce7bbb none null local

在没有指定相关网络的情况下,默认情况,会使用bridge网络模式。

网络验证

启动两个busybox容器。

1
2
3
4
5
docker run -dit --name busybox1 busybox
4f3c61775b5e8bcd38a0c97ff97bcd16ed717ab31bea417b198192f83b493846

docker run -dit --name busybox2 busybox
c523392d8949b53abfbe736c43d2d47ea60a3420e8931bebc69d05baff93889b

查看一下bridge网络

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
docker network inspect bridge
[
{
"Name": "bridge",
"Id": "a1681b4a3bc9bf973e2ff712677e373b663cc65b3a9dd6e868f5635fff295a6a",
"Created": "2020-02-05T16:24:09.290786154+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"4f3c61775b5e8bcd38a0c97ff97bcd16ed717ab31bea417b198192f83b493846": {
"Name": "busybox1",
"EndpointID": "bb138601e4d4018e8f36b4679cc22823fca3020d53fb7c372e47d1c73345b374",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
},
"c523392d8949b53abfbe736c43d2d47ea60a3420e8931bebc69d05baff93889b": {
"Name": "busybox2",
"EndpointID": "8f3d2661b8bd9fdf4f9bf2d763e36ab87a7e1fa8dca6e1e97b727c7a8ab2d0fe",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
}
},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Labels": {}
}
]

可以看到busybox1和busybox2两个容器都连接到了bridge网络。两个容器的ip地址也能看到。

进入busybox1容器

1
docker exec -it busybox1 sh

通过ip地址和容器名ping容器

1
2
3
4
5
6
7
/ # ping 172.17.0.3
PING 172.17.0.3 (172.17.0.3): 56 data bytes
64 bytes from 172.17.0.3: seq=0 ttl=64 time=0.285 ms
64 bytes from 172.17.0.3: seq=1 ttl=64 time=0.130 ms

/ # ping busybox2
^C

可以得出,通过ip地址是可以ping通的,但通过容器名是不行的。

查看hosts信息,也找不到任何busybox2的信息

1
2
3
4
5
6
7
8
cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2 4f3c61775b5e

新建bridge网络

1
2
docker network create --driver bridge busybox_bridge
bebfcba064b2afdfb265d4df19eaccd950a1d957dcb3e2256d05dc544b64369e

查看网络

1
2
3
4
5
6
docker network ls
NETWORK ID NAME DRIVER SCOPE
a1681b4a3bc9 bridge bridge local
bebfcba064b2 busybox_bridge bridge local
d724eb42948a host host local
c9381cce7bbb none null local

分别创建busybox3和busybox4容器,并加入到busybox_bridge网络中

1
2
3
4
5
docker run -dit --network busybox_bridge --name busybox3 busybox
8e39a57b7543288fb716f66eaa4a54609a571c7d30194ca456d4a1dc443f19e6

docker run -dit --network busybox_bridge --name busybox4 busybox
ed1715cfaf168f64351d46aaa7f393cae00962c7ef850d8824abe06a4906843c

进入busybox3

本以为可以PING得通busybox4,但貌似不行,此实验失败。后面再研究。

host模式

以host模式启动一个nginx容器。

1
2
docker run --rm -d --net host nginx
956b96f5d0c25835a1d4470c12ed5e4abda341e44f9344e72cf42864976d96fa

因为是host模式,容器和主机共享一样的网络。

1
2
3
4
5
6
7
8
9
curl localhost
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
<p><em>Thank you for using nginx.</em></p>
</body>
</html>