开篇
本文承接前文 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
etcd集群部署
node1、node2、node3、coordinator操作
yum install etcd -y
附上etcd基础配置文件
#etcd配置文件/etc/etcd/etcd.confETCD_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,node4ETCD_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=false58c7b83d06ee71dc: name=node3 peerURLs=http://node3:2380 clientURLs=http://node3:2379 isLeader=falsea9aee06e6a14d468: 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.ymlcoordinator:listenAddress:type: "config"value: "0.0.0.0:7201" # 交互端口local:namespaces:- namespace: default # 数据要存入的表type: unaggregated # 数据类型retention: 720h # 数据保存时间logging:level: errormetrics: # coordinator本身的metricscope:prefix: "coordinator"prometheus:handlerPath: metricslistenAddress: 0.0.0.0:7203 # until https://github.com/m3db/m3/issues/682 is resolvedsanitization: prometheussamplingRate: 1.0extended: nonelimits:maxComputedDatapoints: 10000tagOptions:# Configuration setting for generating metric IDs from tags.idScheme: quoted # 这个必须db:logging:level: errormetrics:prometheus:handlerPath: metricssanitization: prometheussamplingRate: 1.0extended: detailedlistenAddress: 0.0.0.0:9000clusterListenAddress: 0.0.0.0:9001httpNodeListenAddress: 0.0.0.0:9002httpClusterListenAddress: 0.0.0.0:9003debugListenAddress: 0.0.0.0:9004hostID: #采用此配置文件自定义hostnameresolver: configvalue: node1 #hostname为 node1,其余节点依次为node2,node3,node4[coordinator]client:writeConsistencyLevel: majority # 写一致性级别readConsistencyLevel: unstrict_majoritygcPercentage: 100writeNewSeriesAsync: truewriteNewSeriesLimitPerSecond: 1048576writeNewSeriesBackoffDuration: 2msbootstrap:bootstrappers: # 启动顺序- filesystem- commitlog- peers- uninitialized_topologycommitlog:returnUnfulfilledForCorruptCommitLogFiles: falsecache:series:policy: lrupostingsList:size: 262144commitlog:flushMaxBytes: 524288flushEvery: 1squeue:calculationType: fixedsize: 2097152fs:filePathPrefix: etcd-data/m3db # m3dbnode数据目录config:service:env: default_envzone: embeddedservice: m3db # 服务名。可以按照consul中的service进行理解cacheDir: etcd-data/m3db/cacheetcdClusters:- zone: embeddedendpoints:- node1:2379- node2:2379- node3:2379EOF
参数解释:
hostID: 官方模板为系统环境,如
hostID:resolver: environmentenvVarName: M3DB_HOST_ID
或者
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
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
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
remote_read:- url: "http://172.16.7.172:7201/api/v1/prom/remote/read"read_recent: trueremote_write:- url: "http://172.16.10.12:7201/api/v1/prom/remote/write"queue_config:batch_send_deadline: 60scapacity: 40000max_backoff: 600msmax_samples_per_send: 8000max_shards: 10min_backoff: 50msmin_shards: 6remote_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
历史文章

福利






