Skip to content

K8s 最佳实践与综合面试题

面试官:如果让你从零开始在 K8s 上部署一个 Java 微服务,你会怎么做?

:我会创建一个完整的部署清单,包括 Deployment(管理副本)、Service(服务发现)、ConfigMap/Secret(配置分离)、资源配额(Request/Limit)、探针(健康检查)、HPA(弹性伸缩)、反亲和性(高可用)等关键配置,确保服务稳定、可观测、易维护。

面试官:具体配置如何设计?资源配额如何估算?如何保证零停机?

这是一道综合性问题,考察对 K8s 的整体把握,能说清每个配置项背后原理的候选人,才具备生产实战能力。


链式追问一:微服务上 K8s 完整清单

Section titled “链式追问一:微服务上 K8s 完整清单”

Q1:一个完整的 Java 微服务 K8s 部署清单包含哪些要素?必考

Section titled “Q1:一个完整的 Java 微服务 K8s 部署清单包含哪些要素?”

七要素完整配置

# 要素 1:Deployment(管理 Pod 副本)
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
labels:
app: order-service
spec:
replicas: 3
selector:
matchLabels:
app: order-service
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1 # 滚动更新:最多 1 个 Pod 不可用
maxSurge: 1 # 滚动更新:最多超配 1 个 Pod
# 要素 2:Pod 模板
template:
metadata:
labels:
app: order-service
version: v1.2.0
spec:
# 要素 6:调度策略(Pod 反亲和,避免单点)
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: order-service
topologyKey: kubernetes.io/hostname
# 优雅关闭
terminationGracePeriodSeconds: 60
containers:
- name: order-service
image: registry.example.com/order-service:1.2.0
ports:
- containerPort: 8080
# 要素 2:资源配额(必须设置!)
resources:
requests:
cpu: "500m" # 调度保证:500m(0.5 核)
memory: "512Mi" # 调度保证:512Mi
limits:
cpu: "1000m" # 运行上限:1 核
memory: "1Gi" # 运行上限:1Gi
# 要素 3:环境变量(从 ConfigMap/Secret 注入)
env:
- name: SPRING_PROFILES_ACTIVE
value: "prod"
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: order-config
key: db-host
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password
# 要素 4:探针(零停机发布的关键)
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 60 # 启动后 60s 开始探测
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 30
periodSeconds: 5
failureThreshold: 3
startupProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 30 # 150s 内启动完成
# 要素 4:优雅关闭钩子
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 15"]
# 挂载配置
volumeMounts:
- name: config-volume
mountPath: /app/config
volumes:
- name: config-volume
configMap:
name: order-config
---
# 要素 3:ConfigMap(普通配置)
apiVersion: v1
kind: ConfigMap
metadata:
name: order-config
data:
application.yml: |
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://mysql:3306/orders
username: order_user
---
# 要素 3:Secret(敏感信息)
apiVersion: v1
kind: Secret
metadata:
name: db-secret
type: Opaque
data:
password: cGFzc3dvcmQ= # echo -n "password" | base64
---
# 要素 5:Service(服务发现)
apiVersion: v1
kind: Service
metadata:
name: order-service
spec:
type: ClusterIP
selector:
app: order-service
ports:
- port: 80
targetPort: 8080
---
# 要素 7:HPA(弹性伸缩)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: order-service-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: order-service
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
---
# ServiceAccount + RBAC(安全配置)
apiVersion: v1
kind: ServiceAccount
metadata:
name: order-service-sa
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: order-service-role
rules:
- apiGroups: [""]
resources: ["configmaps", "secrets"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: order-service-rolebinding
subjects:
- kind: ServiceAccount
name: order-service-sa
roleRef:
kind: Role
name: order-service-role
apiGroup: rbac.authorization.k8s.io

七要素清单总结

要素配置项作用缺失后果
Deploymentreplicas、strategy管理副本、滚动更新无法扩缩容、更新失败无法回滚
Resourcesrequests、limits资源配额、调度依据节点资源耗尽、OOM Kill、HPA 失效
Probeliveness、readiness、startup健康检查、零停机Pod 死锁不重启、滚动更新流量失败
优雅关闭preStop、terminationGracePeriod优雅关闭连接请求中断、数据丢失
配置分离ConfigMap、Secret配置与镜像分离镜像包含敏感信息、环境切换困难
反亲和性podAntiAffinity多副本分散节点单节点故障导致服务全挂
HPAminReplicas、maxReplicas弹性伸缩流量突增时服务雪崩

本质一句话:完整的 K8s 部署清单不仅是 Pod 运行,更包括资源管理、健康检查、配置分离、高可用、弹性伸缩,是生产级服务的基石。


Q2:ConfigMap 和 Secret 的区别?如何安全使用 Secret?必考

Section titled “Q2:ConfigMap 和 Secret 的区别?如何安全使用 Secret?”

ConfigMap vs Secret 对比

维度ConfigMapSecret
用途普通配置(端点、参数、环境变量)敏感信息(密码、Token、证书)
存储格式明文存储在 etcdBase64 编码存储在 etcd
大小限制1MB(单个 ConfigMap)1MB(单个 Secret)
挂载方式环境变量 / 文件挂载环境变量 / 文件挂载
安全性无加密Base64(非加密),需配合 etcd 加密
更新机制更新后 Pod 需重启或热加载同 ConfigMap

Secret 类型

# 1. Opaque(通用 Secret,自定义数据)
apiVersion: v1
kind: Secret
metadata:
name: db-secret
type: Opaque
data:
password: cGFzc3dvcmQ= # Base64 编码
# 2. kubernetes.io/dockerconfigjson(镜像拉取密钥)
kubectl create secret docker-registry regcred \
--docker-server=<your-registry-server> \
--docker-username=<your-username> \
--docker-password=<your-password>
# 3. kubernetes.io/tls(TLS 证书)
kubectl create secret tls tls-secret \
--cert=path/to/tls.crt \
--key=path/to/tls.key
# 4. ServiceAccount Token(自动创建)
# 每个命名空间默认创建 default serviceaccount

Secret 安全最佳实践

1. etcd 加密(Encryption at Rest)
┌─────────────────────────────────┐
│ Secret 明文(Base64) │
└──────────┬──────────────────────┘
│ KMS 插件加密
┌─────────────────────────────────┐
│ etcd 存储(加密后) │
└─────────────────────────────────┘
配置:
apiServer:
encryption-provider-config: /etc/kubernetes/encryption-config.yaml
encryption-config.yaml:
resources:
- resources: ["secrets"]
providers:
- aescbc:
keys:
- name: key1
secret: <base64-encoded-32-byte-key>
- identity: {}
效果:Secret 在 etcd 中加密存储,防止 etcd 泄露导致密钥泄露
2. RBAC 限制访问
├── 最小权限原则:只允许特定 ServiceAccount 读取 Secret
└── 避免使用 default serviceaccount(权限过大)
示例:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: secret-reader
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["db-secret"] # 只允许读取特定 Secret
verbs: ["get"]
3. 外部密钥管理系统(Vault、AWS Secrets Manager)
┌──────────────────────────────────┐
│ 应用启动时读取外部密钥管理系统 │
│ └── HashiCorp Vault / AWS SM │
└──────────┬───────────────────────┘
│ API 调用
┌──────────────────────────────────┐
│ 应用内存中持有密钥 │
│ (不写入 K8s Secret) │
└──────────────────────────────────┘
优势:
✓ Secret 不存储在 K8s(更安全)
✓ 密钥轮换自动化
✓ 审计日志
4. 避免环境变量注入(推荐文件挂载)
├── 环境变量泄露风险:ps -ef、日志、崩溃转储
└── 文件挂载更安全:只挂载到容器内,不易泄露
配置:
volumes:
- name: secret-volume
secret:
secretName: db-secret
volumeMounts:
- name: secret-volume
mountPath: /app/secrets
readOnly: true

实战案例

# 完整的 Secret 安全配置
apiVersion: v1
kind: Secret
metadata:
name: db-secret
annotations:
# 使用 KMS 加密
kms.aws/encrypted: "true"
type: Opaque
data:
password: cGFzc3dvcmQ=
---
# Pod 使用 Secret(文件挂载)
apiVersion: v1
kind: Pod
metadata:
name: my-app
spec:
serviceAccountName: app-sa # 使用专用 ServiceAccount
containers:
- name: app
image: myapp:1.0
volumeMounts:
- name: secret-volume
mountPath: /app/secrets
readOnly: true # 只读挂载
volumes:
- name: secret-volume
secret:
secretName: db-secret
items:
- key: password
path: db-password.txt

本质一句话:ConfigMap 存普通配置,Secret 存敏感信息,但 Secret 的 Base64 不是加密,生产环境需配合 etcd 加密、RBAC 限制、外部密钥管理系统。


链式追问二:调度策略与高可用

Section titled “链式追问二:调度策略与高可用”

Q3:K8s 调度器如何决定 Pod 放在哪个节点?必考

Section titled “Q3:K8s 调度器如何决定 Pod 放在哪个节点?”

调度流程三步

K8s 调度流程:
1. 预选(Predicates):过滤不合格节点
├── PodFitsResources:节点剩余资源 ≥ Pod Request
├── PodFitsHostPorts:节点端口未被占用
├── PodMatchNodeSelector:节点标签匹配 nodeSelector
├── PodToleratesNodeTaints:Pod 容忍节点污点
├── CheckNodeCondition:节点状态正常(Ready)
└── PodAffinityFilter:满足 Pod 亲和性/反亲和性
结果:候选节点列表(可能为空 → Pod Pending)
2. 优选(Priorities):为候选节点打分
├── LeastRequestedPriority:资源空闲最多的节点得分高
│ └── score = (node.capacity - node.requested) / node.capacity
├── BalancedResourceAllocation:CPU 和内存使用均衡
├── ImageLocalityPriority:节点已有镜像的得分高
├── NodeAffinityPriority:节点亲和性权重
└── TaintTolerationPriority:污点容忍权重
结果:每个候选节点一个分数(0-100)
3. 绑定(Bind):选择最高分节点
├── 如果多个节点同分 → 随机选择
└── 将 Pod 绑定到节点(更新 Pod.spec.nodeName)

调度器配置示例

# 节点选择(nodeSelector)
spec:
nodeSelector:
disk-type: ssd # 节点必须有 label: disk-type=ssd
---
# 节点亲和性(nodeAffinity,更灵活)
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 硬性要求
nodeSelectorTerms:
- matchExpressions:
- key: zone
operator: In
values: [cn-hangzhou-a, cn-hangzhou-b]
preferredDuringSchedulingIgnoredDuringExecution: # 软性偏好
- weight: 80
preference:
matchExpressions:
- key: disk-type
operator: In
values: [ssd]

污点(Taint)与容忍(Toleration)

污点(Taint):标记节点专用
场景 1:GPU 节点专用
kubectl taint nodes gpu-node nvidia.com/gpu=true:NoSchedule
└── 普通Pod无法调度到GPU节点
只有配置了容忍的Pod才能调度:
tolerations:
- key: "nvidia.com/gpu"
operator: "Exists"
effect: "NoSchedule"
场景 2:节点维护
kubectl taint nodes node-1 key=value:NoExecute
└── NoExecute:不容忍的Pod立即驱逐
污点 Effect 类型:
├── NoSchedule:不容忍的Pod不能调度(已存在的Pod不受影响)
├── PreferNoSchedule:尽量不调度(软性约束)
└── NoExecute:不容忍的Pod立即驱逐
容忍(Toleration)配置:
tolerations:
- key: "key1"
operator: "Equal" # 或 Exists(不比较value)
value: "value1"
effect: "NoSchedule"

Pod 亲和性/反亲和性

# Pod 反亲和性:避免同一应用的 Pod 聚集在同一节点
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 硬性要求
- labelSelector:
matchLabels:
app: order-service
topologyKey: kubernetes.io/hostname
# 含义:同一节点(hostname)不能有两个 app=order-service 的 Pod
# Pod 亲和性:相关服务调度到同一节点(减少网络延迟)
affinity:
podAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: cache-service
topologyKey: kubernetes.io/hostname
# 含义:尽量调度到有 cache-service 的节点

拓扑分布约束(TopologySpreadConstraints,K8s 1.19+)

# 更精细的 Pod 分布控制
topologySpreadConstraints:
- maxSkew: 1 # 最大偏差 1 个 Pod
topologyKey: zone # 按可用区分布
whenUnsatisfiable: DoNotSchedule # 无法满足时不调度
labelSelector:
matchLabels:
app: order-service
# 效果:
# zone-a: 2 Pod
# zone-b: 3 Pod
# zone-c: 2 Pod
# 最大偏差 = max(3-2, 2-2) = 1(满足要求)

本质一句话:调度器通过预选过滤不合格节点,优选打分选择最优节点,支持 nodeSelector、亲和性、污点容忍等策略实现精细化调度控制。


Q4:如何保证 Pod 分散在不同节点(高可用)?高频

Section titled “Q4:如何保证 Pod 分散在不同节点(高可用)?”

高可用部署策略

高可用核心:避免单点故障
单节点故障影响:
├── 无反亲和性:3个Pod全在node-1 → node-1故障 → 服务全挂
└── 有反亲和性:Pod分散在node-1/2/3 → node-1故障 → 剩余2个Pod服务正常
实现方式:
1. Pod 反亲和性(PodAntiAffinity)
2. 拓扑分布约束(TopologySpreadConstraints)
3. PodDisruptionBudget(PDB,防止同时驱逐过多Pod)

方案对比

方案优点缺点适用场景
Pod 反亲和性简单、K8s 原生支持只能按节点维度,无法精确控制数量小规模集群(<100节点)
拓扑分布约束精确控制偏差、支持多维度(节点/可用区)需 K8s 1.19+大规模集群、多可用区部署
PodDisruptionBudget防止节点维护时服务中断只能防止主动驱逐,不能防止节点故障所有生产服务

配置示例

# 方案 1:Pod 反亲和性(硬性要求)
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: order-service
topologyKey: kubernetes.io/hostname
# 含义:每个节点最多 1 个 order-service Pod
# 局限:如果 replicas > 节点数,Pod 无法调度(Pending)
---
# 方案 2:Pod 反亲和性(软性偏好)
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: order-service
topologyKey: kubernetes.io/hostname
# 含义:尽量分散,实在不行也可以聚集
# 优点:replicas > 节点数时仍可调度
---
# 方案 3:拓扑分布约束(推荐)
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: order-service
# 效果:每个节点的 Pod 数量差异不超过 1
---
# 方案 4:多可用区部署
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone # 按可用区分布
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: order-service
# 效果:每个可用区的 Pod 数量均衡

PodDisruptionBudget(PDB)

# PDB:防止节点维护时服务中断
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: order-service-pdb
spec:
minAvailable: 2 # 至少保持 2 个 Pod 运行
selector:
matchLabels:
app: order-service
# 或使用 maxUnavailable
spec:
maxUnavailable: 1 # 最多允许 1 个 Pod 不可用
# 作用:
# kubectl drain node-1(驱逐节点上的Pod)
# → PDB 检查:如果驱逐后可用Pod < minAvailable → 拒绝驱逐
# → 确保滚动更新/节点维护时服务可用

实战案例

场景:订单服务 3 副本,部署在 3 个可用区
节点分布:
zone-a: node-1, node-2
zone-b: node-3, node-4
zone-c: node-5, node-6
配置:
replicas: 3
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
调度结果:
zone-a: node-1 → order-service-1
zone-b: node-3 → order-service-2
zone-c: node-5 → order-service-3
故障模拟:
node-1 故障 → order-service-1 被调度到 zone-a 的 node-2
zone-a 故障 → order-service-1 被调度到 zone-b 或 zone-c
效果:
✓ 单节点故障:服务正常(剩余2个Pod)
✓ 单可用区故障:服务正常(剩余2个Pod在其他可用区)

本质一句话:通过 Pod 反亲和性或拓扑分布约束将 Pod 分散在不同节点/可用区,配合 PDB 防止主动驱逐导致服务中断,实现真正的高可用。


Q5:K8s 如何做监控和日志收集?必考

Section titled “Q5:K8s 如何做监控和日志收集?”

监控技术栈(Prometheus + Grafana)

监控架构:
┌──────────────────────────────────────┐
│ 应用暴露 /actuator/prometheus │
│ 指标: │
│ - http_requests_total │
│ - jvm_memory_used_bytes │
│ - hikaricp_connections_active │
└──────────────┬───────────────────────┘
│ Prometheus 拉取(每 15s)
┌──────────────────────────────────────┐
│ Prometheus │
│ ├── 时序数据库(TSDB) │
│ ├── PromQL 查询语言 │
│ └── 告警规则(Alerting Rules) │
└──────────────┬───────────────────────┘
│ 数据查询
┌──────────────────────────────────────┐
│ Grafana │
│ ├── 可视化仪表盘 │
│ ├── 告警通知(邮件/钉钉/Slack) │
│ └── 多数据源支持 │
└──────────────┬───────────────────────┘
│ 告警触发
┌──────────────────────────────────────┐
│ AlertManager │
│ └── 告警路由、分组、静默 │
└──────────────────────────────────────┘

Prometheus 监控指标

# Prometheus 配置(prometheus.yml)
global:
scrape_interval: 15s # 每 15s 拉取一次指标
scrape_configs:
- job_name: 'kubernetes-pods'
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: true
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
action: replace
target_label: __metrics_path__
regex: (.+)
# Pod 注解(应用侧)
metadata:
annotations:
prometheus.io/scrape: "true"
prometheus.io/path: "/actuator/prometheus"
prometheus.io/port: "8080"

告警规则配置

# 告警规则(alerting-rules.yml)
groups:
- name: app-alerts
rules:
# 告警 1:Pod CPU 使用率 > 80%
- alert: HighCPUUsage
expr: |
rate(container_cpu_usage_seconds_total{container="order-service"}[5m]) > 0.8
for: 5m
labels:
severity: warning
annotations:
summary: "Pod {{ $labels.pod }} CPU使用率过高"
description: "CPU使用率 {{ $value }}%,持续 5min"
# 告警 2:HTTP 错误率 > 5%
- alert: HighErrorRate
expr: |
sum(rate(http_requests_total{status=~"5.."}[5m])) /
sum(rate(http_requests_total[5m])) > 0.05
for: 2m
labels:
severity: critical
annotations:
summary: "HTTP 错误率过高"
# 告警 3:Pod 重启次数 > 3次/小时
- alert: PodRestartTooOften
expr: |
increase(kube_pod_container_status_restarts_total[1h]) > 3
labels:
severity: warning
annotations:
summary: "Pod {{ $labels.pod }} 频繁重启"

日志收集(ELK Stack)

日志架构:
┌──────────────────────────────────────┐
│ Pod 标准输出(stdout/stderr) │
│ ├── /var/log/containers/*.log │
│ └── /var/log/pods/*.log │
└──────────────┬───────────────────────┘
│ Fluentd 采集
┌──────────────────────────────────────┐
│ Fluentd(DaemonSet,每个节点一个) │
│ ├── 解析日志格式 │
│ ├── 添加元数据(Pod名、命名空间) │
│ └── 过滤、缓冲 │
└──────────────┬───────────────────────┘
│ 发送到 Elasticsearch
┌──────────────────────────────────────┐
│ Elasticsearch │
│ ├── 索引日志(按日期分片) │
│ ├── 全文检索 │
│ └── 聚合分析 │
└──────────────┬───────────────────────┘
│ 查询接口
┌──────────────────────────────────────┐
│ Kibana │
│ ├── 日志查询界面 │
│ ├── 可视化图表 │
│ └── 仪表盘 │
└──────────────────────────────────────┘

Fluentd 配置示例

# Fluentd ConfigMap
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
@id filter_kube_metadata
kubernetes_url "#{ENV['KUBERNETES_SERVICE_HOST']}:#{ENV['KUBERNETES_SERVICE_PORT']}"
</filter>
<match kubernetes.**>
@type elasticsearch
host "#{ENV['ELASTICSEARCH_HOST']}"
port "#{ENV['ELASTICSEARCH_PORT']}"
logstash_format true
logstash_prefix k8s-logs
<buffer>
@type file
path /var/log/fluentd/buffer
flush_interval 5s
</buffer>
</match>
---
# Fluentd DaemonSet
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd
spec:
selector:
matchLabels:
app: fluentd
template:
metadata:
labels:
app: fluentd
spec:
containers:
- name: fluentd
image: fluent/fluentd-kubernetes-daemonset:v1.14
env:
- name: ELASTICSEARCH_HOST
value: "elasticsearch.logging.svc.cluster.local"
- name: ELASTICSEARCH_PORT
value: "9200"
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
- name: config-volume
mountPath: /fluentd/etc/fluent.conf
subPath: fluent.conf
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
- name: config-volume
configMap:
name: fluentd-config

日志查询示例(Kibana)

查询 1:查询特定 Pod 的错误日志
kubernetes.pod_name: "order-service-123-*" AND level: ERROR
查询 2:查询过去 1 小时的异常堆栈
@timestamp: [now-1h TO now] AND message: "Exception"
查询 3:聚合分析:错误日志 TOP 10
{
"aggs": {
"top_errors": {
"terms": {
"field": "error_type.keyword",
"size": 10
}
}
}
}

本质一句话:监控用 Prometheus + Grafana 采集指标和可视化,日志用 Fluentd + Elasticsearch + Kibana 收集和查询,两者配合实现完整的可观测性。