Files
Qiniu/technical/k8s/k8s-08-observability.md
T

678 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
tags: [k8s, observability, logging, monitoring, devops]
create time: 2026-07-07 15:48
---
# Kubernetes 可观测性 — 日志、监控与健康检查
## 概述
可观测性(Observability)是理解系统内部状态的能力,在 Kubernetes 中通常由三大支柱构成:**Logging(日志)**、**Metrics(指标)** 和 **Tracing(链路追踪)**。日志告诉你「发生了什么」,指标告诉你「系统状态如何」,追踪告诉你「请求经过了哪里」。三者相互补充,缺一不可。在 K8s 环境中,由于 Pod 生命周期短暂、服务动态伸缩,传统的 SSH 进机器查日志方式不再适用,必须依赖系统化的可观测性方案。本文将从健康检查探针入手,逐步覆盖日志采集、指标监控和事件管理的完整实践。
---
## 健康检查(Probe)深入
Kubernetes 通过探针(Probe)机制持续监测容器的健康状态,自动完成故障隔离与自愈。这是 K8s 可观测性的第一道防线——不需要外部工具,原生能力就能实现基本的服务可用性保障。
### 三种探针详解
K8s 提供三种探针,各有分工:
| 探针 | 作用 | 失败后果 | 典型场景 |
|------|------|----------|----------|
| **livenessProbe** | 检测容器是否存活 | 杀死容器并重启(遵循 restartPolicy) | 应用死锁、无限循环 |
| **readinessProbe** | 检测容器是否就绪接收流量 | 从 Service 的 Endpoints 中摘除 | 依赖未就绪、预热加载中 |
| **startupProbe** | 检测容器是否完成启动 | 杀死容器并重启 | 慢启动应用(如 Java、大型模型加载) |
#### 配置参数说明
所有探针共享以下参数:
| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `initialDelaySeconds` | int | 0 | 容器启动后多久开始第一次探测 |
| `periodSeconds` | int | 10 | 探测间隔(秒) |
| `timeoutSeconds` | int | 1 | 探测超时时间(秒) |
| `failureThreshold` | int | 3 | 连续失败多少次后判定为不健康 |
| `successThreshold` | int | 1 | 连续成功多少次后恢复(liveness 固定为 1) |
> [!tip] startupProbe 与 livenessProbe 的协作
> 当 `startupProbe` 配置后,`livenessProbe` 和 `readinessProbe` 会在 startupProbe 成功之后才生效。这意味着你可以用一个宽松的 startupProbe(比如 `failureThreshold: 30`、`periodSeconds: 10`,即允许最多 300 秒启动时间),而 livenessProbe 保持较严格的配置,避免慢启动应用被误杀。
### 探测方式
K8s 支持四种探测机制,选择哪种取决于应用特性和暴露的端口。
#### exec — 执行命令探测
适用于:自定义健康检查逻辑、需要检查内部状态的场景(如数据库连接池)。
```yaml
livenessProbe:
exec:
command:
- /bin/sh
- -c
- "pg_isready -U postgres"
initialDelaySeconds: 15
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
```
容器内执行指定命令,退出码为 0 表示健康,非 0 表示不健康。适用于需要检查进程内部状态(如自定义脚本检测连接池)的场景。
#### httpGet — HTTP 请求探测
适用于:Web 服务、API 服务,最常用的探测方式。
```yaml
livenessProbe:
httpGet:
path: /healthz
port: 8080
httpHeaders:
- name: X-Custom-Header
value: "k8s-probe"
initialDelaySeconds: 10
periodSeconds: 15
timeoutSeconds: 3
failureThreshold: 3
```
向容器的指定端口和路径发起 HTTP GET 请求,2xx/3xx 状态码视为健康。建议为健康检查设计轻量级的专用端点,避免在 `/` 上做复杂业务校验。
#### tcpSocket — TCP 端口探测
适用于:非 HTTP 协议的 TCP 服务(如数据库、Redis、gRPC 服务端口)。
```yaml
readinessProbe:
tcpSocket:
port: 3306
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
```
尝试建立 TCP 连接,连接成功即视为健康。适合 MySQL、Redis 等数据库服务,或尚不支持 HTTP 健康检查端点的遗留服务。
#### gRPC — gRPC 健康检查探测
适用于:gRPC 微服务(K8s 1.24+ GA)。
```yaml
readinessProbe:
grpc:
port: 50051
# 可选:指定服务名
# service: "my-grpc-service"
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
```
利用 gRPC 标准的 [Health Checking Protocol](https://github.com/grpc/grpc/blob/master/doc/health-checking.md) 进行探测,比 httpGet 更自然地适配 gRPC 服务。需要应用实现 `grpc.health.v1.Health` 服务。
### 探针设计原则
> [!tip] 原则一:慢启动应用必须用 startupProbe
> Java 应用、机器学习模型加载、大型缓存预热等场景,启动时间可能超过 livenessProbe 的 `initialDelaySeconds`。此时应该用 startupProbe 兜底,而不是无限增大 `initialDelaySeconds`——后者会导致所有重启都白白等待固定时长。
```yaml
# 典型的慢启动应用探针配置
startupProbe:
httpGet:
path: /healthz
port: 8080
failureThreshold: 30 # 允许最多 30 次失败
periodSeconds: 10 # 每 10 秒检查一次
# 总共允许 30 × 10 = 300 秒启动时间
livenessProbe:
httpGet:
path: /healthz
port: 8080
periodSeconds: 15
timeoutSeconds: 3
failureThreshold: 3 # 启动后 3 次失败即重启
```
> [!tip] 原则二:readiness ≠ liveness,职责不能混
> - **readinessProbe**:「我准备好接收请求了吗?」——可以是暂时性的(如依赖 Redis 还没连上),摘除流量后恢复即可重新加入。
> - **livenessProbe**:「我还活着吗?」——失败意味着进程级故障(死锁、崩溃),需要重启。
> - 常见错误:把依赖检查(如数据库连接)放在 livenessProbe 里。数据库短暂不可达会导致 Pod 被重启,反而加重问题。
> [!tip] 原则三:健康端点要轻量
> livenessProbe 的 `/healthz` 端点不应该检查下游依赖(数据库、Redis 等),否则下游抖动会导致 Pod 被误杀。readinessProbe 可以做适度的依赖检查,但也要控制超时。
> [!warning] 避免探针与应用抢占资源
> exec 探针会 fork 子进程,在资源受限的容器中可能加剧 OOM。高并发场景下优先使用 httpGet 或 tcpSocket。
#### 探针决策流程图
```mermaid
flowchart TD
A[应用需要健康检查] --> B{启动时间 > 30 秒?}
B -->|是| C[配置 startupProbe]
B -->|否| D{需要检查依赖?}
C --> D
D -->|是, 如 DB/Redis| E[readinessProbe 检查依赖]
D -->|否| F[readinessProbe 基础端口/路径]
E --> G{livenessProbe 如何配置?}
F --> G
G --> H[仅检查进程存活<br>不检查下游依赖]
H --> I{协议类型?}
I -->|HTTP| J[httpGet /healthz]
I -->|gRPC| K[grpc health check]
I -->|TCP/数据库| L[tcpSocket port]
I -->|自定义逻辑| M[exec command]
```
---
## 日志管理
### K8s 日志架构
在 Kubernetes 中,日志的生命周期跨越三个层级:容器层、节点层、集群层。理解这个链路是做好日志管理的前提。
```mermaid
flowchart LR
subgraph 容器层
A[应用进程] -->|stdout / stderr| B[容器运行时<br>CRI]
end
subgraph 节点层
B -->|写入文件| C["/var/log/pods/"]
C --> D[kubelet 日志轮转]
end
subgraph 集群层
D --> E[日志采集 Agent<br>Fluentd / Promtail / Filebeat]
E --> F[存储后端<br>Elasticsearch / Loki / S3]
F --> G[查询 UI<br>Kibana / Grafana]
end
A -.->|Sidecar 模式| H[日志 Sidecar 容器]
H --> F
```
K8s 推荐的最佳实践是:**应用将日志输出到 stdout/stderr**,由 kubelet 负责写入节点磁盘,再由集群级采集工具统一收集。这样做解耦了应用和日志基础设施,应用无需关心日志的存储和传输。
### 节点级日志
#### 目录结构
kubelet 将每个容器的 stdout/stderr 写入节点上的日志文件,路径格式为:
```
/var/log/pods/<namespace>_<pod-name>_<pod-uid>/<container-name>/<restart-count>.log
```
实际示例:
```
/var/log/pods/default_nginx-7d8b49557c-abc123_5d1f3a2e-1234-5678-abcd-ef0123456789/
├── nginx/
│ ├── 0.log # 第 0 次启动的日志
│ └── 1.log # 第 1 次重启后的日志
└── nginx/
└── 0.log -> /var/log/containers/nginx-xxx.log # 符号链接
```
`/var/log/containers/` 目录下的文件是指向 `/var/log/pods/` 的符号链接,文件名包含容器名和容器 ID,方便按容器名查找。
#### 日志轮转(Log Rotation)
kubelet 通过以下参数控制日志轮转(配置在 kubelet 的 `--container-log-*` 参数中):
| 参数 | 说明 | 默认值 |
|------|------|--------|
| `--container-log-max-size` | 单个日志文件最大大小 | 10Mi |
| `--container-log-max-files` | 每个容器保留的日志文件数 | 5 |
在 `KubeletConfiguration` 中配置:
```yaml
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
containerLogMaxSize: "50Mi"
containerLogMaxFiles: 10
```
> [!warning] 节点磁盘空间
> 日志量大的应用(如高并发网关)可能快速占满磁盘。务必设置合理的轮转策略,并配合监控节点磁盘使用率(`node_filesystem_avail_bytes` 指标)。
### 集群级方案
#### 方案对比
| 维度 | EFK(Elasticsearch + Fluentd + Kibana) | Loki + Promtail + Grafana |
|------|------------------------------------------|---------------------------|
| **存储模型** | 全文索引,存储成本高 | 标签索引 + 压缩块存储,成本低 |
| **查询语言** | KQL / Lucene | LogQL(类 PromQL 语法) |
| **全文搜索** | 原生支持,性能好 | 不支持原生全文搜索,需配合标签 |
| **资源消耗** | 高(JVM 内存、磁盘 IO) | 低(Go 二进制,无全文索引) |
| **与 Metrics 集成** | 需额外对接 Prometheus | 天然与 Prometheus/Grafana 生态融合 |
| **适用场景** | 大规模日志分析、全文检索需求强 | 资源敏感、与 Prometheus 混合部署 |
| **社区趋势** | 成熟但逐渐被替代 | CNCF 毕业项目,增长迅速 |
#### EFK 架构
```mermaid
flowchart LR
A[Pod stdout/stderr] --> B[节点日志文件]
B --> C[Fluentd DaemonSet]
C --> D[Elasticsearch]
D --> E[Kibana]
E --> F[用户查询]
```
Fluentd 以 DaemonSet 形式部署在每个节点上,tail 节点日志文件并推送到 Elasticsearch。
#### Loki + Promtail 架构
```mermaid
flowchart LR
A[Pod stdout/stderr] --> B[节点日志文件]
B --> C[Promtail DaemonSet]
C --> D[Loki]
D --> E[Grafana]
E --> F[用户查询]
```
Promtail 同样以 DaemonSet 部署,但它只提取标签(namespace、pod、container)和日志行,发送给 Loki 进行存储。Loki 的核心设计理念是「like Prometheus, but for logs」——只对标签建索引,日志内容以压缩块存储。
### 结构化日志
非结构化日志(纯文本)在大规模集群中难以高效查询。结构化日志以 JSON 格式输出,便于日志系统自动解析和过滤。
#### JSON 日志输出示例(Go / zap)
```go
import "go.uber.org/zap"
logger, _ := zap.NewProduction()
defer logger.Sync()
logger.Info("request processed",
zap.String("method", "GET"),
zap.String("path", "/api/v1/users"),
zap.Int("status", 200),
zap.Duration("latency", 150*time.Millisecond),
zap.String("request_id", "abc-123"),
)
```
输出:
```json
{"level":"info","ts":1720340880.123,"msg":"request processed","method":"GET","path":"/api/v1/users","status":200,"latency":0.15,"request_id":"abc-123"}
```
#### 结构化日志最佳实践
1. **统一字段命名**:团队内约定日志字段名(如 `request_id` 而非 `reqId` / `rid`),便于跨服务聚合。
2. **必须包含的字段**:时间戳(`ts`)、日志级别(`level`)、消息(`msg`)、请求 ID(`request_id`)、服务名(`service`)。
3. **避免在日志中输出敏感信息**:密码、Token、身份证号等必须脱敏。
4. **日志级别控制**:生产环境默认 `info`,可通过环境变量动态调整为 `debug` 以便排查问题。
> [!tip] kubectl 查看结构化日志
> 使用 `kubectl logs` 结合 `jq` 可以快速过滤 JSON 日志:
> ```bash
> kubectl logs my-pod -c my-container | jq 'select(.level == "error")'
> kubectl logs my-pod -c my-container | jq 'select(.latency > 0.5)'
> ```
---
## 指标监控
### Metrics Server
Metrics Server 是 Kubernetes 内置的轻量级指标采集组件,提供 CPU 和内存使用数据,是 `kubectl top` 和 HPA(基于资源指标)的基础设施。
#### 工作原理
```mermaid
flowchart LR
A[kubelet] -->|Summary API| B[Metrics Server]
B -->|Metrics API| C[kubectl top]
B -->|Metrics API| D[HPA Controller]
```
- 每个 kubelet 暴露 **Summary API**(`/stats/summary`),包含该节点上所有 Pod 的 CPU/内存使用数据。
- Metrics Server 定期(默认 15 秒)从所有 kubelet 拉取数据,聚合后通过 **Metrics API**(`metrics.k8s.io`)暴露。
#### 安装
```bash
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
```
验证:
```bash
kubectl top nodes
kubectl top pods -n default
```
#### 局限性
- 只提供 CPU 和内存指标,不支持磁盘、网络等。
- 数据只保留短期窗口(约 1 分钟),不支持历史查询。
- 不支持自定义指标——需要 Prometheus 等外部系统。
> [!info] Metrics Server vs Prometheus
> Metrics Server 是 K8s 内置的「最小可用」指标方案,适合 `kubectl top` 和基于资源的 HPA。如果你需要更丰富的指标、历史数据和告警,必须部署 Prometheus。
### Prometheus + Grafana
Prometheus 是 CNCF 毕业项目,是 Kubernetes 生态中最主流的监控方案。Grafana 作为可视化层,提供丰富的仪表盘。
#### 架构概览
```mermaid
flowchart LR
subgraph K8s 集群
A[应用 /export_metrics] -->|pull| B[Prometheus Server]
C[kube-state-metrics] -->|pull| B
D[Node Exporter] -->|pull| B
E[ServiceMonitor CRD] -.->|配置发现| B
end
B --> F[Grafana]
B --> G[Alertmanager]
G --> H[邮件 / Slack / 钉钉]
B --> I[长期存储<br>Thanos / VictoriaMetrics]
```
核心组件:
- **Prometheus Server**:时序数据库 + 采集引擎,定期从 targets 拉取(pull)指标。
- **kube-state-metrics**:将 K8s 对象状态(Pod、Deployment、Node 等)转换为 Prometheus 指标。
- **Node Exporter**:采集节点级系统指标(CPU、内存、磁盘、网络)。
- **ServiceMonitor CRD**:由 Prometheus Operator 提供,以声明式方式配置采集目标。
- **Alertmanager**:告警路由、去重、分组、静默。
#### ServiceMonitor 示例
```yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: my-app
namespace: monitoring
labels:
release: prometheus # 匹配 Prometheus 实例的标签选择器
spec:
namespaceSelector:
matchNames:
- default
selector:
matchLabels:
app: my-app # 匹配目标 Service 的标签
endpoints:
- port: metrics # Service 中定义的端口名
path: /metrics
interval: 15s
```
> [!info] ServiceMonitor 的工作方式
> Prometheus Operator 会 watch ServiceMonitor 资源的变化,自动更新 Prometheus 的采集配置。运维人员不再需要手写 `prometheus.yml` 的 scrape 配置,实现了采集配置的 GitOps 管理。
#### 常见 K8s 监控指标
| 指标名称 | 含义 | 告警建议 |
|----------|------|----------|
| `container_cpu_usage_seconds_total` | 容器累计 CPU 使用时间 | 持续 > 80% requests 告警 |
| `container_memory_working_set_bytes` | 容器内存工作集 | 接近 limits 时告警 |
| `kube_pod_restart_total` | Pod 重启次数 | 短时间内重启 > 3 次告警 |
| `kube_pod_status_phase` | Pod 状态(Pending/Running/Failed) | Pending > 5 分钟告警 |
| `kube_node_status_condition` | 节点状态(Ready/NotReady) | NotReady 告警 |
| `kube_deployment_status_replicas_available` | Deployment 可用副本数 | 小于期望副本数告警 |
| `node_filesystem_avail_bytes` | 节点可用磁盘空间 | < 10% 剩余告警 |
### HPA 自定义指标
HPA 默认支持基于 CPU/Memory 的扩缩容,但实际场景中,基于请求速率(QPS)、队列长度、延迟等自定义指标更有意义。
#### 前置条件
1. 安装 Prometheus Adapter(将 Prometheus 指标转换为 K8s Custom Metrics API):
```bash
helm install prometheus-adapter prometheus-community/prometheus-adapter \
--namespace monitoring \
--set prometheus.url=http://prometheus-server.monitoring.svc
```
2. 确认 Custom Metrics API 可用:
```bash
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1" | jq .
```
#### HPA 配置示例(基于 HTTP 请求速率)
```yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: my-app-hpa
namespace: default
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
minReplicas: 2
maxReplicas: 20
metrics:
# 自定义指标:每 Pod 每秒请求数
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: "100" # 每个 Pod 目标 100 QPS
behavior:
scaleUp:
stabilizationWindowSeconds: 30
policies:
- type: Percent
value: 50
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300 # 缩容冷却 5 分钟,避免抖动
policies:
- type: Percent
value: 25
periodSeconds: 60
```
> [!tip] 扩缩容行为配置
> `behavior` 字段(K8s 1.18+)允许精细控制扩缩容速度。建议扩容激进(快速应对流量高峰)、缩容保守(`stabilizationWindowSeconds` 设大,避免频繁抖动导致连接中断)。
---
## 事件(Events)
Kubernetes Events 是集群中发生的重大状态变更记录,是排查问题的第一手信息。
### 查看事件
```bash
# 当前 namespace 的事件,按时间排序
kubectl get events --sort-by='.lastTimestamp'
# 所有 namespace 的事件
kubectl get events -A
# 只看 Warning 级别事件
kubectl get events --field-selector type=Warning
# 查看特定 Pod 的事件
kubectl describe pod my-pod
```
### 常见事件类型
| Reason | 类型 | 含义 |
|--------|------|------|
| `Scheduled` | Normal | Pod 成功调度到节点 |
| `Pulling` / `Pulled` | Normal | 正在拉取 / 已拉取镜像 |
| `Created` / `Started` | Normal | 容器已创建 / 已启动 |
| `Unhealthy` | Warning | 健康检查失败 |
| `BackOff` | Warning | 重启退避(CrashLoopBackOff) |
| `FailedScheduling` | Warning | 调度失败(资源不足、亲和性冲突等) |
| `OOMKilling` | Warning | 容器因内存不足被杀死 |
| `Evicted` | Warning | Pod 被驱逐(磁盘压力、内存压力等) |
### 事件 TTL 与持久化
> [!warning] Events 会被自动清理
> 默认情况下,Events 的 TTL(Time To Live)为 **1 小时**。这意味着如果一个 Pod 在 2 小时前 OOMKilled,你通过 `kubectl describe pod` 看不到相关事件。
解决方法:
1. **调整 TTL**(K8s 1.19+):
```bash
# 在 kube-apiserver 中设置
--event-ttl=24h
```
2. **持久化到日志系统**:将 Events 采集到 Elasticsearch/Loki 等系统中长期保存。
- 使用 kube-state-metrics 的 `kube_event_*` 指标采集到 Prometheus。
- 使用专门的 Events 采集工具(如 `event-exporter`)推送到日志系统。
3. **event-exporter 示例**:
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: event-exporter
namespace: monitoring
spec:
template:
spec:
containers:
- name: event-exporter
image: ghcr.io/resmoio/kubernetes-event-exporter:latest
args:
- -conf=/data/config.yaml
volumeMounts:
- name: config
mountPath: /data
volumes:
- name: config
configMap:
name: event-exporter-config
```
---
## 常见陷阱与最佳实践
### 陷阱一:日志量爆炸
**问题**:高并发服务的 debug 日志未关闭,一天产生几十 GB 日志,磁盘告警或 Elasticsearch 索引膨胀。
**解决方案**:
- 生产环境默认 `info` 级别,通过环境变量或配置中心动态调整。
- 使用 Fluentd/Promtail 的 filter 功能丢弃无用日志(如 kube-system 的健康检查日志)。
- 设置 Elasticsearch 的 ILM(Index Lifecycle Management)策略,自动过期删除旧索引。
```yaml
# Fluentd filter 示例:丢弃 kube-system 命名空间的日志
<filter kubernetes.**>
@type grep
<exclude>
key $.kubernetes.namespace_name
pattern /^kube-system$/
</exclude>
</filter>
```
### 陷阱二:探针超时与应用启动时间不匹配
**问题**:Java 应用启动需要 60 秒,但 livenessProbe 的 `initialDelaySeconds` 只设了 30 秒,导致每次重启都进入 CrashLoopBackOff。
**解决方案**:使用 startupProbe 代替盲目增大 `initialDelaySeconds`(详见探针设计原则一)。
### 陷阱三:监控指标标签爆炸
**问题**:在 Prometheus 指标中使用 `user_id`、`request_path` 等高基数(high cardinality)标签,导致时间序列数暴涨,Prometheus 内存 OOM。
**解决方案**:
- 高基数字段(如 request_id、user_id)放到日志中,不作为指标标签。
- `request_path` 应归一化(如 `/api/v1/users/123` → `/api/v1/users/:id`)。
- 使用 Prometheus 的 `metric_relabel_configs` 丢弃不需要的标签。
```yaml
# Prometheus 配置:限制标签基数
metric_relabel_configs:
- source_labels: [__name__]
regex: "container_.*"
action: keep
- regex: "pod_name"
action: labeldrop
```
### 陷阱四:readinessProbe 失败但 livenessProbe 正常
**问题**:readinessProbe 依赖 Redis,Redis 短暂不可达,Pod 被从 Endpoints 摘除。但 livenessProbe 正常,Pod 不会被重启。流量被摘除后 Pod 中积压的请求超时,用户报错。
**解决方案**:
- readinessProbe 的依赖检查要设置合理的超时(如 2 秒内无响应即判定失败)。
- 在应用内部实现熔断机制,不要完全依赖 K8s 探针。
- 确保 readinessProbe 恢复时,Pod 能快速重新加入 Endpoints(`successThreshold: 1`)。
### 陷阱五:kubectl top 数据不准
**问题**:`kubectl top pods` 显示的 CPU 使用率为 0 或与实际不符。
**原因**:
- Metrics Server 启动后需要约 1-2 分钟才能采集到数据。
- 容器的 CPU request 未设置,百分比无法计算。
- Metrics Server 的 `--kubelet-insecure-tls` 未配置导致与 kubelet 通信失败。
**解决方案**:
```bash
# 检查 Metrics Server 是否正常运行
kubectl get deployment metrics-server -n kube-system
# 检查是否有错误日志
kubectl logs -n kube-system deployment/metrics-server
# 使用 --resource 选项查看原始数值而非百分比
kubectl top pods --containers
```
### 最佳实践汇总
| 领域 | 实践 |
|------|------|
| 日志 | stdout 输出、JSON 结构化、统一字段命名、合理轮转 |
| 探针 | startupProbe 兜底慢启动、liveness 不查依赖、readiness 做适度依赖检查 |
| 监控 | Prometheus + Grafana 标准栈、ServiceMonitor 声明式配置 |
| 告警 | 分级告警(Warning/Critical)、去重分组、值班轮转 |
| 指标 | 避免高基数标签、区分 RED(Rate/Error/Duration)指标与 USE(Utilization/Saturation/Errors)指标 |
| 事件 | 配置持久化、重要事件告警 |
---
## 延伸阅读
- [[k8s-03-pod]] — Pod 生命周期、调度与资源管理
- [[k8s-02-architecture]] — K8s 控制平面与节点组件架构
- [Kubernetes 官方文档 - Logging Architecture](https://kubernetes.io/docs/concepts/cluster-administration/logging/)
- [Kubernetes 官方文档 - Configure Liveness, Readiness and Startup Probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/)
- [Prometheus Operator 文档](https://prometheus-operator.dev/)
- [Grafana Loki 官方文档](https://grafana.com/docs/loki/latest/)
- [Google SRE Book - Monitoring Distributed Systems](https://sre.google/sre-book/practical-alerting/)