4、日志存储与索引:Elasticsearch集群搭建、索引生命周期管理、数据分片策略
好,咱们进入第四章。日志存进去了,怎么查得快、存得稳、还不爆盘?这就要靠Elasticsearch了。说实话,ES这东西,用起来爽,坑也不少。我最早接触ES是在一个金融项目里,每天几个TB的日志量,集群一挂,整个运维团队都炸了。后来慢慢摸索出一套相对靠谱的玩法,今天跟你聊聊。
4.1 Elasticsearch集群搭建:别一上来就搞三节点
很多人觉得ES集群嘛,三台机器一搭就完事了。嗯,没那么简单。我建议你从硬件选型开始想清楚。
核心原则:ES是IO密集型应用,磁盘和内存是瓶颈。CPU反而不是最关键的。
4.1.1 节点角色怎么分?
ES 7.x之后,节点角色分得很细。我个人习惯至少分三种角色:
- Master节点:负责集群管理,不存数据。3台起步,奇数台防脑裂。
- Data节点:真正存日志的地方。根据数据量横向扩展。
- Ingest节点:做数据预处理。如果你的日志格式乱七八糟,这个节点很有用。
你想想看,如果Master和Data混在一起,集群一挂,连恢复都难。我在项目中遇到过,有人图省事把所有角色放一起,结果一次磁盘写满,整个集群管理都瘫痪了。
4.1.2 部署示例(Docker Compose)
这里给一个我常用的单机测试配置,生产环境请调大内存:
version: '3'
services:
es-master:
image: elasticsearch:7.17.0
container_name: es-master
environment:
- node.name=master-1
- node.roles=master
- discovery.seed_hosts=es-master,es-data-1
- cluster.initial_master_nodes=master-1
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
volumes:
- es-master-data:/usr/share/elasticsearch/data
es-data-1:
image: elasticsearch:7.17.0
container_name: es-data-1
environment:
- node.name=data-1
- node.roles=data
- discovery.seed_hosts=es-master,es-data-1
- "ES_JAVA_OPTS=-Xms2g -Xmx2g"
volumes:
- es-data1-data:/usr/share/elasticsearch/data
volumes:
es-master-data:
es-data1-data:
小提示:生产环境建议用裸机或K8s部署。Docker跑ES,磁盘性能损耗不小,尤其是日志量大的时候。
4.2 索引生命周期管理:让日志自动“退休”
日志这东西,越存越多。如果不做管理,磁盘迟早爆掉。ES提供了ILM(Index Lifecycle Management),说白了就是给索引定个“人生规划”。
4.2.1 四个阶段,一个策略
ILM把索引的生命周期分成四个阶段:
| 阶段 | 做什么 | 典型配置 |
|---|---|---|
| Hot | 正在写入,追求写入性能 | SSD磁盘,副本数=1 |
| Warm | 不再写入,但还在查询 | HDD磁盘,压缩存储 |
| Cold | 很少查询,但需要保留 | 降低副本数,甚至只留1份 |
| Delete | 过期删除,释放空间 | 设定保留天数 |
我曾经在一个日志平台上,没配ILM,结果半年后磁盘报警,一查发现半年前的日志还在Hot阶段占着SSD。嗯,那感觉,就像把冬天的棉袄放在夏天的衣柜里,又占地方又没用。
4.2.2 实战:定义一个ILM策略
下面这个策略,我经常用在Nginx访问日志上:
PUT _ilm/policy/nginx-access-policy
{
"policy": {
"phases": {
"hot": {
"min_age": "0ms",
"actions": {
"rollover": {
"max_size": "50GB",
"max_age": "1d"
},
"set_priority": {
"priority": 100
}
}
},
"warm": {
"min_age": "7d",
"actions": {
"shrink": {
"number_of_shards": 1
},
"forcemerge": {
"max_num_segments": 1
},
"set_priority": {
"priority": 50
}
}
},
"cold": {
"min_age": "30d",
"actions": {
"set_priority": {
"priority": 0
},
"freeze": {}
}
},
"delete": {
"min_age": "90d",
"actions": {
"delete": {}
}
}
}
}
}
注意:rollover操作依赖索引别名。如果你直接用原始索引名写入,ILM不会自动触发滚动。我见过有人配了策略但忘了配别名,结果索引一直不滚动,最后单个索引几百GB,查询慢得要命。
4.3 数据分片策略:分多了是灾难,分少了也是灾难
分片(Shard)是ES的并行单位。分片太多,集群管理开销大;分片太少,写入并发上不去。说白了,这是个平衡的艺术。
4.3.1 分片数怎么定?
我有个简单的公式,用了好几年:
每个分片的大小控制在 20GB - 50GB 之间
分片总数 = 数据节点数 × 2 到 3 倍
举个例子:你有5个数据节点,每天日志量500GB。那么:
- 分片数 = 500GB / 30GB ≈ 17个分片
- 每个节点分摊 ≈ 3-4个分片
- 加上副本,每个节点实际承载6-8个分片
这个量级,ES跑起来很舒服。我曾经接手过一个项目,一个索引分了200个分片,每个分片才几百MB。结果集群状态经常黄,查询也慢。后来我重建索引,缩到20个分片,世界清净了。
4.3.2 分片分配:别让数据“偏科”
ES默认的分片分配策略是“平均主义”,但有时候会出现“热分片”——某个分片写入量特别大。这时候可以手动调整:
PUT _cluster/settings
{
"transient": {
"cluster.routing.allocation.total_shards_per_node": 10
}
}
这个配置限制了每个节点上的分片总数,防止某个节点被塞爆。
避坑指南:我曾经在双十一大促前,忘了调整分片分配。结果流量一上来,某个节点磁盘IO打满,整个集群写入延迟飙升。后来我养成了习惯:大促前一周,先做一次分片均衡检查。
4.3.3 副本数:不是越多越好
副本(Replica)是为了高可用和查询性能。但副本多了,写入性能会下降。我一般这样配:
- Hot阶段:副本数=1,保证写入速度
- Warm/Cold阶段:副本数=0或1,反正查询少
- 关键业务日志:副本数=2,防止数据丢失
你想想看,如果副本数设成3,每次写入都要写4份数据(1主+3副),磁盘IO压力直接翻4倍。没必要。
4.4 小结:我的ES运维清单
最后,给你一份我自己的ES运维检查清单,每次上线前我都会过一遍:
- 集群角色:Master和Data分开了吗?
- ILM策略:每个索引都绑定了生命周期策略吗?
- 分片大小:单个分片在20-50GB之间吗?
- 分片数量:每个节点上的分片数不超过20个吗?
- 副本配置:Hot阶段副本数=1,冷数据副本数=0吗?
- 磁盘水位:磁盘使用率超过85%了吗?超过就报警。
嗯,这套清单帮我扛过了好几次大流量冲击。ES这东西,配置好了是利器,配置不好是定时炸弹。希望你能少踩我踩过的坑。
下一章,咱们聊聊日志采集的最后一公里——如何把日志从各个服务端安全、高效地送到ES里。到时候见。