告别散落日志:用华为云 CSS 从零搭建企业级日志分析与全文检索平台
【摘要】 摘要:微服务化后,日志散落在几十个节点上,grep 翻日志效率极低,出了问题靠“人肉排错”。本文以 Spring Boot 微服务集群为例,基于华为云云搜索服务 CSS(Elasticsearch + Kibana),结合 LTS(日志传输)和 Logstash,搭建集中式日志采集、存储、检索与可视化平台,实现秒级日志查询、错误实时告警和接口性能分析,把运维从“翻日志”升级到“看面板”。标签...
摘要:微服务化后,日志散落在几十个节点上,grep 翻日志效率极低,出了问题靠“人肉排错”。本文以 Spring Boot 微服务集群为例,基于华为云云搜索服务 CSS(Elasticsearch + Kibana),结合 LTS(日志传输)和 Logstash,搭建集中式日志采集、存储、检索与可视化平台,实现秒级日志查询、错误实时告警和接口性能分析,把运维从“翻日志”升级到“看面板”。
标签建议:云搜索服务 CSS、日志分析、Elasticsearch、可观测性、云端实践
一、背景与痛点
订单、用户、库存三个微服务各部署 4 个实例,日志分散在 12 台机器上。日常排错流程:
# 1. 登录跳板机
# 2. 找到对应节点
ssh order-node-03
# 3. grep 关键字
grep "OutOfMemory" /var/log/app/order-service.log | tail -100
# 4. 时间不对还得切文件
grep "2024-01-15 14:3" /var/log/app/order-service.log.2024-01-15-14
痛点清单:
|
问题
|
具体表现
|
|---|---|
|
定位慢
|
不知道报错在哪个节点,逐个登录翻,平均 15 分钟起
|
|
跨服务追踪难
|
一个请求经过 3 个服务,靠 traceId 手动串联
|
|
无法聚合分析
|
想知道“过去 1 小时 500 错误最多的接口”,做不到
|
|
日志易丢失
|
节点重启、磁盘满,日志直接被覆盖
|
|
无主动告警
|
错误只有用户反馈了才知道
|
核心诉求:日志集中存、秒级搜、自动告警、可视化。
二、方案设计
2.1 为什么用 CSS 而不是自建 ELK
|
维度
|
自建 ELK
|
华为云 CSS
|
|---|---|---|
|
部署运维
|
自己装 ES + Kibana + Logstash
|
控制台一键创建集群
|
|
扩容
|
手动加节点、改分片
|
在线扩节点/磁盘
|
|
高可用
|
自己配主从、快照
|
多 AZ 自动分布
|
|
安全
|
自己搞 TLS + 账号体系
|
内置 RBAC + HTTPS + 审计
|
|
成本
|
机器 + 人力
|
按需付费,免运维人力
|
小团队选 CSS 的核心原因:不用养一个 Elasticsearch 专家。
2.2 日志链路架构
微服务实例(Spring Boot)
│
│ Filebeat / LTS 采集
▼
日志汇聚层(Logstash / LTS 直接转发)
│
├─ 过滤:grok 解析、字段提取
├─ 富化:添加 env、service_name、trace_id
└─ 路由:按索引模板分发
│
▼
CSS 集群(Elasticsearch)
│
├─ 索引:按月滚动(logs-2024-01)
├─ 分片:3 primary + 1 replica
└─ ILM:热温冷数据生命周期管理
│
▼
Kibana 可视化
│
├─ Discover:秒级全文检索
├─ Dashboard:错误率/吞吐量/延迟面板
└─ Alerting:错误突增告警
2.3 索引设计
按服务 + 时间分索引:
logs-order-service-2024.01
logs-user-service-2024.01
logs-inventory-service-2024.01
好处:
- 按时间删除旧数据,直接删索引,不影响写入
- 按服务查时只搜对应索引,性能高
- 不同服务字段结构不同,mapping 互不干扰
三、实操步骤
3.1 创建 CSS 集群
- 进入云搜索服务 CSS 控制台 → 创建集群
- 集群类型:Elasticsearch
- 版本:7.10.2(稳定版,与 Spring Boot 生态兼容好)
- 节点规格:
- 热节点:ess.spec.m6.large.8(4U32G)× 3
- 数据盘:超高 I/O,200GB/节点
- 网络:选业务同 VPC,安全组开放 9200(内网)
- 开启 Kibana:自动分配访问地址,绑定 EIP 或走 VPN 访问
- 安全模式:开启 HTTPS + 账号认证
避坑:节点数至少 3 个才能保证主节点选举的高可用。单节点只适合开发测试。
3.2 Spring Boot 应用日志改造
引入 Logback + Logstash Encoder:
<!-- pom.xml -->
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>7.4</version>
</dependency>
logback-spring.xml 配置:
<appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<destination>logstash.internal:5000</destination>
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp/>
<logLevel/>
<message/>
<loggerName/>
<threadName/>
<mdc/> <!-- 输出 MDC 中的 traceId -->
<stackTrace/>
<pattern>
<pattern>
{
"service": "order-service",
"env": "prod",
"node_ip": "${HOSTNAME}"
}
</pattern>
</pattern>
</providers>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="LOGSTASH"/>
</root>
MDC 中放入 traceId(通过拦截器或 Spring Cloud Sleuth 自动注入):
// 拦截器中
MDC.put("trace_id", UUID.randomUUID().toString());
输出到 Logstash 的 JSON 格式示例:
{
"timestamp": "2024-01-15T14:32:10.123Z",
"level": "ERROR",
"service": "order-service",
"env": "prod",
"trace_id": "a1b2c3d4-1234-5678",
"message": "Failed to create order: inventory deduction timeout",
"stack_trace": "..."
}
3.3 Logstash 管道配置
Logstash 部署在独立 ECS 或容器中,接收 Filebeat/TCP 日志并写入 CSS:
# pipeline/logs.conf
input {
tcp {
port => 5000
codec => json_lines
}
}
filter {
# 提取时间戳
date {
match => ["timestamp", "ISO8601"]
target => "@timestamp"
}
# 按服务名路由
mutate {
add_field => {
"index_prefix" => "%{service}"
}
}
# 解析异常堆栈为多行
if [stack_trace] {
mutate {
gsub => ["stack_trace", "\n", " | "]
}
}
}
output {
elasticsearch {
hosts => ["https://css-cluster-endpoint:9200"]
user => "admin"
password => "${CSS_PASSWORD}"
index => "logs-%{service}-%{+YYYY.MM}"
ssl_certificate_verification => false
}
}
3.4 索引模板与 ILM 策略
在 Kibana Dev Tools 中创建索引模板:
PUT _index_template/logs-template
{
"index_patterns": ["logs-*"],
"template": {
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1,
"refresh_interval": "5s",
"index.lifecycle.name": "logs-ilm-policy"
},
"mappings": {
"properties": {
"timestamp": { "type": "date" },
"level": { "type": "keyword" },
"service": { "type": "keyword" },
"env": { "type": "keyword" },
"trace_id": { "type": "keyword" },
"message": { "type": "text", "analyzer": "ik_max_word" },
"stack_trace": { "type": "text" },
"node_ip": { "type": "ip" }
}
}
}
}
ILM 生命周期策略(热→温→冷→删除):
PUT _ilm/policy/logs-ilm-policy
{
"policy": {
"phases": {
"hot": {
"actions": {
"rollover": {
"max_size": "50GB",
"max_age": "7d"
}
}
},
"warm": {
"min_age": "7d",
"actions": {
"shrink": { "number_of_shards": 1 },
"forcemerge": { "max_num_segments": 1 }
}
},
"cold": {
"min_age": "30d",
"actions": {
"freeze": {}
}
},
"delete": {
"min_age": "90d",
"actions": {
"delete": {}
}
}
}
}
}
3.5 Kibana 可视化与告警
① Discover 搜索
- 索引模式:
logs-* - 常用查询:
level:ERROR AND service:order-servicetrace_id:"a1b2c3d4-1234-5678"(全链路追踪)message:"OutOfMemory" AND timestamp:>now-1h
② Dashboard 面板
创建关键图表:
|
面板
|
查询
|
可视化类型
|
|---|---|---|
|
错误率趋势
|
level:ERROR 按时间聚合 |
折线图
|
|
服务吞吐量
|
所有日志按 service 分组计数
|
柱状图
|
|
Top 10 错误接口
|
level:ERROR + 正则提取 URL |
数据表
|
|
P99 响应时间
|
从 MDC 提取
duration_ms 字段 |
直方图
|
|
节点日志分布
|
按
node_ip 聚合 |
饼图
|
③ 告警配置
Kibana → Stack Management → Alerts:
- 规则:Error count threshold
- 条件:
level:ERROR在 5 分钟内 > 50 条 - 动作:Webhook 推送到企业微信群
{
"msgtype": "markdown",
"markdown": {
"content": "## 🚨 日志告警\n**服务**:{{context.service}}\n**错误数**:{{context.hits}}\n**时间**:{{context.timestamp}}\n[查看详情]({{context.kibana_url}})"
}
}
四、验证结果
|
验证项
|
操作
|
预期
|
|---|---|---|
|
日志实时写入
|
触发一个接口异常
|
5 秒内 Kibana Discover 可见
|
|
全文检索
|
搜 trace_id
|
跨 3 个服务的日志全部串联
|
|
聚合统计
|
Dashboard 刷新
|
错误率/吞吐量实时更新
|
|
告警触发
|
手动制造 60 条 ERROR
|
企业微信 1 分钟内收到告警
|
|
数据过期
|
等 90 天或手动触发 ILM
|
旧索引自动删除
|
|
节点故障
|
停一个 CSS 数据节点
|
集群自动重分配分片,写入不中断
|
排错效率对比:
|
指标
|
改造前
|
改造后
|
|---|---|---|
|
定位错误所在节点
|
5-15 分钟
|
5 秒(Kibana 直接显示 node_ip)
|
|
跨服务追踪请求
|
不可行
|
trace_id 一键串联
|
|
统计过去 1 小时错误分布
|
手动写脚本
|
Dashboard 实时展示
|
|
发现异常
|
用户投诉
|
告警主动推送
|
五、生产避坑清单
❌ 常见错误
- 所有日志写同一个索引:单索引膨胀到 TB 级,查询慢、分片恢复慢
- 字段全用 text 类型:text 不分词无法精确聚合,该用 keyword 的用 keyword
- 不配 ILM 策略:日志无限堆积,磁盘撑满集群只读
- Logstash 单点:Logstash 挂了日志全丢,至少部署 2 个实例 + Filebeat 持久化
- JVM 堆内存超过 32GB:ES 禁用压缩指针,反而性能下降
- 分片数过多:一个节点上千个分片,集群状态更新卡顿
- 没开慢查询日志:ES 自身慢查询不监控,排查时不知道是 ES 慢还是应用慢
- Kibana 直接暴露公网:安全风险,应走 VPN 或堡垒机
✅ 正确姿势
- 索引按服务 + 月份滚动,单索引控制在 50GB 以内
- mapping 提前设计:日志级别、服务名、trace_id 用 keyword
- ILM 四阶段:热(7天)→ 温(压缩)→ 冷(冻结)→ 删(90天)
- Logstash 至少 2 节点,Filebeat 开持久化队列
- ES 堆内存设物理内存的 50%,不超过 32GB
- 分片数 = 数据节点数 × 1~3,不要过度分片
- 监控 ES 自身:CPU、JVM GC、线程池队列、索引延迟
- Kibana 走内网 + 反向代理鉴权,不直挂公网
六、成本结构
|
计费项
|
说明
|
优化建议
|
|---|---|---|
|
节点计算
|
按节点规格和数量
|
热节点用高 IO 型,冷数据阶段用低配节点
|
|
存储
|
高性能云盘
|
ILM 自动降冷,历史数据用大容量盘
|
|
公网带宽
|
Kibana 访问
|
走内网或 VPN,省公网流量
|
|
备份存储
|
快照到 OBS
|
定期备份,OBS 低频存储降成本
|
经验:日志量 < 50GB/天 的小集群,3 个 4U32G 节点足够扛住。超过 200GB/天 需要单独规划热温架构。
七、总结与扩展
日志平台的价值不只是“存日志”,而是把运维经验数据化:
- 从被动到主动:告警在用户感知前触发
- 从模糊到精确:5 秒定位到哪个节点、哪个接口、哪行代码
- 从孤岛到关联:trace_id 串联全链路,跨服务问题一目了然
后续演进:
- 接入 APM Trace 数据:把 SkyWalking/Zipkin span 也写入 ES,日志 + 链路统一检索
- 机器学习异常检测:ES ML 自动学习错误率基线,异常偏离自动告警
- 安全日志审计:将 WAF、HSS 安全日志也汇入 CSS,做安全事件关联分析
- 日志即证据:结合 LTS 做合规审计,满足等保日志留存要求
- Serverless 日志:函数工作流日志自动接入,无服务器架构也有完整可观测性
本文为原创日志平台实践,基于华为云云搜索服务 CSS 整理,操作步骤以当前控制台页面为准。转载请注明出处。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)