0. 写在前面:为什么你需要“神器”而非“常用命令
大家好,欢迎来到干货、技术、专业全方位遥遥领先的老杨的博客.
帮老杨点赞、转发、在看以及打开小星标哦
攒今世之功德,修来世之福报
兄弟们有没有遇到过看监控中的CPU使用率85%,内存使用率78%,数据库连接数1200。按理说这些指标都挺正常的,但客服群里已经炸了锅——用户疯狂投诉"下不了单"的情况? 就是指标看起来都没毛病,但系统就是有问题。
咱们今天就聊聊:为什么传统的监控总是让我们抓瞎,而可观测性能帮我们看到问题的本质。
我刚入行那会儿,大家对监控的理解很简单:设个阈值,超了就告警。就像古代的烽火台一样,敌人来了点火,然后一站一站传递消息。
那时候用Zabbix比较多,配置起来也挺麻烦:
# 记得那时候配置CPU监控项
zabbix_agentd -c /etc/zabbix/zabbix_agentd.conf -t system.cpu.util[,idle]
zabbix_agentd [12345]: system.cpu.util[,idle] [t|15.23]
# 然后设个触发器
Trigger expression: {server01:system.cpu.util[,idle].avg(5m)}<20
...这套东西的问题很明显:你得提前知道要监控什么,提前设好阈值。就像烽火台只能传递固定的信号一样,碰到没见过的异常情况就傻眼了。
这几年接触了可观测性之后,我发现这完全是另一种思路。它不是让你提前设定所有可能的问题场景,而是给你足够多的数据,让你能够回答任何关于系统行为的问题。
可观测性主要靠三根支柱:指标、日志和链路追踪。我觉得这就像中医看病,要望闻问切,从不同角度了解病情。
现在的指标和以前不太一样了。以前我们只关心CPU、内存这些系统指标,现在更关注业务指标。
我们项目里用Prometheus收集指标,配置大概是这样:
# prometheus.yml的一部分
scrape_configs:
- job_name: 'ecommerce-order-service'
static_configs:
- targets: ['localhost:8080']
scrape_interval: 15s
metrics_path: /actuator/prometheus
# 查询订单转化率的时候会这样写
curl -G 'http://localhost:9090/api/v1/query' \
--data-urlencode 'query=rate(order_success_total[5m])/rate(order_attempt_total[5m])*100'
{
"status": "success",
"data": {
"resultType": "vector",
"result": [
{
"metric": {"instance": "order-service:8080"},
"value": [1640995200, "85.7"]
}
]
}
}这样就能直接看到业务层面的问题,而不是光盯着技术指标。
以前看日志就像看天书,一堆文本混在一起。现在用结构化日志,每条日志都是一个完整的故事。
我们用ELK Stack处理日志,Logstash的配置会把订单相关的日志都标记出来:
input {
beats {
port => 5044
}
}
filter {
if [fields][service] == "order-service" {
json {
source => "message"
}
mutate {
add_field => { "business_domain" => "ecommerce" }
}
if [order_id] {
mutate {
add_tag => [ "order_lifecycle" ]
}
}
}
}
# 然后在Kibana里查询特定订单的完整轨迹
GET /logstash-*/_search
{
"query": {
"bool": {
"must": [
{"term": {"order_id.keyword": "ORDER_20241201_001"}},
{"range": {"@timestamp": {"gte": "2024-12-01T00:00:00Z"}}}
]
}
},
"sort": [{"@timestamp": {"order": "asc"}}]
}
# 结果大概是这样
{
"hits": {
"total": {"value": 23},
"hits": [
{
"_source": {
"@timestamp": "2024-12-01T10:30:15.123Z",
"order_id": "ORDER_20241201_001",
"event": "order_created",
"user_id": "user_12345",
"amount": 299.99,
"payment_method": "alipay"
}
},
...
]
}
}这样一个订单从创建到完成的每个环节都能看得清清楚楚。
现在微服务这么多,一个用户请求可能要跨十几个服务。没有链路追踪的话,出了问题根本不知道卡在哪里。
我们用Jaeger做链路追踪,Spring Boot应用集成起来还算简单:
# application.yml里加这么几行
opentracing:
jaeger:
service-name: order-service
sampler:
type: const
param: 1
reporter:
log-spans: true
# 查询某个请求的完整链路
curl "http://localhost:16686/api/traces/5e9c7b8a2f1d3456?prettyPrint=true"
{
"data": [
{
"traceID": "5e9c7b8a2f1d3456",
"spans": [
{
"spanID": "1a2b3c4d5e6f",
"operationName": "create_order",
"startTime": 1640995200000000,
"duration": 2500000,
"tags": [
{"key": "order.id", "value": "ORDER_20241201_001"},
{"key": "user.id", "value": "user_12345"},
{"key": "payment.method", "value": "alipay"}
],
"process": {
"serviceName": "order-service",
"tags": [{"key": "hostname", "value": "k8s-node-01"}]
}
},
...
]
}
]
}有了这个,就能看到请求在各个服务之间是怎么流转的,哪一步耗时最长。
一个本应该浪漫的秋天,但是告警系统却突然疯狂报警,说订单服务响应时间飙到了8.5秒。按老办法,我们先看了系统指标:
# 当时的告警信息长这样
[ALERT] 2024-11-11 20:30:15
Service: order-service
Metric: response_time
Current: 8.5s
Threshold: 5s
Status: CRITICAL
# 但系统指标看起来都正常
# CPU: 65% (正常)
# Memory: 72% (正常)
# DB连接数: 850/1000 (正常)这种情况最头疼了,指标正常但用户投诉一片。
我们先从业务指标开始分析。当时我在代码里埋了很多业务相关的监控点:
@RestController
public class OrderController {
private final MeterRegistry meterRegistry;
private final Counter orderAttempts;
private final Counter orderSuccess;
private final Timer orderProcessingTime;
public OrderController(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
this.orderAttempts = Counter.builder("order.attempts")
.description("Total order attempts")
.tag("service", "order")
.register(meterRegistry);
// ... 更多指标定义
}
@PostMapping("/orders")
public ResponseEntity<Order> createOrder(@RequestBody OrderRequest request) {
Timer.Sample sample = Timer.start(meterRegistry);
orderAttempts.increment();
try {
Order order = orderService.createOrder(request);
orderSuccess.increment();
return ResponseEntity.ok(order);
} finally {
sample.stop(orderProcessingTime);
}
}
}然后在Grafana里查询各种支付方式的成功率:
# 查询不同支付方式的订单成功率
sum(rate(order_success_total[5m])) by (payment_method) /
sum(rate(order_attempts_total[5m])) by (payment_method) * 100
# 结果让我们找到了问题
# alipay: 95.2%
# wechat_pay: 94.8%
# credit_card: 67.3% <-- 这里明显有问题!原来问题出在信用卡支付上!成功率只有67%,难怪用户投诉这么多。
知道是信用卡支付的问题后,我们开始分析相关的错误日志:
# 在Elasticsearch里查询信用卡支付的错误
POST /logs-*/_search
{
"query": {
"bool": {
"must": [
{"term": {"payment_method": "credit_card"}},
{"term": {"level": "ERROR"}},
{"range": {"@timestamp": {"gte": "now-1h"}}}
]
}
},
"aggs": {
"error_types": {
"terms": {
"field": "error_code.keyword",
"size": 10
}
}
}
}
# 分析结果显示超时是主要问题
{
"aggregations": {
"error_types": {
"buckets": [
{
"key": "PAYMENT_GATEWAY_TIMEOUT",
"doc_count": 1247
},
{
"key": "INVALID_CARD_FORMAT",
"doc_count": 89
}
...
]
}
}
}大部分错误都是"PAYMENT_GATEWAY_TIMEOUT",看来是支付网关超时了。
有了日志的线索,我们用Jaeger查看了支付相关的调用链路。发现了一个很有意思的现象:
# 通过Jaeger分析发现调用链路是这样的:
# order-service -> payment-service -> bank-gateway -> 第三方银行API
# 关键发现:
# 1. payment-service到bank-gateway的调用很正常,只要50ms
# 2. bank-gateway到第三方银行API经常超时,要8000ms
# 3. 而且我们没有设置合理的超时和重试机制问题清楚了:第三方银行接口在双11期间压力大,响应慢,而我们没有做好容错处理。
找到原因后,我们给支付服务加了断路器:
@Component
public class PaymentServiceClient {
@CircuitBreaker(name = "payment-gateway", fallbackMethod = "fallbackPayment")
@TimeLimiter(name = "payment-gateway")
@Retry(name = "payment-gateway")
public CompletableFuture<PaymentResult> processPayment(PaymentRequest request) {
return CompletableFuture.supplyAsync(() -> {
// 调用外部支付网关
return bankGatewayClient.charge(request);
});
}
public CompletableFuture<PaymentResult> fallbackPayment(PaymentRequest request, Exception ex) {
// 降级处理:引导用户使用其他支付方式
return CompletableFuture.completedFuture(
PaymentResult.builder()
.status("FALLBACK")
.message("请尝试使用支付宝或微信支付")
.build()
);
}
}修改上线后,效果立竿见影:
# 信用卡支付成功率从67.3%提升到91.2%
sum(rate(order_success_total{payment_method="credit_card"}[5m])) /
sum(rate(order_attempts_total{payment_method="credit_card"}[5m])) * 100
# 整体订单响应时间P95从8.5秒降到2.1秒
histogram_quantile(0.95,
sum(rate(order_processing_time_bucket[5m])) by (le)
)
# 用户投诉量下降了78%
sum(rate(user_complaint_total[1h]))这些年用过不少可观测性工具,给大家分享点经验。
我们现在用的是比较经典的组合,Docker Compose一键部署:
version: '3.8'
services:
# 指标收集与存储
prometheus:
image: prom/prometheus:v2.45.0
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
- '--web.enable-lifecycle'
# 指标可视化
grafana:
image: grafana/grafana:10.0.0
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin123
volumes:
- grafana-data:/var/lib/grafana
# 日志收集
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.8.0
environment:
- discovery.type=single-node
- "ES_JAVA_OPTS=-Xms2g -Xmx2g"
ports:
- "9200:9200"
# 链路追踪
jaeger:
image: jaegertracing/all-in-one:1.47
ports:
- "14268:14268"
- "16686:16686"
environment:
- COLLECTOR_OTLP_ENABLED=true
# 启动整个监控栈
docker-compose up -d
# 检查各个服务是否正常
curl http://localhost:9090/api/v1/status/config # Prometheus
curl http://localhost:9200/_cluster/health # Elasticsearch
curl http://localhost:16686/api/services # Jaeger
...这套组合用起来还挺顺手的,社区活跃,资料也多。
不过考虑到数据安全,有些项目我们也在试国产化方案。比如用TDengine替代Prometheus存储时序数据:
# TDengine的安装过程
wget https://www.taosdata.com/assets-download/3.0/TDengine-server-3.0.0.0-Linux-x64.tar.gz
tar -xzf TDengine-server-3.0.0.0-Linux-x64.tar.gz
cd TDengine-server-3.0.0.0/
sudo ./install.sh
# 启动服务
systemctl start taosd
systemctl enable taosd
# 创建业务数据库和表结构
taos> CREATE DATABASE ecommerce PRECISION 'ms';
taos> USE ecommerce;
taos> CREATE TABLE order_metrics (
ts TIMESTAMP,
order_id NCHAR(32),
user_id NCHAR(16),
amount FLOAT,
payment_method NCHAR(16),
processing_time INT,
status NCHAR(16)
) TAGS (service NCHAR(32), instance NCHAR(64));
# 插入测试数据
taos> INSERT INTO order_metrics_001 USING order_metrics TAGS ('order-service', 'pod-001')
VALUES (now, 'ORDER_001', 'user_123', 299.99, 'alipay', 1250, 'success');
Query OK, 1 row(s) affected (0.002968s)TDengine在处理时序数据方面确实有优势,压缩比很高,查询速度也不错。
这些年下来,我觉得可观测性不只是技术工具的升级,更重要的是思维方式的转变。
以前我们是"救火队员"的心态,哪里着火往哪里跑。现在更像"侦探",要通过各种线索找到问题的根本原因。这需要我们:
现在我们的故障处理已经有一定程度的自动化了。我写了个脚本,能根据不同的告警类型自动执行对应的处理步骤:
#!/bin/bash
# 智能故障处理脚本
ALERT_TYPE=$1
SERVICE_NAME=$2
THRESHOLD=$3
case $ALERT_TYPE in
"high_latency")
echo "检测到${SERVICE_NAME}延迟异常,开始自动处理..."
# 1. 收集诊断信息
kubectl top pods -l app=${SERVICE_NAME}
# 2. 检查链路追踪数据
curl -s "http://jaeger:16686/api/traces?service=${SERVICE_NAME}&limit=10"
# 3. 自动扩容处理
kubectl scale deployment ${SERVICE_NAME} --replicas=6
# 4. 通知钉钉群组
curl -X POST "https://oapi.dingtalk.com/robot/send?access_token=${DINGTALK_TOKEN}" \
-H 'Content-Type: application/json' \
-d "{\"msgtype\":\"text\",\"text\":{\"content\":\"${SERVICE_NAME}已自动扩容处理延迟问题\"}}"
;;
"error_rate")
echo "检测到${SERVICE_NAME}错误率异常,触发熔断保护..."
# ... 其他处理逻辑
;;
esac这样很多常见问题都能在几分钟内自动解决,不用半夜爬起来了。
最近我也在关注AI在运维领域的应用。用机器学习做异常检测确实有不少优势,能发现一些人工很难注意到的模式。
我用Python写了个简单的异常检测器:
import pandas as pd
from sklearn.ensemble import IsolationForest
from sklearn.preprocessing import StandardScaler
import numpy as np
class IntelligentAnomalyDetector:
def __init__(self):
self.model = IsolationForest(contamination=0.1, random_state=42)
self.scaler = StandardScaler()
def train(self, metrics_data):
"""用历史数据训练模型"""
features = ['cpu_usage', 'memory_usage', 'response_time',
'error_rate', 'throughput']
X = metrics_data[features]
# 标准化处理
X_scaled = self.scaler.fit_transform(X)
# 训练异常检测模型
self.model.fit(X_scaled)
print("异常检测模型训练完成")
def detect_anomalies(self, current_metrics):
"""实时检测异常"""
X_current = self.scaler.transform([current_metrics])
anomaly_score = self.model.decision_function(X_current)[0]
is_anomaly = self.model.predict(X_current)[0] == -1
return {
'is_anomaly': is_anomaly,
'anomaly_score': anomaly_score,
'confidence': abs(anomaly_score)
}
# 实际使用效果
detector = IntelligentAnomalyDetector()
# 用一年的历史数据训练
historical_data = pd.DataFrame({
'cpu_usage': np.random.normal(50, 10, 1000),
'memory_usage': np.random.normal(60, 15, 1000),
'response_time': np.random.normal(200, 50, 1000),
'error_rate': np.random.normal(0.1, 0.05, 1000),
'throughput': np.random.normal(1000, 200, 1000)
})
detector.train(historical_data)
# 检测当前状态
current_state = [85, 92, 5000, 0.15, 200] # 明显的异常状态
result = detector.detect_anomalies(current_state)
print(f"异常检测结果: {result}")
# 输出: {'is_anomaly': True, 'anomaly_score': -0.23, 'confidence': 0.23}这个模型能够学习系统的正常行为模式,对于一些不太明显的异常也能提前预警。不过说实话,AI模型的可解释性还是个问题,有时候它说有异常,但你不太清楚具体是什么原因。
运维人挑灯守夜,为亿万连接负重前行,机器轰鸣中,运维人以鲜血与诗意修补世界的脆弱,朝露未晞,使命已沉。 亿万方阵,任将帅坐帷帐指点江山,运维人护军旗立于风雨之夜.挽大厦于将倾,填江湖与决口.运维不死,只是慢慢凋零. 评论区等你们!
这里老杨先声明一下,日常生活中大家都叫老杨波哥,跟辈分没关系,主要是岁数大了.就一个代称而已. 老杨的00后小同事老杨喊都是带哥的.张哥,李哥的. 但是这个称呼呀,在线下参加一些活动时.金主爸爸也这么叫就显的不太合适. 比如上次某集团策划总监,公司开大会来一句:“今个咱高兴!有请IT运维技术圈的波哥讲两句“ 这个氛围配这个称呼在互联网这行来讲就有点对不齐! 每次遇到这个情况老杨老杨周末浅聊服务器开在公网的那些坑老杨干了,你们随意!” 所以以后咱们改叫老杨,即市井又低调.还挺亲切,老杨觉得挺好.
运维X档案系列文章:
企业级 Kubernetes 集群安全加固全攻略( 附带一键检查脚本)
看完别走.修行在于点赞、转发、在看.攒今世之功德,修来世之福报
老杨AI的号: 98dev