diff --git a/k8s&container/PromQL.md b/k8s&container/PromQL.md new file mode 100644 index 0000000..499789c --- /dev/null +++ b/k8s&container/PromQL.md @@ -0,0 +1,565 @@ + + +# 数据类型 + +在 Prometheus 的表达式语言中,表达式或子表达式包括以下四种类型之一: + +- **瞬时向量(Instant vector)** - 一组时间序列,每个时间序列包含单个样本,它们共享相同的时间戳。也就是说,表达式的返回值中只会包含该时间序列中的最新的一个样本值。而相应的这样的表达式称之为**瞬时向量表达式**。 +- **区间向量(Range vector)** - 一组时间序列,每个时间序列包含一段时间范围内的样本数据。 +- **标量(Scalar)** - 一个浮点型的数据值。 +- **字符串(String)** - 一个简单的字符串值。 + +# 字面量 + +## 字符串 + +字符串可以用单引号、双引号或反引号指定为文字常量。 + +PromQL 遵循与 Go 相同的转义规则。在单引号或双引号中,用反斜杠来表示转义序列,后面可以跟 `a`, `b`, `f`, `n`, `r`, `t`, `v` 或 `\`。特殊字符可以使用八进制(`\nnn`)或者十六进制(`\xnn`,`\unnnn` 和 `\Unnnnnnnn`)。 + +与 Go 不同,Prometheus 不会对反引号内的换行符进行转义。 + +``` +"this is a string" +'these are unescaped: \n \\ \t' +`these are not unescaped: \n ' " \t` +``` + +## 标量 + +标量浮点值可以字面上写成 `[-](digits)[.(digits)]` 的形式。 + +``` +-2.43 +``` + +# 时间序列过滤器 + +## 瞬时向量过滤器 + +瞬时向量过滤器允许在指定的时间戳内选择一组时间序列和每个时间序列的单个样本值。在最简单的形式中,近指定指标(metric)名称。这将生成包含此指标名称的所有时间序列的元素的瞬时向量。 + +例如:选择指标名称为 `http_requests_total` 的所有时间序列: + +``` +http_requests_total +``` + +可以通过向花括号(`{}`)里附加一组标签来进一步过滤时间序列。 + +例如:选择指标名称为 `http_requests_total`,`job` 标签值为 `prometheus`,`group` 标签值为 `canary` 的时间序列: + +``` +http_requests_total{job="prometheus",group="canary"} +``` + +PromQL 还支持用户根据时间序列的标签匹配模式来对时间序列进行过滤,目前主要支持两种匹配模式:完全匹配和正则匹配。总共有以下几种标签匹配运算符: + +- `=` : 选择与提供的字符串完全相同的标签。 +- `!=` : 选择与提供的字符串不相同的标签。 +- `=~` : 选择正则表达式与提供的字符串(或子字符串)相匹配的标签。 +- `!~` : 选择正则表达式与提供的字符串(或子字符串)不匹配的标签。 + +例如:选择指标名称为 `http_requests_total`,环境为 `staging`、`testing` 或 `development`,HTTP 方法为 `GET` 的时间序列: + +``` +http_requests_total{environment=~"staging|testing|development",method!="GET"} +``` + +没有指定标签的标签过滤器会选择该指标名称的所有时间序列。 + +所有的 PromQL 表达式必须至少包含一个指标名称,或者一个不会匹配到空字符串的标签过滤器。 + +以下表达式是非法的(因为会匹配到空字符串): + +``` +{job=~".*"} # 非法! +``` + +以下表达式是合法的: + +``` +{job=~".+"} # 合法! +{job=~".*",method="get"} # 合法! +``` + +除了使用 `{label=value}` 的形式以外,我们还可以使用内置的 `__name__` 标签来指定监控指标名称。例如:表达式 `http_requests_total` 等效于 `{__name__="http_requests_total"}`。也可以使用除 `=` 之外的过滤器(`=`,`=~`,`~`)。以下表达式选择指标名称以 `job:` 开头的所有指标: + +``` +{__name__=~"job:.*"} +``` + + + +## 区间向量过滤器 + +区间向量与瞬时向量的工作方式类似,唯一的差异在于在区间向量表达式中我们需要定义时间选择的范围,时间范围通过时间范围选择器 `[]` 进行定义,以指定应为每个返回的区间向量样本值中提取多长的时间范围。 + +时间范围通过数字来表示,单位可以使用以下其中之一的时间单位: + +- `s` - 秒 +- `m` - 分钟 +- `h` - 小时 +- `d` - 天 +- `w` - 周 +- `y` - 年 + +例如:选择在过去 5 分钟内指标名称为 `http_requests_total`,`job` 标签值为 `prometheus` 的所有时间序列: + +``` +http_requests_total{job="prometheus"}[5m] +``` + +## 时间位移操作 + +在瞬时向量表达式或者区间向量表达式中,都是以当前时间为基准: + +``` +http_request_total{} # 瞬时向量表达式,选择当前最新的数据 +http_request_total{}[5m] # 区间向量表达式,选择以当前时间为基准,5分钟内的数据 +``` + +而如果我们想查询,5 分钟前的瞬时样本数据,或昨天一天的区间内的样本数据呢? 这个时候我们就可以使用位移操作,位移操作的关键字为 `offset`。 + +例如,以下表达式返回相对于当前查询时间过去 5 分钟的 `http_requests_total` 值: + +``` +http_requests_total offset 5m +``` + +**注意:**`offset` 关键字需要紧跟在选择器(`{}`)后面。以下表达式是正确的: + +``` +sum(http_requests_total{method="GET"} offset 5m) // GOOD. +``` + +下面的表达式是不合法的: + +``` +sum(http_requests_total{method="GET"}) offset 5m // INVALID. +``` + +该操作同样适用于区间向量。以下表达式返回指标 `http_requests_total` 一周前的 5 分钟之内的 HTTP 请求量的增长率: + +``` +rate(http_requests_total[5m] offset 1w) +``` + +# 操作符 + +使用PromQL除了能够方便的按照查询和过滤时间序列以外,PromQL还支持丰富的操作符,用户可以使用这些操作符对进一步的对事件序列进行二次加工。这些操作符包括:数学运算符,逻辑运算符,布尔运算符等等。 + +## 二元运算符 + +Prometheus 的查询语言支持基本的逻辑运算和算术运算。对于两个瞬时向量, 匹配行为可以被改变。 + +### 算术二元运算符 + +在 Prometheus 系统中支持下面的二元算术运算符: + +- `+` 加法 +- `-` 减法 +- `*` 乘法 +- `/` 除法 +- `%` 模 +- `^` 幂等 + +二元运算操作符支持 `scalar/scalar(标量/标量)`、`vector/scalar(向量/标量)`、和 `vector/vector(向量/向量)` 之间的操作。 + +在两个标量之间进行数学运算,得到的结果也是标量。 + +在向量和标量之间,这个运算符会作用于这个向量的每个样本值上。例如:如果一个时间序列瞬时向量除以 2,操作结果也是一个新的瞬时向量,且度量指标名称不变, 它是原度量指标瞬时向量的每个样本值除以 2。 + +如果是瞬时向量与瞬时向量之间进行数学运算时,过程会相对复杂一点,运算符会依次找到与左边向量元素匹配(标签完全一致)的右边向量元素进行运算,如果没找到匹配元素,则直接丢弃。同时新的时间序列将不会包含指标名称。 + +例如,如果我们想根据 `node_disk_bytes_written` 和 `node_disk_bytes_read` 获取主机磁盘IO的总量,可以使用如下表达式: + +``` +node_disk_bytes_written + node_disk_bytes_read +``` + +该表达式返回结果的示例如下所示: + +``` +{device="sda",instance="localhost:9100",job="node_exporter"}=>1634967552@1518146427.807 + 864551424@1518146427.807 +{device="sdb",instance="localhost:9100",job="node_exporter"}=>0@1518146427.807 + 1744384@1518146427.807 +``` + +### 布尔运算符 + +目前,Prometheus 支持以下布尔运算符: + +- `==` (相等) +- `!=` (不相等) +- `>` (大于) +- `<` (小于) +- `>=` (大于等于) +- `<=` (小于等于) + +布尔运算符被应用于 `scalar/scalar(标量/标量)`、`vector/scalar(向量/标量)`,和`vector/vector(向量/向量)`。默认情况下布尔运算符只会根据时间序列中样本的值,对时间序列进行过滤。我们可以通过在运算符后面使用 `bool` 修饰符来改变布尔运算的默认行为。使用 bool 修改符后,布尔运算不会对时间序列进行过滤,而是直接依次瞬时向量中的各个样本数据与标量的比较结果 `0` 或者 `1`。 + +在两个标量之间进行布尔运算,必须提供 bool 修饰符,得到的结果也是标量,即 `0`(`false`)或 `1`(`true`)。例如: + +``` +2 > bool 1 # 结果为 1 +``` + +瞬时向量和标量之间的布尔运算,这个运算符会应用到某个当前时刻的每个时序数据上,如果一个时序数据的样本值与这个标量比较的结果是 `false`,则这个时序数据被丢弃掉,如果是 `true`, 则这个时序数据被保留在结果中。如果提供了 bool 修饰符,那么比较结果是 `0` 的时序数据被丢弃掉,而比较结果是 `1` 的时序数据被保留。例如: + +``` +http_requests_total > 100 # 结果为 true 或 false +http_requests_total > bool 100 # 结果为 1 或 0 +``` + +瞬时向量与瞬时向量直接进行布尔运算时,同样遵循默认的匹配模式:依次找到与左边向量元素匹配(标签完全一致)的右边向量元素进行相应的操作,如果没找到匹配元素,或者计算结果为 false,则直接丢弃。如果匹配上了,则将左边向量的度量指标和标签的样本数据写入瞬时向量。如果提供了 bool 修饰符,那么比较结果是 `0` 的时序数据被丢弃掉,而比较结果是 `1` 的时序数据(只保留左边向量)被保留。 + +### 集合运算符 + +使用瞬时向量表达式能够获取到一个包含多个时间序列的集合,我们称为瞬时向量。 通过集合运算,可以在两个瞬时向量与瞬时向量之间进行相应的集合操作。目前,Prometheus 支持以下集合运算符: + +- `and` (并且) +- `or` (或者) +- `unless` (排除) + +**vector1 and vector2** 会产生一个由 `vector1` 的元素组成的新的向量。该向量包含 vector1 中完全匹配 `vector2` 中的元素组成。 + +**vector1 or vector2** 会产生一个新的向量,该向量包含 `vector1` 中所有的样本数据,以及 `vector2` 中没有与 `vector1` 匹配到的样本数据。 + +**vector1 unless vector2** 会产生一个新的向量,新向量中的元素由 `vector1` 中没有与 `vector2` 匹配的元素组成。 + + + +## 匹配模式 + +向量与向量之间进行运算操作时会基于默认的匹配规则:依次找到与左边向量元素匹配(标签完全一致)的右边向量元素进行运算,如果没找到匹配元素,则直接丢弃。 + +接下来将介绍在 PromQL 中有两种典型的匹配模式:一对一(one-to-one),多对一(many-to-one)或一对多(one-to-many)。 + +### 一对一匹配 + +一对一匹配模式会从操作符两边表达式获取的瞬时向量依次比较并找到唯一匹配(标签完全一致)的样本值。默认情况下,使用表达式: + +``` +vector1 vector2 +``` + +在操作符两边表达式标签不一致的情况下,可以使用 `on(label list)` 或者 `ignoring(label list)`来修改便签的匹配行为。使用 `ignoreing` 可以在匹配时忽略某些便签。而 `on` 则用于将匹配行为限定在某些便签之内。 + +``` + ignoring(