<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>绿色记忆 &#187; TSDB</title>
	<atom:link href="https://blog.gmem.cc/tag/tsdb/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.gmem.cc</link>
	<description></description>
	<lastBuildDate>Sat, 18 Apr 2026 12:23:57 +0000</lastBuildDate>
	<language>en-US</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.9.14</generator>
	<item>
		<title>Prometheus学习笔记</title>
		<link>https://blog.gmem.cc/prometheus-study-note</link>
		<comments>https://blog.gmem.cc/prometheus-study-note#comments</comments>
		<pubDate>Fri, 27 Apr 2018 03:45:21 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Cloud]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[K8S]]></category>
		<category><![CDATA[TSDB]]></category>

		<guid isPermaLink="false">https://blog.gmem.cc/?p=20779</guid>
		<description><![CDATA[<p>简介 Prometheus是一个开源监控系统，它既适用于面向服务器等硬件指标的监控，也适用于高动态的面向服务架构的监控。对于现在流行的微服务，Prometheus的多维度数据收集和数据筛选查询语言也是非常的强大。 Prometheus的主要特性包括： 多维度数据模型 灵活的查询语言 不依赖分布式存储，单个服务器节点是自治的 通过服务（sd，准确的说是监控目标）发现或者静态配置，来发现目标服务对象 支持多种多样的图表和界面展示，可以和Grafana集成 数据采集方式： Pull：通过HTTP协议定期去采集指标，只要被监控系统提供HTTP接口即可接入 Push：被监控系统主动推送指标到网关，Prometheus定期从网关Pull Prometheus包括以下组件： Prometheus Server：负责抓取和存储时间序列数据 Push Gateway：推送网关，第三方可以推送数据到此网关，Prometheus Server再从此网关拉取数据 多种导出工具，支持导出Graphite、StatsD等所需的格式 命令行查询工具 Alert Manager：告警管理器 PromQL查询语言 <a class="read-more" href="https://blog.gmem.cc/prometheus-study-note">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/prometheus-study-note">Prometheus学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h1"><span class="graybg">简介</span></div>
<p>Prometheus是一个开源监控系统，它既适用于面向服务器等<span style="background-color: #c0c0c0;">硬件指标的监控</span>，也适用于高动态的<span style="background-color: #c0c0c0;">面向服务架构的监控</span>。对于现在流行的微服务，Prometheus的<span style="background-color: #c0c0c0;">多维度数据收集和数据筛选查询语言</span>也是非常的强大。</p>
<p>Prometheus的主要特性包括：</p>
<ol>
<li>多维度数据模型</li>
<li>灵活的查询语言</li>
<li>不依赖分布式存储，单个服务器节点是自治的</li>
<li>通过<span style="background-color: #c0c0c0;">服务（sd，准确的说是监控目标）发现</span>或者静态配置，来发现目标服务对象</li>
<li>支持多种多样的图表和界面展示，可以和Grafana集成</li>
<li>数据采集方式：
<ol>
<li>Pull：通过HTTP协议定期去采集指标，只要被监控系统提供HTTP接口即可接入</li>
<li>Push：被监控系统主动推送指标到网关，Prometheus定期从网关Pull</li>
</ol>
</li>
</ol>
<p>Prometheus包括以下组件：</p>
<ol>
<li>Prometheus Server：负责抓取和存储时间序列数据</li>
<li>Push Gateway：推送网关，第三方可以推送数据到此网关，Prometheus Server再从此网关拉取数据</li>
<li>多种导出工具，支持导出Graphite、StatsD等所需的格式</li>
<li>命令行查询工具</li>
<li>Alert Manager：告警管理器</li>
<li>PromQL查询语言</li>
</ol>
<div class="blog_h2"><span class="graybg">架构图</span></div>
<p><a href="https://blog.gmem.cc/wp-content/uploads/2018/04/prometheus-architecture.png"><img class="aligncenter  wp-image-20855" src="https://blog.gmem.cc/wp-content/uploads/2018/04/prometheus-architecture.png" alt="prometheus-architecture" width="922" height="635" /></a></p>
<div class="blog_h2"><span class="graybg">数据模型</span></div>
<p>Prometheus中所有数据都存放为时间序列——具有时间戳的数据流，这些数据属于同一指标、以及由一系列标签定义的维度集。每个<span style="background-color: #c0c0c0;">时间序列由指标名+标签集唯一的识别</span>，标签由键、值组成。时间序列通常使用如下记号表示：</p>
<pre class="crayon-plain-tag">&lt;metric name&gt;{&lt;label name&gt;=&lt;label value&gt;, ...}
# 示例：
api_http_requests_total{method="POST", handler="/messages"}</pre>
<p>指标名是需要监控系统特性的一般性名称，例如http_requests_total可以表示HTTP请求计数。</p>
<p>标签为一个具体的指标“实例”提供维度信息。使用PromQL你可以基于标签进行过滤、分组。增加、修改、删除某个标签，则新的时间序列会被创建。标签名只能ASCII字符，但是标签值可以是任何Unicode字符。</p>
<p>样本（Sample）构成了实际的时间序列的数据点。样本由<span style="background-color: #c0c0c0;">float64类型的数值+毫秒精度的时间戳</span>组成。</p>
<div class="blog_h2"><span class="graybg">指标类型</span></div>
<p>Prometheus的客户端库区分了4种指标类型，目前服务器端不理解这些类型的不同，但是未来可能改变。</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 120px; text-align: center;">指标类型</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>counter</td>
<td>单调递增的计数器</td>
</tr>
<tr>
<td>gauge</td>
<td>可以任意变化的数值</td>
</tr>
<tr>
<td>histogram</td>
<td>
<p>长尾问题：某个API的绝大部分请求延迟100ms，但是个别请求延迟高达5s。这种情况下使用指标平均值无法分析出问题所在</p>
<p>为了区分延迟是平均的、普遍的，还是由于长尾问题造成的。最简单的方式是统计延迟在0-50ms的请求有多少，50-100ms的请求有多少…… Prometheus的指标类型histogram、summary都可以用于这种<span style="background-color: #c0c0c0;">样本分布的分析</span></p>
<p>histogram对监控得到的数值（<span style="background-color: #c0c0c0;">例如请求用时、响应大小</span>）进行采样，并在可配置的Bucket（<span style="background-color: #c0c0c0;">例如请求用时区间、响应大小区间</span>）中对采样进行计数，同时提供对所有监控数值的求和</p>
<p>具有名称&lt;basename&gt;的histogram，暴露以下几个时间序列：</p>
<ol>
<li>&lt;basename&gt;_bucket{le="&lt;upper inclusive bound&gt;"}，基于观察桶（observation buckets）的累加计数器</li>
<li>&lt;basename&gt;_sum，所有观察值求和</li>
<li>&lt;basename&gt;_count，所有观察值计数</li>
</ol>
<p>下面是一个例子：</p>
<pre class="crayon-plain-tag"># prometheus_tsdb_compaction_chunk_range_count{instance="localhost:9090"}
# 样本总数
tsdb_compaction_chunk_range_count{}                   64388371

# prometheus_tsdb_compaction_chunk_range_sum{instance="localhost:9090"}
# 所有样本的值的总和
tsdb_compaction_chunk_range_sum{}                     456743796630059

# sort(prometheus_tsdb_compaction_chunk_range_bucket{instance="localhost:9090"})
# 值小于25600的样本数量
tsdb_compaction_chunk_range_bucket{le="25600"         1301
tsdb_compaction_chunk_range_bucket{le="400"}          1301
tsdb_compaction_chunk_range_bucket{le="100"}          1301
tsdb_compaction_chunk_range_bucket{le="1600"}         1301
tsdb_compaction_chunk_range_bucket{le="6400"}         1301
tsdb_compaction_chunk_range_bucket{le="102400"}       20000
tsdb_compaction_chunk_range_bucket{le="409600"}       108533
tsdb_compaction_chunk_range_bucket{le="1.6384e+06"}   254369
tsdb_compaction_chunk_range_bucket{le="6.5536e+06"}   655322
tsdb_compaction_chunk_range_bucket{le="2.62144e+07"}  64272199
# 所有样本数量
tsdb_compaction_chunk_range_bucket{le="+Inf"}         64272199</pre>
<p>从上面的例子中，很容易发现绝大部分的样本都落在1.6e+6到2.6e+7这个区间</p>
<p>对于Histogram的指标，我们还可以通过histogram_quantile()函数<span style="background-color: #c0c0c0;">在服务器端拟合</span>出其值的分位数：</p>
<pre class="crayon-plain-tag">// 估算99分位数
histogram_quantile(0.99, 
  // 以le分组求和
  sum(
    // 以interval为区间获得平均值
    irate(
      istio_request_duration_seconds_bucket{
        reporter="destination",
        destination_workload=~"$workload",
        destination_workload_namespace=~"$namespace"
      }[$interval]
    )
  ) by (le)
)</pre>
</td>
</tr>
<tr>
<td>summary</td>
<td>
<p>类似于histogram，基于滑动窗口来计算可配置的分位数（quantile）
<p>下面是一个例子：</p>
<pre class="crayon-plain-tag"># 样本总数
tsdb_wal_fsync_duration_seconds_count{job="prometheus"}                  369717
# 样本值总和
tsdb_wal_fsync_duration_seconds_sum{job="prometheus"}                   24423.798953890488
# 中位数耗时0.05秒，也就是说，有一半的磁盘同步操作在0.05秒内完成
tsdb_wal_fsync_duration_seconds{job="prometheus",quantile="0.5"}	0.053841949
# 九分位数耗时0.12秒，也就是说，90%的磁盘同步操作都在0.12秒内完成
tsdb_wal_fsync_duration_seconds{job="prometheus",quantile="0.9"}	0.116763012
tsdb_wal_fsync_duration_seconds{job="prometheus",quantile="0.99"}	0.263791273</pre>
</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">Job和实例</span></div>
<p>一个你可以从中<span style="background-color: #c0c0c0;">抓取监控数据的endpoint称为实例（Instance）</span>，实例通常对应一个进程，例如NodeExporter、RedisExporter……
<p><span style="background-color: #c0c0c0;">一系列相同目的的实例，称为Job</span>。多实例的原因可能是为了可靠性、扩容。</p>
<p>当Prometheus服务器抓取数据时，它会自动为时间序列添加标签：</p>
<ol>
<li>job，抓取目标所属的Job的名称</li>
<li>instance，目标从什么host:port抓取得到</li>
</ol>
<p>如果上述两个标签已经存在于抓取的数据上，则配置项honor_labels影响服务器的行为。</p>
<p>除了添加标签之外，还会为以下时间序列添加样本：</p>
<ol>
<li>up{job="&lt;job-name&gt;", instance="&lt;instance-id&gt;"}:1/0。如果实例可达则取值1，否则0</li>
<li>scrape_duration_seconds{job="&lt;job-name&gt;", instance="&lt;instance-id&gt;"}:抓取目标消耗的时间</li>
<li>scrape_samples_post_metric_relabeling{job="&lt;job-name&gt;", instance="&lt;instance-id&gt;"}:指标重打标签后，剩余的样本数量</li>
<li>scrape_samples_scraped{job="&lt;job-name&gt;", instance="&lt;instance-id&gt;"}: 目标暴露的样本数量</li>
</ol>
<div class="blog_h2"><span class="graybg">对比其它TSDB</span></div>
<div class="blog_h3"><span class="graybg">Graphite</span></div>
<p>Graphite专注于作为一个被动的时间序列数据库，同时提供查询语言、图形化特性。Prometheus则是一个全功能的监控和趋势分析系统，内置主动拉取、存储、图形化、报警功能。</p>
<p>Graphite存储数字采样，但是其元数据模型不如Prometheus丰富。Graphite的指标名称使用点号分隔的单词，暗含维度信息。Prometheus则将维度信息作为明确的标签存储。Prometheus更容易支持过滤、分组操作。</p>
<p>Graphite在本地磁盘上，以Whisper格式存储时间序列数据。每个时间序列存储一个文件，一段时间后，新采样会覆盖旧采样，此外采样频率是固定的。Prometheus也是每个时间序列对应一个文件，但是采样频率是动态的，新数据简单的Append到文件尾部。</p>
<div class="blog_h3"><span class="graybg">InfluxDB</span></div>
<p>InfluxDB的持续查询类似于Prometheus的Recording规则。Kapacitor类似于Recording规则、Alerting规则和Alertmanager的通知功能的组合。Alertmanager具有额外的分组、去重、静默功能。</p>
<p>InfluxDB的Tag和Prometheus的Label一样，都是键值对形式的维度信息。InfluxDB还提供第二级的“标签”——字段（Field）</p>
<div class="blog_h2"><span class="graybg">术语</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 25%; text-align: center;">名词</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>Alert</td>
<td>Prometheus中报警规则的输出，从Prometheus服务器发送给报警管理器</td>
</tr>
<tr>
<td>Alertmanager</td>
<td>接收Alert，聚合成组、去重、应用 silence、throttles，然后发送电子邮件或者发送到Slack、Pagerduty</td>
</tr>
<tr>
<td>Bridge</td>
<td>从客户端库提取采样，暴露给非Prometheus监控系统的组件</td>
</tr>
<tr>
<td>Collector</td>
<td>Exporter的一部分，可以收集单个或者多个指标</td>
</tr>
<tr>
<td>Exporter</td>
<td>收集指标的应用程序，将各种指标转化为Prometheus支持的数据处理格式</td>
</tr>
<tr>
<td>Notification</td>
<td>Altermanger发出的各种通知</td>
</tr>
<tr>
<td>PromQL</td>
<td>Prometheus查询语言。支持聚合、分片、切割、断言和连接操作</td>
</tr>
<tr>
<td>Silence</td>
<td>根据标签（Label）匹配来禁用警告</td>
</tr>
<tr>
<td>Target</td>
<td>需要抓取（Scrape）的对象的定义，包括以下信息：需要增加的标签、身份验证信息、如何抓取</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">安装</span></div>
<div class="blog_h2"><span class="graybg">K8S</span></div>
<pre class="crayon-plain-tag">helm install gmem/prometheus --name prometheus --namespace=kube-system</pre>
<p>此Chart的定义位于：<a href="https://github.com/gmemcc/charts/tree/master/prometheus">https://github.com/gmemcc/charts/tree/master/prometheus</a></p>
<p>安装完毕，到<a href="https://prometheus.k8s.gmem.cc/targets">https://prometheus.k8s.gmem.cc/targets</a>可以查看各监控目标的状态。</p>
<div class="blog_h2"><span class="graybg">Standalone</span></div>
<pre class="crayon-plain-tag">wget https://github.com/prometheus/prometheus/releases/download/v2.2.1/prometheus-2.2.1.linux-amd64.tar.gz
tar xzf prometheus-2.2.1.linux-amd64.tar.gz
rm prometheus-2.2.1.linux-amd64.tar.gz
mv prometheus-2.2.1.linux-amd64 prometheus
cd prometheus
./prometheus --config.file="prometheus.yml"       # 指定配置文件
             --web.listen-address="0.0.0.0:9090"  # UI/API/Telemetry监听地址
             --storage.tsdb.path="data/"          # 时间序列数据库存储路径
             --storage.tsdb.retention=15d         # 时间序列数据存储时长
             # 等待处理的的告警管理器通知队列长度
             --alertmanager.notification-queue-capacity=10000
             --alertmanager.timeout=10s           # 发送告警给告警管理器的超时</pre>
<div class="blog_h1"><span class="graybg">配置</span></div>
<p>Prometheus通过命令行、配置文件进行配置。命令行参数可以配置一些不变的系统参数，例如存储位置、存留在内存和磁盘中的数据量。配置文件则用于指定Job、Instance、Rule的配置。</p>
<div class="blog_h2"><span class="graybg">配置文件</span></div>
<p>配置文件的格式是YAML，使用--config.file指定配置文件的位置。本节列出重要的配置项。</p>
<div class="blog_h3"><span class="graybg">全局配置</span></div>
<pre class="crayon-plain-tag">global:
  # 默认抓取周期，可用单位ms、smhdwy
  [ scrape_interval: &lt;duration&gt; | default = 1m ]
  # 默认抓取超时
  [ scrape_timeout: &lt;duration&gt; | default = 10s ]
  # 估算规则的默认周期
  [ evaluation_interval: &lt;duration&gt; | default = 1m ]
  # 和外部系统（例如AlertManager）通信时为时间序列或者警情（Alert）强制添加的标签列表
  external_labels:
    [ &lt;labelname&gt;: &lt;labelvalue&gt; ... ]

# 规则文件列表
rule_files:
  [ - &lt;filepath_glob&gt; ... ]

# 抓取配置列表
scrape_configs:
  [ - &lt;scrape_config&gt; ... ]

# 和Alertmanager相关的配置
alerting:
  alert_relabel_configs:
    [ - &lt;relabel_config&gt; ... ]
  alertmanagers:
    [ - &lt;alertmanager_config&gt; ... ]

# 和远程读写特性相关的配置
remote_write:
  [ - &lt;remote_write&gt; ... ]
remote_read:
  [ - &lt;remote_read&gt; ... ]</pre>
<div class="blog_h3"><span class="graybg">scrape_config</span></div>
<p>配置一系列的目标，以及如何抓取它们的参数。一般情况下，每个scrape_config对应单个Job。</p>
<p>目标可以在scrape_config中静态的配置，也<span style="background-color: #c0c0c0;">可以使用某种服务发现机制动态发现</span>。</p>
<pre class="crayon-plain-tag"># 任务名称，自动作为抓取到的指标的一个标签
job_name: &lt;job_name&gt;

# 抓取周期
[ scrape_interval: &lt;duration&gt; | default = &lt;global_config.scrape_interval&gt; ]
# 每次抓取的超时
[ scrape_timeout: &lt;duration&gt; | default = &lt;global_config.scrape_timeout&gt; ]
# 从目标抓取指标的URL路径
[ metrics_path: &lt;path&gt; | default = /metrics ]
# 当添加标签发现指标已经有同名标签时，是否保留原有标签不覆盖
[ honor_labels: &lt;boolean&gt; | default = false ]
# 抓取协议
[ scheme: &lt;scheme&gt; | default = http ]
# 可选的请求参数
params:
  [ &lt;string&gt;: [&lt;string&gt;, ...] ]

# 身份验证信息
basic_auth:
  [ username: &lt;string&gt; ]
  [ password: &lt;secret&gt; ]
  [ password_file: &lt;string&gt; ]
# Authorization请求头取值
[ bearer_token: &lt;secret&gt; ]
# 从文件读取Authorization请求头
[ bearer_token_file: /path/to/bearer/token/file ]

# TLS配置
tls_config:
  [ &lt;tls_config&gt; ]

# 代理配置
[ proxy_url: &lt;string&gt; ]

# DNS服务发现配置
dns_sd_configs:
  [ - &lt;dns_sd_config&gt; ... ]
# 文件服务发现配置
file_sd_configs:
  [ - &lt;file_sd_config&gt; ... ]
# K8S服务发现配置
kubernetes_sd_configs:
  [ - &lt;kubernetes_sd_config&gt; ... ]

# 此Job的静态配置的目标列表
static_configs:
  [ - &lt;static_config&gt; ... ]

# 目标重打标签配置
relabel_configs:
  [ - &lt;relabel_config&gt; ... ]
# 指标重打标签配置
metric_relabel_configs:
  [ - &lt;relabel_config&gt; ... ]

# 每次抓取允许的最大样本数量，如果在指标重打标签后，样本数量仍然超过限制，则整个抓取认为失败
# 0表示不限制
[ sample_limit: &lt;int&gt; | default = 0 ]</pre>
<div class="blog_h3"><span class="graybg">kubernetes_sd_config</span></div>
<p>使用该配置，可以从K8S API Server暴露的REST API中<span style="background-color: #c0c0c0;">发现抓取目标</span>，并且和K8S集群保持同步。你可以配置以下role，以发现目标：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 100px; text-align: center;">role</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>node</td>
<td>
<p>为每个集群节点发现一个目标，目标的端口是Kubelet的HTTP端口、目标的地址是K8S节点对象的NodeInternalIP、NodeExternalIP、NodeLegacyHostIP或NodeHostName</p>
<p>可用的元标签：</p>
<p style="padding-left: 30px;">__meta_kubernetes_node_name 节点的名称<br />__meta_kubernetes_node_label_&lt;labelname&gt; 节点的每个标签<br />__meta_kubernetes_node_annotation_&lt;annotationname&gt; 节点的每个注解<br />__meta_kubernetes_node_address_&lt;address_type&gt; 节点的每种地址的第一个</p>
<p>节点的instance标签被设置为从API Server获取的节点名</p>
</td>
</tr>
<tr>
<td>service</td>
<td>
<p>为每个服务的端口发现一个目标，一般用于服务的黑盒监控。目标地址为服务的DNS名称</p>
<p>可用的元标签：</p>
<p style="padding-left: 30px;">__meta_kubernetes_namespace 服务所在命名空间<br />__meta_kubernetes_service_name 服务的名字<br />__meta_kubernetes_service_label_&lt;labelname&gt; 服务的每个标签<br />__meta_kubernetes_service_annotation_&lt;annotationname&gt; 服务的每个注解<br />__meta_kubernetes_service_port_name 服务端口的名称<br />__meta_kubernetes_service_port_numbe 服务的端口号<br />__meta_kubernetes_service_port_protocol 服务的协议</p>
</td>
</tr>
<tr>
<td>pod</td>
<td>
<p>发现所有Pod并将其容器暴露为目标。对于每个容器+声明端口的组合，生成独立的目标。<span style="background-color: #c0c0c0;">如果容器没有指定端口则仅仅为容器生成一个目标，在重打标签阶段可以为这种目标添加端口</span></p>
<p>可用的元标签：</p>
<p style="padding-left: 30px;">__meta_kubernetes_namespace Pod所在命名空间<br />__meta_kubernetes_pod_name Pod名称<br />__meta_kubernetes_pod_ip Pod地址<br />__meta_kubernetes_pod_label_&lt;labelname&gt; Pod的每个标签<br />__meta_kubernetes_pod_annotation_&lt;annotationname&gt; Pod的每个注解<br />__meta_kubernetes_pod_container_name 容器名<br />__meta_kubernetes_pod_container_port_name 容器端口名<br />__meta_kubernetes_pod_container_port_number 容器端口号<br />__meta_kubernetes_pod_container_port_protocol 容器端口协议<br />__meta_kubernetes_pod_ready 如果Pod就绪设置为true<br />__meta_kubernetes_pod_node_name 节点名<br />__meta_kubernetes_pod_host_ip Pod的宿主机IP<br />__meta_kubernetes_pod_uid Pod的UID<br />__meta_kubernetes_pod_controller_kind Pod控制器的对象类型<br />__meta_kubernetes_pod_controller_name Pod控制器的名称</p>
</td>
</tr>
<tr>
<td>endpoints</td>
<td>
<p>为服务的每个端点发现目标，每个Endpoint+Port的组合生成一个目标。如果Endpoint是基于Pod的，则Pod的任何端口都生成目标</p>
<p>可用的元标签：</p>
<p style="padding-left: 30px;">如果endpoint是基于Pod的，则role: pod发现的所有元标签可用<br />如果endpoint是基于Service的，则role: service发现的所有元标签可用</p>
<p style="padding-left: 30px;">__meta_kubernetes_namespace 端点的命名空间<br />__meta_kubernetes_endpoints_name 端点的名称</p>
<p style="padding-left: 30px;">对于Endpoint中定义的Pod端口，以下元标签可用：<br />__meta_kubernetes_endpoint_ready 端点是否就绪<br />__meta_kubernetes_endpoint_port_name 端点端口的名称<br />__meta_kubernetes_endpoint_port_protocol 端点端口的协议<br />__meta_kubernetes_endpoint_address_target_kind 端点地址目标类型<br />__meta_kubernetes_endpoint_address_target_name 端点地址目标名称</p>
</td>
</tr>
<tr>
<td>ingress</td>
<td>
<p>为所有Ingress的每个路径发现目标，一般用于Ingress的黑盒监控。目标地址设置为Ingress的host字段</p>
<p>可用的元标签：</p>
<p style="padding-left: 30px;">__meta_kubernetes_namespace 所属命名空间<br />__meta_kubernetes_ingress_name Ingress的名字<br />__meta_kubernetes_ingress_label_&lt;labelname&gt; 每个标签<br />__meta_kubernetes_ingress_annotation_&lt;annotationname&gt; 每个注解<br />__meta_kubernetes_ingress_scheme 协议，http/https<br />__meta_kubernetes_ingress_path Ingress的路径，默认/</p>
</td>
</tr>
</tbody>
</table>
<p>通常，你会给相关K8S资源添加以下注解：</p>
<pre class="crayon-plain-tag">annotations:                                                                                                                                                                           
  prometheus.io/path: /metrics                                                                                                                                                         
  prometheus.io/port: "8080"                                                                                                                                                           
  prometheus.io/scrape: "true"</pre>
<p>并配合以下Relabel配置，提示这些资源需要作为Prometheus的抓取目标：</p>
<pre class="crayon-plain-tag">relabel_configs:
  - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
    action: keep
    regex: true
  - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
    action: replace
    target_label: __metrics_path__
    regex: (.+)
  - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
    action: replace
    regex: ([^:]+)(?::\d+)?;(\d+)
    replacement: $1:$2
    target_label: __address__ </pre>
<div class="blog_h3"><span class="graybg">relabel_config</span></div>
<p>重打标签是动态修改目标的标签集的强大工具。每个抓取配置可以定义<span style="background-color: #c0c0c0;">多个重打标签步骤，这些步骤按照声明顺序依次执行、在实际抓取指标数据之前执行</span>。</p>
<p>在一开始，除了为每个目标配置的标签之外，目标的：</p>
<ol>
<li>job标签被设置为抓取配置的job_name字段</li>
<li>__address__标签被设置为目标的&lt;host&gt;:&lt;port&gt;</li>
</ol>
<p>在重打标签之后，目标的：</p>
<ol>
<li>instance标签默认被设置为__address__，如果没有此标签的话</li>
<li>__scheme__标签被设置为http或https</li>
<li>__metrics_path__标签被设置为目标的指标路径，即URL路径</li>
<li>__param_&lt;name&gt;标签为请求时使用的每个参数</li>
</ol>
<p>在重打标签期间，额外的<span style="background-color: #c0c0c0;"> __meta_ 开头的元标签可用，这些标签由服务发现机制自动添加</span>。</p>
<p>在重打标签结束后，<span style="background-color: #c0c0c0;">以__开头的标签会被移除</span>。</p>
<p>如果某个步骤需要临时的设置一些标签，仅仅作为后续步骤的输入，<span style="background-color: #c0c0c0;">应当以__tmp作为前缀</span>。</p>
<p>每个重打标签步骤（relabel_configs的元素）具有以下子配置项：</p>
<pre class="crayon-plain-tag"># 从已有的标签中选取值
[ source_labels: '[' &lt;labelname&gt; [, ...] ']' ]
# 并且使用下面的分隔符连接那些值
[ separator: &lt;string&gt; | default = ; ]
# 然后基于下面的正则式进行匹配，或者保留，或者替换，或者删除

# 对于替换操作来说，替换为的目标标签的名字，可以使用正则式捕获组
[ target_label: &lt;labelname&gt; ]

# 用于匹配源标签值的正则式
[ regex: &lt;regex&gt; | default = (.*) ]

# 用于获取源标签值的哈希的模数
[ modulus: &lt;uint64&gt; ]

# 如果正则式匹配，使用什么替换值，可以使用正则式捕获组
[ replacement: &lt;string&gt; | default = $1 ]

# 如果正则式匹配，执行何种操作
# replace 如果正则式匹配source_labels的值，则设置target_label为指定的内容
# keep 如果正则式匹配，维持目标不变
# drop 如果正则式匹配，丢弃目标
# hashmod 设置target_label标签名为source_labels的值的哈希的取模
# labelmap 针对所有标签名来匹配regex，然后将匹配的标签的值拷贝到replacement所指定的新标签中
# labeldrop 针对所有标签来匹配regex，不匹配的标签都丢弃
# labelkeep 针对所有标签来匹配regex，匹配的标签都丢弃
[ action: &lt;relabel_action&gt; | default = replace ]</pre>
<p>重打标签配置示例：</p>
<pre class="crayon-plain-tag"># 将元标签__meta_kubernetes_pod_node_name替换为nodename
- source_labels: [__meta_kubernetes_pod_node_name]
  action: replace
  target_label: nodename </pre>
<div class="blog_h3"><span class="graybg">metric_relabel_configs</span></div>
<p>在存储（ingestion）样本数据之前，作为最后一个步骤，配置同上。可以用于屏蔽存储成本过高的时间序列。</p>
<div class="blog_h2"><span class="graybg">配置文件示例</span></div>
<div class="blog_h3"><span class="graybg">默认配置文件</span></div>
<p>Prometheus使用YAML格式的配置文件，默认的配置文件内容如下：</p>
<pre class="crayon-plain-tag"># Prometheus服务器的全局配置
global:
  # 拉取Target的间隔，默认1分钟
  scrape_interval:     15s 
  # 执行Rule的间隔，默认1分钟
  evaluation_interval: 15s 
  # 拉取Target的超时时间
  scrape_timeout: 10s      

# 报警管理器配置
alerting:
  alertmanagers:
  - static_configs:
    - targets:
      - alertmanager:9093

# 加载规则文件，并每evaluation_interval执行规则一次
rule_files:
  - "first_rules.yml"
  - "second_rules.yml"

# 拉取配置，说明Prometheus需要监控什么
scrape_configs:
  # 这个默认抓取任务，监控Prometheus服务器自己（Prometheus通过HTTP暴露了自己的Metrics）
  - job_name: 'prometheus'
    metrics_path: /metrics
    scheme: http
    static_configs:
      # 此Job仅具有一个目标
      - targets: ['localhost:9090']</pre>
<div class="blog_h1"><span class="graybg">PromQL</span></div>
<p>Prometheus提供了一种查询语言，用来实时的查询、聚合时间序列数据。查询结果可以在Prometheus的WebUI中展示，或者通过HTTP API暴露给第三方系统。</p>
<div class="blog_h2"><span class="graybg">语法示例</span></div>
<pre class="crayon-plain-tag"># 返回一个指标的所有时间序列
http_requests_total
# 返回一个指标具有特定Label的时间序列
http_requests_total{job="apiserver", handler="/api/comments"}
# 在满足上一条的前提下，返回5分钟内所有时间序列，并形成范围矢量（range vector）
http_requests_total{job="apiserver", handler="/api/comments"}[5m]
# 支持使用正则式来匹配Label
http_requests_total{job=~".*server"}
# 反向匹配
http_requests_total{status!~"4.."}

# 以最近5分钟的范围矢量为基础，统计每秒的数据增量
rate(http_requests_total[5m])

# 按Label job进行分组统计
sum(rate(http_requests_total[5m])) by (job)
# 如果两个指标具有相同的Label（维度信息），则可以对指标值进行运算
sum(
  instance_memory_limit_bytes - instance_memory_usage_bytes
) by (app, proc) / 1024 / 1024

# 取前三
topk(3, sum(rate(instance_cpu_time_ns[5m])) by (app, proc))</pre>
<div class="blog_h2"><span class="graybg">数据类型</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">数据类型</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>Instant vector</td>
<td>一系列时间序列，每个时间序列包含单个采样</td>
</tr>
<tr>
<td>Range vector</td>
<td>一系列时间序列，每个时间序列包含多个采样，采样分布在特定的时间区间</td>
</tr>
<tr>
<td>Scalar</td>
<td>单个浮点数</td>
</tr>
<tr>
<td>String</td>
<td>单个字符串</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">语法</span></div>
<div class="blog_h3"><span class="graybg">瞬时矢量选择器</span></div>
<p>匹配单个时间点的一个或多个时间序列的采样：</p>
<pre class="crayon-plain-tag"># 选择指标的所有时间序列
http_requests_total
# 根据标签匹配
http_requests_total{job="prometheus",group="canary"}</pre>
<p>匹配标签时可以使用四种操作符：=、!=、=~、!~，前两者用于精确匹配，后两者用于正则式匹配：</p>
<pre class="crayon-plain-tag">http_requests_total{environment=~"staging|testing|development",method!="GET"}</pre>
<div class="blog_h3"><span class="graybg">范围矢量选择器 </span></div>
<p>只需要在瞬时矢量选择器后面添加<pre class="crayon-plain-tag">[timePeriod]</pre> 即可，时间的单位可以是s、m、h、d、w、y。示例：</p>
<pre class="crayon-plain-tag">http_requests_total{job="prometheus"}[5m]</pre>
<div class="blog_h3"><span class="graybg">偏移量修饰符</span></div>
<pre class="crayon-plain-tag"># 获取相对当前查询时间，之前5分钟的数据
http_requests_total offset 5m

# 一周前的每秒请求数
rate(http_requests_total[5m] offset 1w)</pre>
<div class="blog_h3"><span class="graybg">操作符 </span></div>
<p>算术：加减乘除、取模、乘方（^）。可以在两个标量、标量vs瞬时矢量、 两个瞬时矢量之间进行。</p>
<p>比较：== != &gt; &gt;= &lt; &lt;=。可以在两个标量、标量vs瞬时矢量、 两个瞬时矢量之间进行。</p>
<p>逻辑：and or unless。仅仅用于两个瞬时矢量之间：</p>
<ol>
<li>and，取v1 v2中具有完全一致Label的那些时间序列，构成v3返回</li>
<li>or，取v1所有时间序列，外加v2中那些Label在v1中不存在的时间序列，构成v3返回</li>
<li>unless，取v1中那些没有在v2中具有相同Label的时间序列</li>
</ol>
<div class="blog_h3"><span class="graybg">矢量匹配</span></div>
<p>对两个瞬时矢量应用操作符时，牵涉到如何找到左侧矢量元素在右侧矢量中的匹配元素的问题。匹配都是基于Label的。</p>
<p>矢量匹配的行为有两种：1对1匹配，1对多匹配：</p>
<table class="full-width fixed-word-wrap">
<tbody>
<tr>
<td style="width: 100px; text-align: center;"><strong>一对一</strong></td>
<td>
<p>语法格式：<pre class="crayon-plain-tag">vector1 &lt;operator&gt; vector2</pre></p>
<p>默认情况下，如果两个元素的标签集完全一致则匹配，可以使用ignoring或者on关键字来限制哪些标签需要匹配：</p>
<pre class="crayon-plain-tag"># 匹配时忽略一些标签
&lt;vector expr&gt; &lt;bin-op&gt; ignoring(&lt;label list&gt;) &lt;vector expr&gt;
# 仅仅针对某些标签进行匹配
&lt;vector expr&gt; &lt;bin-op&gt; on(&lt;label list&gt;) &lt;vector expr&gt;</pre>
<p>示例：</p>
<pre class="crayon-plain-tag"># 500错误占比分析
method_code:http_errors:rate5m{code="500"} / ignoring(code) method:http_requests:rate5m</pre>
</td>
</tr>
<tr>
<td style="text-align: center;"><strong>一对多</strong></td>
<td> </td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">聚合操作符</span></div>
<p>操作符列表：
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">操作符</td>
<td style="text-align: center;">说明</td>
<td style="text-align: center;">操作符</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>min</td>
<td>最小值</td>
<td>max</td>
<td>最大值</td>
</tr>
<tr>
<td>sum</td>
<td>求和</td>
<td>avg</td>
<td>求平均</td>
</tr>
<tr>
<td>count</td>
<td>统计数量</td>
<td>count_values</td>
<td>统计同值元素量</td>
</tr>
<tr>
<td>bottomk</td>
<td>最小N元素</td>
<td>topk</td>
<td>最大N元素</td>
</tr>
<tr>
<td>quantile</td>
<td>分位数（例如求中位数）</td>
<td> </td>
<td> </td>
</tr>
</tbody>
</table>
<p>语法：</p>
<pre class="crayon-plain-tag">&lt;aggr-op&gt;([parameter,] &lt;vector expression&gt;) [without|by (&lt;label list&gt;)]
# parameter：仅count_values, quantile, topk, bottomk需要
# without：从结果矢量中移除指定的标签
# by：仅仅保留指定的标签

# 示例：
sum(http_requests_total) without (instance)
sum(http_requests_total) by (application, group)
count_values("version", build_version)
topk(5, http_requests_total)</pre>
<div class="blog_h2"><span class="graybg">函数</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">函数</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>abs(iv)</td>
<td>将输入瞬时矢量的元素的采样值取绝对值</td>
</tr>
<tr>
<td>absent(iv)</td>
<td>如果输入矢量没有元素，返回空矢量，否则返回包含单个元素，采样值为1的矢量</td>
</tr>
<tr>
<td>ceil(iv)<br />round(v)</td>
<td>取整</td>
</tr>
<tr>
<td>changes(rv)</td>
<td>对于范围矢量中每个时间序列，获取其值的变更次数，返回瞬时矢量</td>
</tr>
<tr>
<td>day_of_month()<br />day_of_week()<br />days_in_month()<br />hour()<br />minute()<br />month()<br />year()</td>
<td>时间转换</td>
</tr>
<tr>
<td>delta(rv)</td>
<td>对于范围矢量中每个时间序列，获取首尾两个采样的差值</td>
</tr>
<tr>
<td>idelta(rv)</td>
<td>对于范围矢量中每个时间序列，获取最后两个采样的差值</td>
</tr>
<tr>
<td>rate(rv)</td>
<td>对于范围矢量中每个时间序列，依据首尾两个采样的差值，计算每秒平均增量</td>
</tr>
<tr>
<td>irate(rv)</td>
<td>对于范围矢量中每个时间序列，依据最后两个采样的差值，计算每秒平均增量</td>
</tr>
<tr>
<td>resets(rv)</td>
<td>返回counter重置次数，只要值变小就认为被重置</td>
</tr>
<tr>
<td>sort(iv)<br />sort_desc(iv)</td>
<td>对矢量进行排序</td>
</tr>
<tr>
<td>AGG_over_time()</td>
<td>对范围矢量中每个时间序列进行基于时间的聚合操作，AGG可以是avg、min、max、sum、count等</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">注意点</span></div>
<div class="blog_h3"><span class="graybg">先rate再sum</span></div>
<p>不能对已经聚合过的数据再进行rate，只能对原始counter进行rate。正确的示例：</p>
<pre class="crayon-plain-tag"># 对平均值，按service进行分组
    # 对每个指标求每分钟平均值
sum(rate(dubbo_consumer_elapsed_ms{kubernetes_namespace="$namespace"}[2m]) * 60) by (service)    /
# 分组之后的数据仍然可以进行算术运算，按service值分别进行运算
sum(rate(dubbo_consumer_success_count{kubernetes_namespace="$namespace"}[2m]) * 60) by (service)</pre>
<div class="blog_h1"><span class="graybg">集成K8S</span></div>
<div class="blog_h2"><span class="graybg">安装</span></div>
<p>这里使用Helm Chart方式安装，Chart的源码位于：</p>
<p style="padding-left: 30px;"><a href="https://git.gmem.cc/alex/helm-charts/src/branch/master/prometheus">https://git.gmem.cc/alex/helm-charts/src/branch/master/prometheus</a>。</p>
<p>安装脚本如下：</p>
<pre class="crayon-plain-tag">rm -rf prometheus
helm fetch gmem/prometheus --untar
helm install prometheus --name=prometheus --namespace=kube-system  -f prometheus/overrides/gmem.yaml</pre>
<div class="blog_h2"><span class="graybg">说明</span></div>
<p>上面的Chart默认配置了以下Job：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 35%; text-align: center;">Job</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>kubernetes-apiservers</td>
<td>通过API Server采集指标，例如API的用量</td>
</tr>
<tr>
<td>kubernetes-nodes</td>
<td>采集节点的监控指标</td>
</tr>
<tr>
<td>kubernetes-nodes-cadvisor</td>
<td>采集容器的监控指标</td>
</tr>
<tr>
<td>kubernetes-service-endpoints</td>
<td>监控K8S的服务端点</td>
</tr>
<tr>
<td>kubernetes-services</td>
<td>监控K8S的服务</td>
</tr>
<tr>
<td>kubernetes-pods</td>
<td>监控K8S的Pod</td>
</tr>
<tr>
<td>prometheus-pushgateway</td>
<td>从推送网关拉取指标</td>
</tr>
<tr>
<td>prometheus</td>
<td>自我监控</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">指标采集</span></div>
<div class="blog_h2"><span class="graybg">自我监控</span></div>
<p>启动Prometheus后，访问http://localhost:9090/metrics可以查看Prometheus本身的指标信息：</p>
<pre class="crayon-plain-tag"># curl http://localhost:9090/metrics
# 输出示意片断
# HELP http_requests_total Total number of HTTP requests made.
# TYPE http_requests_total counter
# 指标名             {标签=值,标签=值...}                       指标值
http_requests_total{code="200",handler="graph",method="get"} 3
http_requests_total{code="200",handler="label_values",method="get"} 5
http_requests_total{code="200",handler="prometheus",method="get"} 4105
http_requests_total{code="200",handler="query",method="get"} 13
http_requests_total{code="200",handler="query_range",method="get"} 3
http_requests_total{code="200",handler="static",method="get"} 26
http_requests_total{code="304",handler="static",method="get"} 20
# 在http://localhost:9090/graph的控制台中输入http_requests_total进行查询，也可以得到上面的输出</pre>
<p>http_requests_total是Prometheus暴露（Export）的关于自身的指标 —— 接受的HTTP请求总数，包括了多个时间序列数据。这些时间序列数据的指标名都一样，但是具有不同的标签，这些标签用于区分不同类型的HTTP请求。</p>
<p>我们可以在控制台输入<pre class="crayon-plain-tag">http_requests_total{code="200"}</pre>，表示仅仅查询具有标签code=200的HTTP请求数。</p>
<p>类似的，还可以使用表达式<pre class="crayon-plain-tag">count(http_requests_total)</pre>，来统计指标http_requests_total具有的时间序列数据的数量。</p>
<p>在http://localhost:9090/graph页面，点击Graph选项卡，可以生成图表。例如表达式<pre class="crayon-plain-tag">rate(http_requests_total[1m])</pre> 表示生成最近一分钟的HTTP请求总数的图表，每个时间序列产生一条曲线。</p>
<div class="blog_h2"><span class="graybg">Node Exporter</span></div>
<p>Node Exporter是Prometheus提供的，用于监控Linux系统的组件。对于Windows，则有功能类似的<a href="https://github.com/martinlindhe/wmi_exporter">WMIExporter</a>。</p>
<p>下载NodeExporter后运行，它默认会在9100端口上暴露本机的各项指标。你可以访问http://localhost:9100/metrics来查看。修改Prometheus默认配置文件尾部的9090端口为9100即可采集这些指标。</p>
<div class="blog_h2"><span class="graybg">cAdvisor</span></div>
<p><a href="https://github.com/google/cadvisor">这个项目</a>是Google开源的，专门采集容器资源用量、性能指标。cAdvisor嵌入在kubelet中运行。</p>
<div class="blog_h2"><span class="graybg">JMX Exporter</span></div>
<p><a href="https://github.com/prometheus/jmx_exporter">此项目</a>可以暴露JMX管理Beans给Prometheus采集。它作为Java Agent运行，开启一个HTTP端口，对外提供本地JVM的各项指标。</p>
<p>为JVM添加参数：<pre class="crayon-plain-tag">-javaagent:jmx_prometheus_javaagent-0.3.1.jar=9100:config.yaml</pre>即可运行此Exporter。其中9100为暴露的端口号，config.yaml为配置文件路径。要采集指标，访问http://host:9100/metrics即可。</p>
<div class="blog_h3"><span class="graybg">配置文件</span></div>
<p>格式为YAML：</p>
<pre class="crayon-plain-tag"># 延迟于JVM启动HTTP端口的时间
startDelaySeconds: 0
# 如果连接到远程JVM，采集JMX信息，则要么指定hostPort，要么指定jmxUrl
hostPort: 127.0.0.1:1234
jmxUrl: service:jmx:rmi:///jndi/rmi://127.0.0.1:1234/jmxrmi
# 远程JMX身份验证信息
username: 
password: 
# 远程JMX是否通过SSL连接
ssl: false
# 是否小写化指标名、标签名
lowercaseOutputName: false
lowercaseOutputLabelNames: false
# 是否采集的ObjectNames的白、黑名单。默认采集所有mBeans
whitelistObjectNames: ["org.apache.cassandra.metrics:*"]
blacklistObjectNames: ["org.apache.cassandra.metrics:type=ColumnFamily,*"]
# 规则列表，从上往下执行，遇到匹配的规则则终止
# 不匹配的Attributes不被采集，默认情况下，以默认格式采集所有
rules:
    # 匹配mBean Attributes的正则式
  - pattern: 'org.apache.cassandra.metrics&lt;type=(\w+), name=(\w+)&gt;&lt;&gt;Value: (\d+)'
    # 指标名
    name: cassandra_$1_$2
    # 指标值
    value: $3
    # 指标值需要乘以的系数
    valueFactor: 0.001
    # 标签，可以使用捕获组
    labels: {}
    # 指标帮助信息
    help: "Cassandra metric $1 $2"
    # 指标类型
    type: GAUGE
    # 转换为下划线 + 小写风格
    attrNameSnakeCase: false</pre>
<p>如果没有任何配置内容，则以默认格式采集本地JVM的所有指标。</p>
<p>官方提供了<a href="https://github.com/prometheus/jmx_exporter/tree/master/example_configs">若干中间件的示例配置文件</a>。 </p>
<div class="blog_h3"><span class="graybg">输入pattern</span></div>
<p>传递给配置文件rules.pattern的格式为：</p>
<pre class="crayon-plain-tag"># domain mBean的名称，JMX ObjectNames的冒号前的部分
# beanProperyName/Value mBean属性列表，JMX ObjectNames的冒号后的部分
# keyN 如果是组合或表格数据，则包含键列表
# attrName 属性的名称，如果是表格数据，则为列名称。如果设置了attrNameSnakeCase则转换为下划线小写
# value 属性值
domain&lt;beanpropertyName1=beanPropertyValue1, beanpropertyName2=beanPropertyValue2, ...&gt;&lt;key1, key2, ...&gt;attrName: value </pre>
<p>不经配置，输出的默认指标格式为：</p>
<pre class="crayon-plain-tag">domain_beanPropertyValue1_key1_key2_...keyN_attrName{beanpropertyName2="beanPropertyValue2", ...}: value </pre>
<div class="blog_h1"><span class="graybg">客户端编程</span></div>
<p>Prometheus提供了主流语言的客户端库。要使用Prometheus的Go客户端库，导入：</p>
<pre class="crayon-plain-tag">import "github.com/prometheus/client_golang/prometheus"</pre>
<div class="blog_h2"><span class="graybg">核心类型</span></div>
<div class="blog_h3"><span class="graybg">Desc</span></div>
<p>该结构是任何Prometheus指标都需要使用的描述符，它本质上是<span style="background-color: #c0c0c0;">指标（Metrics）的不可变元数据</span>：</p>
<pre class="crayon-plain-tag">struct {
	// 全限定名称，由命名空间 - 子系统 - 名称组成
	fqName string
	// 指标的帮助信息
	help string
	// 常量标签键值
	constLabelPairs []*dto.LabelPair
	// 可变标签的名字
	variableLabels []string
	// 基于ConstLabels和fqName生成的哈希，所有注册的Desc都必须具有独特的值
        // 以作为Desc的唯一标识
	id uint64
	// 维度哈希，所有常量/可变标签的哈希，所有具有相同fqName的Desc必须具有相同的dimHash
	// 这意味着每个fqName对应的标签集是固定的
	dimHash uint64
	// 构造时出现的错误，注册时报告
	err error
}</pre>
<div class="blog_h3"><span class="graybg">Metric</span></div>
<p>所有指标的通用接口，表示需要导出到Prometheus的单个采样值+关联的元数据集 。此接口的实现包括Gauge、Counter、Histogram、Summary、Untyped。</p>
<pre class="crayon-plain-tag">type Metric interface {
	// 幂等的返回该指标的、不可变的描述符
	// 不能描述子集的指标，必须返回一个无效的描述符。无效描述符通过NewInvalidDesc创建
	Desc() *Desc
	// 将指标对象编码为ProtoBuffer数据传输对象
	// 指标实现必须考虑并发安全性，因为对指标的读可能随时发生，任何阻塞操作都会影响
	// 所有已经注册的指标的整体渲染性能
	// 理想的实现应该支持并发读
	//
	// 除了产生dto.Metric，实现还负责确保Metric的ProtoBuf合法性验证
	// 建议使用字典序排序标签，LabelPairSorter可能对指标实现者有帮助
	Write(*dto.Metric) error
}</pre>
<div class="blog_h3"><span class="graybg">MetricVec</span></div>
<p>此结构表示用于bundle全限定名称、标签值有所不同的指标。通常不会直接使用此结构，而是将它作为具体指标向量GaugeVec, CounterVec, SummaryVec,  UntypedVec的一部分：</p>
<pre class="crayon-plain-tag">type MetricVec struct {
	mtx      sync.RWMutex // 保护元素的锁
	children map[uint64][]metricWithLabelValues // 所有指标实例（值+标签集）
	desc     *Desc // 描述符

	newMetric   func(labelValues ...string) Metric  // 以指定的标签值创建新指标
	hashAdd     func(h uint64, s string) uint64
	hashAddByte func(h uint64, b byte) uint64
} </pre>
<div class="blog_h3"><span class="graybg">Opts</span></div>
<p>创建大部分的指标类型时，可以通过该接口提供选项：</p>
<pre class="crayon-plain-tag">type Opts struct {
	// Namespace, Subsystem, Name是指标的全限定名称的组成部分，这些部分使用下划线连接
	// 仅仅Name是必须的
	Namespace string
	Subsystem string
	Name      string

	// 帮助信息，单个全限定名称，其帮助信息必须一样
	Help string

	// 常量标签用于为指标提供固定的标签，单个全限定名称，其常量标签集的所包含的标签名必须一致
	// 注意在大部分情况下，标签的值会变化，这些标签通常由指标矢量收集器（metric vector collector）来
	// 处理，例如CounterVec、GaugeVec、UntypedVec，而ConstLabels则仅用于特殊情况，例如：
	// vector collector (like CounterVec, GaugeVec, UntypedVec). ConstLabels
	// 1、在整个处理过程中，标签的值绝不会改变。这种标签例如运行中的二进制程序的修订版号
	// 2、在具有多个收集器（collector）来收集相同全限定名称的指标的情况下，那么每个收集器收集的
	//    指标的常量标签的值必须有所不同
	// 如果任何情况下，标签的值都不会改变，它可能更适合编码到全限定名称中
	ConstLabels Labels
}</pre>
<div class="blog_h3"><span class="graybg">Collector</span></div>
<p>任何Prometheus用来收集指标的对象，都需要实现此接口：</p>
<pre class="crayon-plain-tag">type Collector interface {
	// 将此收集器收集的指标的所有可能的描述符发送到参数提供的通道。并且在最后一个描述符
	// 发送成功后返回。发送的描述符必须满足Desc文档声明的一致性、唯一性要求
	// 
	// 同一收集器发送重复的描述符是允许的，重复自动忽视
	// 但是两个收集器不得发送重复的描述符
	//
	// 如果不发送任何描述符，则收集器标记为unchecked状态，也就是说在注册时，不会进行任何检查
	// 收集器以后可能产生任何匹配它的Collect方法签名的指标
	//
	// 该方法在收集器的生命周期里，幂等的发送相同的描述符
	// 
	// 该方法可能被并发的调用，实现时需要注意线程安全问题
	//
	// 如果在执行该方法的过程中收集器遇到错误，务必发送一个无效的描述符（NewInvalidDesc）来提示注册表
	Describe(chan&lt;- *Desc)
	// 在收集指标时，该方法被Prometheus注册表（Registry）调用。方法的实现必须将所有它收集到的指标
	// 经由参数提供的通道发送，并且在最后一个指标发送后返回
	// 
	// 每个发送的指标的描述符，必须是Describe方法提供的之一（除非收集器是Unchecked）
	// 发送的共享相同描述符的指标，其标签集必须有所不同
	// 
	//
	// 该方法可能被并发的调用，实现时需要注意线程安全问题
	// 
	// 阻塞会导致影响所有已注册的指标的渲染性能，理想情况下，实现应该支持并发读
	Collect(chan&lt;- Metric)
} </pre>
<p>收集器必须被注册（Registerer.Register）才能收集指标值。</p>
<p>内置的指标类型实现了此接口，包括GaugeVec、CounterVec、HistogramVec、SummaryVec。</p>
<div class="blog_h3"><span class="graybg">Registry</span></div>
<p>注册表，此结构实现了Registerer、Gatherer接口。</p>
<p>Registerer接口为注册表提供注册/反注册功能：</p>
<pre class="crayon-plain-tag">type Registerer interface {
	// 注册一个需要包含在指标集中的收集器。如果收集器提供的描述符非法、
	// 或者不满足metric.Desc的一致性/唯一性需求，则返回错误
	//
	// 如果相等的收集器已经注册过，返回AlreadyRegisteredError，其中包含先前注册的收集器的实例
	//
	// 其Describe方法不产生任何Desc的收集器，视为Unchecked，对这种收集器的注册总是成功
	// 重现注册它时也不会有检查。因此，调用者必须负责确保不会重复注册
	Register(Collector) error
	// 注册多个收集器，并且在遇到第一个失败时就Panic
	MustRegister(...Collector)
	// 反注册
	Unregister(Collector) bool
}</pre>
<p>Gatherer为注册表提供汇集（gathering）功能 —— 将已经收集的指标汇集到若干指标族（MetricFamily）中：</p>
<pre class="crayon-plain-tag">type Gatherer interface {
	// 该方法调用所有已经注册的收集器的Collect方法，然后将获得的指标存放到一个字典序排列
	// 的MetricFamily的切片中。该方法保证返回的切片是有效的、自我一致的，可以用于对外
	// 暴露（给Prometheus服务器）该方法容忍相同指标族中具有不同标签集的指标
	//
	// 即时发生错误，该方法也会尝试尽可能收集更多的指标。因此，当该方法返回非空error时
	// 同时返回的dto.MetricFamily切片可能是nil（意味着致命错误）或者包含一定数量的
	// MetricFamily —— 切片可能是不完整的
	Gather() ([]*dto.MetricFamily, error)
} </pre>
<div class="blog_h3"><span class="graybg">Gauge</span></div>
<p>表示gauge类型的指标： </p>
<pre class="crayon-plain-tag">type Gauge interface {
	// 实现的接口
	Metric
	Collector

	// 设值
	Set(float64)
	// 增1
	Inc()
	// 减1
	Dec()
	// 加上一个值
	Add(float64)
	// 减去一个值
	Sub(float64)
}</pre>
<p>要创建一个Gauge，可以调用：</p>
<pre class="crayon-plain-tag">func NewGauge(opts GaugeOpts) Gauge {
	// func newValue(desc *Desc, valueType ValueType, val float64, labelValues ...string) *value
	return newValue(NewDesc(
		BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
		opts.Help,
		nil,
		opts.ConstLabels,
	), GaugeValue, 0)
}</pre>
<p>GaugeVec表示Gauge的向量：</p>
<pre class="crayon-plain-tag">type GaugeVec struct {
	*MetricVec
}</pre>
<div class="blog_h3"><span class="graybg">Histogram</span></div>
<p>表示histogram类型的指标。</p>
<p>Histogram对可配置的Bucket（观察值的区间）中的事件或样本流，基于Bucket进行独立计数观察。它支持对观察值（observations）进行计数，或者求和。</p>
<p>在Prometheus中，分位数可以基于Histogram，通过函数histogram_quantile计算得到。Histogram依赖于用户定义的、适当的buckets，一般来说精确度相对较低，但是和Summary比起来，Histogram的性能成本较低。</p>
<pre class="crayon-plain-tag">type Histogram interface {
	Metric
	Collector

	// 添加一个观察值
	Observe(float64)
}</pre>
<p>默认的Buckets如下，主要用于测量网络响应延迟的场景：</p>
<pre class="crayon-plain-tag">DefBuckets = []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10}</pre>
<p>对于其它场景，你应当自己定义Buckets。 </p>
<div class="blog_h2"><span class="graybg">创建指标</span></div>
<p>我们不会直接操控指标，而是使用它们的向量 —— GaugeVec、CounterVec、HistogramVec、SummaryVec。这些向量都是Collector接口的实现。</p>
<p>要创建向量，需要调用prometheus.New***Vec方法，例如：</p>
<pre class="crayon-plain-tag">func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec {
// 描述符，关键信息是全限定名 + 标签集
	desc := NewDesc(
		BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
		opts.Help,
		labelNames,
		opts.ConstLabels,
	)
	return &amp;GaugeVec{
		metricVec: newMetricVec(desc, func(lvs ...string) Metric {
// 第二个参数是回调，标签集创建一个Metric —— 指标，准确的说是时间序列
			if len(lvs) != len(desc.variableLabels) {
				panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, lvs))
			}
			result := &amp;gauge{desc: desc, labelPairs: makeLabelPairs(desc, lvs)}
			result.init(result) // Init self-collection.
			return result
		}),
	}
} </pre>
<div class="blog_h3"><span class="graybg">Gauge</span></div>
<p>下面的例子创建一个Gauge向量：</p>
<pre class="crayon-plain-tag">weight := prometheus.NewGaugeVec(prometheus.GaugeOpts{
	Subsystem: "flagger",
	Name:      "canary_weight",
	Help:      "The virtual service destination weight current value",
}, []string{"workload", "namespace"})  // 注意标签顺序</pre>
<p>获取具有指定标签值的Gauge（时间序列）： </p>
<pre class="crayon-plain-tag">// 注意标签值的顺序，和上面对应
gauge := cr.weight.WithLabelValues(cd.Spec.TargetRef.Name, cd.Namespace)</pre>
<p>产生一个指标值：</p>
<pre class="crayon-plain-tag">gauge.Set(float64(canary))</pre>
<div class="blog_h3"><span class="graybg">Histogram</span></div>
<p>下面的例子使用默认Bucket创建Histogram向量：</p>
<pre class="crayon-plain-tag">duration := prometheus.NewHistogramVec(prometheus.HistogramOpts{
	Subsystem: controllerAgentName,
	Name:      "canary_duration_seconds",
	Help:      "Seconds spent performing canary analysis.",
	Buckets:   prometheus.DefBuckets,
}, []string{"name", "namespace"})</pre>
<p>产生一个指标值：</p>
<pre class="crayon-plain-tag">cr.duration.WithLabelValues(cd.Spec.TargetRef.Name, cd.Namespace).Observe(duration.Seconds())</pre>
<div class="blog_h2"><span class="graybg">注册指标</span></div>
<div class="blog_h3"><span class="graybg">默认注册表</span></div>
<p>Prometheus客户端提供了开箱即用的默认注册表：<pre class="crayon-plain-tag">prometheus.DefaultRegisterer</pre></p>
<div class="blog_h3"><span class="graybg">创建注册表</span></div>
<p>你可以调用以下函数来创建注册表：</p>
<pre class="crayon-plain-tag">// 不预先注册任何收集器的注册表
func NewRegistry() *Registry {
	return &amp;Registry{
		collectorsByID:  map[uint64]Collector{},
		descIDs:         map[uint64]struct{}{},
		dimHashesByName: map[string]uint64{},
	}
}

