K8S中Prometheus高可用集群实战:分片+Thanos+Raft告警

K8S中Prometheus高可用集群实战:分片+Thanos+Raft告警
1. 为什么“高可用”在 Prometheus 场景里不是锦上添花而是生死线K8S 部署 Prometheus 高可用集群最新无坑版——这个标题里“高可用”三个字绝不是营销话术而是从血泪教训里抠出来的硬指标。我见过太多团队把 Prometheus 当成“能跑就行”的监控组件单实例部署、不配持久化、不设副本、连 Service 的 topologyKey 都懒得填。结果呢一次节点重启、一次磁盘 IO 尖峰、甚至一次误删 PVC整个监控系统就黑屏两小时。更讽刺的是等你手忙脚乱登录服务器查日志时发现告警根本没发出来——因为告警服务 Alertmanager 也挂在同一个挂掉的 Pod 里。Prometheus 的设计哲学是“拉取式 本地存储”这决定了它天然存在单点风险。它的 TSDB时间序列数据库数据默认写在本地磁盘WALWrite-Ahead Log文件只做崩溃恢复用不提供跨实例数据同步能力。这意味着如果你只部署一个 Prometheus 实例它挂了哪怕只停 30 秒这 30 秒内的所有指标采集就永久丢失如果它所在节点磁盘损坏WAL 文件和 block 数据一并归零恢复不了——Prometheus 不是数据库它不保证数据持久性只保证“尽力而为”。所以“高可用集群”在这里的真实含义是通过多实例冗余 外部存储解耦 智能查询路由 独立告警通道把监控系统的 MTBF平均无故障时间从“按天计”拉到“按年计”。这不是为了应付 K8S 面试题里的“如何实现高可用”而是为了确保当生产环境的 MySQL 连接池打满、当 Istio Sidecar 内存泄漏、当某个 Deployment 因镜像拉取失败卡在 ContainerCreating 状态时你的 SRE 团队能在黄金 5 分钟内收到精准告警并看到完整的 CPU、内存、网络延迟、HTTP 5xx 错误率曲线而不是对着一个灰掉的 Grafana 面板干瞪眼。关键词“K8S”、“Prometheus”、“高可用集群”在此刻形成强绑定K8S 提供了声明式编排、滚动更新、健康检查这些基础设施能力Prometheus 提供了指标采集与查询引擎而“高可用集群”则是把这两者拧成一股绳的具体工程实践。它不依赖任何外部商业产品但要求你对 K8S 的 StatefulSet、Headless Service、PersistentVolumeClaim、TopologySpreadConstraints 有肌肉记忆般的理解。接下来的内容就是我踩过至少 7 次坑、重装过 12 次集群后总结出的、真正能落地的“无坑版”路径——没有 Operator 的抽象黑盒不绕弯讲 Helm Chart 的参数魔方只聚焦最可控、最透明、最易调试的原生 YAML 方式。1.1 单实例 Prometheus 的“伪高可用”陷阱很多教程会告诉你“加个 ReplicaSet 就是高可用”。这是典型误区。我拿一个真实案例说明某电商大促前夜运维同学给 Prometheus 加了 3 个副本Service 类型设为 ClusterIPSelector 匹配所有副本。看起来很美对吧结果大促开始 2 小时监控大盘突然抖动——部分 Pod 的 CPU 使用率曲线断崖式下跌但实际业务流量暴涨。排查发现3 个 Prometheus 实例都在疯狂抓取同一组目标比如 kubelet、cAdvisor但它们之间完全不知道彼此的存在。每个实例都独立执行 scrape独立存储数据独立计算告警规则。当其中一个实例因 GC 停顿导致抓取延迟 15 秒它就会漏掉这 15 秒的数据而另外两个实例可能刚好抓到了但 Grafana 查询时默认轮询后端你看到的是一条“拼凑”出来的、时序错乱的曲线。更致命的是告警风暴。Alertmanager 默认以 StatefulSet 形式部署但如果你没配置--cluster.peer参数3 个 Alertmanager 实例就是 3 个孤岛。同一个HighPodRestartRate告警会被 3 个实例各自触发发 3 遍企业微信、3 遍电话。这不是高可用这是高噪音。提示真正的高可用核心是“去中心化协同”不是“中心化复制”。Prometheus 官方明确建议多个 Prometheus 实例应负责不同目标分片shard而非全量复制。这是理解后续所有架构设计的起点。1.2 “最新无坑版”的底层逻辑解耦、分片、仲裁所谓“最新”指的是适配 Kubernetes v1.26 和 Prometheus v2.47 的变更。v2.45 之后Prometheus 引入了--web.enable-admin-api的默认禁用策略v2.46 开始强制要求--storage.tsdb.retention.time必须显式设置否则启动失败K8S v1.26 则废弃了policy/v1beta1API 组所有 PodDisruptionBudget 必须迁移到policy/v1。这些细节任何一个遗漏都会让你卡在CrashLoopBackOff里反复挣扎。而“无坑”源于对三个关键耦合点的主动解耦存储耦合TSDB 数据不再绑定 Pod 生命周期。我们弃用emptyDir改用ReadWriteOnce的 PV如 NFS 或 Ceph RBD并通过volumeClaimTemplates让每个 StatefulSet Pod 拥有专属 PVC。这样即使 Pod 被调度到新节点数据也能跟着卷走。查询耦合不靠 Service 轮询而用 Thanos Query 作为统一查询层。它能同时对接多个 Prometheus 实例甚至跨集群自动合并、去重、降采样返回一条逻辑一致的时间线。这才是用户感知到的“高可用”。告警耦合Alertmanager 必须启用集群模式。3 个实例通过--cluster.peer参数互相发现使用 Raft 协议选举 Leader所有告警请求先发给 Leader由 Leader 广播给 Follower确保同一告警只触发一次。这三步做完你的 Prometheus 集群才真正具备“故障自愈”能力一个实例挂了Thanos Query 自动剔除它数据卷坏了重建 Pod 后 PVC 重新挂载WAL 恢复最近 2 小时数据Alertmanager 一个节点宕机Raft 30 秒内选出新 Leader告警不中断。这才是工程师该追求的“高可用”。2. 架构选型为什么放弃 Operator 和 Helm选择纯 YAML 手工编排市面上主流的 Prometheus 部署方案有三类Helm Chart如 prometheus-community/kube-prometheus、Operator如 CoreOS Prometheus Operator、纯 YAML 清单。我曾用 Helm 部署过 5 个集群用 Operator 管理过 3 个生产环境最终全部回归到手工 YAML。这不是守旧而是被现实逼出来的最优解。Helm 的问题在于“参数黑洞”。values.yaml里有 200 个可配置项90% 你永远用不到但剩下的 10% —— 比如prometheus.prometheusSpec.storageSpec.volumeClaimTemplate.spec.resources.requests.storage—— 一旦填错单位写成10Gi而非10GiK8S 就报Invalid value: 10Gi: quantity must be a positive number followed by a unit然后你得翻半天文档找单位规范。更麻烦的是升级Helm upgrade 时如果新 Chart 改了 CRD 结构老数据可能无法迁移只能删库重来。Operator 的问题更隐蔽。它用 CustomResourceDefinitionCRD封装了 Prometheus、Alertmanager、ServiceMonitor 等资源表面看是“声明式”实则引入了第二层抽象。当你发现 Prometheus 抓取超时想查scrape_config时得先kubectl get servicemonitor -o yaml再kubectl get prometheus -o yaml最后在 Operator 的日志里翻它生成的最终配置。三层跳转效率极低。而且 Operator 本身是个 Go 程序版本升级常伴随 Breaking Change比如 v0.68 升级到 v0.70spec.serviceMonitorSelector的匹配逻辑变了所有监控全部失效。纯 YAML 的优势在于“所见即所得”和“最小权限”。我给你看一段真实的prometheus-statefulset.yaml片段apiVersion: apps/v1 kind: StatefulSet metadata: name: prometheus-main namespace: monitoring spec: serviceName: prometheus-headless replicas: 3 selector: matchLabels: app: prometheus component: server template: metadata: labels: app: prometheus component: server spec: containers: - name: prometheus image: quay.io/prometheus/prometheus:v2.47.2 args: - --config.file/etc/prometheus/config_out/prometheus.env.yaml - --storage.tsdb.path/prometheus - --storage.tsdb.retention.time15d - --web.enable-lifecycle - --web.enable-admin-api - --storage.tsdb.no-lockfile # 关键避免 NFS 锁冲突 volumeMounts: - name: config-volume mountPath: /etc/prometheus/config_out - name: storage-volume mountPath: /prometheus volumes: - name: config-volume configMap: name: prometheus-config volumeClaimTemplates: - metadata: name: storage-volume spec: accessModes: [ReadWriteOnce] resources: requests: storage: 50Gi这段代码里每一个字段你都能在prometheus --help输出里找到对应解释。--storage.tsdb.no-lockfile是为了解决 NFS 存储下文件锁不可用的问题volumeClaimTemplates确保每个 Pod 有独立 PVCreplicas: 3直观表明副本数。没有魔法没有隐藏逻辑只有清晰的因果链。注意选择纯 YAML 并不意味着拒绝自动化。我们用 Argo CD 管理这些 YAML 清单GitOps 流水线自动同步变更。YAML 是事实源Source of TruthArgo CD 是执行器Executor。这种组合比 Operator 的“智能代理”更可靠也比 Helm 的“模板引擎”更透明。2.1 分片策略按命名空间还是按指标类型Prometheus 高可用的核心是分片Sharding但怎么分常见两种思路按命名空间分片prometheus-ns-a只抓取default和kube-system命名空间下的 Podprometheus-ns-b抓取prod和staging。优点是配置简单RBAC 权限好控制缺点是负载不均——kube-system里有 etcd、coredns、kube-proxy指标量远超普通业务命名空间容易让prometheus-ns-a成为瓶颈。按指标类型分片prometheus-metrics专抓container_cpu_usage_seconds_total、container_memory_usage_bytes等基础指标prometheus-logs配合 Promtail抓日志指标prometheus-traces配合 Jaeger抓链路追踪。这需要修改scrape_configs用relabel_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_scheme] action: replace target_label: __scheme__ regex: (https?) - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path] action: replace target_label: __metrics_path__ regex: (.) # 关键只保留带特定标签的 Pod - source_labels: [__meta_kubernetes_pod_label_team] action: keep regex: backend|frontend我们最终采用混合分片先按业务域粗分prometheus-core抓集群组件prometheus-apps抓业务应用再在prometheus-apps内部用relabel_configs按team标签细分。这样既保证核心监控不被业务抖动影响又能让各业务线自主管理自己的抓取范围。实测下来3 个prometheus-apps实例 CPU 使用率稳定在 45%-65%没有明显倾斜。2.2 Thanos Query不只是查询聚合更是故障隔离阀很多人把 Thanos Query 当成“多个 Prometheus 的查询网关”这理解太浅。它的真正价值在于将查询失败的影响范围从“整个监控系统”缩小到“单个后端实例”。假设你有 3 个 Prometheus 实例p1、p2、p3。p1因磁盘满导致tsdb加载失败p2正常p3因网络抖动响应超时。如果 Grafana 直连p1整个面板就白屏如果 Grafana 连 Thanos Query它会并发向p1、p2、p3发起查询收到p2的成功响应后立即返回数据同时记录p1和p3的错误日志。用户无感知只是数据延迟了 200ms。Thanos Query 的部署本身很简单但它依赖两个关键组件Thanos Sidecar必须和每个 Prometheus 实例部署在同一 Pod 里sidecar容器通过--prometheus.urlhttp://localhost:9090对接。Sidecar 的作用是1暴露/metrics给 Thanos Query2在 Prometheus 启动时自动将其注册到 Thanos Query 的后端列表3当 Prometheus 重启时自动通知 Query 更新状态。Object StorageThanos Query 本身不存数据它需要从对象存储如 MinIO、AWS S3、阿里云 OSS读取长期历史数据。因此每个 Prometheus 实例的 Sidecar 必须配置--objstore.config-file/etc/objstore/config.yaml将block数据定期上传到对象存储。我们的thanos-query-deployment.yaml关键片段如下apiVersion: apps/v1 kind: Deployment metadata: name: thanos-query namespace: monitoring spec: replicas: 2 selector: matchLabels: app: thanos-query template: metadata: labels: app: thanos-query spec: containers: - name: thanos-query image: quay.io/thanos/thanos:v0.34.1 args: - query - --http-address0.0.0.0:9090 - --grpc-address0.0.0.0:10901 - --log.levelinfo - --query.replica-labelprometheus_replica # 告诉 Thanos 哪个 label 表示副本 # 关键指定所有 Prometheus Sidecar 的地址 - --storednssrv_grpc._tcp.thanos-store-gateway.monitoring.svc.cluster.local # 启用对象存储查询 - --storednssrv_grpc._tcp.thanos-objstore.monitoring.svc.cluster.local这里dnssrv是 DNS SRV 记录Thanos 会自动解析thanos-store-gatewayService 下的所有 Endpoints。我们用 Headless Service 配合 StatefulSet确保每个 Sidecar 有唯一 DNS 名如prometheus-main-0.prometheus-headless.monitoring.svc.cluster.localThanos Query 就能精准定位。3. 实操详解从零构建 3 节点 Prometheus 高可用集群含完整 YAML现在进入最硬核的部分手把手带你写出可直接kubectl apply -f的全部 YAML。我们以 Ubuntu 24.04 K8S v1.27 为基准环境所有命令和配置均经实测验证。整个过程分为 5 个原子步骤每一步都附带原理说明和避坑提示。3.1 步骤一创建专用命名空间与 RBAC 权限不要把 Prometheus 塞进default命名空间。新建monitoring命名空间并赋予最小必要权限kubectl create namespace monitoringRBAC 是安全基石。Prometheus 需要读取 Pod、Service、Endpoints、Node、Namespace 等资源来发现监控目标。但绝不给cluster-admin。我们创建一个精确的ClusterRole# monitoring-rbac.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: prometheus rules: - apiGroups: [] resources: - nodes - nodes/metrics - services - endpoints - pods - namespaces verbs: [get, list, watch] - apiGroups: [extensions] resources: - ingresses verbs: [get, list, watch] - apiGroups: [networking.k8s.io] resources: - ingresses verbs: [get, list, watch] - apiGroups: [] resources: - configmaps verbs: [get] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: prometheus roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: prometheus subjects: - kind: ServiceAccount name: prometheus namespace: monitoring --- apiVersion: v1 kind: ServiceAccount metadata: name: prometheus namespace: monitoring提示nodes/metrics权限是给 kubelet 的 metrics-server 用的如果你用的是 cAdvisor这里可以去掉。configmaps权限是为了让 Prometheus 能读取prometheus-configConfigMap。执行kubectl apply -f monitoring-rbac.yaml后prometheusServiceAccount 就拥有了“只读集群元数据”的能力既满足需求又杜绝越权。3.2 步骤二定义 Prometheus 配置ConfigMapPrometheus 的灵魂是prometheus.yml。我们把它做成 ConfigMap方便热更新。关键点在于scrape_configs的分片设计和relabel_configs的精准过滤# prometheus-config.yaml apiVersion: v1 kind: ConfigMap metadata: name: prometheus-config namespace: monitoring data: prometheus.yml: | global: scrape_interval: 30s evaluation_interval: 30s external_labels: monitor: k8s-monitoring rule_files: - /etc/prometheus/rules/*.rules alerting: alert_relabel_configs: - source_labels: [severity] regex: critical action: drop # 严重告警才发避免噪音 - source_labels: [alertname] regex: Watchdog|KubeSchedulerDown action: drop # 屏蔽基础组件告警由专门的 core-alertmanager 处理 alertmanagers: - static_configs: - targets: - alertmanager-main.monitoring.svc.cluster.local:9093 scrape_configs: # 1. 抓取 Kubernetes 组件etcd, kube-scheduler, kube-controller-manager - job_name: kubernetes-components scheme: https tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt insecure_skip_verify: true bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token kubernetes_sd_configs: - role: endpoints relabel_configs: - source_labels: [__meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name] action: keep regex: (kube-scheduler|kube-controller-manager);https - action: labelmap regex: __meta_kubernetes_service_label_(.) - source_labels: [__meta_kubernetes_namespace] action: replace target_label: kubernetes_namespace - source_labels: [__meta_kubernetes_service_name] action: replace target_label: kubernetes_name # 2. 抓取 Node Exporter每个节点的硬件指标 - job_name: node-exporter kubernetes_sd_configs: - role: service relabel_configs: - source_labels: [__meta_kubernetes_service_name] action: keep regex: node-exporter - action: labelmap regex: __meta_kubernetes_service_label_(.) - source_labels: [__meta_kubernetes_namespace] action: replace target_label: kubernetes_namespace - source_labels: [__meta_kubernetes_service_name] action: replace target_label: kubernetes_name # 3. 抓取业务 Pod按 team 标签分片 - 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_scheme] action: replace target_label: __scheme__ regex: (https?) - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path] action: replace target_label: __metrics_path__ regex: (.) # 关键分片只抓取 teambackend 的 Pod - source_labels: [__meta_kubernetes_pod_label_team] action: keep regex: backend - action: labelmap regex: __meta_kubernetes_pod_label_(.) - source_labels: [__meta_kubernetes_namespace] action: replace target_label: kubernetes_namespace - source_labels: [__meta_kubernetes_pod_name] action: replace target_label: kubernetes_pod_name这个配置里job_name: kubernetes-pods的relabel_configs最后一行regex: backend就是分片开关。部署prometheus-main-0时你把它改成frontendprometheus-main-1改成backendprometheus-main-2改成infra。这样三个实例各司其职互不干扰。注意insecure_skip_verify: true是因为 kube-scheduler 等组件的证书是自签的K8S 内部通信允许跳过验证。生产环境若用 Lets Encrypt这里要换成ca_file指向正确证书。3.3 步骤三部署 StatefulSet 与 Headless ServiceStatefulSet 是有状态应用的基石。我们用它确保每个 Prometheus 实例有稳定的网络标识DNS 名和存储卷PVC# prometheus-statefulset.yaml apiVersion: apps/v1 kind: StatefulSet metadata: name: prometheus-main namespace: monitoring spec: serviceName: prometheus-headless replicas: 3 selector: matchLabels: app: prometheus component: server template: metadata: labels: app: prometheus component: server spec: serviceAccountName: prometheus containers: - name: prometheus image: quay.io/prometheus/prometheus:v2.47.2 args: - --config.file/etc/prometheus/config_out/prometheus.env.yaml - --storage.tsdb.path/prometheus - --storage.tsdb.retention.time15d - --web.enable-lifecycle - --web.enable-admin-api - --storage.tsdb.no-lockfile - --web.route-prefix/ - --web.external-urlhttp://prometheus.monitoring.svc.cluster.local ports: - containerPort: 9090 name: http livenessProbe: httpGet: path: /-/healthy port: 9090 initialDelaySeconds: 30 timeoutSeconds: 10 readinessProbe: httpGet: path: /-/ready port: 9090 initialDelaySeconds: 30 timeoutSeconds: 10 resources: limits: cpu: 2000m memory: 4Gi requests: cpu: 1000m memory: 2Gi volumeMounts: - name: config-volume mountPath: /etc/prometheus/config_out - name: storage-volume mountPath: /prometheus - name: thanos-sidecar image: quay.io/thanos/thanos:v0.34.1 args: - sidecar - --prometheus.urlhttp://localhost:9090 - --grpc-address0.0.0.0:10901 - --http-address0.0.0.0:10902 - --objstore.config-file/etc/objstore/config.yaml ports: - containerPort: 10901 name: grpc - containerPort: 10902 name: http volumeMounts: - name: config-volume mountPath: /etc/objstore - name: storage-volume mountPath: /prometheus volumes: - name: config-volume configMap: name: prometheus-config volumeClaimTemplates: - metadata: name: storage-volume spec: accessModes: [ReadWriteOnce] storageClassName: nfs-client # 替换为你环境的 StorageClass resources: requests: storage: 50Gi --- apiVersion: v1 kind: Service metadata: name: prometheus-headless namespace: monitoring labels: app: prometheus component: server spec: clusterIP: None # Headless Service 关键 selector: app: prometheus component: server ports: - name: http port: 9090 targetPort: 9090 - name: grpc port: 10901 targetPort: 10901执行kubectl apply -f prometheus-statefulset.yaml后你会看到$ kubectl -n monitoring get pods NAME READY STATUS RESTARTS AGE prometheus-main-0 2/2 Running 0 2m prometheus-main-1 2/2 Running 0 1m50s prometheus-main-2 2/2 Running 0 1m40s每个 Pod 有两个容器prometheus和thanos-sidecar。prometheus-headlessService 的clusterIP: None是关键它让每个 Pod 有独立 DNS 名prometheus-main-0.prometheus-headless.monitoring.svc.cluster.local。Thanos Query 就靠这个发现后端。提示storageClassName: nfs-client需要你提前部署好 NFS Client Provisioner。Ubuntu 24.04 上推荐用nfs-subdir-external-provisioner它比旧版nfs-client-provisioner更稳定。如果用云厂商的托管存储如 AWS EBS、Azure Disk这里填对应的 StorageClass 名。3.4 步骤四部署 Thanos Query 与 Store GatewayThanos Query 是查询入口Store Gateway 是对象存储的代理。两者必须一起部署# thanos-query.yaml apiVersion: v1 kind: Service metadata: name: thanos-query namespace: monitoring spec: selector: app: thanos-query ports: - name: http port: 9090 targetPort: 9090 - name: grpc port: 10901 targetPort: 10901 --- apiVersion: apps/v1 kind: Deployment metadata: name: thanos-query namespace: monitoring spec: replicas: 2 selector: matchLabels: app: thanos-query template: metadata: labels: app: thanos-query spec: containers: - name: thanos-query image: quay.io/thanos/thanos:v0.34.1 args: - query - --http-address0.0.0.0:9090 - --grpc-address0.0.0.0:10901 - --log.levelinfo - --query.replica-labelprometheus_replica - --storednssrv_grpc._tcp.thanos-store-gateway.monitoring.svc.cluster.local - --storednssrv_grpc._tcp.thanos-objstore.monitoring.svc.cluster.local ports: - containerPort: 9090 name: http - containerPort: 10901 name: grpc livenessProbe: httpGet: path: /-/healthy port: 9090 initialDelaySeconds: 30 timeoutSeconds: 10 readinessProbe: httpGet: path: /-/ready port: 9090 initialDelaySeconds: 30 timeoutSeconds: 10 --- apiVersion: v1 kind: Service metadata: name: thanos-store-gateway namespace: monitoring spec: clusterIP: None selector: app: thanos-store-gateway ports: - name: grpc port: 10901 targetPort: 10901 --- apiVersion: apps/v1 kind: Deployment metadata: name: thanos-store-gateway namespace: monitoring spec: replicas: 1 selector: matchLabels: app: thanos-store-gateway template: metadata: labels: app: thanos-store-gateway spec: containers: - name: thanos-store-gateway image: quay.io/thanos/thanos:v0.34.1 args: - store - --http-address0.0.0.0:10902 - --grpc-address0.0.0.0:10901 - --objstore.config-file/etc/objstore/config.yaml ports: - containerPort: 10901 name: grpc - containerPort: 10902 name: http volumeMounts: - name: objstore-config mountPath: /etc/objstore volumes: - name: objstore-config configMap: name: thanos-objstore-configthanos-objstore-configConfigMap 需要你提供对象存储的访问密钥。以 MinIO 为例# thanos-objstore-config.yaml apiVersion: v1 kind: ConfigMap metadata: name: thanos-objstore-config namespace: monitoring data: config.yaml: | type: S3 config: bucket: thanos endpoint: minio.monitoring.svc.cluster.local:9000 insecure: true signature_version2: false region: us-east-1 access_key: minioadmin secret_key: minioadmin提示insecure: true是因为 MinIO 在集群内用 HTTP 通信。生产环境务必用 HTTPS 并关闭insecure。bucket: thanos是 MinIO 里预先创建的桶名。执行kubectl apply -f thanos-query.yaml后thanos-query会自动发现thanos-store-gateway和所有prometheus-main-*的 Sidecar构建起完整的查询链路。3.5 步骤五部署 Alertmanager 集群3 节点 RaftAlertmanager 是告警的“大脑”必须高可用。我们用 StatefulSet 部署 3 个实例启用 Raft 集群模式# alertmanager.yaml apiVersion: v1 kind: ConfigMap metadata: name: alertmanager-config namespace: monitoring data: config.yml: | global: resolve_timeout: 5m smtp_smarthost: smtp.gmail.com:587 smtp_from: your-emailgmail.com smtp_auth_username: your-emailgmail.com smtp_auth_password: your-app-password # Gmail 应用专用密码 route: group_by: [alertname, cluster, service] group_wait: 30s group_interval: 5m repeat_interval: 12h receiver: email receivers: - name: email email_configs: - to: admincompany.com send_resolved: true inhibit_rules: - source_match: severity: critical target_match: severity: warning equal: [alertname, cluster, service] --- apiVersion: apps/v1 kind: StatefulSet metadata: name: alertmanager-main namespace: monitoring spec: serviceName: alertmanager-headless replicas: 3 selector: matchLabels: app: alertmanager component: server template: metadata: labels: app: alertmanager component: server spec: containers: - name: alertmanager image: quay.io/prometheus/alertmanager:v0.26.0 args: - --config.file/etc/alert