离线环境下基于 Argo CD 的 GitOps 监控栈交付方案 - 下
7. Argo CD 应用清单配置
7.1 Argo CD 服务自身配置
argocd/install/values.yaml
Argo CD 自身通过 Helm 安装,以下为关键配置项说明:
# =============================================================================
# Argo CD 自身安装配置(Helm values)
# =============================================================================
# 适用范围:腾讯云 4C8G Ubuntu 24.04,K3s v1.34+ 单节点集群
# 部署方式:手动执行 helm install,不属于 App of Apps 监控栈的管控范围
# Chart 版本:Argo CD v3.3.8
#
# 本文件的核心职责:
# 1. 将所有 Argo CD 组件镜像重定向至阿里云 ACR(保证离线可用)
# 2. 禁用不需要的组件(Dex),简化单节点部署
# 3. 调整 repo-server 的超时和健康检查,适配大型 Helm Chart 渲染
# 4. 使用 HTTP 模式暴露 Argo CD Server(无外部 Ingress,通过 kubectl port-forward 访问)
# 5. 设定合理的资源限制,确保在 4C8G 节点上与其他组件共存
#
# 注意:本文件不涉及任何监控栈资源,仅用于部署 Argo CD 平台本身。
# 部署完成后,Argo CD 将由 bootstrap/root-app.yaml 接入 GitOps 闭环,
# 接管 apps/ 目录下的所有可观测性子应用。
# =============================================================================
# ──────────────────────────── 用户自定义值 ────────────────────────────
configs:
params:
# repo-server 请求超时时间(秒)。默认值 60s 在渲染 kube-prometheus-stack
# 等大型 Chart 时极易超时,导致同步失败。提高到 300s 可充分容忍复杂渲染。
controller.repo.server.timeout.seconds: "300"
# Argo CD Server 启动时是否禁用 TLS。设置为 true 后 API Server 将直接
# 在 HTTP 端口监听(默认 8080),适合通过 kubectl port-forward 或内部
# Service 访问的轻量部署场景。生产对外暴露时应配合 Ingress TLS 使用。
server.insecure: true
# ────────────────────────── 控制器配置 ──────────────────────────────
controller:
# Argo CD Application Controller 的资源限制
resources:
limits:
cpu: 500m
memory: 768Mi
requests:
cpu: 100m
memory: 400Mi
# 注意:未显式配置 args,使用默认参数即可满足小规模集群管理
# ────────────────────────── Dex 配置 ──────────────────────────────────
dex:
# Argo CD 的 SSO 组件,用于集成外部 OIDC/OAuth2 身份提供者。
# 单节点或小团队部署时通常不需要,禁用后可节省约 200Mi 内存和 0.2 核 CPU。
enabled: false
# ────────────────────────── 全局镜像配置 ──────────────────────────────
global:
image:
# Argo CD 核心组件(API Server、Controller、Repo Server 等)的统一镜像地址。
# 由于 Argo CD 社区镜像托管在 quay.io,国内拉取困难,必须同步至 ACR。
repository: <ACR_REGISTRY>/<ACR_NAMESPACE>/argoproj-argocd
tag: v3.3.8
# 拉取策略隐含为 IfNotPresent,未显式设置但可接受
# ────────────────────────── Redis 配置 ────────────────────────────────
redis:
image:
# Argo CD 使用 Redis 作为缓存,存储应用列表、集群状态等数据。
# 替换镜像为 ACR 地址,避免从 Docker Hub 拉取失败。
repository: <ACR_REGISTRY>/<ACR_NAMESPACE>/redis
tag: 8.6.2-alpine
# 未定义资源限制,单节点环境下 Redis 占用极低,暂无需调整
# ────────────────────────── Repo Server 配置 ──────────────────────────
repoServer:
# ── 环境变量 ─────────────────────────────────────────────────
env:
# 覆盖 repo server 处理请求的总超时时间,与环境变量 ARGOCD_EXEC_TIMEOUT 对应。
# 该值应与 controller.repo.server.timeout.seconds 保持一致,避免不匹配。
- name: ARGOCD_EXEC_TIMEOUT
value: 300s
# 限制 repo server 可使用的最大 CPU 核心数。设置为 2 可防止在渲染大型 Chart
# 时占用过多节点 CPU,影响同节点的其他 Pod(如 Prometheus、Grafana)。
# 注意:该值并非 Pod 资源限制,而是进程内的 Go 调度器限制。
- name: GOMAXPROCS
value: "2"
# ── 存活探针 ─────────────────────────────────────────────────
livenessProbe:
failureThreshold: 5 # 连续失败 5 次才判定不健康,为大型 Chart 渲染留足时间
initialDelaySeconds: 60 # 启动后等待 60s 再开始检查,避免冷启动时误杀
periodSeconds: 60 # 每分钟检查一次,减少频繁探测带来的额外负载
successThreshold: 1
timeoutSeconds: 30 # 单次探针操作超时,30s 足够完成一次健康检查
# ── 就绪探针 ─────────────────────────────────────────────────
readinessProbe:
failureThreshold: 5 # 同样放宽失败阈值
initialDelaySeconds: 30 # 启动后 30s 开始检查就绪状态
periodSeconds: 30
successThreshold: 1
timeoutSeconds: 30
# ── 资源限制 ─────────────────────────────────────────────────
resources:
limits:
cpu: 300m # repo server 在渲染 Chart 时 CPU 峰值较高,给予合理上限
memory: 768Mi # 渲染大型 Chart(如 kube-prometheus-stack)需要约 500-600Mi
requests:
cpu: 100m
memory: 256Mi
# ────────────────────────── API Server 配置 ──────────────────────────
server:
extraArgs:
# 与 configs.params.server.insecure 一致,显式声明启动时禁用 TLS。
# 当未配置 Ingress TLS 时,必须保留此参数,否则 API Server 会尝试加载
# 默认的自签名证书,可能导致启动失败。
- --insecure
# 覆盖 API Server 等待 repo server 处理的时间,避免因大型 Chart 渲染
# 超时而导致 UI 或 CLI 报错。此参数与 controller 和 repo server 超时配合使用。
- --repo-server-timeout-seconds=300
# ── Ingress 配置 ──────────────────────────────────────────────
ingress:
# 禁用 Argo CD 自身的 Ingress 创建。集群统一通过 Ingress 暴露 Prometheus 和
# Grafana,而 Argo CD 管理界面建议通过 kubectl port-forward 或独立的内部
# 网络策略控制访问,避免管理接口意外暴露于公网。
enabled: false
# 注意:未定义 Argo CD Server 的资源限制,使用 Helm Chart 默认值即可。7.2 root-app 父应用清单
bootstrap/root-app.yaml
root-app 是整个 App of Apps 架构的入口,只需手工执行一次 kubectl apply 即可引导整个可观测性栈:
# =============================================================================
# Root Application — App of Apps 父应用(GitOps 引导入口)
# =============================================================================
# 适用范围:腾讯云 4C8G Ubuntu 24.04,K3s v1.34+ 单节点集群
# 部署方式:一次性手工执行 kubectl apply,之后完全由 Argo CD 自管理
#
# 本文件的唯一职责:
# - 作为 Argo CD 的顶层 Application,监听仓库的 apps/ 目录
# - 当 apps/ 目录下出现任何符合格式的子 Application YAML 时,自动在集群中创建它们
# - 这实现了 "用 GitOps 管理 GitOps" 的闭环:只需提交子应用清单,Argo CD 就会同步
#
# 关键设计决策:
# 1. root-app 只负责 "生成子应用",不直接部署任何业务负载(Prometheus、Grafana 等)
# 2. 它自身只被手工 apply 一次,之后它的状态也由 Argo CD 维护(若开启自管理)
# 3. 所有敏感资源(Secret、TLS 证书)不在它的管理范围内,由 bootstrap 阶段手工创建
# 4. 通过 syncPolicy.automated 开启自动同步和自愈,确保 Git 仓库是唯一真相来源
#
# ⚠️ 部署前请将 <YOUR_GIT_REPO_URL> 替换为实际的 Git 仓库地址(SSH 或 HTTPS)
# =============================================================================
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
# root-app 的名称,是 Argo CD 中识别父应用的唯一标识。
# 所有子应用都会在 UI 中显示为它的下级,因此不要随意修改。
name: root-app
# 父应用必须部署在 Argo CD 所在的命名空间(通常是 argocd)。
# 这样 Argo CD 才能正确追踪其状态并展示层级关系。
namespace: argocd
labels:
# 附加标签方便在集群中定位所有 GitOps 管理组件
app.kubernetes.io/part-of: gitops-observability
app.kubernetes.io/managed-by: argocd
# finalizers 确保删除父应用时,Argo CD 先清理所有子应用,再清理自身。
# 如果缺少这个 finalizer,直接删除 root-app 会导致子应用成为孤儿资源,
# 驻留在集群中无法通过 GitOps 回收。
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
# 项目:使用 Argo CD 内置的 default 项目,拥有对所有目标集群的完全权限。
# 如需更细粒度的权限控制(例如限制可部署的命名空间),可创建自定义项目。
project: default
# ── 源配置:从哪里读取应用清单,以及如何解析 ─────────────────
source:
# 替换为实际 Git 仓库地址,例如 git@github.com:team/gitops-argocd.git
# 该仓库已在 Argo CD Settings → Repositories 中添加凭证
repoURL: <YOUR_GIT_REPO_URL>
# 跟踪的分支/标签/commit,通常使用 HEAD 跟踪主分支最新提交
targetRevision: HEAD
# 核心:指定为仓库根目录下的 apps/ 目录。
# Argo CD 会扫描该目录下所有 YAML 文件,识别其中的 Application 资源,
# 并自动将它们创建为 root-app 的子应用。
# 这使得添加新组件只需在 apps/ 中放一个子应用清单,无需变更 root-app 本身。
path: apps
# ── 目标集群与命名空间 ──────────────────────────────────────
destination:
# 目标 Kubernetes 集群的 API Server 地址。
# https://kubernetes.default.svc 是集群内 ServiceAccount 自动发现的
# 内置地址,表示部署到 Argo CD 所在的同一个集群。
server: https://kubernetes.default.svc
# 子应用清单中声明的 Application 对象将被创建在此命名空间中。
# 注意:这指的是子 Application CR 的命名空间,而非业务 Pod 的命名空间。
# 业务负载的命名空间由各个子应用的 spec.destination.namespace 决定。
namespace: argocd
# ── 同步策略:定义如何自动将 Git 状态应用到集群 ───────────────
syncPolicy:
automated:
# prune: true 表示当 Git 中删除某个子 Application YAML 时,
# Argo CD 会自动删除集群中对应的 Application 及其管理的所有资源。
# 这是 GitOps "单点真相" 的核心保障。
prune: true
# selfHeal: true 表示当集群中任何由 Argo CD 管理的资源被手动修改后,
# Argo CD 会自动将其恢复到 Git 中定义的状态。
# 这确保了集群状态始终收敛到 Git 声明,防止手动漂移。
selfHeal: true
# 同步选项
syncOptions:
# 允许子应用在目标命名空间不存在时自动创建。
# 与子应用清单中的 CreateNamespace=true 配合,实现全自动部署。
- CreateNamespace=true
# 注意:此处不设置 ServerSideApply,因为 root-app 本身只生成子应用 CR,
# 不直接渲染大型资源。ServerSideApply 应在子应用(如 kube-prometheus-stack)
# 的清单中单独声明,以应对 CRD 体积过大问题。
# 未设置 retry,因为 root-app 本身非常简单,若失败通常是 Git 连接问题,
# 需要人工介入而非无限重试。
# ── 差异忽略规则 ─────────────────────────────────────────────
# root-app 本身管理的只有子 Application CR,这些对象通常不会产生意外的差异,
# 因此此处未配置 ignoreDifferences。如果 Argo CD 版本或环境导致子应用
# 的 metadata 字段出现漂移,可在此添加忽略规则。7.3 kube-prometheus-stack 子应用清单
apps/kube-prometheus-stack.yaml
sync-wave: "0" — 必须最先同步,因为它负责安装 prometheus-operator 并注册 ServiceMonitor、PodMonitor、Probe、PrometheusRule 等 CRD,是整个可观测性栈的“底座”。
所有后续 Wave 1 的应用(如 postgresql-exporter、blackbox-exporter)都依赖这些 CRD 的存在。
# =============================================================================
# kube-prometheus-stack — 核心监控栈子应用
# =============================================================================
# 适用范围:腾讯云 4C8G Ubuntu 24.04,K3s v1.34+ 单节点集群
# 部署模式:由父应用 root-app 自动发现并创建,无需手工 apply
# sync-wave:0(第一批同步,是所有后续应用的依赖基础)
#
# 本 Application 的核心职责:
# 1. 在 monitoring 命名空间中部署 kube-prometheus-stack Helm Chart(本地化版本)
# 2. 通过 Helm 安装 prometheus-operator,自动注册 CRD:
# ServiceMonitor、PodMonitor、Probe、PrometheusRule 等
# 3. 提供 Prometheus、Grafana、Alertmanager 等核心监控组件
# 4. 作为整个可观测性栈的底座,wave 0 健康后 wave 1 才能开始同步
#
# 关键设计决策:
# - CRD 体积约 500KB+,超过 Kubernetes annotation 262KB 限制,
# 必须启用 ServerSideApply=true,否则 Argo CD 会报错
# - admissionWebhooks 的 caBundle 会在每次 patch Job 运行后发生变化,
# 必须添加到 ignoreDifferences 中,避免 Argo CD 反复尝试同步
# - 使用本地化 Helm Chart(charts/kube-prometheus-stack),
# values 通过相对路径 ../../values/... 引用,兼容离线部署
#
# ⚠️ 部署前请将 <YOUR_GIT_REPO_URL> 替换为实际 Git 仓库地址
# =============================================================================
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: kube-prometheus-stack
namespace: argocd
labels:
# 将所有观测组件统一标记,方便在 Argo CD UI 和集群中筛选
app.kubernetes.io/part-of: gitops-observability
app.kubernetes.io/managed-by: argocd
annotations:
# ── Sync-Wave 注解 ──────────────────────────────────────────────
# 数值为 "0",表示本应用在所有子应用中第一批同步。
#
# 为什么必须为 "0"?
# 本 Chart 包含 prometheus-operator,operator 启动后会在集群中注册以下 CRD:
# - monitoring.coreos.com/v1: ServiceMonitor, PodMonitor
# - monitoring.coreos.com/v1: Probe, PrometheusRule, AlertmanagerConfig
# - monitoring.coreos.com/v1: Prometheus, Alertmanager, ThanosRuler
# Wave 1 的应用(postgresql-exporter、blackbox-exporter)需要创建
# ServiceMonitor、PrometheusRule 等 CR,这些 CR 的 CRD 必须已经存在,
# 否则 Kubernetes API 会拒绝创建并报 "no matches for kind ..."。
#
# Argo CD 的 sync-wave 机制会等待所有 wave 0 应用 Healthy 后,
# 才开始同步 wave 1,从而自然地解决了 CRD 就绪问题。
argocd.argoproj.io/sync-wave: "0"
# finalizers:确保删除本应用时,Argo CD 先清理所有关联的 Kubernetes 资源
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
# ── 源配置:指向本地化 Helm Chart ─────────────────────────────
source:
repoURL: <YOUR_GIT_REPO_URL>
targetRevision: HEAD
# 直接指向仓库中的 charts/kube-prometheus-stack 目录。
# 该目录包含完整的 Chart(含所有子 chart 的 tgz 解压内容),
# Argo CD 的 repo-server 无需访问任何外部 Helm 仓库即可完成渲染。
path: charts/kube-prometheus-stack
helm:
# releaseName 决定所有资源的命名前缀,例如 Release 名称为
# kube-prometheus-stack,则 Prometheus StatefulSet 的名字可能是
# prometheus-kube-prometheus-stack。明确指定避免歧义。
releaseName: kube-prometheus-stack
# valueFiles 路径是相对于 Chart 目录(即 path 指定的目录)解析的。
# 当前 Chart 目录:charts/kube-prometheus-stack/
# ../../ 先回到仓库根目录,再进入 values/kube-prometheus-stack/
# 因此完整路径为:values/kube-prometheus-stack/values.yaml
# 这样设计的目的是将 Chart 模板和运行环境的值严格分离。
valueFiles:
- ../../values/kube-prometheus-stack/values.yaml
# ── 目标部署位置 ────────────────────────────────────────────
destination:
server: https://kubernetes.default.svc # 部署到本地集群
namespace: monitoring # 所有监控基础组件运行在此命名空间
# ── 同步策略 ─────────────────────────────────────────────────
syncPolicy:
automated:
prune: true # Git 中删除的资源在集群中同步删除
selfHeal: true # 手动修改自动回滚到 Git 定义的状态
syncOptions:
# 如果 monitoring 命名空间未提前创建(如 bootstrap 阶段遗漏),
# 由 Argo CD 自动创建
- CreateNamespace=true
# 【关键】启用 Server-Side Apply
# 原因:prometheus-operator 的 CRD 定义文件总体积超过 500KB,
# 而 Kubernetes 使用 last-applied-configuration annotation 存储
# 客户端 apply 时的完整配置,该 annotation 的大小上限为 262144 字节。
# 若使用默认的客户端 apply(ServerSideApply=false),Argo CD 会报错:
# "metadata.annotations: Too long: must have at most 262144 bytes"
# 启用 ServerSideApply=true 后,Kubernetes API Server 直接在
# 服务端完成合并,不再依赖 annotation,从而绕过此限制。
- ServerSideApply=true
# 只同步与 Git 定义不一致的资源(跳过已同步资源),大幅缩短大型 Chart
# 的同步耗时。对于包含 100+ 资源的 kube-prometheus-stack 尤其重要。
- ApplyOutOfSyncOnly=true
# 重试策略:若同步失败,Argo CD 会自动重试,避免因瞬时问题导致人工介入
retry:
limit: 5 # 最多重试 5 次
backoff:
duration: 5s # 第一次重试等待 5s
factor: 2 # 指数退避因子,后续等待 10s, 20s, 40s...
maxDuration: 3m # 单次重试等待上限 3 分钟
# ── 差异忽略规则 ────────────────────────────────────────────
# 目的:避免因非预期变化导致 Argo CD 反复尝试同步,保持应用状态为 Healthy
ignoreDifferences:
# 规则 1 & 2:忽略 admissionWebhooks 的 caBundle 变化
#
# 背景:prometheus-operator 的 admission webhook 证书由 init container 或
# patch Job(kube-webhook-certgen)动态生成并写入 MutatingWebhookConfiguration
# 和 ValidatingWebhookConfiguration 的 caBundle 字段。每次 Job 运行后,
# 证书内容会变化,导致 Argo CD 检测到 Git 中定义的 Webhook 与集群状态不一致,
# 从而持续 OutOfSync。
#
# 解决方案:忽略 caBundle 字段的差异。由于证书由可靠的 certgen 工具生成,
# 其有效性由 Job 保证,Git 中无需跟踪具体的证书内容。
- group: admissionregistration.k8s.io
kind: MutatingWebhookConfiguration
jsonPointers:
- /webhooks/0/clientConfig/caBundle
- group: admissionregistration.k8s.io
kind: ValidatingWebhookConfiguration
jsonPointers:
- /webhooks/0/clientConfig/caBundle
# 注意:如果 Operator 的 Webhook 数量或路径结构发生变化,jsonPointer 可能需要调整。
# 当前配置假设仅有一个 webhook 条目,适用于 kube-prometheus-stack v84.5.0。7.4 postgresql-exporter 子应用清单
apps/postgresql-exporter.yaml
# =============================================================================
# postgresql-exporter — PostgreSQL 指标采集子应用
# =============================================================================
# 适用范围:腾讯云 4C8G Ubuntu 24.04,K3s v1.34+ 单节点集群
# 部署模式:由父应用 root-app 自动发现并创建(属于 wave 1)
# sync-wave:1(在 wave 0 的 kube-prometheus-stack 健康后同步)
#
# 本 Application 的核心职责:
# 1. 在 exporters 命名空间中部署 PostgreSQL Exporter Deployment 和 Service
# 2. 通过 Kubernetes Secret 安全注入数据库连接字符串(不在 Git 中暴露密码)
# 3. 创建 ServiceMonitor CR,使 Prometheus 自动发现并抓取数据库指标
# 4. 创建 PrometheusRule CR,定义数据库健康告警规则(连接数、长事务等)
#
# 关键依赖与设计说明:
# - Wave 0 的 kube-prometheus-stack 必须先注册 ServiceMonitor 和
# PrometheusRule 的 CRD,否则本应用创建 CR 时会报 "no matches for kind"。
# - Prometheus 必须配置跨命名空间选择器(已在 kube-prometheus-stack 中
# 将 serviceMonitorNamespaceSelector 等置为 {}),否则无法发现 exporters
# 命名空间中的 ServiceMonitor,数据库指标将丢失。
# - 数据库连接字符串存储在 Secret `postgres-exporter-secret` 中,该 Secret
# 在 bootstrap 阶段手工创建,不被 Argo CD 接管。
#
# ⚠️ 部署前请将 <YOUR_GIT_REPO_URL> 替换为实际 Git 仓库地址
# =============================================================================
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: postgresql-exporter
namespace: argocd
labels:
# 便于在 Argo CD UI 中与监控栈其他应用统一筛选和分组
app.kubernetes.io/part-of: gitops-observability
annotations:
# ── Sync-Wave 注解 ──────────────────────────────────────────────
# 数值为 "1",表示本应用在 wave 0 的 kube-prometheus-stack 健康后才同步。
#
# 为什么必须为 "1"?
# 本应用会创建以下两种 Custom Resource:
# 1. ServiceMonitor —— 声明 Prometheus 应如何抓取 Exporter 的 /metrics
# 2. PrometheusRule —— 定义告警规则(如 PostgreSQLDown)
# 这两种 CR 的 CRD 均由 wave 0 安装的 prometheus-operator 注册。
# 若在 CRD 未就绪时就尝试创建 CR,Kubernetes API 会拒绝并返回:
# "no matches for kind ServiceMonitor in version monitoring.coreos.com/v1"
# Argo CD 的 sync-wave 机制保证所有 wave 0 应用 Healthy 后才开始
# 同步 wave 1,从而自然解决 CRD 就绪问题。
argocd.argoproj.io/sync-wave: "1"
finalizers:
# 确保删除本应用时,Argo CD 会先清理所有对应的 Kubernetes 资源
- resources-finalizer.argocd.argoproj.io
spec:
project: default
# ── 源配置:指向本地化 Helm Chart ─────────────────────────────
source:
repoURL: <YOUR_GIT_REPO_URL>
targetRevision: HEAD
# 指定仓库中 charts/postgresql-exporter 目录,该目录是已下载并解压的完整 Chart
path: charts/postgresql-exporter
helm:
# 固定 release 名称,确保所有资源前缀一致,便于识别和管理
releaseName: postgresql-exporter
# valueFiles 路径相对于 Chart 目录解析。
# ../../ 回到仓库根目录,再进入 values/postgresql-exporter/
# 因此完整路径为:values/postgresql-exporter/values.yaml
valueFiles:
- ../../values/postgresql-exporter/values.yaml
# ── 目标部署位置 ────────────────────────────────────────────
destination:
server: https://kubernetes.default.svc
# 所有 Exporter 统一运行在 exporters 命名空间,与监控底座(monitoring ns)隔离
namespace: exporters
# ── 同步策略 ─────────────────────────────────────────────────
syncPolicy:
automated:
prune: true # Git 中删除的资源在集群中同步删除
selfHeal: true # 手动修改自动回滚到 Git 定义的状态
syncOptions:
# 若 exporters 命名空间未提前创建(如在 bootstrap 阶段遗漏),自动创建
- CreateNamespace=true
# 重试策略:应对瞬时网络波动、仓库访问问题或 Operator 短暂不可用
retry:
limit: 5 # 最多重试 5 次
backoff:
duration: 5s # 第一次重试等待 5s
factor: 2 # 指数退避:后续等待 10s, 20s, 40s...
maxDuration: 3m # 单次重试等待上限 3 分钟
# ── 差异忽略规则 ────────────────────────────────────────────
# 本应用创建的资源主要是 ServiceMonitor 和 PrometheusRule,这些资源的
# 字段在部署后通常不会由外部组件更改,因此未配置 ignoreDifferences。
# 如果后续 Prometheus Operator 自动注入某些字段导致漂移,可在此追加规则。7.5 blackbox-exporter 子应用清单
apps/blackbox-exporter.yaml
sync-wave: "1",与 postgresql-exporter 同批次,部署拨测引擎容器:
# =============================================================================
# blackbox-exporter — 主动探测引擎子应用
# =============================================================================
# 适用范围:腾讯云 4C8G Ubuntu 24.04,K3s v1.34+ 单节点集群
# 部署模式:由父应用 root-app 自动发现并创建(属于 wave 1)
# sync-wave:1(在 wave 0 的 kube-prometheus-stack 健康后同步)
#
# 本 Application 的核心职责:
# 1. 在 exporters 命名空间中部署 Blackbox Exporter Deployment 和 Service
# 2. 通过 values/blackbox-exporter/values.yaml 定义探测模块(http_2xx、http_2xx_tls 等)
# 3. 开启自监控 ServiceMonitor,暴露 Exporter 自身运行指标给 Prometheus
# 4. 仅提供探测能力,不包含具体探测目标——探测目标由独立的 blackbox-targets 应用管理
#
# 关键依赖:
# - Wave 0 的 kube-prometheus-stack 必须已注册 Probe CRD,否则本应用虽能正常部署
# (因为本应用不创建 Probe),但后续 wave 2 的 blackbox-targets 将无法工作。
# - Prometheus 必须已配置 probeNamespaceSelector: {},才能发现 blackbox-targets
# 创建的 Probe 资源。此配置已在 kube-prometheus-stack 的 values 中完成。
# - Blackbox Exporter 的 Service 名称已通过 fullnameOverride 强制固定为
# "blackbox-exporter",确保 probe.yaml 中的 DNS 地址可解析。
#
# ⚠️ 部署前请将 <YOUR_GIT_REPO_URL> 替换为实际 Git 仓库地址
# =============================================================================
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: blackbox-exporter
namespace: argocd
# 附加标签便于在 Argo CD UI 中与监控栈其他应用统一筛选
labels:
app.kubernetes.io/part-of: gitops-observability
annotations:
# ── Sync-Wave 注解 ──────────────────────────────────────────────
# 数值为 "1",表示本应用在 wave 0 的 kube-prometheus-stack 健康后才开始同步。
#
# 为什么是 "1" 而不是 "0"?
# 虽然本应用仅部署 Exporter 自身(不依赖 Probe CRD 的 ServiceMonitor 等),
# 但为了整体架构的有序性和未来扩展,遵循分层原则:
# Wave 0:安装基础设施和 CRD(监控底座)
# Wave 1:安装采集器和引擎(PostgreSQL Exporter、Blackbox Exporter、Grafana 面板)
# Wave 2:下发业务目标(Probe CR)
# 此外,本应用的 ServiceMonitor 依赖于 Prometheus Operator 的 CRD,
# 而 Operator 是在 wave 0 中部署的,因此必须在 wave 0 之后执行。
argocd.argoproj.io/sync-wave: "1"
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
# ── 源配置:指向本地化 Helm Chart ─────────────────────────────
source:
repoURL: <YOUR_GIT_REPO_URL>
targetRevision: HEAD
# 指定仓库中的 charts/blackbox-exporter 目录,该目录是已下载并解压的完整 Helm Chart
path: charts/blackbox-exporter
helm:
# 固定的 release 名称,避免 Helm 默认的 "release-name-chart-name" 冗长格式
releaseName: blackbox-exporter
# valueFiles 路径相对于 Chart 目录解析。
# ../../ 回到仓库根目录,再进入 values/blackbox-exporter/
valueFiles:
- ../../values/blackbox-exporter/values.yaml
# ── 目标部署位置 ────────────────────────────────────────────
destination:
server: https://kubernetes.default.svc
# 与 PostgreSQL Exporter 共享 exporters 命名空间,实现基础设施组件资源隔离
namespace: exporters
# ── 同步策略 ─────────────────────────────────────────────────
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
# 自动创建 exporters 命名空间(若未在 bootstrap 阶段手动创建)
- CreateNamespace=true
# 重试策略:应对瞬时网络波动或仓库访问问题
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m
# ── 差异忽略规则 ────────────────────────────────────────────
# 本应用管理的资源相对简单,通常不会产生非预期的漂移,因此未配置 ignoreDifferences。
# 如果未来在 values 中添加了动态生成的内容(如证书),可在此追加规则。7.6 blackbox-targets 子应用清单
apps/blackbox-targets.yaml
sync-wave: "2",最后同步,确保 CRD 和 Exporter 全部就绪后再下发拨测目标 Probe CR:
# =============================================================================
# blackbox-targets — 主动探测目标子应用(Probe CR 管理)
# =============================================================================
# 适用范围:腾讯云 4C8G Ubuntu 24.04,K3s v1.34+ 单节点集群
# 部署模式:由父应用 root-app 自动发现并创建(属于 wave 2,最后同步)
# sync-wave:2(确保所有基础设施和 Exporter 都就绪后再下发探测目标)
#
# 本 Application 的核心职责:
# 1. 管理所有主动探测目标(Probe CR),不包含 Exporter 部署本身
# 2. 将探测目标与 Exporter 基础设施彻底解耦,实现业务目标独立变更
# 3. 使用原生 Kubernetes 清单(不经 Helm 渲染),简洁且无模板依赖
# 4. 在 monitoring 命名空间中创建 Probe,便于 Prometheus 就近发现
#
# 为什么探测目标要独立于 blackbox-exporter 应用?
# - 避免 Argo CD 资源归属冲突:若同一 Probe 被两个 Application 管理,
# Argo CD 会永久 OutOfSync
# - 变更频率不同:Exporter 是稳定基础设施,探测目标是业务配置,频繁变化
# - 部署顺序控制:探测目标必须在 CRD 和 Exporter 全部就绪后才能下发
#
# 关键依赖:
# - Wave 0 的 kube-prometheus-stack:必须已注册 Probe CRD
# - Wave 1 的 blackbox-exporter:必须已部署且 Service DNS 可解析
# (blackbox-exporter.exporters.svc:9115)
# - Prometheus 的 probeNamespaceSelector 已设为 {},可发现 monitoring
# 命名空间中的 Probe(已在 kube-prometheus-stack values 中配置)
#
# ⚠️ 部署前请将 <YOUR_GIT_REPO_URL> 替换为实际 Git 仓库地址
# =============================================================================
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: blackbox-targets
namespace: argocd
labels:
app.kubernetes.io/part-of: gitops-observability
annotations:
# ── Sync-Wave 注解 ──────────────────────────────────────────────
# 数值为 "2",表示本应用在所有子应用中最后同步。
#
# 为什么必须为 "2"?
# 本应用创建的 Probe CR 依赖以下两项前提条件:
# 1. Probe CRD 必须已注册 —— 由 wave 0 的 kube-prometheus-stack 完成
# 2. Blackbox Exporter 的 Service 必须已存在且 DNS 可解析 ——
# 由 wave 1 的 blackbox-exporter 完成
# 如果 Probe 在 CRD 注册前创建,Kubernetes API 会返回 "no matches for kind Probe"。
# 如果 Probe 在 Exporter 就绪前下发,Prometheus 会立即尝试通过
# blackbox-exporter.exporters.svc:9115 执行探测,导致连接失败或超时。
# 放在 wave 2 可确保所有依赖就绪后再挂载业务目标,实现 "底座 → 引擎 → 目标" 的严格顺序。
argocd.argoproj.io/sync-wave: "2"
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
# ── 源配置:指向原生 Kubernetes 清单目录 ─────────────────────
source:
repoURL: <YOUR_GIT_REPO_URL>
targetRevision: HEAD
# 不同于前面的子应用,这里直接指向 manifests/ 下的一个子目录,
# 因为 Probe 是简单的原生 Kubernetes CR,不需要 Helm 模板渲染。
# 不使用 Helm 的好处:
# - 无 values.yaml 维护负担
# - 清单内容一目了然,便于快速审查探测目标变更
# - 避免 Helm release 管理带来的额外复杂度
path: manifests/blackbox-targets
# 注意:没有 helm 块,因为此目录下是纯 YAML 文件,Argo CD 会
# 直接应用目录中的所有资源(通常是 probes.yaml)
# ── 目标部署位置 ────────────────────────────────────────────
destination:
server: https://kubernetes.default.svc
# 选择 monitoring 命名空间的原因:
# 1. Probe 需要被 Prometheus 发现,而 Prometheus 实例运行在 monitoring。
# 虽然 probeNamespaceSelector 已设为 {}(可发现任意命名空间的 Probe),
# 但将 Probe 放在与 Prometheus 相同的命名空间可以减少潜在的网络策略
# 和 RBAC 权限问题,且符合 "监控相关资源统一管理" 的原则。
# 2. 区分职责:exporters 命名空间放 Exporter 引擎,monitoring 放监控目标声明,
# 避免混淆。
namespace: monitoring
# ── 同步策略 ─────────────────────────────────────────────────
syncPolicy:
automated:
prune: true # 从 Git 中删除的 Probe 会在集群中自动清理,停止对旧 URL 的探测
selfHeal: true # 手动修改的 Probe 自动回滚到 Git 定义,防止私自更改探测目标
# 未设置 CreateNamespace=true,因为 monitoring 命名空间已在
# wave 0 的 kube-prometheus-stack 同步时创建(或 bootstrap 阶段手动创建)
# 未设置 ServerSideApply,因为 Probe 清单通常体积很小,不会触发
# annotation 超限问题,使用客户端 apply 即可
# ── 差异忽略规则 ────────────────────────────────────────────
# Probe 资源在部署后可能会被 Prometheus Operator 自动填充某些状态字段,
# 但目前版本的 operator 不会修改 spec 部分,因此暂不配置 ignoreDifferences。
# 如果未来出现 status 字段导致的假 OutOfSync,可添加对 /status 的忽略。8. Helm Chart 目录结构说明
背景说明:charts/ 目录存放完整的本地化 Helm Chart,包含所有子依赖 chart(subcharts),是实现离线部署的核心。所有文件均已存储在 Git 仓库,可直接使用,无需联网拉取。
8.1 目录总览
charts/
├── blackbox-exporter/
│ ├── Chart.yaml
│ ├── templates/
│ │ ├── configmap.yaml # Blackbox 探测模块配置
│ │ ├── deployment.yaml # Exporter Deployment
│ │ ├── _helpers.tpl # Helm 模板辅助函数
│ │ ├── probes.yaml # Probe CR 模板(由 values.probes 控制渲染)
│ │ ├── servicemonitor.yaml # Exporter 自监控 ServiceMonitor
│ │ └── service.yaml # Exporter Service
│ └── values.yaml # Chart 默认 values(被 values/ 目录覆盖)
│
├── kube-prometheus-stack/
│ ├── Chart.lock # 子 chart 版本锁定文件
│ ├── charts/ # 解压的子 chart(不可缺失,否则无法离线渲染)
│ │ ├── crds/ # Prometheus Operator CRD 定义(约 500KB+)
│ │ ├── grafana/ # Grafana 子 chart
│ │ ├── kube-state-metrics/ # kube-state-metrics 子 chart
│ │ └── prometheus-node-exporter/
│ ├── Chart.yaml
│ ├── templates/ # 主 chart 模板(Prometheus、Alertmanager 等)
│ └── values.yaml
│
└── postgresql-exporter/
├── Chart.yaml
├── templates/
│ ├── deployment.yaml
│ ├── _helpers.tpl
│ ├── prometheusrule.yaml # 内置告警规则模板
│ ├── servicemonitor.yaml
│ └── service.yaml
└── values.yaml
8.2 关键文件说明
文件路径 | 说明 | 注意事项 |
charts/kube-prometheus-stack/charts/crds/ | Prometheus Operator 安装的所有 CRD,包括 ServiceMonitor、PodMonitor、Probe 等 | 体积约 500KB+,超出 K8s annotation 限制,Application 必须开启 ServerSideApply=true |
charts/kube-prometheus-stack/charts/ | 所有子 chart 解压目录,包含 grafana、kube-state-metrics 等 | 不能为空目录,否则 Helm 渲染报错。执行 ls 验证各子 chart 目录存在 |
charts/blackbox-exporter/templates/probes.yaml | Probe CR 模板,通过 range .Values.probes 渲染 | 由于探测目标已解耦到 manifests/,此处必须保持 values.probes: [],禁止在 values.yaml 添加目标 |
charts/postgresql-exporter/templates/prometheusrule.yaml | PrometheusRule CR 模板,内含数据库监控告警规则 | 告警规则的 expr 表达式需在部署后结合实际指标名称验证,不同 Exporter 版本指标名可能变化 |
9. 部署执行步骤
9.1 部署前检查
在执行正式部署前,请逐项确认以下检查项均已通过:
检查项 | 验证命令 | 期望结果 |
kubectl 上下文正确 | kubectl config current-context | 显示目标集群的 context 名称 |
K3s 集群节点健康 | kubectl get nodes | 所有节点 STATUS 为 Ready |
Argo CD 运行正常 | kubectl get pod -n argocd | 所有 Pod READY 状态正常 |
Bootstrap 资源已创建 | kubectl get secret -n monitoring && kubectl get secret -n exporters | 两命名空间均有 acr-secret 等 Secret |
charts/ 子 chart 完整 | ls charts/kube-prometheus-stack/charts/ | 包含 grafana/、kube-state-metrics/ 等目录 |
本地 Helm 渲染通过 | helm template test ./charts/kube-prometheus-stack --values values/kube-prometheus-stack/values.yaml | 渲染无报错 |
Git 仓库 Argo CD 已连接 | Argo CD UI → Settings → Repositories | 仓库状态为 Successful |
9.2 执行部署
所有检查项通过后,执行以下命令启动部署流程:
步骤 1 推送配置到 Git 仓库
确保所有清单和 values 文件已提交
git add . && git commit -m "feat: 初始化 App of Apps 可观测性栈配置"
git push origin main步骤 2 部署 root-app 父应用
只需执行一次,之后由 Argo CD 接管
kubectl apply -f bootstrap/root-app.yaml -n argocd步骤 3 观察 Argo CD 自动同步进度
子应用将按 sync-wave 顺序依次创建
# 查看所有 Application 状态
kubectl get app -n argocd -w
# 查看 root-app 详细同步信息
argocd app get root-app --refresh步骤 4 实时查看组件 Pod 启动情况
# 监控 monitoring 命名空间 Pod 启动
kubectl get pod -n monitoring -w
# 监控 exporters 命名空间 Pod 启动
kubectl get pod -n exporters -w10. 部署验证清单
10.1 Argo CD 健康状态验证
# 查看所有 Application 状态
kubectl get app -n argocd
# 期望结果:所有 Application SYNC STATUS 为 Synced,HEALTH STATUS 为 Healthy
# 示例输出:
# NAME SYNC STATUS HEALTH STATUS
# kube-prometheus-stack Synced Healthy
# postgresql-exporter Synced Healthy
# blackbox-exporter Synced Healthy
# grafana-dashboards Synced Healthy
# blackbox-targets Synced Healthy
10.2 Sync-Wave 顺序验证
# 确认各子应用的 wave 注解配置正确
kubectl get app -n argocd -o json | jq '.items[] | {name: .metadata.name, wave: .metadata.annotations["argocd.argoproj.io/sync-wave"]}'期望返回:
{
"name": "blackbox-exporter",
"wave": "1"
}
{
"name": "blackbox-targets",
"wave": "2"
}
{
"name": "grafana-dashboards",
"wave": "1"
}
{
"name": "kube-prometheus-stack",
"wave": "0"
}
{
"name": "postgresql-exporter",
"wave": "1"
}10.3 CRD 注册验证
# 确认 Prometheus Operator 的 CRD 已完整注册
kubectl get crd | grep monitoring.coreos.com
# 期望输出包含(不限于):
# alertmanagers.monitoring.coreos.com
# prometheuses.monitoring.coreos.com
# servicemonitors.monitoring.coreos.com
# podmonitors.monitoring.coreos.com
# probes.monitoring.coreos.com
# prometheusrules.monitoring.coreos.com10.4 Secret 完整性验证
# 验证 monitoring 命名空间的所有 Secret
kubectl get secret -n monitoring
# 期望包含:acr-secret、grafana-admin-secret、prometheus-tls、grafana-tls
# 验证 exporters 命名空间的所有 Secret
kubectl get secret -n exporters
# 期望包含:acr-secret、postgres-exporter-secret
10.5 Prometheus Targets 验证
# 浏览器访问: https://prometheus.example.com
# 期望结果:所有抓取目标均为 UP 状态,无 DOWN 或 UNKNOWN10.6 Grafana 登录与面板验证
验证项 | 操作方式 | 期望结果 |
Grafana 可访问 | 通过 Ingress 域名访问 https://grafana.example.com | 正常加载登录页 |
管理员登录 | 使用 Bootstrap 阶段配置的 admin 账号密码登录 | 进入主仪表板页面 |
数据源配置 | Connections → Data sources | Prometheus 数据源显示 Data source connected |
PostgreSQL 面板 | Dashboards → 搜索 PostgreSQL | 找到并可正常打开 PostgreSQL Database 面板,数据非空 |
Blackbox 面板 | Dashboards → 搜索 Blackbox | 找到并可正常打开拨测面板,探测成功率图表有数据 |
获取管理员密码:
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d; echo11.Grafana 监控面板离线管理
11.1 解决的核心问题
在 Kubernetes / K3s 环境下,将 Grafana Dashboard 纳入 GitOps 管理,实现:
完全离线交付:Dashboard JSON 文件存储在 Git 仓库,无需在集群内访问外网
声明式管理:所有配置通过 YAML 描述,Git 即唯一可信来源
自动热加载:Grafana Sidecar 容器监听 ConfigMap 变化,无需重启 Grafana Pod
可重复操作:任意环境执行相同步骤,结果完全一致
方案 | 说明 | 本项目选择 |
|---|---|---|
Grafana UI 手动导入 | 不进 Git,无法版本控制 | ❌ |
Helm values 内嵌 | JSON 与 Helm chart 耦合,升级困难 | ❌ |
kubectl 手动创建 ConfigMap | 不可重复,依赖脚本,标签注入脆弱 | ❌ |
Kustomize configMapGenerator | 声明式、原生 ArgoCD 支持、零额外工具 | ✅ |
11.2技术原理
11.2.1 整体数据流
外网机器下载 Dashboard JSON
↓
清洗 JSON 文件
(移除 __inputs,替换数据源变量)
↓
提交到 Git 仓库
↓
ArgoCD 检测到变更
↓
ArgoCD 调用内置 Kustomize 渲染
kustomization.yaml → ConfigMap YAML
↓
ArgoCD 将 ConfigMap apply 到集群
namespace: monitoring
↓
Grafana Sidecar 容器
扫描带有 grafana_dashboard=1 标签的 ConfigMap
↓
Sidecar 将 JSON 注入 Grafana
Dashboard 自动出现在指定 Folder11.2.2 关键机制说明
Kustomize configMapGenerator
Kustomize 的 configMapGenerator 读取本地文件内容,自动生成包含完整 labels 和 annotations 的 ConfigMap YAML,替代原来需要 kubectl dry-run + Python 脚本注入标签的脆弱流程。
disableNameSuffixHash
Kustomize 默认在 ConfigMap 名称后追加内容哈希(如 grafana-dashboard-postgresql-7f8d9c2b),每次 JSON 变更后哈希不同,ArgoCD 会认为是新建资源而非更新。设置 disableNameSuffixHash: true 后名称固定,diff 稳定。
ServerSideApply
Dashboard JSON 嵌入 ConfigMap 后,metadata.annotations 中的 last-applied-configuration 字段会存储整个 JSON 字符串,超过 Kubernetes 262144 字节上限导致 apply 失败。启用 ServerSideApply=true 后 Kubernetes 使用服务端合并机制,绕过此限制。
Grafana Sidecar
kube-prometheus-stack 的 Grafana Helm Chart 默认部署一个 Sidecar 容器(grafana-sc-dashboard),持续 Watch 集群内所有带有 grafana_dashboard=1 标签的 ConfigMap,发现变化后自动将 JSON 文件写入 Grafana 的 Dashboard 目录,无需重启 Grafana 主进程。
11.3目录结构总览
gitops-argocd/
├── apps/
│ └── grafana-dashboards.yaml # ArgoCD Application 定义
└── manifests/
└── grafana-dashboards/
├── kustomization.yaml # Kustomize 核心配置(声明所有 Dashboard)
├── grafana-folder-config.yaml # Grafana Folder 预创建配置
└── dashboards/
├── postgresql-9628.json # PostgreSQL Dashboard JSON
└── blackbox-13659.json # Blackbox Exporter Dashboard JSON文件职责说明
文件 | 职责 |
|---|---|
| 告知 ArgoCD 监听哪个 Git 路径,如何同步到集群 |
| 声明将哪些 JSON 文件生成为 ConfigMap,附加哪些标签和注解 |
| 预先在 Grafana 中创建 Dashboard Folder(文件夹),避免 Dashboard 归入 General |
| Dashboard 原始 JSON,经过清洗,去除手动导入时的交互依赖 |
11.4核心配置文件说明
11.4.1 kustomization.yaml
manifests/grafana-dashboards/kustomization.yaml
# =============================================================================
# Grafana Dashboard Kustomize 编排文件
# =============================================================================
# 适用范围:腾讯云 4C8G Ubuntu 24.04,K3s v1.34+ 单节点集群
# 部署方式:由 Argo CD 子应用 grafana-dashboards 引用(apps/grafana-dashboards.yaml)
# sync-wave:1(依赖 wave 0 的 kube-prometheus-stack 先部署 Grafana 和 Sidecar)
#
# 本文件的核心职责:
# 1. 利用 Kustomize 的 configMapGenerator 将 dashboards/ 目录下的 JSON 文件
# 自动转化为 Kubernetes ConfigMap,无需手工编写冗长的 YAML
# 2. 为每个 ConfigMap 注入精确的标签(labels)和注解(annotations),
# 使其能够被 Grafana Sidecar 容器自动发现并加载到对应的 Folder 中
# 3. 通过 disableNameSuffixHash 固定 ConfigMap 名称,确保 Argo CD 的 diff
# 始终稳定——Kustomize 默认会在名称后追加内容哈希,导致每次变更产生新资源
# 4. 引用 grafana-folder-config.yaml 预创建 Dashboard Folder,保证面板不散落
# 在 General 目录下
#
# 架构前提:
# - Grafana 由 kube-prometheus-stack 部署在 monitoring 命名空间,其 Sidecar 容器
# 会持续 Watch 带有 label "grafana_dashboard: 1" 的 ConfigMap
# - Sidecar 根据 ConfigMap 的 annotation "grafana_folder" 将面板 JSON 放入对应
# 文件夹(Folder 由 grafana-folder-config.yaml 预定义,名称须一致)
# - 所有 Dashboard JSON 已经过清洗(移除 __inputs、__requires,置空 id,替换
# 数据源变量为实际名称),确保可离线注入
# =============================================================================
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# ── 全局命名空间 ──────────────────────────────────────────────
# Kustomize 生成的资源默认都会被添加上此命名空间。
# monitoring 是 Grafana 所在的命名空间,ConfigMap 必须部署在此命名空间中
# 才能被 Grafana Sidecar 扫描到(Sidecar 的 searchNamespace 设为 ALL,
# 但放在同命名空间可减少潜在的网络策略障碍)。
namespace: monitoring
# ── 预置资源 ─────────────────────────────────────────────────
# 引用同目录下的 grafana-folder-config.yaml,该文件定义了一批 Grafana Folder
# (如 Databases、Networking)。Kustomize 会将其一同渲染并确保先于 Dashboard
# ConfigMap 创建(资源声明顺序)。Folder 的 uid 会用于 Dashboard 的归属定位。
resources:
- grafana-folder-config.yaml
# ── ConfigMap 生成器 ─────────────────────────────────────────
configMapGenerator:
# ===========================================================================
# Dashboard 1:PostgreSQL Database Overview(ID: 9628)
# ===========================================================================
- name: grafana-dashboard-postgresql
namespace: monitoring
# files 字段将本地 JSON 文件内容作为 ConfigMap 的 data 键值。
# 语法:<ConfigMap中的键名>=<本地文件路径>
# 此处键名 postgresql-9628.json 是 Grafana Sidecar 读取的文件名,
# 确保 JSON 内容被挂载到 /tmp/dashboards/ 后以 .json 结尾即可。
files:
- postgresql-9628.json=dashboards/postgresql-9628.json
options:
# 【关键】禁用名称后缀哈希
# Kustomize 默认行为:在生成的 ConfigMap 名称后追加一个内容哈希值,
# 例如 grafana-dashboard-postgresql-7f8d9c2b。这样做的好处是当 ConfigMap
# 内容变化时,Deployment 能自动感知并滚动更新 Pod。但本场景是 Grafana
# Sidecar 通过 Watch 机制动态加载,Pod 无需重启,因此哈希后缀只会带来
# 严重问题:
# 1. 每次 Dashboard JSON 变更后,Argo CD 会检测到旧 ConfigMap 被删除、
# 新 ConfigMap 被创建,而非原地更新,导致同步状态频繁变化
# 2. 旧 ConfigMap 上的标签和注解不会被自动携带,可能出现 Folder 归属丢失
# 设置 disableNameSuffixHash: true 后,ConfigMap 名称保持固定,Argo CD 的
# diff 只是对 data 内容的差异比较,行为更符合 GitOps 的直觉。
disableNameSuffixHash: true
# ── 标签:让 Grafana Sidecar 发现并加载此 ConfigMap ─────
labels:
# Grafana Sidecar 的 Dashboard 扫描器会 Watch 所有带有此标签的 ConfigMap。
# 标签键和值必须与 kube-prometheus-stack 中 grafana.sidecar.dashboards
# 的配置严格一致,当前配置为:
# label: grafana_dashboard
# labelValue: "1"
# 若标签不匹配,Sidecar 会完全忽略此 ConfigMap,面板不会出现在 Grafana 中。
grafana_dashboard: "1"
# 附加标签:标识本资源属于可观测性栈,由 Kustomize 管理
app.kubernetes.io/part-of: observability-stack
app.kubernetes.io/managed-by: kustomize
app.kubernetes.io/component: grafana-dashboard
# ── 注解:控制 Dashboard 在 Grafana 中的存放位置和元数据 ─
annotations:
# grafana_folder:指定此 Dashboard 应归属到哪个 Grafana Folder。
# Folder 的 title 必须与 grafana-folder-config.yaml 中定义的某个
# folder title 一致(此处为 "Databases")。
# 若指定了一个不存在的 Folder,Dashboard 会被放入 General 文件夹。
grafana_folder: "Databases"
# 自定义注解:记录面板的来源和修订版本,方便审计
dashboard.grafana.com/source: "https://grafana.com/dashboards/9628"
dashboard.grafana.com/revision: "8"
# ===========================================================================
# Dashboard 2:Blackbox Exporter Overview(ID: 13659)
# ===========================================================================
- name: grafana-dashboard-blackbox
namespace: monitoring
files:
- blackbox-13659.json=dashboards/blackbox-13659.json
options:
disableNameSuffixHash: true
labels:
grafana_dashboard: "1"
app.kubernetes.io/part-of: observability-stack
app.kubernetes.io/managed-by: kustomize
app.kubernetes.io/component: grafana-dashboard
annotations:
grafana_folder: "Networking"
dashboard.grafana.com/source: "https://grafana.com/dashboards/13659"
# revision 可缺省,首次导入通常为 1
# ===========================================================================
# 扩展指引:如需新增 Dashboard,复制上面任意一个块并修改:
# - name(ConfigMap 名称,建议格式 grafana-dashboard-<用途>)
# - files 映射(键为 JSON 文件名,值为 dashboards/ 下的实际文件路径)
# - annotations 中的 grafana_folder(若需新 Folder,先在
# grafana-folder-config.yaml 中定义)
# - 其他标签和注解按需调整
# ===========================================================================11.4.2 grafana-folder-config.yaml
manifests/grafana-dashboards/grafana-folder-config.yaml
# =============================================================================
# Grafana Dashboard Folder 预创建配置
# =============================================================================
# 适用范围:腾讯云 4C8G Ubuntu 24.04,K3s v1.34+ 单节点集群
# 部署方式:由 Kustomize 统一渲染(manifests/grafana-dashboards/kustomization.yaml),
# 再由 Argo CD 子应用 grafana-dashboards 同步到集群
# 资源类型:ConfigMap(原生 Kubernetes 资源)
#
# 本文件的唯一职责:
# 在 Grafana 中预创建一组有意义的 Dashboard 文件夹(Folder),使后续注入的
# 面板能够自动归入对应的分类目录(如 Databases、Networking),而不是全部堆在
# General 文件夹中,提升可观测性面板的组织性和可维护性。
#
# 工作原理:
# kube-prometheus-stack 的 Grafana Chart 内嵌了一个 Sidecar 容器(通常名为
# grafana-sc-dashboard),它不仅会扫描 Dashboard JSON 的 ConfigMap,还能处理
# 一种特殊的 ConfigMap —— 其中包含一个 `folders.yaml` 的 data 键。
# 当 Sidecar 发现带有 `grafana_dashboard: "1"` 标签的 ConfigMap 且 data 中
# 有 `folders.yaml` 时,它会根据该 YAML 的定义调用 Grafana API 创建或更新
# Folder。这意味着 Folder 的声明可以与 Dashboard 一样,完全通过 Git 管理。
#
# 与 kustomization.yaml 的关系:
# 本文件作为 Kustomize 的 `resources` 被引用,会在 Dashboard ConfigMap 之前
# 被应用到集群中(虽然最终是并发提交,但先定义 Folder 再导入面板可以避免
# 面板首次出现时 Folder 尚未存在的短暂时间窗口)。Folder 的 title 会与
# 各 Dashboard ConfigMap 的 `grafana_folder` annotation 进行匹配,确保面板
# 被放入正确的文件夹。
#
# 安全与注意事项:
# - 本 ConfigMap 中的 Folder 定义是静态的,如需新增或删除 Folder,只需修改
# 此文件并提交 Git,Argo CD 会自动同步。
# - Folder 的 uid 是唯一标识符,修改 uid 会导致 Grafana 创建一个新 Folder,
# 原有面板不会自动迁移。因此 uid 一经设定,最好不要更改。
# - overwrite: true 允许 Sidecar 在 Folder 已存在时更新其名称等属性,避免
# 手动修改后在下次同步时丢失。
# =============================================================================
apiVersion: v1
kind: ConfigMap
metadata:
name: grafana-dashboard-folders
namespace: monitoring
# ── 标签:让 Grafana Sidecar 发现并处理此 ConfigMap ─────────
labels:
# Sidecar 必须依靠此标签来识别包含 Dashboard 或 Folder 定义的 ConfigMap。
# 键值必须与 kube-prometheus-stack 中 grafana.sidecar.dashboards 的配置
# 完全一致,此处为 grafana_dashboard: "1"。
# 注意:虽然这里管理的是文件夹而非面板,但 Sidecar 同样通过此标签识别。
grafana_dashboard: "1"
# 附加标签,标识资源所属体系和管理工具
app.kubernetes.io/part-of: observability-stack
app.kubernetes.io/managed-by: kustomize
app.kubernetes.io/component: grafana-folder
# 注解:额外元数据,Grafana Sidecar 不会直接使用,但方便运维审计
annotations:
description: "Grafana Dashboard 文件夹预创建配置,由 GitOps 管理"
# ── 核心数据:folders.yaml ───────────────────────────────────────
# data 字段中的键名必须是 `folders.yaml`,Grafana Sidecar 会专门
# 寻找这个键,并将其内容作为 Folder API 的配置加载。
data:
folders.yaml: |
# ============================================================
# Grafana Folder 定义文件
# 格式遵循 Grafana 的 HTTP API 结构
# ============================================================
apiVersion: 1
folders:
# ── Databases 文件夹 ─────────────────────────────────────
# 用于存放各类数据库相关的监控面板,如 PostgreSQL、MySQL、Redis 等。
- title: Databases
uid: folder-databases # 全局唯一标识符,建议使用前缀 "folder-" 避免与 Dashboard uid 冲突
overwrite: true # 允许 Sidecar 覆盖同 uid Folder 的名称等属性
# ── Networking 文件夹 ────────────────────────────────────
# 用于存放网络探测、HTTP/TCP 监控等面板,如 Blackbox Exporter 仪表板。
- title: Networking
uid: folder-networking
overwrite: true
# ── Infrastructure 文件夹 ────────────────────────────────
# 用于存放 Kubernetes 集群基础设施相关面板,如 Node Exporter、Kube-State-Metrics。
- title: Infrastructure
uid: folder-infrastructure
overwrite: true
# ── Applications 文件夹 ──────────────────────────────────
# 用于存放业务应用级别的自定义面板,可根据需要扩展。
- title: Applications
uid: folder-applications
overwrite: true
# 扩展指引:
# 如需新增 Folder,按上述格式追加即可。需注意:
# 1. title 必须与对应 Dashboard ConfigMap 的 annotation
# `grafana_folder` 值完全一致(区分大小写)。
# 2. uid 应保持稳定,不要随意修改,否则会导致 Folder 重建。
# 3. overwrite: true 建议保留,防止手动调整后被 Sidecar 回滚。11.4.3 apps/grafana-dashboards.yaml
apps/grafana-dashboards.yaml
# =============================================================================
# grafana-dashboards — Grafana 离线仪表板子应用
# =============================================================================
# 适用范围:腾讯云 4C8G Ubuntu 24.04,K3s v1.34+ 单节点集群
# 部署模式:由父应用 root-app 自动发现并创建(属于 wave 1)
# sync-wave:1(在 wave 0 的 kube-prometheus-stack 健康后同步,确保 Grafana 及 Sidecar 就绪)
#
# 本 Application 的核心职责:
# 1. 将 manifests/grafana-dashboards/ 目录下的 Kustomize 工程同步到 monitoring 命名空间
# 2. 利用 Kustomize 的 configMapGenerator 将 Dashboard JSON 文件转化为 ConfigMap,
# 并自动注入标签和注解,供 Grafana Sidecar 发现和加载
# 3. 通过 ConfigMap 同时创建 Dashboard Folder(文件夹),实现仪表板的组织分类
# 4. 以纯声明式、离线的方式管理面板,完全摆脱 Grafana 社区导入和手工操作
#
# 为什么使用 Kustomize 而不是 Helm?
# - Dashboard JSON 是静态文件,无需模板化。Kustomize 的 configMapGenerator
# 可以一键从 JSON 文件生成 ConfigMap,并自动附加标签/注解,比 Helm 更轻量
# - Argo CD 原生支持 Kustomize,无需额外渲染工具,简化部署链路
# - Kustomize 的 disableNameSuffixHash 选项确保 ConfigMap 名称稳定,避免
# 因内容变化导致资源重建(这对 Sidecar 热加载场景至关重要)
#
# 关键依赖:
# - Wave 0 的 kube-prometheus-stack 必须已部署 Grafana 实例及其 Sidecar 容器
# - Sidecar 配置的标签选择器(grafana_dashboard: "1")必须与本应用中
# Kustomize 生成的标签完全一致,否则面板无法被加载
# - Grafana Folder 已在 grafana-folder-config.yaml 中预定义,Dashboard 的
# grafana_folder 注解必须引用实际存在的 Folder title
#
# ⚠️ 部署前请将 <YOUR_GIT_REPO_URL> 替换为实际 Git 仓库地址
# =============================================================================
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: grafana-dashboards
namespace: argocd
labels:
# 统一标识,用于在 Argo CD UI 中筛选可观测性相关应用
app.kubernetes.io/part-of: gitops-observability
app.kubernetes.io/managed-by: argocd
annotations:
# ── Sync-Wave 注解 ──────────────────────────────────────────────
# 数值为 "1",表示本应用在 wave 0 的 kube-prometheus-stack 健康后才同步。
#
# 为什么是 "1" 而不是 "0"?
# 虽然 Dashboard ConfigMap 只是普通资源,不直接依赖 CRD,但它的注入目标
# (Grafana 及其 Sidecar)是在 wave 0 中部署的。如果 wave 0 尚未完成,
# ConfigMap 虽然能创建成功,但 Sidecar 可能还未启动,导致面板加载延迟
# 或需要在 Sidecar 就绪后手动触发重新加载。放在 wave 1 保证了消费者就绪。
argocd.argoproj.io/sync-wave: "1"
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
# ── 源配置:指向 Kustomize 工程目录 ─────────────────────────
source:
repoURL: <YOUR_GIT_REPO_URL>
targetRevision: HEAD
# 指向仓库中的 manifests/grafana-dashboards 目录,Argo CD 会检测到其中的
# kustomization.yaml,并自动使用 Kustomize 渲染资源。
# 该目录包含:
# kustomization.yaml —— Kustomize 核心配置,声明资源与生成器
# grafana-folder-config.yaml —— 预创建 Dashboard Folder 的 ConfigMap
# dashboards/ —— 存放清洗后的 Dashboard JSON 文件
path: manifests/grafana-dashboards
# 显式声明使用 Kustomize 渲染,空对象表示使用 Argo CD 内置的默认 Kustomize 版本。
# 如果需要对 Kustomize 版本或参数进行精细控制,可在此添加配置。
kustomize: {}
# ── 目标部署位置 ────────────────────────────────────────────
destination:
server: https://kubernetes.default.svc
# ConfigMap 必须部署在 Grafana 所在的命名空间。
# Grafana 由 kube-prometheus-stack 部署在 monitoring 命名空间,
# 其 Sidecar 会扫描同命名空间(或所有命名空间,取决于配置)中带有
# grafana_dashboard: "1" 标签的 ConfigMap。
namespace: monitoring
# ── 同步策略 ─────────────────────────────────────────────────
syncPolicy:
automated:
prune: true # Git 中删除的 Dashboard JSON 会同步清理对应 ConfigMap
selfHeal: true # 手动修改的 ConfigMap 会被自动还原,保证 Git 为单一事实源
syncOptions:
# 自动创建 monitoring 命名空间(作为兜底,通常 wave 0 已创建)
- CreateNamespace=true
# 【关键】启用 Server-Side Apply
# Dashboard JSON 文件(如 PostgreSQL Dashboard)可达几十 KB,嵌入 ConfigMap
# 后,Kubernetes 会在 metadata.annotations 中生成一个名为
# "kubectl.kubernetes.io/last-applied-configuration" 的注解,用于客户端 apply
# 的差异计算。该注解的大小随 JSON 内容线性增长,极易超过 262KB 上限。
# 一旦超限,Argo CD 会报错无法同步。启用 ServerSideApply=true 后,
# Kubernetes 使用服务端合并机制,不再依赖此巨型注解,从而避免该问题。
- ServerSideApply=true
# 【关键】尊重忽略差异规则
# 必须配合 ignoreDifferences 使用。如果此项未开启,即使定义了 ignoreDifferences,
# Argo CD 在某些同步模式下仍可能强行覆盖差异字段,导致同步循环。
# 开启后,Argo CD 会尊重下面的忽略规则,维持应用状态为 Healthy。
- RespectIgnoreDifferences=true
# ── 差异忽略规则 ────────────────────────────────────────────
# 目的:消除因 Kubernetes 自动注入的巨型注解导致的虚假 OutOfSync
ignoreDifferences:
# 虽然启用了 ServerSideApply,但 Argo CD 在某些情况下仍会对比 Git 中定义的
# ConfigMap 与集群中实际 ConfigMap 的完整 annotations。
# "kubectl.kubernetes.io/last-applied-configuration" 注解包含
# 上次 apply 的完整配置(含整个 Dashboard JSON),其内容每次都可能不同,
# 且不可预测。忽略此字段可确保 Dashboard 内容变更不会因为注解不一致
# 而被 Argo CD 反复尝试同步。
#
# 规则 1:忽略 PostgreSQL Dashboard ConfigMap 的 last-applied-configuration
- group: "" # 核心 API 组为空
kind: ConfigMap
name: grafana-dashboard-postgresql
jsonPointers:
# jsonPointer 中的 / 用 ~1 转义,因此 /metadata/annotations/kubectl.kubernetes.io~1last-applied-configuration
# 指向该特定注解字段
- /metadata/annotations/kubectl.kubernetes.io~1last-applied-configuration
# 规则 2:忽略 Blackbox Dashboard ConfigMap 的 last-applied-configuration
- group: ""
kind: ConfigMap
name: grafana-dashboard-blackbox
jsonPointers:
- /metadata/annotations/kubectl.kubernetes.io~1last-applied-configuration
# 扩展指引:
# 当新增一个 Dashboard 时,除了在 kustomization.yaml 中添加 configMapGenerator
# 条目外,还需要在此处追加一条忽略规则(复制上面的块并修改 name 为新的 ConfigMap 名称)。
# 否则新面板的 ConfigMap 可能也会因巨型注解而频繁 OutOfSync。11.5首次部署流程(PostgreSQL & BlackBox Dashboard)
11.5.1 下载 Dashboard JSON
# 进入 GitOps 仓库根目录:
cd ~/gitops-argocd
# 创建目录结构
mkdir -p manifests/grafana-dashboards/dashboards
# 下载 Dashboard JSON
curl -fsSL \
"https://grafana.com/api/dashboards/9628/revisions/8/download" \
-o manifests/grafana-dashboards/dashboards/postgresql-9628.json
curl -fsSL \
"https://grafana.com/api/dashboards/13659/revisions/1/download" \
-o manifests/grafana-dashboards/dashboards/blackbox-13659.json
# 验证下载
wc -l manifests/grafana-dashboards/dashboards/postgresql-9628.json
wc -l manifests/grafana-dashboards/dashboards/blackbox-13659.json
# 检查 JSON 中的数据源变量
# 可以将"postgresql-9628.json"替换为"blackbox-13659.json"
python3 -c "
import json
with open('manifests/grafana-dashboards/dashboards/postgresql-9628.json') as f:
d = json.load(f)
inputs = d.get('__inputs', [])
for i in inputs:
print(f'变量名: {i[\"name\"]} 类型: {i.get(\"type\")} pluginId: {i.get(\"pluginId\")}')
"11.5.2 清洗PostgreSQL 面板JSON
# 执行清洗脚本(动态解析版,针对 PostgreSQL 仪表盘)
python3 - << 'EOF'
import json
filepath = "manifests/grafana-dashboards/dashboards/postgresql-9628.json"
# ─── 配置区(根据实际环境修改) ─────────────────────────────────────────
POSTGRESQL_DATASOURCE_NAME = "PostgreSQL" # 实际 Grafana 中 PostgreSQL 数据源名称
# ──────────────────────────────────────────────────────────────────────────
with open(filepath, "r", encoding="utf-8") as f:
dashboard = json.load(f)
print("=== 清洗前信息 ===")
print(f" title: {dashboard.get('title')}")
print(f" id: {dashboard.get('id')}")
inputs = dashboard.get("__inputs", [])
print(f" __inputs 变量: {[i['name'] for i in inputs]}")
# 1. 收集 __inputs 中所有数据源变量及其 pluginId
datasource_map = {}
for inp in inputs:
if inp.get("type") == "datasource":
var_name = inp["name"]
plugin_id = inp.get("pluginId", "")
datasource_map[var_name] = plugin_id
print(f" 发现数据源变量: ${{{var_name}}} (pluginId={plugin_id})")
# 2. 移除 __inputs 和 __requires
dashboard.pop("__inputs", None)
dashboard.pop("__requires", None)
# 3. 将 id 置为 null,避免冲突
dashboard["id"] = None
# 4. 全文替换数据源变量引用
dashboard_str = json.dumps(dashboard, ensure_ascii=False)
for var_name, plugin_id in datasource_map.items():
old_ref = "${" + var_name + "}"
# 根据 pluginId 精确匹配,防止误替换其他类型数据源变量
if plugin_id == "postgres":
new_name = POSTGRESQL_DATASOURCE_NAME
else:
# 如果发现意外的数据源类型,给出警告并回退到配置名称
print(f" ⚠️ 意外的 pluginId={plugin_id} (变量 {var_name}),将默认替换为 {POSTGRESQL_DATASOURCE_NAME}")
new_name = POSTGRESQL_DATASOURCE_NAME
count = dashboard_str.count(old_ref)
dashboard_str = dashboard_str.replace(old_ref, new_name)
print(f" 替换 {old_ref} → \"{new_name}\" (共 {count} 处)")
dashboard = json.loads(dashboard_str)
# 5. 写回文件
with open(filepath, "w", encoding="utf-8") as f:
json.dump(dashboard, f, indent=2, ensure_ascii=False)
f.write("\n")
print("\n=== 清洗后验证 ===")
print(f" __inputs 已移除: {'__inputs' not in dashboard}")
print(f" __requires 已移除: {'__requires' not in dashboard}")
print(f" id 已置 null: {dashboard.get('id') is None}")
print(f" panels 数量: {len(dashboard.get('panels', []))}")
print(f"\n✅ 清洗完成:{filepath}")
EOF输出说明:
=== 清洗后验证 ===
__inputs 已移除: True
__requires 已移除: True
id 已置 null: True
panels 数量: 35
✅ 清洗完成:manifests/grafana-dashboards/dashboards/postgresql-9628.json11.5.3 清洗Blackbox 面板JSON
# 执行清洗脚本
# 注意:将下面 "Prometheus" 替换为步骤1中查到的实际数据源名称
python3 - << 'EOF'
import json, re
filepath = "manifests/grafana-dashboards/dashboards/blackbox-13659.json"
# ─── 步骤1结果填写区 ────────────────────────────────────────────────────────
# 将步骤1查到的 Prometheus 数据源 name 填入此处(区分大小写)
PROMETHEUS_DATASOURCE_NAME = "Prometheus" # ← 根据步骤1输出修改
# ────────────────────────────────────────────────────────────────────────────
with open(filepath, "r", encoding="utf-8") as f:
dashboard = json.load(f)
print("=== 清洗前信息 ===")
print(f" title: {dashboard.get('title')}")
print(f" id: {dashboard.get('id')}")
inputs = dashboard.get("__inputs", [])
print(f" __inputs 变量: {[i['name'] for i in inputs]}")
# 1. 收集 __inputs 中所有数据源变量名与其 pluginId
# 例:{"DS_PROMETHEUS": "prometheus"}
datasource_map = {}
for inp in inputs:
if inp.get("type") == "datasource":
var_name = inp["name"] # 如 "DS_PROMETHEUS"
plugin_id = inp.get("pluginId", "") # 如 "prometheus"
datasource_map[var_name] = plugin_id
print(f" 发现数据源变量: ${{{var_name}}} (type={plugin_id})")
# 2. 移除 __inputs 和 __requires
dashboard.pop("__inputs", None)
dashboard.pop("__requires", None)
# 3. 将 id 置为 null,避免与已有面板 ID 冲突
dashboard["id"] = None
# 4. 全文替换数据源变量引用
# 将 "${DS_PROMETHEUS}" 替换为实际数据源名称
dashboard_str = json.dumps(dashboard, ensure_ascii=False)
for var_name, plugin_id in datasource_map.items():
old_ref = "${" + var_name + "}"
# 根据 plugin_id 决定替换的数据源名称
if plugin_id == "prometheus":
new_name = PROMETHEUS_DATASOURCE_NAME
else:
# 其他类型数据源(如 loki、alertmanager)保持变量名(不常见)
new_name = PROMETHEUS_DATASOURCE_NAME
print(f" ⚠️ 未知 plugin_id={plugin_id},使用默认: {new_name}")
count = dashboard_str.count(old_ref)
dashboard_str = dashboard_str.replace(old_ref, new_name)
print(f" 替换 {old_ref} → \"{new_name}\" (共 {count} 处)")
dashboard = json.loads(dashboard_str)
# 5. 写回文件
with open(filepath, "w", encoding="utf-8") as f:
json.dump(dashboard, f, indent=2, ensure_ascii=False)
f.write("\n")
print("\n=== 清洗后验证 ===")
print(f" __inputs 已移除: {'__inputs' not in dashboard}")
print(f" __requires 已移除: {'__requires' not in dashboard}")
print(f" id 已置 null: {dashboard.get('id') is None}")
print(f" panels 数量: {len(dashboard.get('panels', []))}")
print(f"\n✅ 清洗完成:{filepath}")
EOF期望输出:
=== 清洗后验证 ===
__inputs 已移除: True
__requires 已移除: True
id 已置 null: True
panels 数量: 3
✅ 清洗完成:manifests/grafana-dashboards/dashboards/blackbox-13659.json11.5.4 本地验证
执行 kustomize 渲染:
kubectl kustomize manifests/grafana-dashboards/检查输出,确认:
ConfigMap 名称为
grafana-dashboard-postgresql(无哈希后缀)labels中包含grafana_dashboard: "1"annotations中包含grafana_folder: Databasesnamespace: monitoring
服务端预演(不真正写入集群)
kubectl apply -k manifests/grafana-dashboards/ \
--dry-run=server \
--namespace monitoring期望输出:
configmap/grafana-dashboard-blackbox configured (server dry run)
configmap/grafana-dashboard-folders configured (server dry run)
configmap/grafana-dashboard-postgresql configured (server dry run)11.5.5 提交到 Git
git diff --name-only应显示:
manifests/grafana-dashboards/grafana-folder-config.yaml
manifests/grafana-dashboards/kustomization.yaml以及新文件:
manifests/grafana-dashboards/dashboards/blackbox-13659.json
manifests/grafana-dashboards/dashboards/postgresql-9628.json暂存
git add manifests/grafana-dashboards/dashboards/blackbox-13659.json
git add manifests/grafana-dashboards/dashboards/postgresql-9628.json
git add manifests/grafana-dashboards/kustomization.yaml
git add manifests/grafana-dashboards/grafana-folder-config.yaml提交
git commit -m "feat(grafana): add Blackbox Exporter dashboard (ID: 13659) && add PostgreSQL Exporter dashboard (ID: 9628)"推送
git push origin main等待 ArgoCD 同步并验证
kubectl get application -n argocd确认 ConfigMap 已创建:
kubectl get configmap -n monitoring -l grafana_dashboard=1 | grep grafana-dashboard期望输出:
grafana-dashboard-blackbox 1 3h2m
grafana-dashboard-folders 1 3h58m
grafana-dashboard-postgresql 1 2d6h11.6 后续新增 Dashboard 标准流程
每次新增一个 Dashboard,只需重复以下 5 步,无需修改任何其他文件:
步骤 | 操作 | 备注 |
1 | 下载 Dashboard JSON 到 dashboards/ 目录 | 命名规范:{名称}-{ID}.json |
2 | 清洗 JSON(移除 inputs、requires,替换数据源变量,id 置 null) | 数据源变量名见 __inputs[].name |
3 | 在 kustomization.yaml 的 configMapGenerator 中追加一个新条目 | 复制已有条目修改名称和文件路径 |
4 | 若 Dashboard 归属新 Folder,在 grafana-folder-config.yaml 追加该 Folder | title 必须与 grafana_folder 注解值完全一致 |
5 | git add → git commit → git push | ArgoCD 自动同步 |
12 - Web UI 页面
Prometheus:
Grafana:
账号:interviewer
密码:interviewer
Argo CD:
账号:interviewer
密码:interviewer
注意:账号/密码前后不能有空格
13. 故障排查与回滚操作
13.1 常见故障排查
13.1.1 子应用同步失败 —— CRD 不存在
项目 | 说明 |
错误现象 | Argo CD 中子应用(postgresql-exporter 等)同步失败,报错 "no matches for kind ServiceMonitor in version monitoring.coreos.com/v1" |
根本原因 | kube-prometheus-stack(wave 0)未完全健康前,wave 1 子应用尝试创建 ServiceMonitor 等 CR,但 CRD 尚未注册 |
解决方案 | ① 确认 kube-prometheus-stack Application 状态为 Healthy(kubectl get app -n argocd) ② 手工重同步失败的子应用:argocd app sync postgresql-exporter ③ 检查子 Af根本原因pplication YAML 的 sync-wave 注解是否写错 |
13.1.2 镜像拉取失败(ImagePullBackOff)
项目 | 说明 |
错误现象 | Pod 状态为 ImagePullBackOff,describe 显示 "Failed to pull image" 或 "unauthorized: authentication required" |
排查步骤 | ① kubectl describe pod <pod-name> -n <namespace> 查看详细事件 ② kubectl get secret acr-secret -n <namespace> 确认 Secret 存在 ③ 对比 Secret 中的 docker-server 与 values.yaml 中的 registry 地址是否完全一致(含协议、端口) |
解决方案 | 重新创建正确的 acr-secret,或在 values.yaml 中修正 registry 地址后重新同步应用 |
13.1.3 Ingress TLS 无法访问 HTTPS
项目 | 说明 |
错误现象 | 浏览器访问 HTTPS 域名报 SSL 错误或 502/404 |
排查步骤 | ① kubectl get ingress -n monitoring 确认 Ingress 已创建,CLASS 为 traefik ② kubectl get secret prometheus-tls -n monitoring 确认 TLS Secret 存在且含 tls.crt 和 tls.key ③ kubectl logs -n kube-system deployment/traefik 查看 Traefik 日志 ④ 确认 Ingress annotations 包含 traefik.ingress.kubernetes.io/router.entrypoints: websecure |
解决方案 | 补充缺失的 TLS Secret 或修正 values.yaml 中 Ingress 的 hosts 与 secretName 映射(两者必须同时存在) |
13.1.4 Prometheus 无法发现跨命名空间 ServiceMonitor
项目 | 说明 |
错误现象 | Prometheus Targets 页面缺少 postgresql-exporter 或 blackbox-exporter 的抓取目标,且相关指标不存在 |
排查步骤 | kube-prometheus-stack values.yaml 中未配置跨命名空间选择器(serviceMonitorNamespaceSelector 等) |
解决方案 | 确认 values.yaml 中以下四组配置均已设置为 {}:serviceMonitorNamespaceSelector、podMonitorNamespaceSelector、ruleNamespaceSelector、probeNamespaceSelector |
13.2 回滚操作
Argo CD 支持两种回滚方式,根据场景选择:
13.2.1 UI 回滚(快速单次操作)
● 进入 Argo CD UI,找到需要回滚的 Application。
● 点击右上角 History and Rollback 按钮,查看历史同步记录。
● 选择目标版本,点击 Rollback,确认操作。
● 适用场景:紧急回滚单个应用,无需修改 Git 历史。
13.2.2 Git 回退
# 查看提交历史,找到需要回退的提交 SHA
git log --oneline -10
# 回退到指定提交(保留历史,生成新的 revert 提交)
git revert <COMMIT_SHA>
git push origin main
# Argo CD 自动检测 Git 变更并同步回上一版本
# 也可手动触发同步加速:argocd app sync <app-name>