开篇
本文承接前文 Prometheus 监控架构 -- 生产级别,其中主要介绍Prometheus的远端TSDB存储 -- M3DB,包括M3DB集群部署及原理。
介绍
M3于2015年发布,目前拥有超过66亿个时间序列。M3每秒聚合5亿个指标,并在全球范围内(使用M3DB)每秒持续存储2000万个度量指标,批量写入将每个指标持久保存到区域中的三个副本。它还允许工程师编写度量策略,告诉M3以更短或更长的保留时间(两天,一个月,六个月,一年,三年,五年等)以特定的粒度(一秒,十秒,一分钟,十分钟等)。这允许工程师和数据科学家使用与定义的存储策略匹配的度量标签(标签),在精细和粗粒度范围内智能地存储不同保留的时间序列。例如,工程师可以选择存储“应用程序”标记为“mobile_api”且“端点”标记为“注册”的所有度量标准,这些标记在10秒粒度下为30天,在一小时粒度下为5年。
M3特性
M3具有作为离散组件提供的多个功能,使其成为大规模时间序列数据的理想平台,主要包括四个组件 -- M3 Coordinator、M3DB、M3 Query、M3 Aggregator:
- 分布式时间序列数据库M3DB,它为时间序列数据和反向索引提供可伸缩的存储。
- 辅助进程M3Coordinator,允许M3DB充当Prometheus的长期存储。
- 分布式查询引擎M3Query,其对PromQL和Graphite的原生支持(即将推出M3QL)。
- 聚合层M3Aggregator,作为专用的度量聚合器/下采样器运行,允许以不同的分辨率以各种保留方式存储度量。
M3DB特性
- 分布式时间序列存储,单个节点使用WAL提交日志并独立保存每个分片的时间窗口
- 建立在etcd之上的集群管理
- 内置同步复制,具有可配置的持久性和读取一致性(一个,多数,全部等)
- M3TSZ float64压缩灵感来自Gorilla TSZ压缩,可配置为无损或有损任意时间精度可配置,范围从秒到纳秒,可通过任意写入切换精度可配置的无序写入,当前限于已配置时间窗口的块大小
M3DB限制
- 由于M3DB需求的性质,主要是为了减少摄取和存储数十亿个时间序列的成本并提供快速可伸缩的读取,因此目前存在一些限制,使M3DB不适合用作通用时间序列数据库。
- M3DB旨在尽可能避免压缩,目前,M3DB仅在可变压缩时间序列窗口(默认配置为2小时)内执行压缩。因此,乱序写入仅限于单个压缩时间序列窗口的大小。因此,当前无法回填大量数据。
- M3DB还针对float64值的存储和检索进行了优化,因此尚无法将其用作包含任意数据结构的通用时间序列数据库。
分布式架构
M3db共有三种角色类型:
- Coordinator:m3coordinator用于协调群集中所有主机之间的读取和写入。这是一个轻量级的过程,不存储任何数据。该角色通常将与Prometheus实例一起运行,或者被嵌入到收集器代理中。
- Storage Node:在这些主机上运行的m3dbnode进程是数据库的主力,它们存储数据,并提供读写功能。
- Seed Node:首先,这些主机本身就是存储节点。除了该职责外,他们还运行嵌入式ETCD服务器。这是为了允许跨集群运行的各种M3DB进程以一致的方式推断集群的拓扑/配置。
存储引擎
压缩算法
主要压缩算法M3TSZ,是Gorilla paper算法的变体,根据UBER的数据特征,每个points压缩后达到1.45 bytes大小,压缩率很高。
内存架构
架构说明
- 每个M3DB进程只有一个database
- 每个database有多个namespace, 每个namespace有独立的名字和配置(根据数据保留和块代销)namespace相当于普通数据库中的表
- 一个namespace包含多个shard, shards按照series ID的哈希值来分配数据
- 一个shard包含多个series
- 一个series包含多个blocks对象
block类型
- fileset file:已写入磁盘,数据密封、压缩
- In-memory cache:已在内存中缓存,还未写入磁盘,数据密封、压缩
- active buffer:数据已压缩、未密封
集群部署
环境准备
- 系统环境 Centos7.4 4c/16G
- 172.16.7.170 node1
- 172.16.7.171 node2
- 172.16.7.172 node3
- 172.16.10.12 coordinator
本文安装方式Docker,K8S集群安装m3db-operator的可以参考官方Github m3db-operator。
etcd集群部署
node1、node2、node3、coordinator操作
yum install etcd -y
附上etcd基础配置文件
#etcd配置文件/etc/etcd/etcd.conf
ETCD_DATA_DIR="/etcd-data"
ETCD_LISTEN_PEER_URLS="http://0.0.0.0:2380"
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"
ETCD_NAME="node1" #依次为node2,node3,node4
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://node1:2380"
ETCD_ADVERTISE_CLIENT_URLS="http://node1:2379"
ETCD_INITIAL_CLUSTER="node1=http://node1:2380,node2=http://node2:2380,node3=http://node3:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"
依次启动etcd节点:systemctl start etcd
查看节点:etcdctl member list
43bb846a7344a01f: name=node2 peerURLs=http://node2:2380 clientURLs=http://node2:2379 isLeader=false
58c7b83d06ee71dc: name=node3 peerURLs=http://node3:2380 clientURLs=http://node3:2379 isLeader=false
a9aee06e6a14d468: name=node1 peerURLs=http://node1:2380 clientURLs=http://node1:2379 isLeader=true
M3DB集群部署
mkdir /opt/m3db /etcd-data/m3db/cache -p
cat << EOF >/opt/m3db/m3dbnode.yml
coordinator:
listenAddress:
type: "config"
value: "0.0.0.0:7201" # 交互端口
local:
namespaces:
- namespace: default # 数据要存入的表
type: unaggregated # 数据类型
retention: 720h # 数据保存时间
logging:
level: error
metrics: # coordinator本身的metric
scope:
prefix: "coordinator"
prometheus:
handlerPath: /metrics
listenAddress: 0.0.0.0:7203 # until https://github.com/m3db/m3/issues/682 is resolved
sanitization: prometheus
samplingRate: 1.0
extended: none
limits:
maxComputedDatapoints: 10000
tagOptions:
# Configuration setting for generating metric IDs from tags.
idScheme: quoted # 这个必须
db:
logging:
level: error
metrics:
prometheus:
handlerPath: /metrics
sanitization: prometheus
samplingRate: 1.0
extended: detailed
listenAddress: 0.0.0.0:9000
clusterListenAddress: 0.0.0.0:9001
httpNodeListenAddress: 0.0.0.0:9002
httpClusterListenAddress: 0.0.0.0:9003
debugListenAddress: 0.0.0.0:9004
hostID: #采用此配置文件自定义hostname
resolver: config
value: node1 #hostname为 node1,其余节点依次为node2,node3,node4[coordinator]
client:
writeConsistencyLevel: majority # 写一致性级别
readConsistencyLevel: unstrict_majority
gcPercentage: 100
writeNewSeriesAsync: true
writeNewSeriesLimitPerSecond: 1048576
writeNewSeriesBackoffDuration: 2ms
bootstrap:
bootstrappers: # 启动顺序
- filesystem
- commitlog
- peers
- uninitialized_topology
commitlog:
returnUnfulfilledForCorruptCommitLogFiles: false
cache:
series:
policy: lru
postingsList:
size: 262144
commitlog:
flushMaxBytes: 524288
flushEvery: 1s
queue:
calculationType: fixed
size: 2097152
fs:
filePathPrefix: /etcd-data/m3db # m3dbnode数据目录
config:
service:
env: default_env
zone: embedded
service: m3db # 服务名。可以按照consul中的service进行理解
cacheDir: /etcd-data/m3db/cache
etcdClusters:
- zone: embedded
endpoints:
- node1:2379
- node2:2379
- node3:2379
EOF
参数解释: hostID: 官方模板为系统环境,如
代码语言:javascript复制hostID:
resolver: environment
envVarName: M3DB_HOST_ID
或者
代码语言:javascript复制 hostID:
resolver: hostname
本文采用config方式,读取配置参数。 tagOptions: 官方强烈推荐quoted,因为这个跟prometheus结合最好,另外的还有prepend_meta、legacy。 config: 官方演示文档上会有seedNodes的配置参数,由于采用了外部etcd集群,这里将完全删除seedNodes的配置项。 namespaces: 默认default namespace数据保存时间为48h。 cache: 内存缓存的算法和大小,首推lru算法,即Least Recently Used (LRU) Cache Policy,当缓存已满的时候首先剔除最近最少读取的块。另外还有none -- None Cache Policy,all -- All Cache Policy,recently_read -- Recently Read Cache Policy。 bootstrap: 启动m3db节点时引导shard,M3DB支持5种不同的bootstrappers -- filesystem、commitlog、peers、uninitialized_topology、noop-all,默认为本文的引导顺序。
启动
docker run -d -v /opt/m3db/m3dbnode.yml:/etc/m3dbnode/m3dbnode.yml -v /etcd-data/m3db:/etcd-data/m3db -p 7201:7201 -p 7203:7203 -p 9000:9000 -p 9001:9001 -p 9002:9002 -p 9003:9003 -p 9004:9004 --name m3db quay.io/m3db/m3dbnode:latest
集群初始化
placement init
代码语言:javascript复制curl -sSf -X POST localhost:7201/api/v1/placement/init -d '
{
"num_shards": 1024,
"replication_factor": 3,
"instances": [
{
"id": "node1",
"isolation_group": "node1",
"zone": "embedded",
"weight": 100,
"endpoint": "172.16.7.170:9000",
"hostname": "172.16.7.170",
"port": 9000
},
{
"id": "node2",
"isolation_group": "node2",
"zone": "embedded",
"weight": 100,
"endpoint": "172.16.7.171:9000",
"hostname": "172.16.7.171",
"port": 9000
},
{
"id": "node3",
"isolation_group": "node3",
"zone": "embedded",
"weight": 100,
"endpoint": "172.16.7.172:9000",
"hostname": "172.16.7.172",
"port": 9000
},
{
"id": "node4",
"isolation_group": "node4",
"zone": "embedded",
"weight": 99,
"endpoint": "172.16.10.12:9000",
"hostname": "172.16.10.12",
"port": 9000
}
]
}'
Replication Factor Recommendations
Replica Factor | Quorum Size | Failure Tolerance |
---|---|---|
1 | 1 | 0 |
2 | 2 | 0 |
3 | 2 | 1 |
4 | 3 | 1 |
5 | 3 | 2 |
6 | 4 | 2 |
7 | 4 | 3 |
建议运行奇数个副本。因为偶数RF N的仲裁大小为(N / 2) 1,所以具有偶数复制因子N的任何集群都具有与RF = N-1的相同的容错能力。
例如,本文有4个node节点,采用3个副本数,可允许1个副本出现问题,2个副本将导致数据异常。
namespace init
代码语言:javascript复制
curl -X POST localhost:7201/api/v1/namespace -d '
{
"name": "default",
"options": {
"bootstrapEnabled": true,
"flushEnabled": true,
"writesToCommitLog": true,
"cleanupEnabled": true,
"snapshotEnabled": true,
"repairEnabled": false,
"retentionOptions": {
"retentionPeriodDuration": "720h",
"blockSizeDuration": "12h",
"bufferFutureDuration": "1h",
"bufferPastDuration": "1h",
"blockDataExpiry": true,
"blockDataExpiryAfterNotAccessPeriodDuration": "5m"
},
"indexOptions": {
"enabled": true,
"blockSizeDuration": "12h"
}
}
}'
参数解析:
- bootstrapEnabled:默认为true,控制M3DB在启动时是否将尝试引导名称空间。
- flushEnabled:默认为true,控制了M3DB一旦块变为不可变状态,是否将定期将块刷新到磁盘。
- writesToCommitlog:默认为true,控制M3DB是否将在提交日志中包括对此名称空间的写入。
- snapshotEnabled:默认为true,控制M3DB是否会定期写出此命名空间的快照文件,这些快照文件充当压缩的commitlog文件。
- repairEnabled:默认为false,如果启用,则M3DB节点将尝试将其拥有的数据与对等节点的数据进行比较,并发出有关任何差异的度量。
retentionOptions:
- retentionPeriod:控制了M3DB将保留名称空间数据的持续时间。
- blockSize:在调整M3DB名称空间的性能时,这是要考虑的最重要的值。更大的blockSizes将使用更多的内存,但可获得更高的压缩率。同样,较小的blockSize将使用较少的内存,但压缩效果较差。在测试中,每个时间序列包含约720个样本的块大小会产生良好的压缩效果。以下是基于分辨率的块大小建议:
Resolution | Block Size |
---|---|
5s | 60m |
15s | 3h |
30s | 6h |
1m | 12h |
5m | 60h |
Prometheus remote Write/Read
代码语言:javascript复制remote_read:
- url: "http://172.16.7.172:7201/api/v1/prom/remote/read"
read_recent: true
remote_write:
- url: "http://172.16.10.12:7201/api/v1/prom/remote/write"
queue_config:
batch_send_deadline: 60s
capacity: 40000
max_backoff: 600ms
max_samples_per_send: 8000
max_shards: 10
min_backoff: 50ms
min_shards: 6
remote_timeout: 30s
Remote write/read的对象是M3 Coordinator,由于m3db集群资源有限,除了对m3db进行优化外,还需要对prometheus进行优化。这里将remote write进行优化,详细可参考官方文档。
结语
当prometheus采集的数据越来越大,查询效率越来越低时,本地存储已经满足不了需求,希望这篇文章可以帮助到你,更多TSDB,敬请期待···
参考链接
https://github.com/m3db/m3db-operator https://github.com/m3db/m3/blob/master/src/dbnode/config/m3dbnode-cluster-template.yml https://prometheus.io/docs/practices/remote_write/#max_backoff