一、为什么需要可观测性
在云原生时代,应用部署在Kubernetes集群中,往往由多个微服务组成,每个服务可能分布在不同的Pod里。这种情况下,如果某个服务出现性能问题或者直接宕机,传统的日志查看方式就会显得力不从心。想象一下,你正在度假,突然收到报警说线上服务响应变慢,但你连问题出在哪里都不知道,更别提快速修复了。
可观测性(Observability)就是为了解决这个问题而生的。它不仅仅是监控(Monitoring),而是通过日志(Logging)、指标(Metrics)和追踪(Tracing)三个维度,让你能够像"X光"一样透视整个系统的运行状态。
举个例子,假设你的电商应用突然出现订单提交变慢的情况:
- 日志告诉你某个Pod抛出了数据库连接超时的异常
- 指标显示数据库的CPU使用率飙升到90%
- 追踪发现是某个查询语句没有走索引
有了这些信息,你就能快速定位到根本原因。
二、可观测性的三大支柱
1. 日志收集
在Kubernetes中,容器日志默认是输出到stdout/stderr的,这些日志会被kubelet收集并存储在节点上。但这种方式有个明显问题——当Pod被删除或节点宕机时,日志就丢失了。
我们通常使用EFK栈(Elasticsearch + Fluentd + Kibana)来收集日志。这里以Fluentd为例展示如何配置:
# fluentd-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: fluentd-config
data:
fluent.conf: |
<source>
@type tail
path /var/log/containers/*.log
pos_file /var/log/fluentd-containers.log.pos
tag kubernetes.*
read_from_head true
<parse>
@type json
time_format %Y-%m-%dT%H:%M:%S.%NZ
</parse>
</source>
<filter kubernetes.**>
@type kubernetes_metadata
</filter>
<match kubernetes.**>
@type elasticsearch
host elasticsearch
port 9200
logstash_format true
</match>
这段配置做了三件事:
- 监控所有容器日志文件的变化
- 添加Kubernetes元数据(如Pod名称、命名空间)
- 将日志发送到Elasticsearch
2. 指标监控
Prometheus已经成为云原生监控的事实标准。它通过Pull模式从各个服务收集指标数据。在Kubernetes中,我们可以用ServiceMonitor来定义监控目标:
# servicemonitor.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: webapp-monitor
spec:
selector:
matchLabels:
app: webapp
endpoints:
- port: web
interval: 15s
path: /metrics
这个配置告诉Prometheus:每15秒去抓取所有带有app=webapp标签的Service的/metrics端点。
3. 分布式追踪
当请求要经过多个微服务时,我们需要Jaeger这样的分布式追踪系统。以Spring Boot应用为例,只需添加依赖:
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-extension-trace-propagators</artifactId>
</dependency>
然后在代码中创建Span:
@GetMapping("/checkout")
public String checkout(@RequestHeader HttpHeaders headers) {
// 从请求头中提取追踪上下文
Span span = Span.current();
span.setAttribute("user.id", getCurrentUserId());
try(Scope scope = span.makeCurrent()) {
// 调用支付服务
paymentService.charge();
// 调用库存服务
inventoryService.deduct();
return "success";
}
}
这样在Jaeger UI中就能看到完整的调用链了。
三、实战:全栈可观测性方案
让我们用一个电商平台的例子,把三大支柱串联起来。假设我们有这些服务:
- 前端服务(Node.js)
- 订单服务(Java)
- 支付服务(Go)
- MySQL数据库
日志配置
给所有Pod添加Fluentd边车容器:
containers:
- name: fluentd
image: fluent/fluentd-kubernetes-daemonset:v1.14.6
env:
- name: FLUENT_ELASTICSEARCH_HOST
value: "elasticsearch"
volumeMounts:
- name: varlog
mountPath: /var/log
指标暴露
每个服务都需要暴露Prometheus格式的指标。以Node.js服务为例:
const promClient = require('prom-client');
// 定义自定义指标
const httpRequestDuration = new promClient.Histogram({
name: 'http_request_duration_seconds',
help: 'Duration of HTTP requests in seconds',
labelNames: ['method', 'route', 'code'],
buckets: [0.1, 0.5, 1, 2, 5]
});
// 在中间件中记录指标
app.use((req, res, next) => {
const end = httpRequestDuration.startTimer();
res.on('finish', () => {
end({ method: req.method, route: req.route.path, code: res.statusCode });
});
next();
});
追踪集成
在所有服务中配置OpenTelemetry:
# opentelemetry-collector.yaml
receivers:
otlp:
protocols:
grpc:
exporters:
jaeger:
endpoint: "jaeger:14250"
service:
pipelines:
traces:
receivers: [otlp]
exporters: [jaeger]
四、注意事项与最佳实践
采样策略:全量追踪会产生大量数据,建议对生产环境采用动态采样
# Jaeger采样配置 sampling: type: probabilistic param: 0.1 # 只收集10%的请求日志分级:合理使用DEBUG/INFO/WARN/ERROR级别
// 不好的写法 log.error("User logged in"); // 好的写法 if (failedAttempts > 3) { log.warn("Multiple login failures for user {}", username); }标签设计:Prometheus指标标签要谨慎选择,避免高基数问题
# 不好的标签(会导致指标爆炸) http_requests_total{path="/users/1234"} # 好的标签 http_requests_total{method="GET", status="200", route="/users/:id"}安全考虑:确保Elasticsearch和Jaeger有适当的访问控制
五、技术选型对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| ELK | 日志搜索能力强 | 资源消耗大 | 需要全文检索的场景 |
| Prometheus | 实时告警做得好 | 不支持长期存储 | 指标监控和告警 |
| Jaeger | 调用链可视化优秀 | 数据量大时成本高 | 微服务架构调试 |
六、总结
在Kubernetes中实现可观测性不是简单的工具堆砌,而是要建立完整的可观测性体系。通过本文的实践方案,你可以:
- 通过EFK快速定位错误日志
- 利用Prometheus发现性能瓶颈
- 使用Jaeger分析跨服务调用问题
记住,好的可观测性系统应该像汽车的仪表盘——不需要你时刻盯着,但在出现问题时能第一时间给出明确指示。
评论