// 创建一个严格的注册表。该注册表在收集期间，检查
// 1、每个指标是否和它的Desc一致
// 2、指标的Desc是否已经注册到注册表
// Unchecked的收集器不被检查
func NewPedanticRegistry() *Registry {
	r := NewRegistry()
	r.pedanticChecksEnabled = true
	return r
}</pre>
<p>示例：</p>
<pre class="crayon-plain-tag">registry = prometheus.NewPedanticRegistry()</pre>
<div class="blog_h3"><span class="graybg">注册收集器 </span></div>
<pre class="crayon-plain-tag">// 进行注册
if err := registry.Register(gauge); err != nil {
// 如果已经注册
	if are, ok := err.(prometheus.AlreadyRegisteredError); ok {
// 返回先前注册的收集器
		return are.ExistingCollector, nil
	}
	return nil, err
} </pre>
<div class="blog_h2"><span class="graybg">暴露指标</span></div>
<p>Prometheus客户端不能直接将指标发送给Prometheus服务器。只能通过网络端口暴露一个Exporter。</p>
<p>如果要基于HTTP协议暴露，将promhttp包提供的Handler传递给你的HTTP服务器，<span style="background-color: #c0c0c0;">每个Handler读取单个注册表，从中收集指标信息，生成HTTP响应</span>：</p>
<pre class="crayon-plain-tag">// 使用默认注册表创建Handler
func Handler() http.Handler {
	return InstrumentMetricHandler(
		prometheus.DefaultRegisterer, HandlerFor(prometheus.DefaultGatherer, HandlerOpts{}),
	)
}</pre>
<p>如果你需要使用非默认注册表，直接调用：</p>
<pre class="crayon-plain-tag">func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler {}</pre>
<p> 然后将Handler传递给你的ServeMux即可：</p>
<pre class="crayon-plain-tag">mux := http.NewServeMux()
import "github.com/prometheus/client_golang/prometheus/promhttp"
mux.Handle("/metrics", promhttp.Handler())</pre>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/prometheus-study-note">Prometheus学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/prometheus-study-note/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>InfluxDB学习笔记</title>
		<link>https://blog.gmem.cc/influxdb-study-note</link>
		<comments>https://blog.gmem.cc/influxdb-study-note#comments</comments>
		<pubDate>Fri, 23 Mar 2018 02:05:14 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Database]]></category>
		<category><![CDATA[TSDB]]></category>

		<guid isPermaLink="false">https://blog.gmem.cc/?p=19921</guid>
		<description><![CDATA[<p>简介 InfluxDB是一个用于存储和分析时间序列数据的开源数据库。它的特性包括： 支持HTTP接口 支持类SQL的查询 —— InfluxQL 高效的数据读写 InfluxDB默认使用两个端口：8086用于HTTP接口，8088用于备份/恢复数据的RPC服务。 安装 Ubuntu [crayon-69e3b23eb8435181222000/] K8S [crayon-69e3b23eb8439735791446/] 执行下面命令进入CLI： [crayon-69e3b23eb843c072235062/] 入门 CLI 创建数据库 命令格式类似于MySQL： [crayon-69e3b23eb843e077639352/] 读写数据 InfluxDB中存放的是时间序列数据，包括0-N个数据点。数据点由以下部分组成： time，时间戳 <a class="read-more" href="https://blog.gmem.cc/influxdb-study-note">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/influxdb-study-note">InfluxDB学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h1"><span class="graybg">简介</span></div>
<p>InfluxDB是一个用于存储和分析时间序列数据的开源数据库。它的特性包括：</p>
<ol>
<li>支持HTTP接口</li>
<li>支持类SQL的查询 —— InfluxQL</li>
<li>高效的数据读写</li>
</ol>
<p>InfluxDB默认使用两个端口：8086用于HTTP接口，8088用于备份/恢复数据的RPC服务。</p>
<div class="blog_h1"><span class="graybg">安装</span></div>
<div class="blog_h2"><span class="graybg">Ubuntu</span></div>
<pre class="crayon-plain-tag">curl -sL https://repos.influxdata.com/influxdb.key | sudo apt-key add -
source /etc/lsb-release
echo "deb https://repos.influxdata.com/${DISTRIB_ID,,} ${DISTRIB_CODENAME} stable" | sudo tee /etc/apt/sources.list.d/influxdb.list

sudo apt-get update
sudo apt-get install influxdb
sudo service influxdb start</pre>
<div class="blog_h2"><span class="graybg">K8S</span></div>
<pre class="crayon-plain-tag">apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: influxdb-pvc
  namespace: dev
spec:
  storageClassName: rook-block
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

---

apiVersion: v1
kind: Pod
metadata:
  namespace: dev
  name: influxdb
  labels:
    app: influxdb
  annotations:
    "cni.projectcalico.org/ipAddrs": "[\"172.27.0.20\"]"
spec:
  terminationGracePeriodSeconds: 10
  containers:
  - name: influxdb
    image: docker.gmem.cc/influxdb
    ports:
    - containerPort: 8086
      name: http
    - containerPort: 8082
      name: ifql
    volumeMounts:
      - name: influxdb-pv
        mountPath: /var/lib/influxdb
  volumes:
    - name: influxdb-pv
      persistentVolumeClaim:
        claimName: influxdb-pvc

--- 

apiVersion: v1
kind: Service
metadata:
  name: influxdb
  namespace: dev
spec:
  ports:
  - name: http
    port: 8086
    protocol: TCP
    targetPort: 8086
  selector:
    app: influxdb
  sessionAffinity: None
  type: ClusterIP</pre>
<p>执行下面命令进入CLI：</p>
<pre class="crayon-plain-tag">kubectl -n dev exec -it influxdb  influx -- -precision rfc3339</pre>
<div class="blog_h1"><span class="graybg">入门</span></div>
<div class="blog_h2"><span class="graybg">CLI</span></div>
<div class="blog_h3"><span class="graybg">创建数据库</span></div>
<p>命令格式类似于MySQL：</p>
<pre class="crayon-plain-tag">CREATE DATABASE metrics
SHOW DATABASES
# name: databases
# name
# ----
# _internal
# metrics
USE metrics</pre>
<div class="blog_h3"><span class="graybg">读写数据</span></div>
<p>InfluxDB中存放的是时间序列数据，包括0-N个数据点。数据点由以下部分组成：</p>
<ol>
<li>time，时间戳</li>
<li>measurement，此指标的名字。例如CPU_LOAD</li>
<li>field，至少一个，KV对，V为数值例如value=6.12</li>
<li>tag，0-N个，指标的元数据。 host=zircon</li>
</ol>
<p>指标的模式（Schema）是自由的，你可以任意添加字段、Tag。但是<span style="background-color: #c0c0c0;">字段类型不可以改变</span>，如果你第一次写入host的值类型是字符串，就不能在以后写入数字。</p>
<p>写入数据点时，你需要遵守如下格式的行协议：</p>
<pre class="crayon-plain-tag">&lt;measurement&gt;[,&lt;tag-key&gt;=&lt;tag-value&gt;...] &lt;f1-key&gt;=&lt;f1-val&gt;[,&lt;f2-key&gt;=&lt;f2-val&gt;...] [unix-nano-timestamp]
# 示例：
load,host=xenial-100,group=k8s value=3.1</pre>
<p>通过CLI差入数据的InfluxQL语法为：</p>
<pre class="crayon-plain-tag">INSERT load,host=xenial-100,group=k8s value=3.1</pre>
<p>类似的，可以用SELECT查询指标数据：</p>
<pre class="crayon-plain-tag">SELECT * FROM load
# name: load
# time                group host       value
# ----                ----- ----       -----
# 1522746924735704773 k8s   xenial-100 3.1

# 每个Tag、字段都可以作为列名
SELECT "group", host,value FROM "load"</pre>
<p>FROM子句中，支持使用正则式来匹配指标名：</p>
<pre class="crayon-plain-tag">SELECT * FROM /.*/ LIMIT 1</pre>
<p>WHERE子句也是支持的。</p>
<div class="blog_h2"><span class="graybg">HTTP API</span></div>
<div class="blog_h3"><span class="graybg">数据库管理</span></div>
<pre class="crayon-plain-tag">export URL=http://influxdb.dev.svc.k8s.gmem.cc:8086

# 执行InfluxQL             格式化响应JSON                   查询语句
curl -i -XPOST $URL/query?pretty=true --data-urlencode "q=SHOW DATABASES"
curl -i -XPOST $URL/query?pretty=true --data-urlencode "q=CREATE DATABASE metrics" </pre>
<div class="blog_h3"><span class="graybg">写入操作</span></div>
<pre class="crayon-plain-tag"># 写入操作，响应码204，数据正常写入
# 时间戳（纳秒）可以不传，这样自动使用InfluxDB本地时间
curl -i -XPOST $URL/write?db=metrics --data-binary 'host=xenial-100,group=k8s value=3.1 1434055562000000000'

# 可以同时写入多个数据点，使用换行符分隔

# 导入文件中的数据点、
curl -i -XPOST $URL/write?db=metrics --data-binary @load.txt </pre>
<div class="blog_h3"><span class="graybg">查询操作</span></div>
<pre class="crayon-plain-tag">curl -G $URL/query?pretty=true --data-urlencode "db=metrics" --data-urlencode "q=SELECT * FROM load"</pre>
<p>结果同样以JSON形式返回。要同时<span style="background-color: #c0c0c0;">执行多个查询，只需要用分号分开</span>InfluxQL语句。</p>
<p>使用请求参数chunked/chunk_size可以发起分块（Chunked）请求，数据流式返回：</p>
<pre class="crayon-plain-tag">--data-urlencode "chunked=true" --data-urlencode "chunk_size=20000 </pre>
<div class="blog_h2"><span class="graybg">持续查询</span></div>
<p>Influx提供了持续查询（Continuous Queries，CQ）来<span style="background-color: #c0c0c0;">处理数据采样</span>。CQ是按照时间进行聚合的InfluxQL语句，周期性的在Influx数据库内部运行。示例：</p>
<pre class="crayon-plain-tag">-- 查询数据库metrics的指标network的inbound、outbuound字段，对两者取平均值
-- 每30分钟分组聚合一次
-- 聚合结果存放到RP为one_year的指标sampled_load中，该指标具有字段mean_inbound、mean_outbound
CREATE CONTINUOUS QUERY "cq_30m" ON "metrics" BEGIN
    SELECT mean("inbound") AS "mean_inbound",mean("outbound") AS "mean_outbound"
    INTO "one_year"."sampled_load"
    FROM "network"
    GROUP BY time(30m)
END</pre>
<p>配合使用CQ和RP，可以实现近期数据高精度保留，远期数据低精度保留甚至删除。 </p>
<div class="blog_h2"><span class="graybg">驻留策略</span></div>
<p>Retention Policy（RP）定义了InfluxDB保留历史数据的时长。示例：</p>
<pre class="crayon-plain-tag"># 为指标load定义一个驻留策略，时长2小时
# REPLICATION为数据复制的份数，对于单节点InfluxDB，必须设置为1
CREATE RETENTION POLICY "two_hours" ON "load" DURATION 2h REPLICATION 1 DEFAULT </pre>
<div class="blog_h1"><span class="graybg">配置</span></div>
<p> 配置文件默认位于/etc/influxdb/influxdb.conf。</p>
<pre class="crayon-plain-tag"># 禁止向usage.influxdata.com发送使用情况信息
reporting-disabled = false

# 数据备份/恢复服务绑定地址
bind-address = "127.0.0.1:8088"


[meta]
# metadata/raft数据库的存放位置
dir = "/var/lib/influxdb/meta"
# 创建数据库时，自动创建默认的驻留策略
retention-autocreate = true
# 为meta service打印日志
logging-enabled = true


[data]
# TSM存储引擎在何处存储TSM文件
dir = "/var/lib/influxdb/data"
# TSM存储引擎在何处存储TWAL文件
wal-dir = "/var/lib/influxdb/wal"
# 写操作fsync到磁盘的延迟，大于0的值可以用于慢速磁盘，以及WML写出现竞态的情况
# 对于非SSD磁盘，推荐取值范围0-100ms
wal-fsync-delay = "0s"
# 新分片使用的分片索引（Shard index）类型，默认是内存索引，重启后会重新创建
# 取值tsi1使用基于磁盘的索引，支持大cardinality数据集
index-version = "inmem"
# 是否启用tsm引擎的冗长日志 
trace-logging-enabled = false
# 是否启用查询日志
query-log-enabled = true
### TSM引擎配置 ###
# 在拒绝写入操作之前，分片缓存占用的内存最大量
cache-max-memory-size = "1g"
# 当缓存达到多大时，引擎产生其快照，并写入到TSM文件，以释放内存空间
cache-snapshot-memory-size = "25m"
# 如果分片持续多久没有接收到新的写入/删除操作，则引擎产生缓存的快照，并写入到新的TSM文件
cache-snapshot-write-cold-duration = "10m"
# 如果引擎持续多久没有接收到分片的新的写入/删除操作，它将压缩分片的所有TSM文件
compact-full-write-cold-duration = "4h"
# 压缩并行度
max-concurrent-compactions = 0
# 在丢弃写操作之前，每个数据库允许的最大序列（Series）数量
# 可以在数据库级别解决高Cardinality问题，设置为0则禁用
max-series-per-database = 1000000
# 在丢弃写操作之前，每个Tag的最大值数量
# 可以解决Tag值的高Cardinality问题，设置为0则禁用
max-values-per-tag = 100000


# 集群服务配置
[coordinator]
# 写操作超时时间
write-timeout = "10s"
# 同时执行的查询的最大数量
max-concurrent-queries = 0
# 查询超时：在查询被系统杀掉之前，经过的最大时间
query-timeout = "0s"
# 缓慢查询阈值，超过此阈值的被记录到缓慢日志
log-queries-after = "0s"
# 一个SELECT最多能处理的数据点数量
max-select-point = 0
# 一个SELECT可以处理的Series的最大数量
max-select-series = 0
# 一个SELECT最多创建的Group by 时间桶（Time Bucket）数量
max-select-buckets = 0


# 驻留策略配置，驻留策略决定了如何清除老旧数据
[retention]
# 是否启用驻留策略，也就是说是否清除老旧数据
enabled = true
# 每隔多久进行运行驻留策略
check-interval = "30m"


# 控制分配的预创建，这样在数据到达前分片即可用
[shard-precreation]
# 是否启用分片预创建服务
enabled = true
# 预创建服务运行间隔
check-interval = "10m"

advance-period = "30m"


# 系统自我监控、统计和诊断
[monitor]
# 是否在内部进行统计信息
store-enabled = true
# 存储统计信息的目标数据库
store-database = "_internal"
# 每隔多久记录一次统计信息
store-interval = "10s"


[http]
# 是否启用HTTP端点
enabled = true
# HTTP服务的绑定地址
bind-address = ":8086"
# 是否启用用户身份验证
auth-enabled = false
# 基于HTTP基本认证时，发送给客户端的Realm信息
realm = "InfluxDB"
# 是否记录HTTP请求日志
log-enabled = true
# 访问日志路径
access-log-path = ""
# 是否启用详细的写日志
write-tracing = false
# 是否启用pprof端点，用于调试和诊断
pprof-enabled = true
# 是否启用HTTPS
https-enabled = false
# SSL整数路径
https-certificate = "/etc/ssl/influxdb.pem"
# 可以指定单独的私钥路径
https-private-key = ""
# SON web token共享密钥
shared-secret = ""
# 结果集大小限制，防止查询耗尽InfluxDB内存
# 仅仅限制非分块的HTTP API请求，分块请求不受限制
max-row-limit = 0
# 最大连接数
max-connection-limit = 0
# 是否使用UNIX域套接字
unix-socket-enabled = false
# UNIX域套接字路径
bind-socket = "/var/run/influxdb.sock"
# 客户端请求体大小
max-body-size = 25000000


[ifql]
# 是否启用RPC服务
enabled = true
# 是否进行额外的日志记录
log-enabled = true
# 绑定地址
bind-address = ":8082"


[logging]
# 日志格式
format = "auto"
# 日志级别
level = "info"
# 是否禁止显示LOGO
suppress-logo = false


# 控制订阅，订阅可以用于获取整个数据集的拷贝
[subscriber]
# 是否启用订阅者服务
enabled = true
# 写到订阅者的HTTP请求的超时
http-timeout = "30s"
# 是否允许到订阅者的不安全连接
insecure-skip-verify = false
# 数字证书位置
ca-certs = ""
# Goroutine数量
write-concurrency = 40
# 写缓冲区大小
write-buffer-size = 1000


[continuous_queries]
# 是否启用持续查询服务
enabled = true
# 是否记录CQ日志
log-enabled = true
# 是否记录统计信息
query-stats-enabled = false
# 多久检查CQ服务是否需要运行
run-interval = "1s"</pre>
<p>&nbsp;</p>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/influxdb-study-note">InfluxDB学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/influxdb-study-note/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>OpenTSDB学习笔记</title>
		<link>https://blog.gmem.cc/opentsdb-study-note</link>
		<comments>https://blog.gmem.cc/opentsdb-study-note#comments</comments>
		<pubDate>Thu, 20 Oct 2016 02:24:00 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[BigData]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[TSDB]]></category>

		<guid isPermaLink="false">https://blog.gmem.cc/?p=16756</guid>
		<description><![CDATA[<p>简介 OpenTSDB是一个开源的、被广泛使用的时间序列数据库。它提供了一整套和监控有关的功能，可以用来构建分布式、可扩容的监控系统。使用OpenTSDB可以不损失统计精度的永久保存监控数据，统计精度可以达到毫秒级。OpenTSDB的底层是Hadoop/HBase，可以扩容到每秒百万级的监控数据写操作。OpenTSDB自带了前端组件，并且支持通过HTTP Pull API拉取数据，这样你可以选择自己喜爱的前端组件。</p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/opentsdb-study-note">OpenTSDB学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h1"><span class="graybg">简介</span></div>
<p>OpenTSDB是一个开源的、被广泛使用的时间序列数据库。它提供了一整套和监控有关的功能，可以用来构建分布式、可扩容的监控系统。使用OpenTSDB可以不损失统计精度的永久保存监控数据，统计精度可以达到毫秒级。OpenTSDB的底层是Hadoop/HBase，可以扩容到每秒百万级的监控数据写操作。OpenTSDB自带了前端组件，并且支持通过HTTP Pull API拉取数据，这样你可以选择自己喜爱的前端组件。</p>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/opentsdb-study-note">OpenTSDB学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/opentsdb-study-note/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>使用Grafana展示时间序列数据</title>
		<link>https://blog.gmem.cc/time-series-data-renderering-with-grafana</link>
		<comments>https://blog.gmem.cc/time-series-data-renderering-with-grafana#comments</comments>
		<pubDate>Tue, 09 Aug 2016 03:18:54 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[K8S]]></category>
		<category><![CDATA[Monitoring]]></category>
		<category><![CDATA[NoSQL]]></category>
		<category><![CDATA[TSDB]]></category>

		<guid isPermaLink="false">https://blog.gmem.cc/?p=12721</guid>
		<description><![CDATA[<p>简介 Grafana是一个独立运行的系统，内置了Web服务器。它可以基于仪表盘的方式来展示、分析时间序列数据。 Grafana支持多种数据源，例如：Graphite、OpenTSDB、InfluxDB、Elasticsearch。你可以混合使用多种数据源。它对Graphite有以下增强的支持： 点击修改Metrics路径的每一个片断 快速的添加函数，支持点击函数参数以修改之 修改函数顺序 丰富的模板支持 在UI方面，Grafana具有以下特性： 丰富的、基于客户端的图表组件：Bar图、区域图、线图。支持多Y轴 支持点击/选择以缩放（Zoom）时间区间 支持混合多种图表组件，一起展示或者堆叠展示 支持定制图表的配色，支持黑白两种主题 支持拖放仪表盘面板（Panel），支持多种面板类型 支持脚本化仪表盘、仪表盘模板 通过来自数据源的事件（例如Graphite的Events），可以对仪表进行标注 核心概念 术语 说明 数据源（Data Source） Grafana支持多种存储后端，这些后端称为数据源Grafana为每种数据源开发了查询编辑器（ Query <a class="read-more" href="https://blog.gmem.cc/time-series-data-renderering-with-grafana">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/time-series-data-renderering-with-grafana">使用Grafana展示时间序列数据</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h1"><span class="graybg">简介</span></div>
<p>Grafana是一个独立运行的系统，内置了Web服务器。它可以基于仪表盘的方式来展示、分析时间序列数据。</p>
<p>Grafana支持多种数据源，例如：Graphite、OpenTSDB、InfluxDB、Elasticsearch。你可以混合使用多种数据源。它对Graphite有以下增强的支持：</p>
<ol>
<li>点击修改Metrics路径的每一个片断</li>
<li>快速的添加函数，支持点击函数参数以修改之</li>
<li>修改函数顺序</li>
<li>丰富的模板支持</li>
</ol>
<p>在UI方面，Grafana具有以下特性：</p>
<ol>
<li>丰富的、基于客户端的图表组件：Bar图、区域图、线图。支持多Y轴</li>
<li>支持点击/选择以缩放（Zoom）时间区间</li>
<li>支持混合多种图表组件，一起展示或者堆叠展示</li>
<li>支持定制图表的配色，支持黑白两种主题</li>
<li>支持拖放仪表盘面板（Panel），支持多种面板类型</li>
<li>支持脚本化仪表盘、仪表盘模板</li>
<li>通过来自数据源的事件（例如Graphite的Events），可以对仪表进行标注</li>
</ol>
<div class="blog_h2"><span class="graybg">核心概念</span></div>
<table class=" fixed-word-wrap full-width">
<thead>
<tr>
<td style="width: 25%; text-align: center;">术语</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>数据源（Data Source）</td>
<td>Grafana支持多种存储后端，这些后端称为数据源<br />Grafana为每种数据源开发了查询编辑器（ Query Editor）以匹配后端的特点</td>
</tr>
<tr>
<td>组织（Organization）</td>
<td>引入组织机构的概念，以便一个Grafana实例可以被多个可能不受信任的组织使用</td>
</tr>
<tr>
<td>用户（User）</td>
<td>Grafana用户可以归属于1个或者多个组织</td>
</tr>
<tr>
<td>行（Row）</td>
<td>仪表盘中的逻辑分段</td>
</tr>
<tr>
<td>面板（Panel）</td>
<td>
<p>仪表盘中最基本的可视化单元，每个面板提供查询编辑器。Grafana目前内置了Graph, Singlestat, Dashlist和Text这几种面板</p>
<p>每种面板都具有一些样式、格式化选项，你可以利用它们设计出漂亮的图表</p>
<p>可以通过拖拽，在仪表盘中重新放置面板，也可以缩放其大小</p>
</td>
</tr>
<tr>
<td>查询编辑器（Query Editor）</td>
<td>依据特定数据源设计的表单，用以查询需要的Metrics</td>
</tr>
<tr>
<td>仪表盘（Dashboard）</td>
<td>一系列面板的集合，这些面板被组织在一个或者多个行中</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">安装与配置</span></div>
<div class="blog_h2"><span class="graybg">安装Grafana</span></div>
<div class="blog_h3"><span class="graybg">独立安装</span></div>
<p>依次执行下面的命令完成安装：</p>
<pre class="crayon-plain-tag"># CentOS
yum install initscripts fontconfig
wget https://grafanarel.s3.amazonaws.com/builds/grafana-3.1.1-1470047149.x86_64.rpm
rpm -Uvh grafana-3.1.1-1470047149.x86_64.rpm</pre>
<p>设置为开机启动：</p>
<pre class="crayon-plain-tag">/bin/systemctl daemon-reload
/bin/systemctl enable grafana-server.service</pre>
<p>启动服务：</p>
<pre class="crayon-plain-tag">systemctl start grafana-server.service</pre>
<div class="blog_h3"><span class="graybg">K8S</span></div>
<pre class="crayon-plain-tag">helm repo update gmem
helm del --purge grafana
helm install gmem/grafana --name grafana --namespace kube-system

# 执行下面的命令获得初始密码
kubectl get secret --namespace kube-system grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo</pre>
<p>值覆盖情况参考：<a href="https://github.com/gmemcc/charts/tree/master/grafana">https://github.com/gmemcc/charts/tree/master/grafana</a></p>
<div class="blog_h3"><span class="graybg">常用插件</span></div>
<pre class="crayon-plain-tag">grafana-cli plugins install grafana-kubernetes-app
grafana-cli plugins install natel-plotly-panel
grafana-cli plugins install kentik-app
grafana-cli plugins install alexanderzobnin-zabbix-app
grafana-cli plugins install grafana-worldmap-panel
grafana-cli plugins install grafana-clock-panel
grafana-cli plugins install grafana-piechart-panel
grafana-cli plugins install percona-percona-app
grafana-cli plugins install digrich-bubblechart-panel
grafana-cli plugins install digiapulssi-breadcrumb-panel
grafana-cli plugins install petrslavotinek-carpetplot-panel
grafana-cli plugins install neocat-cal-heatmap-panel
grafana-cli plugins install briangann-gauge-panel
grafana-cli plugins install jdbranham-diagram-panel</pre>
<p>安装插件后，需要重新启动Grafana服务：<pre class="crayon-plain-tag">service grafana-server restart</pre></p>
<div class="blog_h2"><span class="graybg">配置文件</span></div>
<div class="blog_h3"><span class="graybg">环境变量配置</span></div>
<p>当Grafana服务启动时，会读取<pre class="crayon-plain-tag">/etc/sysconfig/grafana-server</pre> 中的环境变量。你可以修改日志目录、数据存储目录等变量。</p>
<p>默认的，日志文件存储目录为/var/log/grafana，数据存储目录为/var/lib/grafana。默认使用SQLite数据库/var/lib/grafana/grafana.db。</p>
<div class="blog_h3"><span class="graybg">主配置文件</span></div>
<p>如果基于deb/rpm包进行安装，则主配置文件的位置为：<pre class="crayon-plain-tag">/etc/grafana/grafana.ini</pre> 。使用命令行参数 --config可以覆盖此位置。主配置文件说明如下：</p>
<table class=" fixed-word-wrap full-width">
<thead>
<tr>
<td style="width: 90px; text-align: center;">段</td>
<td style="width: 25%; text-align: center;">配置项</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td> </td>
<td>instance_name</td>
<td>Grafana服务的名称，默认为${HOSTNAME}，即环境变量HOMENAME的值</td>
</tr>
<tr>
<td rowspan="2">[paths]</td>
<td>data</td>
<td>在何处存放SQLite3数据库文件、基于文件的Session，以及其它数据</td>
</tr>
<tr>
<td>logs</td>
<td>在何处存放日志</td>
</tr>
<tr>
<td rowspan="5">[server]</td>
<td>http_addr</td>
<td>内置Web服务的监听地址，默认绑定所有地址</td>
</tr>
<tr>
<td>http_port</td>
<td>内置Web服务的监听端口，默认3000</td>
</tr>
<tr>
<td>protocol</td>
<td>http或者https</td>
</tr>
<tr>
<td>cert_file</td>
<td>https证书文件</td>
</tr>
<tr>
<td>cert_key</td>
<td>https证书密钥</td>
</tr>
<tr>
<td rowspan="6">[database]</td>
<td>type</td>
<td>数据库类型：mysql、postgres或者sqlite3</td>
</tr>
<tr>
<td>path</td>
<td>sqlite3数据库文件路径</td>
</tr>
<tr>
<td>host</td>
<td>mysql或postgres的主机:端口，例如<pre class="crayon-plain-tag">127.0.0.1:3306</pre> </td>
</tr>
<tr>
<td>name</td>
<td>数据库名称默认grafana</td>
</tr>
<tr>
<td>user</td>
<td>数据库用户</td>
</tr>
<tr>
<td>password</td>
<td>数据库密码</td>
</tr>
<tr>
<td rowspan="5">[security]</td>
<td>admin_user</td>
<td>Grafana管理员用户，默认admin</td>
</tr>
<tr>
<td>admin_password</td>
<td>Grafana管理员密码，默认admin</td>
</tr>
<tr>
<td>login_remember_days</td>
<td>记住登陆的最大天数</td>
</tr>
<tr>
<td>secret_key</td>
<td>用于签名记住登陆的Cookie</td>
</tr>
<tr>
<td>disable_gravatar</td>
<td>禁用头像</td>
</tr>
<tr>
<td>[log]</td>
<td>mode</td>
<td>日志记录方式，console、file或者syslog。可以用空格分开多个方式</td>
</tr>
<tr>
<td> </td>
<td>level</td>
<td>日志记录级别，debug、info、warn、error、critical</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">使用Grafana</span></div>
<p>使用Grafana的日常工作包括：用户管理、系统管理、仪表盘设计、数据源管理，等等。所有这些工作都在Web界面<pre class="crayon-plain-tag">http://GRAFANA_HOST:3000</pre>  完成。该界面比较友好，适合最终用户。</p>
<div class="blog_h2"><span class="graybg">配置Graphite数据源</span></div>
<p>点击Web界面左上角的图标，下拉列表中选择Data Sources，即可管理数据源。</p>
<p>点击Add data source按钮，添加新的数据源，参考下图：</p>
<p><img class="aligncenter size-full wp-image-12739" src="https://blog.gmem.cc/wp-content/uploads/2016/08/grafana-ds-cfg.png" alt="grafana-ds-cfg" width="478" height="440" /></p>
<p>注意Access设置成proxy，则数据通过Grafana间接获取，否则，数据直接通过客户端获取。添加Graphite数据源后，可以点击Dashboards选项卡，获得其预置的仪表盘Graphite Carbon Metrics并导入到Grafana中。</p>
<div class="blog_h2"><span class="graybg">设计仪表盘</span></div>
<p>点击Web界面左上角的图标，下拉列表中选择Dashboards  ⇨ New，可以新建仪表盘。在新仪表盘中，点击左侧的绿条，可以添加新的面板；点击右下侧ADD ROW按钮则可以新建一行。</p>
<p>&nbsp;</p>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/time-series-data-renderering-with-grafana">使用Grafana展示时间序列数据</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/time-series-data-renderering-with-grafana/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Graphite学习笔记</title>
		<link>https://blog.gmem.cc/graphite-study-note</link>
		<comments>https://blog.gmem.cc/graphite-study-note#comments</comments>
		<pubDate>Mon, 11 Jul 2016 08:33:51 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Database]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[Monitoring]]></category>
		<category><![CDATA[TSDB]]></category>
		<category><![CDATA[学习笔记]]></category>

		<guid isPermaLink="false">https://blog.gmem.cc/?p=12245</guid>
		<description><![CDATA[<p>简介 Graphite是一个开源项目，可以作为时间序列数据库（TSDB）使用，当你需要存储随着时间变化的数值时，应当考虑使用时间序列数据库。 除了数据的存储、查询外，Graphite还提供数据可视化（UI层）功能，它可以很好的在廉价的硬件上运行。你可以使用Graphite来监控网站、应用程序、网络服务器等的性能数据（Metrics），轻松实现基于时间维的分析。 Graphite本身不负责性能数据的采集，但是它提供了简单易用的接口，公共这些接口你可以把基于数字的性能数据存储到Graphite中。 术语 术语 说明 datapoint 数据点，存放在timestamp bucket中的数值。timestamp bucket中的默认值是None function 时间序列（ time-series）函数，用来转换、合并、计算多个series resolution 分辨率，也称precision。序列中，一个数据点所跨越（代表）的秒数。分辨率确定了存储数据点频率如果一个series每N秒存储一个数据点，则其分辨率为N retention 驻留，series中包含的数据点的个数 series 一已命名的数据点的集合，每个series由其名称唯一确定，名称由点号分隔的字符串组成也称为Metrics、Metric series series <a class="read-more" href="https://blog.gmem.cc/graphite-study-note">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/graphite-study-note">Graphite学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h1"><span class="graybg">简介</span></div>
<p><a href="http://graphiteapp.org/">Graphite</a>是一个开源项目，可以作为时间序列数据库（TSDB）使用，当你需要存储<span style="background-color: #c0c0c0;">随着时间变化的数值</span>时，应当考虑使用时间序列数据库。</p>
<p>除了<span style="background-color: #c0c0c0;">数据的存储、查询</span>外，Graphite还提供<span style="background-color: #c0c0c0;">数据可视化</span>（UI层）功能，它可以很好的在廉价的硬件上运行。你可以使用Graphite来监控网站、应用程序、网络服务器等的性能数据（Metrics），轻松实现基于时间维的分析。</p>
<p>Graphite本身不负责性能数据的采集，但是它提供了简单易用的接口，公共这些接口你可以把基于数字的性能数据存储到Graphite中。</p>
<div class="blog_h2"><span class="graybg">术语</span></div>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">术语</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>datapoint</td>
<td>数据点，存放在timestamp bucket中的数值。timestamp bucket中的默认值是None</td>
</tr>
<tr>
<td>function</td>
<td>时间序列（ time-series）函数，用来转换、合并、计算多个series</td>
</tr>
<tr>
<td>resolution</td>
<td>分辨率，也称precision。序列中，一个数据点所跨越（代表）的秒数。分辨率确定了存储数据点频率<br />如果一个series每N秒存储一个数据点，则其分辨率为N</td>
</tr>
<tr>
<td>retention</td>
<td>驻留，series中包含的数据点的个数</td>
</tr>
<tr>
<td>series</td>
<td>一已命名的数据点的集合，每个series由其名称唯一确定，名称由点号分隔的字符串组成<br />也称为Metrics、Metric series</td>
</tr>
<tr>
<td>series list</td>
<td>包含通配符的series名称，匹配多个series</td>
</tr>
<tr>
<td>target</td>
<td>图形展示时的数据源，可以是metrics名称、metrics通配符、或者基于前两者的函数调用表达式</td>
</tr>
<tr>
<td>timestamp</td>
<td>数据点所关联的时间，1970-01-01到产生数据点那一刻的秒数</td>
</tr>
<tr>
<td>timestamp bucket</td>
<td>经过舍入后，能够整除分辨率的timestamp</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">架构</span></div>
<p>Graphite由三个组件构成：</p>
<ol>
<li>Carbon：一个基于<a href="https://twistedmatrix.com/">Twisted</a>（Python事件驱动网络框架）的守护程序，负责监听外部的时间序列数据</li>
<li>Whisper：一个简单的存储时间序列数据的数据库，设计上和<a href="http://oss.oetiker.ch/rrdtool/">RRDtool</a>类似 </li>
<li>Graphite-Web：一个基于<a href="https://www.djangoproject.com/">Django</a>（Python Web框架）的Web应用，使用<a href="https://www.cairographics.org/">Cairo</a>（一个2D图形库）来渲染性能数据的图表，使用<a href="https://www.sencha.com/products/extjs/">ExtJS</a>作为基础UI框架</li>
</ol>
<p>下面是Graphite的架构图：<img class="aligncenter size-full wp-image-12262" src="https://blog.gmem.cc/wp-content/uploads/2016/07/graphite-arch.png" alt="graphite-arch" width="100%" /></p>
<p>一旦你把数据送给Carbon，它就立刻可以在Webapp的图表中显示，因为数据在被写入文件系统之前，会驻留在缓存中。</p>
<p>除了使用Graphite Webapp，你也可以通过URL API，将图表嵌入到自己的应用程序中去。如何使用Graphite</p>
<div class="blog_h2"><span class="graybg">如何使用Graphite</span></div>
<p>使用Graphite来监控你的性能数据，你需要完成以下工作：</p>
<ol>
<li>理解Graphite组件的职责和相互关系</li>
<li>安装Graphite及其依赖</li>
<li>基础的配置，让Graphite能运行起来</li>
<li>设计Metrics路径</li>
<li>配置Metrics的驻留规则、聚合规则等</li>
<li>向Graphite发送Metrics</li>
<li>从Graphite获取Metrics并展示</li>
</ol>
<div class="blog_h3"><span class="graybg">Metrics路径设计</span></div>
<p>Metrics路径由点号分隔的字符串构成，类似于Python的包名称。路径是Metrics的标识。</p>
<p>你应当仔细的设计此路径的命名空间，以反映出所有Metrics之间的层次关系。例如servers.zircon.cpu，这个三级路径设计中，第一级表示设备类别，第二级表示设备名称，第三级表示监测点类型。</p>
<div class="blog_h1"><span class="graybg">理解Graphite组件</span></div>
<div class="blog_h2"><span class="graybg">Carbon</span></div>
<p>Carbon由一系列的守护进程组成，这些守护进程共同组成Graphite的存储后端。在最小化的安装下，只有一个守护进程carbon-cache.py。根据需要你可以启用carbon-relay.py、carbon-aggregator.py以便实现Metrics分发、定制聚合规则。各Carbon守护进程简介如下：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">守护进程</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>缓存进程<br />carbon-cache</td>
<td>
<p>缓存进程的程序文件是carbon-cache.py。它通过多种协议接收Metrics，然后尽可能高效的将其写入磁盘。从实现角度来说，缓存进程先把Metrics存放在RAM中，然后定期的通过whisper库进行入库</p>
<p>缓存进程提供一个查询服务供Graphite Webapp使用，用来快速获取位于内存中的Metrics数据点</p>
<p>要定制此进程的行为，可以修改carbon.conf的[cache]段、storage-schemas.conf、storage-aggregation.conf</p>
</td>
</tr>
<tr>
<td>中继进程<br />carbon-relay</td>
<td>
<p>该进程可以担任这两个职责之一：复制（<span style="color: #404040;">replication</span>）和分片（<span style="color: #404040;">sharding</span>）</p>
<p>要定制此进程的行为，可以修改carbon.conf的[relay]段、relay-rules.conf</p>
</td>
</tr>
<tr>
<td>前置聚合进程<br />carbon-aggregator</td>
<td>
<p>前置于缓存进程运行，能够在存入whisper之前，缓冲、聚合Metrics。当不需要细粒度的数据时启用该进程，可以减少I/O和.wsp文件的大小</p>
<p>要定制此进程的行为，可以修改carbon.conf的[aggregator]段、aggregation-rules.conf</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">Whisper</span></div>
<p>Whisper是一个固定大小（fixed-size）的数据库，其设计和用途与RRD（round-robin-database）类似。它能提供快速、可靠的数值数据的存储。</p>
<p>Whisper能够对最近的数据进行高分辨率的存储，而对久远的历史数据，自动降低其存储精度（减少样本数量）。</p>
<p>在大多数场景下，Whisper有足够好的性能。它比RRDtool慢，主要原因是Whisper基于Python编写，这个性能差异很小，通常不需要考虑。</p>
<p>Whisper数据库以文件方式存放在磁盘上，扩展名.wsp。Carbon会为每个Metrics创建一个.wsp文件，路径的最后一节作为文件的basename，路径的其它部分形成目录层次。</p>
<div class="blog_h3"><span class="graybg">数据点</span></div>
<p>在Whisper中存储的每一个数值，称为数据点（Data Points）。</p>
<p>在磁盘上，数据点以大端、双精度浮点数存储。每个数据还附带一个时间戳信息，时间戳为1970-01-01到数据采集时间的秒数。</p>
<div class="blog_h3"><span class="graybg">归档</span></div>
<p>一个Whisper数据库文件可以包含一个或者多个“归档”，归档是数据文件中的逻辑段。</p>
<p>每个归档具有不同的数据分辨率（一定时间段内，数据点数量越多，则分辨率越高）。归档以最高分辨率（最小驻留时间） —— 最低分辨率（最长驻留时间）的顺序，在数据库文件中顺序排列。</p>
<p>为了精确的从高分辨率向低分辨率的归档聚合，高分辨率归档和它之后的低分辨率归档，其分辨率应当具有整除关系。例如，第一个归档分辨率为60秒/数据点，那么第二个归档可以是300秒/数据点，第三个归档可以是3600秒/数据点。</p>
<p>一个数据库的总计驻留时间（存放的数据点跨越的时间），由最长驻留时间的（最后一个）归档确定。因为之前的那些归档，时间区间都是它的子区间。</p>
<div class="blog_h3"><span class="graybg">聚合</span></div>
<p>把数据点转移到低分辨率归档时，面临着如何把多个数据点转变为单个数据点的问题。Whisper支持average、sum、last、max、min等聚合函数。</p>
<p>注意，这里的聚合和Carbon提供的前置聚合不是一回事。</p>
<div class="blog_h3"><span class="graybg">多归档的读写策略</span></div>
<p>当Whisper向一个多归档数据写入Metrics时，数据点将被同时写入到所有的归档中。这意味着聚合动作随时可能发生。</p>
<p>当从Whisper中获取数据时，第一个能完整覆盖所需区间的归档被使用。</p>
<div class="blog_h3"><span class="graybg">磁盘空间利用率问题</span></div>
<p>Whisper的磁盘利用效率不高，因为：</p>
<ol>
<li>每个数据点要附加一个时间戳信息</li>
<li>由于归档的时间区间有重叠，因此数据存在冗余</li>
<li>归档中的时间槽位（time-slots）总是占据着磁盘空间，不管有没有值存储在其中</li>
</ol>
<p>这些特征是故意的，主要出于性能方面的考虑。</p>
<div class="blog_h3"><span class="graybg">Whisper与RRD的区别</span></div>
<ol>
<li>RRD不能去填充以前的时间槽位。这意味着每一条数据都必须是更新的，不会“补录”</li>
<li>RRD不能很好的支持不规则的数据更新。如果RRD接收到一条数据，但是后续数据没有到来，则前一条数据可能丢失</li>
</ol>
<div class="blog_h1"><span class="graybg">安装与配置</span></div>
<div class="blog_h2"><span class="graybg">检查并安装依赖</span></div>
<div class="blog_h3"><span class="graybg">检查依赖</span></div>
<pre class="crayon-plain-tag">git clone https://github.com/graphite-project/graphite-web.git
graphite-web/check-dependencies.py
# 根据输出的提示来判断缺少哪些依赖，然后安装</pre>
<div class="blog_h3"><span class="graybg">安装依赖</span></div>
<pre class="crayon-plain-tag">yum -y install python-devel     # Carbon 依赖于 Python Development Headers

yum install pycairo             # Cairo库的Python绑定

pip install django              # Web框架
pip install django-tagging

pip install pytz

yum install fontconfig

yum install -y memcached        # 可选，缓存支持
pip install python-memcached    
                                # 可选，RDDTool
yum install cairo-devel libxml2-devel pango-devel pango libpng-devel freetype freetype-devel libart_lgpl-devel rrdtool-devel

pip install python-rrdtool      # 可选，RRD支持

pip install whitenoise          # 用于Web静态文件处理

yum install pyOpenSSL           # OpenSSL的Python绑定
pip install service_identity    # SSL相关</pre>
<div class="blog_h2"><span class="graybg">通过pip安装Craphite组件</span></div>
<pre class="crayon-plain-tag">pip install https://github.com/graphite-project/ceres/tarball/master
pip install whisper
pip install carbon
pip install graphite-web</pre>
<div class="blog_h3"><span class="graybg">设置目录权限</span></div>
<pre class="crayon-plain-tag">sudo groupadd graphite
sudo usermod -a -G graphite root
sudo usermod -a -G graphite apache
sudo chgrp -R graphite /opt/graphite/storage
sudo chmod -R 770 /opt/graphite/storage</pre>
<div class="blog_h2"><span class="graybg">默认安装布局</span></div>
<p>Whisper被安装到Python全局site-packages目录，另外两个Graphite组件安装到<pre class="crayon-plain-tag">/opt/graphite</pre>，该目录（记为<pre class="crayon-plain-tag">$GRAPHITE_ROOT</pre>  ）的布局如下：</p>
<table class=" fixed-word-wrap full-width">
<thead>
<tr>
<td style="width: 20%; text-align: center;">子目录</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>bin</td>
<td>一些脚本</td>
</tr>
<tr>
<td>conf</td>
<td>配置文件</td>
</tr>
<tr>
<td>lib</td>
<td>一些Python依赖库，Carbon的PYTHONPATH</td>
</tr>
<tr>
<td>storage</td>
<td>存储SQLite数据文件。该目录记为<pre class="crayon-plain-tag">$STORAGE_DIR</pre> </td>
</tr>
<tr>
<td>storage/log</td>
<td>Carbon和Graphite-web的日志</td>
</tr>
<tr>
<td>storage/rrd</td>
<td>待读取的RRD文件</td>
</tr>
<tr>
<td>storages/whisper</td>
<td>Whisper数据文件</td>
</tr>
<tr>
<td>webapp</td>
<td>
<p>Graphite-web的Web资源、PYTHONPATH</p>
</td>
</tr>
<tr>
<td>webapp/graphite</td>
<td>
<p>标准的Django工程结构<br />local_settings.py所在位置</p>
</td>
</tr>
<tr>
<td>webapp/content</td>
<td>静态Web资源</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">配置Graphite-web数据库</span></div>
<p>你需要让Graphite-web的底层框架Django执行数据库的初始化。此数据库被用来存放用户设置、仪表盘，以及支持事件功能。</p>
<p>默认情况下，Graphite-web使用位于$STORAGE_DIR/graphite.db的SQLite数据库。如果要运行多个Graphite-web实例，则必须使用MySQL等数据库以便多个实例可以共享数据。 </p>
<div class="blog_h3"><span class="graybg">配置SQLite</span></div>
<p>执行下面的命令初始化SQLite数据库：</p>
<pre class="crayon-plain-tag">PYTHONPATH=/opt/graphite/webapp django-admin.py migrate --settings=graphite.settings --run-syncdb
# 完毕后 $STORAGE_DIR/graphite.db自动创建</pre>
<p>Django应用需要在Web服务器中运行，Web服务器需要对SQLite数据文件有读写权限。假设你使用Apache2，运行Apache2的用户为apache，则需要执行：</p>
<pre class="crayon-plain-tag">sudo chgrp graphite /opt/graphite/storage/graphite.db</pre>
<div class="blog_h2"><span class="graybg">配置WebApp</span></div>
<div class="blog_h3"><span class="graybg">httplocal_settings.py</span></div>
<p>Graphite不建议修改settings.py，所有定制化的配置，都应该在此文件中进行。常用的设置项如下表：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 30%; text-align: center;">设置项</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>TIME_ZONE</td>
<td>时区，规范化的<a href="https://en.wikipedia.org/wiki/Tz_database#Names_of_time_zones">时区名称</a>。默认America/Chicago</td>
</tr>
<tr>
<td>DEBUG</td>
<td>是否启用Django错误页面。默认False</td>
</tr>
<tr>
<td>FLUSHRRDCACHED</td>
<td>如果设置，在从RRD文件获取数据前，执行<pre class="crayon-plain-tag">rrdtool flushcached</pre> <br />设置为rrdcached的地址或者Socket（例如unix:/var/run/rrdcached.sock）</td>
</tr>
<tr>
<td>MEMCACHE_HOSTS</td>
<td>如果设置，启用对计算出的目标、渲染过的图片的缓存。如果运行Graphite Web应用集群，则每个实例应当设置为一样的值<br />设置为memcached主机的数组，例如：['10.10.10.10:11211', '10.10.10.12:11211']</td>
</tr>
<tr>
<td>DEFAULT_CACHE_DURATION</td>
<td>数据、图片默认缓存时间。默认值60</td>
</tr>
<tr>
<td>DEFAULT_CACHE_POLICY</td>
<td>默认缓存策略。为元组的数组，每个元组指定最小查询时间、数据缓存时间<br />通过该设置，你可以让大的查询缓存更长时间。例如：<br />
<pre class="crayon-plain-tag">DEFAULT_CACHE_POLICY = [
    (0, 60), # 默认缓存60秒
    (7200, 120) # &gt;= 2小时以上时间范围的查询，缓存2分钟
] </pre>
</td>
</tr>
<tr>
<td>GRAPHITE_ROOT</td>
<td>Graphite的安装目录。默认/opt/graphite </td>
</tr>
<tr>
<td>CONF_DIR</td>
<td>额外Graphite-web配置文件目录。默认/opt/graphite/conf</td>
</tr>
<tr>
<td>STORAGE_DIR</td>
<td>存储目录。WHISPER_DIR、RRD_DIR、LOG_DIR、INDEX_FILE参照的基准目录</td>
</tr>
<tr>
<td>STATIC_ROOT</td>
<td>Graphite-web的静态文件目录。默认/opt/graphite/static<br />该目录一开始会不存在，你需要在设置STATIC_ROOT、STATIC_URL后执行：<br />
<pre class="crayon-plain-tag"># 如果报Unknown command: 'collectstatic' 说明INSTALLED_APPS中缺少
# django.contrib.staticfiles	
PYTHONPATH=/opt/graphite/webapp django-admin.py collectstatic 
    --noinput --settings=graphite.settings</pre></p>
<p>你还需要在Web服务器中把/static这个前缀映射到此目录，里Apache2为例：</p>
<pre class="crayon-plain-tag">Alias /static/ "/opt/graphite/static"</pre>
<p>如果你安装了whitenoise包，静态文件可以直接由Graphite webapp来处理，不通过Web服务器</p>
</td>
</tr>
<tr>
<td>DASHBOARD_CONF</td>
<td>仪表盘的配置文件。默认$CONF_DIR/dashboard.conf</td>
</tr>
<tr>
<td>GRAPHTEMPLATES_CONF</td>
<td>图形模板的配置文件。默认$CONF_DIR/graphTemplates.conf </td>
</tr>
<tr>
<td>WHISPER_DIR</td>
<td> Whisper数据文件目录。默认/opt/graphite/storage/whisper</td>
</tr>
<tr>
<td>RRD_DIR</td>
<td> RRD数据文件目录。默认/opt/graphite/storage/rrd</td>
</tr>
<tr>
<td>LOG_DIR</td>
<td> Graphite webapp的日志目录。默认$TORAGE_DIR/log/webapp</td>
</tr>
<tr>
<td>INDEX_FILE</td>
<td>搜索索引位置。默认/opt/graphite/storage/index<br />由build-index.sh脚本生成，运行Web应用的用户必须有写权限</td>
</tr>
<tr>
<td>URL_PREFIX</td>
<td>URL前缀</td>
</tr>
</tbody>
</table>
<p>我们的配置如下：</p>
<pre class="crayon-plain-tag"># 如果不设置，会导致报错：AttributeError: 'Settings' object has no attribute 'URL_PREFIX
URL_PREFIX = '/'</pre>
<div class="blog_h3"><span class="graybg">安装Apache和mod_wsgi</span></div>
<p>多种Web服务器可以用于运行基于Django的Web应用，这里我们选择Apache2。在CentOS下安装Apache2：</p>
<pre class="crayon-plain-tag">yum install httpd</pre>
<p>要让Apache2能够运行Python Web应用，需要安装模块mod_wsgi。参考<a href="https://blog.gmem.cc/django-study-note#mod-wsgi">Django学习笔记</a>完成mod_wsgi的构建与安装。</p>
<div class="blog_h3"><span class="graybg">配置Graphite虚拟主机</span></div>
<p>在/opt/graphite/examples/目录下，example-graphite-vhost.conf可以作为Apache虚拟主机的模板，复制该文件到/etc/httpd/conf.d/目录下，然后修改：</p>
<pre class="crayon-plain-tag">&lt;IfModule !wsgi_module.c&gt;
    LoadModule wsgi_module modules/mod_wsgi.so
&lt;/IfModule&gt;
WSGISocketPrefix run/wsgi
&lt;VirtualHost *:7767&gt;
        ServerName xcentos7.local
        DocumentRoot "/opt/graphite/webapp"
        ErrorLog /opt/graphite/storage/log/webapp/error.log
        CustomLog /opt/graphite/storage/log/webapp/access.log common
        WSGIDaemonProcess graphite processes=5 threads=5 display-name='%{GROUP}' inactivity-timeout=120
        WSGIProcessGroup graphite
        WSGIApplicationGroup %{GLOBAL}
        WSGIImportScript /opt/graphite/conf/graphite.wsgi process-group=graphite application-group=%{GLOBAL}
        WSGIScriptAlias / /opt/graphite/conf/graphite.wsgi
        Alias /content/ /opt/graphite/webapp/content/
        &lt;Directory /opt/graphite/webapp/content/&gt;
            Require all granted
        &lt;/Directory&gt;
        &lt;Directory /opt/graphite/conf/&gt;
            Require all granted
        &lt;/Directory&gt;
&lt;/VirtualHost&gt;</pre>
<div class="blog_h3"><span class="graybg">验证配置</span></div>
<p>如果配置没有问题，启动Web服务器后访问：<pre class="crayon-plain-tag">http://GRAPHITE_HOST:GRAPHITE_PORT/render</pre> ，会显示一张330x250大小的图片，上面写着No Data。</p>
<div class="blog_h2"><span class="graybg">配置Carbon</span></div>
<p>所有Carbon守护进程可以基于多种通信协议，监听时间序列数据，并且对数据进行不同的处理。</p>
<p>Carbon的配置文件位于/opt/graphite/conf/目录，默认情况下没有预置的配置文件，但是Graphite提供了若干配置文件样例。你可以复制这些配置文件并定制：</p>
<pre class="crayon-plain-tag">pushd /opt/graphite/conf
cp carbon.conf.example carbon.conf
cp storage-schemas.conf.example storage-schemas.conf</pre>
<div class="blog_h3"><span class="graybg">carbon.conf</span></div>
<p>这是主配置文件，为每个Carbon守护进程定义配置项。该配置文件按段区分不同守护进程的配置：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 14%; text-align: center;">配置项段</td>
<td style="width: 30%; text-align: center;">配置项</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td rowspan="17">[cache]   </td>
<td>
<p><span style="font-size: 8pt;">ENABLE_LOGROTATION</span></p>
</td>
<td>
<p> 是否启用每日的日志轮转，启用后每天创建一个日志</p>
</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">USER </span></td>
<td>运行该进程的用户 </td>
</tr>
<tr>
<td><span style="font-size: 8pt;">MAX_CACHE_SIZE</span></td>
<td>内存中Metrics缓存的最大尺寸</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">MAX_UPDATES_PER_SECOND</span></td>
<td>每秒执行whisper的update_many()调用的最大次数，对应磁盘IO的次数，该配置项避免过度的磁盘使用</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">MAX_CREATES_PER_MINUTE</span></td>
<td>对每分钟最多创建的.wsp文件的个数进行软限制。超过限制的新Metrics对应的.wsp文件不会被创建，Metrics也被丢弃</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">LINE_RECEIVER_INTERFACE</span></td>
<td rowspan="2">接收文本格式数据的监听端口，默认0.0.0.0:2003</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">LINE_RECEIVER_PORT</span></td>
</tr>
<tr>
<td><span style="font-size: 8pt;">PICKLE_RECEIVER_INTERFACE<br /></span></td>
<td rowspan="2">接收Pickle格式数据的监听端口，默认0.0.0.0:2004</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">PICKLE_RECEIVER_PORT</span></td>
</tr>
<tr>
<td><span style="font-size: 8pt;">ENABLE_UDP_LISTENER</span></td>
<td>是否启用UDP监听</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">UDP_RECEIVER_INTERFACE</span></td>
<td rowspan="2">通过UDP接收数据的监听端口，默认0.0.0.0:2003</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">UDP_RECEIVER_PORT</span></td>
</tr>
<tr>
<td><span style="font-size: 8pt;">LOG_LISTENER_CONNECTIONS</span></td>
<td>对成功的连接请求记录日志</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">CACHE_QUERY_INTERFACE</span></td>
<td rowspan="2">缓存查询服务的监听端口，默认0.0.0.0：7002</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">CACHE_QUERY_PORT</span></td>
</tr>
<tr>
<td><span style="font-size: 8pt;">USE_FLOW_CONTROL</span></td>
<td>是否进行流量控制。如果设置为True，那么达到MAX_CACHE_SIZE后，会暂停接收数据，直到缓存占用小于MAX_CACHE_SIZE的95%</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">CACHE_WRITE_STRATEGY</span></td>
<td>按何种顺序将缓存从内存中移除，并写入到磁盘的策略：sorted、max、naive</td>
</tr>
<tr>
<td rowspan="13">[relay]</td>
<td><span style="font-size: 8pt;">LINE_RECEIVER_INTERFACE</span></td>
<td rowspan="2">
<p>接收文本格式数据的监听端口，默认0.0.0.0:2003</p>
</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">LINE_RECEIVER_PORT</span></td>
</tr>
<tr>
<td><span style="font-size: 8pt;">PICKLE_RECEIVER_INTERFACE</span></td>
<td rowspan="2">接收Pickle格式数据的监听端口，默认0.0.0.0:2004</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">PICKLE_RECEIVER_PORT</span></td>
</tr>
<tr>
<td><span style="font-size: 8pt;">LOG_LISTENER_CONNECTIONS</span></td>
<td>对成功的连接请求记录日志</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">USER</span></td>
<td>运行该进程的用户</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">RELAY_METHOD</span></td>
<td>
<p>设置为rules：则该进程可以代替carbon-cache.py，然后中继所有Metrics给多个作为后端的carbon-cache.py</p>
<p>设置为consistent-hashing：则依据DESTINATIONS定义的分片策略，分发Metrics给多个作为后端的carbon-cache.py</p>
</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">REPLICATION_FACTOR</span></td>
<td>RELAY_METHOD=consistent-hashing时，可以指定N，从而把每个数据点分发到N台机器</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">DESTINATIONS</span></td>
<td>转发的目标，每个目标的格式是IP:PORT<br />RELAY_METHOD=rules时relay-rules.conf每个servers都要在此字段定义</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">MAX_DATAPOINTS_PER_MESSAGE</span></td>
<td>单个转发报文中包含的数据点的最大个数</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">MAX_QUEUE_SIZE</span></td>
<td>待转发的队列最大包含多少数据点</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">QUEUE_LOW_WATERMARK_PCT</span></td>
<td>队列低水位的百分比，0-1之间</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">USE_FLOW_CONTROL</span></td>
<td>是否进行流量控制。如果设置为True，那么达到MAX_QUEUE_SIZE后，会暂停接收数据，直到转发队列低于QUEUE_LOW_WATERMARK_PCT</td>
</tr>
<tr>
<td rowspan="13">[aggregator]</td>
<td><span style="font-size: 8pt;"> LINE_RECEIVER_INTERFACE</span></td>
<td rowspan="2">接收文本格式数据的监听端口，默认0.0.0.0:2023</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">LINE_RECEIVER_PORT</span></td>
</tr>
<tr>
<td><span style="font-size: 8pt;">PICKLE_RECEIVER_INTERFACE</span></td>
<td rowspan="2">接收Pickle格式数据的监听端口，默认0.0.0.0:2004</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">PICKLE_RECEIVER_PORT</span></td>
</tr>
<tr>
<td><span style="font-size: 8pt;">LOG_LISTENER_CONNECTIONS</span></td>
<td>对成功的连接请求记录日志</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">USER</span></td>
<td>运行该进程的用户</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">FORWARD_ALL</span></td>
<td>如果设置为True，除了根据aggregation-rules.conf进行聚合外，还把原始数据转发给DESTINATIONS</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">DESTINATIONS</span></td>
<td>聚合后的数据发送到的地方</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">REPLICATION_FACTOR</span></td>
<td>如果设置为N，则把数据转发给N个DESTINATION</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">MAX_QUEUE_SIZE</span></td>
<td>待转发的队列最大包含多少数据点</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">USE_FLOW_CONTROL</span></td>
<td>是否进行流量控制。如果设置为True，那么达到MAX_QUEUE_SIZE后，会暂停接收数据，直到转发队列低于80%</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">MAX_DATAPOINTS_PER_MESSAGE</span></td>
<td>单个转发报文中包含的数据点的最大个数</td>
</tr>
<tr>
<td><span style="font-size: 8pt;">MAX_AGGREGATION_INTERVALS</span></td>
<td>控制最多记住多少数据点，只有这些数据点才参与聚合<br />仅最近MAX_AGGREGATION_INTERVALS  * intervalSize秒内的数据点会被记住</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">storage-schemas.conf</span></div>
<p>这个配置文件用于定义Metrics的驻留率（ retention rates）——即Metrics的数据点（datapoints）以什么频率保存，保存多长时间。关于该配置文件，需要注意：</p>
<ol>
<li>该配置文件可以由多个段（Section）组成。每个段定义一个存储规则</li>
<li>这些规则按照从上到下的顺序对Metrics进行匹配，第一个匹配的规则对Metrics生效</li>
<li>匹配Metrics时，采用的是正则表达式</li>
<li>对于一个Metrics，其存储规则在接收到第一个数据时固化。因而修改此配置文件不会影响到既有的.wsp文件。要应用到既有的.wsp可以调用whisper-resize.py</li>
</ol>
<p>每个规则由三个部分组成：</p>
<pre class="crayon-plain-tag"># 段名称（规则名称），主要是文档用途。在匹配段的Metrics被创建时，日志creates.log中会显示此名称
[rulename]
# 匹配Metrics路径（Metrics的全限定名称，点号分隔）的正则式
# 举例：^servers\.www.*\.workers\.busyWorkers$
pattern=regex
# 驻留率表达式，数据点间隔:存留天数，时间后缀s,m,h,d,y分别表示秒、分、小时、天、年
# 举例：10s:14d 表示每个数据点表示10秒（相当于每10秒采集数据一次），并且存储14天的数据
retentions=retention rate</pre>
<p>注意，你可以指定多重驻留率表达式（retention rate），逗号分隔每个表达式。一般从最高精度:最短存留时间开始指定，直到最低精度:最长存留时间。例如：<pre class="crayon-plain-tag">15s:7d,1m:21d,15m:5y</pre> 表示7天内每15秒存留一个采样，而大于21天小于5年的则每15分钟一个采样。</p>
<p>设置多重驻留率表达式，可以在保存足够长时间的历史数据的前提下，尽量减少磁盘I/O和消耗的存储空间。当跨越驻留表达式的时间区间（上例中7天，21天）后，whisper会自动降低采样率（downsamples），<span style="background-color: #c0c0c0;">默认算法是取平均值</span>，可以通过storage-aggregation.conf定制聚合方式</p>
<div class="blog_h3"><span class="graybg">storage-aggregation.conf</span></div>
<p>这个配置文件定义如何在降低采样率（转换为低精度存储时） 如何对数据进行聚合。该文件的格式与storage-schemas.conf类似：</p>
<pre class="crayon-plain-tag">[rulename]
pattern = rexexp
# 0-1之间的浮点数，默认0.5。聚合区间内的值，至少多少比例为非空，聚合后的值才是非空
xFilesFactor = 0.5
# 数据聚合方式，默认average
aggregationMethod = average | sum | min | max | last</pre>
<p>同样的，修改此文件不会影响已经生成的.wsp文件， 要应用到既有的.wsp文件，可以调用whisper-set-aggregation-method.py。</p>
<div class="blog_h3"><span class="graybg">relay-rules.conf</span></div>
<p>这个配置文件指定中继规则——即需要把何种Metrics转发给何种后端。中继由Carbon的中继进程负责执行。该文件格式如下：</p>
<pre class="crayon-plain-tag">[rulename]
pattern = regex
servers = ip:port,ip:port,...</pre>
<div class="blog_h3"><span class="graybg">aggregation-rules.conf</span></div>
<p>该配置文件定义聚合出来的Metrics——由几个Metrics聚合而成的新的Metrics。聚合由Carbon的聚合进程负责执行。</p>
<p>注意：这里的聚合storage-aggregation.conf提及的聚合不同。后者用于单一Metrics的降低采样，而前者用于生成新的Metrics。</p>
<p>与其它配置文件不同，该文件一旦更改，立即生效。</p>
<p>该文件的格式如下：</p>
<pre class="crayon-plain-tag"># 捕获任何匹配input_pattern的Metrics，使用method聚合成新的Metrics：output_template
# frequency：每隔多久执行一次聚合
# method：可选sum/avg
output_template (frequency) = method input_pattern</pre>
<p>举例，假设你的Metrics的命名规则是：</p>
<pre class="crayon-plain-tag">&lt;env&gt;.applications.&lt;app&gt;.&lt;server&gt;.&lt;metric&gt;</pre>
<p>这是你可以配置如下聚合规则，来计算所有应用程序的请求的总数：</p>
<pre class="crayon-plain-tag">&lt;env&gt;.applications.&lt;app&gt;.all.requests (60) = sum &lt;env&gt;.applications.&lt;app&gt;.*.requests</pre>
<p>在此规则下，下面的Metrics：</p>
<pre class="crayon-plain-tag">test.applications.pems.221.request
test.applications.pems.6.request
test.applications.pems.5.request
test.applications.pems.201.request</pre>
<p>会每个60秒求和并生成 <pre class="crayon-plain-tag">test.applications.pems.all.request</pre> 的一个数据点。</p>
<div class="blog_h3"><span class="graybg">rewrite-rules.conf </span></div>
<p>该配置文件定义Metrics名称的改写规则。 与其它配置文件不同，该文件一旦更改，立即生效。该文件的格式如下：</p>
<pre class="crayon-plain-tag">[pre]
# pre段的规则，在接收到数据后立即执行改写
[post]
# post段的规则，在聚合完毕后执行改写
regex-pattern = replacement-text</pre>
<p> 举例：</p>
<pre class="crayon-plain-tag"># \1表示第一个捕获，即[a-z0-9]+
^collectd\.([a-z0-9]+)\. = \1.system.
# collectd.prod.cpu-0.idle-time 会被改写为 prod.system.cpu-0.idle-item</pre>
<div class="blog_h3"><span class="graybg">whitelist.conf和blacklist.conf </span></div>
<p>Carbon提供黑白名单功能。白名单：仅仅接受其中列出的Metrics；黑名单：拒绝其中列出Metrics。</p>
<p>要启用黑白名单功能，需要修改carbon.conf，设置<pre class="crayon-plain-tag">USE_WHITELIST = True</pre> </p>
<div class="blog_h2"><span class="graybg">启动Graphite</span></div>
<p>如何启动Graphite Webapp依赖于其运行的Web服务软件，以Apache为例：</p>
<pre class="crayon-plain-tag"># CentOS 7
systemctl restart httpd</pre>
<p>Carbon组件需要执行下面的命令来启动：</p>
<pre class="crayon-plain-tag">/opt/graphite/bin/carbon-cache.py start</pre>
<p>SQLite是一个“文件数据库”，不需要启动。如果Graphite Webapp使用其它数据库，例如MySQL，则需要启动之。 </p>
<div class="blog_h1"><span class="graybg">向Graphite发送数据点</span></div>
<div class="blog_h2"><span class="graybg">数据格式</span></div>
<p>你可以通过多种方式向Graphite（的Carbon组件）发送Metrics数据点。主要的三种数据格式是：Plaintext、Pickle、AMQP </p>
<div class="blog_h3"><span class="graybg">Plaintext协议</span></div>
<p>这种方式非常简单，你可以用如下格式来发送一条Metrics数据点：</p>
<pre class="crayon-plain-tag">&lt;metric path&gt; &lt;metric value&gt; &lt;metric timestamp&gt;
# 举例：
servers.zircon.cpu 85 1470303923</pre>
<p>出于测试目的，你可以直接通过命令向Carbon发送Metrics数据：</p>
<pre class="crayon-plain-tag">PORT=2003
SERVER=127.0.0.1
echo "servers.zircon.cpu 85 `date +%s`" | nc ${SERVER} ${PORT}</pre>
<div class="blog_h3"><span class="graybg">Pickle协议</span></div>
<p>Pickle是Python下的对象串行化框架，Pickle协议即它的串行化格式协议。使用该协议，你可以一次发送多个Metrics，并且串行化后的数据比较紧凑，因此Pickle协议性能更好。</p>
<p>下面是构建Pickle报文的示例代码：</p>
<pre class="crayon-plain-tag">listOfMetricTuples = [
    (path, (timestamp, value)),
    ...
]
payload = pickle.dumps(listOfMetricTuples, protocol=2)
header = struct.pack("!L", len(payload))
message = header + payload
# 然后通过Socket把message发送出去即可</pre>
<div class="blog_h2"><span class="graybg">客户端示例</span></div>
<div class="blog_h3"><span class="graybg">简单Pickle客户端</span></div>
<pre class="crayon-plain-tag">import pickle
import socket
import struct
import sys
import time
from time import sleep

import psutil


def get_cpu_load():
    load = psutil.cpu_percent()
    print load
    return load


if __name__ == '__main__':
    CARBON_HOST = '172.16.87.132'
    CARBON_PORT = 2004
    s = socket.socket()
    try:
        s.connect((CARBON_HOST, CARBON_PORT))
        while True:
            data = [
                ('servers.zircon.cpu', (time.time(), get_cpu_load()))
            ]
            pkg = pickle.dumps(data, 1)
            s.sendall(struct.pack('!L', len(pkg)))
            s.sendall(pkg)
            sleep(5)
    except socket.error:
        raise SystemExit("Failed to connect to %(host)s:%(port)" % {'host': CARBON_HOST, 'port': CARBON_PORT})
    except KeyboardInterrupt:
        sys.stderr.write("\nExiting on CTRL-C\n")
        sys.exit(0) </pre>
<div class="blog_h1"><span class="graybg">从Graphite获取数据</span></div>
<p>要从Graphite获取数据用于展示，可以使用Graphite Webapp暴露的Render URL API（RUA）。你可以通过</p>
<pre class="crayon-plain-tag">http://GRAPHITE_HOST:GRAPHITE_PORT/render</pre>
<p>访问此API。要向RUA传递参数，可以使用URL请求参数方式：<pre class="crayon-plain-tag">&amp;name=value</pre> 。注意大部分的参数名、函数名是大小写敏感的。</p>
<p>下面列出几个RUA的URL示例：</p>
<pre class="crayon-plain-tag"># Zircon的CPU负载图，获取800x600的图片
http://graphite/render?target=servers.zircon.cpu&amp;height=800&amp;width=600
# 最近12小时，所有服务器的CPU负载平均值
http://graphite/render?target=averageSeries(servers.*.load)&amp;from=-12hours
# 获取原始数据而不是图片，JSON格式
http://graphite/render?target=servers.zircon.cpu&amp;format=json</pre>
<div class="blog_h2"><span class="graybg">target参数</span></div>
<p>该参数用来指定从何处获取数据，你可以指定：</p>
<ol>
<li>单个Metrics路径</li>
<li>带有通配符的的Metrics路径，匹配多个Metrics</li>
<li>函数调用，针对作为入参的Metrics进行各种转换、合并操作</li>
</ol>
<div class="blog_h3"><span class="graybg">通配符</span></div>
<p>你可以在路径中使用三种风格的通配符：</p>
<ol>
<li><pre class="crayon-plain-tag">*</pre> 可以匹配0-N个字符，例如<pre class="crayon-plain-tag">servers.dev-*.cpu</pre> ，可以匹配所有开发服务器的CPU负载Metrics。</li>
<li><pre class="crayon-plain-tag">[...]</pre> 可以匹配列表中枚举的单个字符，例如<pre class="crayon-plain-tag">servers.dev-[a-z0-9].cpu</pre> ，可以匹配dev-0、dev-1等服务器的CPU负载Metrics。</li>
<li><pre class="crayon-plain-tag">{...}</pre> 可以匹配列表中枚举的单个字符串，例如<pre class="crayon-plain-tag">servers.{dev-0,dev-1}.cpu</pre> ，匹配dev-0、dev-2的CPU负载Metrics。</li>
</ol>
<p>注意：所有通配符都<span style="background-color: #c0c0c0;">不能跨越点号</span>。 </p>
<div class="blog_h3"><span class="graybg">template函数</span></div>
<p>你可以指定target为template函数调用，从而在Metrics路径中使用变量，例如：</p>
<pre class="crayon-plain-tag"># $varname 用来声明变量占位符
# template[varname]参数用来传递变量值
&amp;target=template(servers.$servername.cpu)&amp;template[servername]=zircon

# 可以使用数字代替变量名
&amp;target=template(servers.$1.cpu)&amp;template[1]=zircon

# template可以内嵌其它函数
&amp;target=template(constantLine($number))&amp;template[number]=123</pre>
<div class="blog_h3"><span class="graybg">所有函数</span></div>
<p>可用的函数较多，这里不一一列举说明，参见<a href="http://graphite.readthedocs.io/en/latest/functions.html">官方文档</a>。</p>
<div class="blog_h2"><span class="graybg">from/until参数</span></div>
<p>这两个可选参数用来指定相对或者绝对的时间区间（time period）。from表示区间起点，如果忽略，默认值是24小时之前；until表示区间终点，如果或略，默认值是当前时间点。</p>
<div class="blog_h3"><span class="graybg">相对时间</span></div>
<p>如果要使用相对时间，需要加上<pre class="crayon-plain-tag">-</pre> 前缀（负号），后面跟着数值和时间单位。时间单位包括：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 25%; text-align: center;">时间单位</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>s</td>
<td>秒</td>
</tr>
<tr>
<td>min</td>
<td>分钟</td>
</tr>
<tr>
<td>h</td>
<td>小时</td>
</tr>
<tr>
<td>d</td>
<td>天</td>
</tr>
<tr>
<td>w</td>
<td>周</td>
</tr>
<tr>
<td>mon</td>
<td>月（30天）</td>
</tr>
<tr>
<td>y</td>
<td>年（365天）</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">绝对时间</span></div>
<p>你可以指定<pre class="crayon-plain-tag">HH:MM_YYYYMMDD</pre> 、<pre class="crayon-plain-tag">YYYYMMDD</pre> 、<pre class="crayon-plain-tag"> MM/DD/YY</pre> 等格式的时间绝对值。</p>
<div class="blog_h2"><span class="graybg">format参数</span></div>
<p>该参数用来指定要获取的数据的格式。 支持以下取值：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 15%; text-align: center;">格式</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>png</td>
<td>根据指定的width、height，直接把数据渲染为PNG图片</td>
</tr>
<tr>
<td>raw</td>
<td>原始数据，分为多行，每行格式为：<br />
<pre class="crayon-plain-tag">&lt;target name&gt;,&lt;start timestamp&gt;,&lt;end timestamp&gt;,&lt;series step&gt;|[data]*</pre></p>
<p> 示例：</p>
<pre class="crayon-plain-tag">entries,1311836008,1311836013,1|1.0,2.0,3.0,5.0,6.0</pre>
</td>
</tr>
<tr>
<td>csv</td>
<td>基于逗号分隔符的格式，每行表示一个数据点。示例：<br />
<pre class="crayon-plain-tag">entries,2011-07-28 01:53:28,1.0
entries,2011-07-28 01:53:29,2.0
entries,2011-07-28 01:53:30,3.0
entries,2011-07-28 01:53:31,5.0
entries,2011-07-28 01:53:32,6.0</pre>
</td>
</tr>
<tr>
<td>json</td>
<td>
<p>JSON数组格式，示例：
<pre class="crayon-plain-tag">[{
  "target": "entries",
  "datapoints": [
    [1.0, 1311836008],
    [2.0, 1311836009],
    [3.0, 1311836010],
    [5.0, 1311836011],
    [6.0, 1311836012]
  ]
}]</pre>
<p>可以和jsonp参数联用，以便把数据包装成函数调用，进行跨域请求</p>
<p>可以和maxDataPoints参数联用，限定最大的数据点个数。超过数量的数据点将被压缩掉</p>
<p>可以和noNullPoints参数联用，移除所有Null值的数据点</p>
</td>
</tr>
<tr>
<td>svg</td>
<td>渲染为SVG图片格式</td>
</tr>
<tr>
<td>pdf</td>
<td>渲染为PDF文档</td>
</tr>
<tr>
<td>dygraph</td>
<td>dygraphs是一个快速、灵活的JavaScript图表（Chart）库。该格式返回dygraphs支持的数据格式。示例：<br />
<pre class="crayon-plain-tag">{
  "labels" : [
    "Time",
    "entries"
  ],
  "data" : [
    [1468791890000, 0.0],
    [1468791900000, 0.0]
  ]
}</pre>
</td>
</tr>
<tr>
<td>rickshaw</td>
<td>rickshaw是一个简单的JavaScript图表库。该格式返回rickshaw支持的数据格式。示例：<br />
<pre class="crayon-plain-tag">[{
  "target": "entries",
  "datapoints": [{
    "y": 0.0,
    "x": 1468791890
  }, {
    "y": 0.0,
    "x": 1468791900
  }]
}]</pre>
</td>
</tr>
<tr>
<td>pickle</td>
<td>返回Pickle串行化格式，设置MIME类型为application/pickle。反串行化后的对象示例：<br />
<pre class="crayon-plain-tag">[
  {
    'name' : 'summarize(test.data, "30min", "sum")',
    'start': 1335398400,
    'end'  : 1335425400,
    'step' : 1800,
    'values' : [None, None, 1.0, None],
  }
]</pre>
</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">图形参数</span></div>
<p>你可以指定多个参数，来控制生成的图形的样式：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 22%; text-align: center;">参数</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>areaAlpha</td>
<td>启用areaMode时，填充区域的透明度。0-1之间的浮点数</td>
</tr>
<tr>
<td>areaMode</td>
<td>填充曲线与X轴之间的区域，形成Area图，可以取值：<br />none 不进行填充<br />first 填充第一个目标<br />all  填充所有目标<br />stacked 堆叠模式，填充所有目标，一个目标的取值为前面所有其它目标的取值+该目标的取值</td>
</tr>
<tr>
<td>bgcolor</td>
<td>背景颜色。示例：<br />
<pre class="crayon-plain-tag"># 颜色名称
&amp;bgcolor=blue
# HEX代码
&amp;bgcolor=2222FF
# HEX代码，包含透明度
&amp;bgcolor=5522FF60</pre>
</td>
</tr>
<tr>
<td>cacheTimeout</td>
<td>被渲染出的图形，其有效缓存时间</td>
</tr>
<tr>
<td>colorList</td>
<td>多个Target时，每个Target的颜色，逗号分隔颜色代码</td>
</tr>
<tr>
<td>drawNullAsZero</td>
<td>是否把空值渲染为0</td>
</tr>
<tr>
<td>fontBold</td>
<td>是否使用粗体</td>
</tr>
<tr>
<td>fontItalic</td>
<td>是否使用斜体</td>
</tr>
<tr>
<td>fontName</td>
<td>字体名称，该字体必须安装在Graphite服务器上</td>
</tr>
<tr>
<td>fontSize</td>
<td>字体大小，大于1的浮点数</td>
</tr>
<tr>
<td>graphOnly</td>
<td>是否不显示网格线、X/Y轴和图例</td>
</tr>
<tr>
<td>graphType</td>
<td>图表类型，line或者pie</td>
</tr>
<tr>
<td>hideLegend</td>
<td>是否隐藏图例</td>
</tr>
<tr>
<td>hideAxes</td>
<td rowspan="3">是否隐藏X/Y轴</td>
</tr>
<tr>
<td>hideXAxis</td>
</tr>
<tr>
<td>hideYAxis</td>
</tr>
<tr>
<td>hideGrid</td>
<td>是否隐藏网格线</td>
</tr>
<tr>
<td>height</td>
<td rowspan="2">图形的高度、宽度，单位像素</td>
</tr>
<tr>
<td>width</td>
</tr>
<tr>
<td>leftColor</td>
<td>在双Y轴模式下，设置与左轴关联的Metrics的颜色</td>
</tr>
<tr>
<td>rightColor</td>
<td>在双Y轴模式下，设置与右轴关联的Metrics的颜色</td>
</tr>
<tr>
<td>leftDashed</td>
<td>在双Y轴模式下，是否以虚线绘制与左轴关联的Metrics</td>
</tr>
<tr>
<td>rightDashed</td>
<td>在双Y轴模式下，是否以虚线绘制与右轴关联的Metrics</td>
</tr>
<tr>
<td>leftWidth</td>
<td>在双Y轴模式下，设置与左轴关联的Metrics的线条宽度</td>
</tr>
<tr>
<td>rightWidth</td>
<td>在双Y轴模式下，设置与右轴关联的Metrics的线条宽度</td>
</tr>
<tr>
<td>lineMode</td>
<td>设置线条绘制的行为：<br />slope：从一个数据点向下一个数据点绘制斜线，Null值的区间不被绘制<br />connected：与slope类似，但是数据点总是被连接起来，无论它们之间是否存在Null值<br />staircase：绘制直方图</td>
</tr>
<tr>
<td>lineWidth</td>
<td>线条的宽度</td>
</tr>
<tr>
<td>majorGridLineColor</td>
<td>网格线主色</td>
</tr>
<tr>
<td>minorGridLineColor</td>
<td>网格线从色</td>
</tr>
<tr>
<td>minorY</td>
<td>每两个网格主线之间，有几个从线，Y方向</td>
</tr>
<tr>
<td>margin</td>
<td>图形四周的边距</td>
</tr>
<tr>
<td>maxDataPoints</td>
<td>使用</td>
</tr>
<tr>
<td>minXStep</td>
<td>
<p>两个连续的数据点之间，间隔的最小像素</p>
<p>如果数据点过多，则压缩，以满足此配置</p>
</td>
</tr>
<tr>
<td>noCache</td>
<td>禁止图片缓存</td>
</tr>
<tr>
<td>pieLabels</td>
<td>饼图标签如何显示，horizontal或者rotated</td>
</tr>
<tr>
<td>pieMode</td>
<td>饼图聚合方式：<br />average，取series中非空数据点的平均值<br />maximum，取series中非空数据点的最大值<br />minimum，取series中非空数据点的最小值</td>
</tr>
<tr>
<td>valueLabels</td>
<td>如何显示饼图分块的标签：<br />none，不显示<br />numbers，显示原始值<br />percent，显示百分比</td>
</tr>
<tr>
<td>valueLabelsColor</td>
<td>如何显示饼图分块的标签的颜色</td>
</tr>
<tr>
<td>valueLabelsMin</td>
<td>饼图中，分块占比小于此数值的分块，不显示其标签</td>
</tr>
<tr>
<td>title</td>
<td>在图形顶端显示的标题</td>
</tr>
<tr>
<td>vtitle</td>
<td>Y轴标题，垂直显示</td>
</tr>
<tr>
<td>vtitleRight</td>
<td>双Y轴模式下，右Y轴的标题</td>
</tr>
<tr>
<td>tz</td>
<td>用于显示时间值的时区</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">展示Graphite数据</span></div>
<p>上一章内容我们讨论了如何获取Graphite数据。通过Render URL API，我们不但可以获得文本数据，还可以直接获得渲染好的图片。这意味着通过Render URL API本身就可以实现Metrics的渲染，你只需要把生成的图片嵌入到自己的应用程序中即可。</p>
<p>Graphite Webapp本身提供了基于ExtJS的一个管理界面，你可以通过<pre class="crayon-plain-tag">http://GRAPHITE_HOST:GRAPHITE_PORT/admin</pre> 浏览Metrics。</p>
<p>Graphite生成的Metrics曲线的图片，不是非常美观，而静态图片也缺乏交互性。因此，实际项目中常常结合使用第三方基于JavaScript的Charts库来做展示，例如：</p>
<ol>
<li><a href="http://grafana.org/">Grafana</a>：UI比较绚丽，支持设计仪表盘、时间区间联动。参见：<a href="/time-series-data-renderering-with-grafana">使用Grafana展示时间序列数据</a></li>
<li><a href="http://jondot.github.io/graphene/">Graphene</a>：一个较为简单的，基于D3.js和Backbone.js的Graphite仪表盘工具</li>
</ol>
<div class="blog_h1"><span class="graybg">Graphite事件</span></div>
<p>除了简单的，基于Key/Value的Metrics数据，Graphite还可以存储、展示随机出现的数据——事件。</p>
<p>事件不适合存储在Whisper中，因此它被存储在Graphite的Webapp的数据库中（默认使用SQLite）。</p>
<div class="blog_h2"><span class="graybg">发布事件</span></div>
<p>通过向<pre class="crayon-plain-tag">http://GRAPHITE_HOST:GRAPHITE_PORT/events/</pre> 发送POST请求，即可发布Graphite事件。事件使用JSON格式编码在请求体中：</p>
<pre class="crayon-plain-tag">{ 
    "what": "事件类型", 
    "tags": "标签",
    "data": "事件相关的数据" 
}</pre>
<div class="blog_h2"><span class="graybg">查询事件</span></div>
<p>指定target为<pre class="crayon-plain-tag">event(*tags)</pre> 函数调用，即可通过<pre class="crayon-plain-tag">/render</pre> 查询事件，例如</p>
<pre class="crayon-plain-tag">[
   {
      "target" : "events(mytag)",
      "datapoints" : [
         [
            1,
            1388966651
         ],
         [
            3,
            1388966652
         ]
      ]
   }
]</pre>
<p>你也可以通过<pre class="crayon-plain-tag">/render/events/get_data</pre> 获得原始的事件数据，例如：</p>
<pre class="crayon-plain-tag">[
   {
      "when" : 1392046352,
      "tags" : "mytag",
      "data" : "...",
      "id" : 2,
      "what" : "Event - deploy"
   }
] </pre>
<div class="blog_h1"><span class="graybg">常见问题</span></div>
<div class="blog_h3"><span class="graybg">AttributeError: 'WSGIRequest' object has no attribute 'REQUEST'</span></div>
<p>Django的request对象曾经有一个属性REQUEST，用来获得通过GET或者POST请求传递的请求参数，在1.9版本中此属性已经移除。</p>
<p>Graphite代码没有即时更新，存在不兼容的问题，修改一下即可：</p>
<pre class="crayon-plain-tag">def parseOptions(request):
   queryParams = request.GET # request.REQUEST已经被移除</pre>
<p>还有很多其它views.py存在同样的问题，最好搜索一下一并修改。如果觉得麻烦可以安装兼容版本的Django：</p>
<pre class="crayon-plain-tag">pip install django==1.8.14</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/graphite-study-note">Graphite学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/graphite-study-note/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
