简介:面向Kubernetes集群运维与监控人员的K8S集群监控资源镜像合集,汇总Prometheus监控体系所需的镜像清单与离线镜像包,适用于离线部署、内网受限环境下的监控搭建。资源共124个文件,其中57个YAML/yml资源清单用于定义部署与Service对象,16个gz镜像压缩包用于离线导入镜像仓库,另有14个txt说明、8个JSON配置以及alertmanager、amtool等组件与告警模板,整体大小约650.88MB。内含grafana.tar.gz、prometheus.tar.gz等常用镜像,可直接导入私有镜像仓库;YAML文件覆盖监控组件部署、服务发现等配置,JSON与txt提供告警规则和配置示例,方便快速搭建Prometheus与Alertmanager监控告警体系,并可根据实际集群调整参数。已有383人学习下载,适合需要离线部署或内网搭建K8S监控的运维工程师,能显著缩短镜像收集与配置编写时间。
1. K8S 监控离线部署:一套镜像合集能解决什么问题
在生产环境里折腾过 K8S 监控的人基本都遇到过同一个场景:服务器在内网、防火墙只开白名单、容器运行时是 containerd,你拿着docker pull的命令在跳板机上敲了半天,发现镜像一个都拉不下来,因为节点压根访问不了 Docker Hub。这种时候,一套提前打好的prometheus.tar.gz、grafana.tar.gz、alertmanager镜像合集就是后悔药。我这篇要拆的这套 K8S 集群监控资源镜像清单及镜像合集,核心就干三件事:把 Prometheus、Grafana、Alertmanager 的镜像离线打包,把多个tar.gz包与amtool等配套工具整理成完整清单,让你在隔离环境里照样把监控栈跑起来。适合正在做私有云交付、信创环境适配、离线机房部署的运维和开发,也适合刚接手 K8S 监控、不想在组件版本和镜像来源上白折腾的新手。
2. 镜像包里的角色:Prometheus、Grafana 与 Alertmanager 的组合从哪来
2.1 三个组件为什么绑在一起
先看这套资源里的实物:prometheus.tar.gz、grafana.tar.gz、alertmanager、amtool,还有一个.gitignore。用 K8S 做监控的人一眼就能认出来,这是最标准的开源监控三角组合。Prometheus 负责采集,Grafana 负责展示,Alertmanager 负责把异常变成告警推送出去。采集端可以挂在 K8S 里做prometheus-operator,也可以直接用单独的 StatefulSet 跑,这套资源里既然给的是镜像包,显然是为了直接导入后编排到 K8S 里用。
prometheus.tar.gz是核心,这个包里一般不止一个镜像,通常包含了 Prometheus 主服务镜像、node-exporter节点采集镜像,甚至可能包了kube-state-metrics。我在实际用的过程中发现,很多人拿到tar.gz之后最容易犯的错就是以为每个包只含一个镜像,其实docker save出来的压缩包里可能有多个镜像层,导入后要用docker images或者ctr images list逐个确认。grafana.tar.gz同理,里面一般是 Grafana 主镜像,有些打包的人会顺带把grafana-cli插件打进去。
alertmanager这个位置比较特殊,它有时候是单独的二进制,有时候是镜像,还有可能同时装了amtool这个命令行工具。amtool是 Alertmanager 的官方命令行客户端,用来做告警的查询、静默、路由测试,在脚本化运维里特别有用。这套资源把alertmanager和amtool并列放在清单里,说明打包者大概率是走二进制方式部署的,或者镜像和二进制都给了。我个人的观点是,在 K8S 环境里优先用镜像部署 Alertmanager,但在调试阶段用amtool二进制去连 service 验证告警路由,比每次看日志快得多。
.gitignore出现在这里有点意思,推测打包者是用 Git 管理配置文件的人的,把tar.gz大文件忽略掉,只留镜像清单的文本和 deploy 的 YAML。这也给使用者提了个醒:这套资源的价值不只是几个压缩包,镜像清单本身才是离线部署的第一手资料,拿到资源先看清单,别直接解压镜像。
2.2 Grafana 数据源与面板导入的逻辑
很多人在离线环境里导入了 Grafana 镜像,起来之后发现是一个空白的页面,数据源、仪表盘全都要重新配,一片茫然。这套资源里的grafana.tar.gz如果只包含服务镜像,那确实会有这个问题。但打包者大概率会把一个provisioning目录一起放进去,里面定义好了 Prometheus 数据源和默认面板,否则这套离线资源是不完整的。
常见做法是 Grafana 通过环境变量GF_PROVISIONING_PATH挂载配置目录,数据源文件是 YAML 格式的,面板则通过dashboardProviders来声明。如果这套资源里没有提供 provisioning 配置,我一般会自己补一份,一定要先确认镜像里的 Grafana 版本,因为 8.x 和 9.x、10.x 的配置结构有差异,主要是dashboardProviders和provisioning.yaml的路径不同。导入镜像后先docker run起一份,进去看/etc/grafana/provisioning目录是否存在,再决定自己的配置挂载方式。
另一个容易忽略的是 Grafana 的插件。离线环境里如果要用图表或者数据源插件,比如grafana-clock-panel、grafana-piechart-panel,必须提前打包进去,否则启动后插件加载失败。这套镜像合集如果只是基础 Grafana 镜像,插件这块可能需要自己补,我在拆包时习惯先查看tar.gz解压后的文件列表,看有没有/var/lib/grafana/plugins这个路径的记录,如果没有,说明是裸镜像。
2.3 版本不一致是最大的隐性成本
Prometheus、Grafana、Alertmanager 三者的版本匹配问题是离线部署里最悬的坑。这套资源里的镜像清单如果能列出具体 tag,我会建议你执行时严格按清单来,因为 Prometheus 2.4x 的 API 和 Alertmanager 0.2x 的配置字段有若干不兼容点。比如 Alertmanager 的route配置里repeat_interval字段,在旧版本还有,新版本在全局配置里默认设置了,改了有警告,不改也有警告,没有任何明确报错,纯靠人工观察。
从实际项目经验来看,拿prometheus.tar.gz导入后,Prometheus 配置里的alerting部分要特别留意alertmanagers的api_version,默认值是v1还是v2,取决于镜像内部编译时的默认配置。如果监听路径用错了,Prometheus 日志里会持续刷Error sending alerts,但是 Grafana 面板显示应用是正常的,这种时候去检查amtoolping 一下最见效,一键就能定位告警链路的连通性。
所以我的结论是:先拿这套资源的镜像清单对版本,再动手导入部署。不要觉得都是开源组件就随意组合,我在某个跨平台系统项目里吃过这种亏,Prometheus 和 Alertmanager 版本错配导致告警丢失了一个多星期,检查 YAML 检查了半天,最后才发现是镜像 tag 不一致。这一点在后面的实战章节里会反复出现。
3. 离线加载与接入集群:ctr、docker 与镜像重打标签的完整实操
3.1 K8S 使用 containerd 时的镜像导入
现在的 K8S 集群把容器运行时默认换成 containerd 之后,最常遇到的场景就是拿着docker save出来的tar.gz包,在节点上用docker load导入,然后发现 K8S 创建 Pod 时始终提示image pull error,或者ErrImageNeverPull。原因是 containerd 的 images 服务管理的是自己的命名空间,而docker load写入的是 docker 的命名空间,两边互不认账,K8S 也看不到 docker 导入的镜像。
正确的离线加载姿势是用ctr命令导入到正确的命名空间。常见的 K8S 环境里 containerd 的命名空间是k8s.io,在导入时如果不指定--namespace,默认是default,这样导入了也还是不能用,Pod 调度时仍然会去找k8s.io空间里的镜像。我一般会在节点上先执行ctr namespaces list确认当前的命名空间,然后按下面这样导入:
# 导入 prometheus 镜像包到 k8s.io 命名空间 ctr -n k8s.io images import --digests=true prometheus.tar.gz # 导入 grafana 镜像包 ctr -n k8s.io images import --digests=true grafana.tar.gz # 导入 alertmanager 镜像包(如果有单独文件) ctr -n k8s.io images import --digests=true alertmanager.tar.gz这条命令的逻辑是明确:-n k8s.io指定命名空间,--digests=true表示同时校验并导入镜像 digest 信息,避免后续镜像引用时出现 not found 的问题。有人会问为什么不加--all-platforms,离线环境里如果你的节点是 x86 架构,镜像本身也是 amd64 的,加了--all-platforms反而会多导入一批无用的 manifest 列表,占空间不说,还可能在 containerd 版本较老时触发解析异常。资源包里如果只有单一平台镜像,不建议加这个参数。
导入完成后,验证是一个不可省略的步骤。很多人在这一步偷懒,直接去写 Deployment,结果 YAML 里的 image tag 和实际导入的 tag 对不上。验证命令建议用ctr -n k8s.io images list配合grep,把镜像仓库名和 tag 完整打出来,再和镜像清单里的记录做核对。
# 列出 k8s.io 命名空间下的所有镜像 ctr -n k8s.io images list | grep prometheus ctr -n k8s.io images list | grep grafana ctr -n k8s.io images list | grep alertmanager这一步的输出会显示docker.io/library/prometheus:latest这样的完整地址,写 Deployment 时建议直接照抄。如果显示的结果是<UNKNOWN>或者只有 digest 没有 tag,说明打包时 image tag 信息丢了,最好用ctr images tag手动补一个,否则 YAML 写prometheus:2.45.0永远拉不到。
3.2 Docker 环境下的加载与重打标签
如果你的集群节点还跑着 docker 容器运行时,那加载就简单一些,用docker load就能搞定。注意一个细节:tar.gz是 gzip 压缩的,docker load支持直接读压缩格式,不用先解压。我之前见过有人先把tar.gz解压成tar再docker load,多此一举,如果磁盘空间不是非常紧张,直接投喂给 docker 即可。
# 加载 prometheus 镜像包 docker load -i prometheus.tar.gz # 加载 grafana 镜像包 docker load -i grafana.tar.gz # 加载后查看镜像列表确认 tag docker images | grep -E "prometheus|grafana|alertmanager"加载之后最常见的需求是重打标签,因为离线打包时镜像仓库地址可能带着私有仓库域名,到你环境里不生效。我建议统一打成localhost或者你内网 Harbor 的地址。注意重打标签只是一个引用操作,不会复制镜像层,所以速度很快:
# 假设原镜像为 myrepo/kube-prometheus/prometheus:v2.45.0 docker tag myrepo/kube-prometheus/prometheus:v2.45.0 prometheus:local docker rmi myrepo/kube-prometheus/prometheus:v2.45.0这里用docker rmi删掉原 tag 是可行,因为镜像层还保留着,重新 tag 的 prometheus:local 引用的还是那套层。但不建议删得太急,K8S 的 Deployment 里 image 地址推送引用的是docker.io默认仓库时,docker 会自动加library/前缀,如果 tag 之后没有 push 到一个私有仓库,不同节点之间还是互相看不见镜像。所以如果你的环境是多节点的,更推荐把镜像推送到内网 Harbor,而不是每台节点单独docker load。
3.3 验证镜像在 K8S 里的真可用性
镜像导入了,tag 也对了,不代表 K8S 真的能拉起来。最有效的一招是直接创建一个临时 Pod 去跑这个镜像,比如用 Nginx 一样的思路来验证 Prometheus 镜像的 entrypoint 能否正常执行。因为 Prometheus 镜像默认的 entrypoint 是/bin/prometheus,如果你直接docker run不带参数,它会以默认配置启动,监听 9090,这没问题。但如果镜像里的 NameSpace 机制有问题,容器起来就退出。
在 K8S 里更靠谱的办法是直接创建一个 Job,拉起来之后看日志状态:
kubectl run prom-test --image=prometheus:local --restart=Never --rm -it -- /bin/prometheus --version如果这条命令能够输出版本号,说明镜像在 K8S 的调度链路里是真正可用的。如果报Failed to create pod sandbox,去节点上crictl images list看看镜像是否真的在 containerd 的k8s.io空间里,这一步能区分出是导入问题还是镜像本身的问题。
我在实际项目里见过一种现象:ctr images list能看到镜像,但 K8S 创建 Pod 依然报 image not found。原因通常在于 YAML 里写的 image 地址带了 docker.io 前缀,而导入时保存的镜像名是docker.io/library/prometheus:latest,这种其实是一致的;但如果打包时镜像名被简化成prometheus:latest而不是docker.io/library/prometheus:latest,K8S 会用默认规则拼成前者,仍然能匹配。真正会出问题是 YAML 写的是prom:latest,和打包名prometheus:latest对不上,只能重新 tag 或改 YAML。这类小差异,用kubectl describe pod看得最直观,里面 Image 字段写得清清楚楚。
4. Alertmanager 告警链路:amtool 手工验证与路由调试技巧
4.1 Alertmanager 配置的写法与生效逻辑
Alertmanager 在这套资源里出现频率高,说明打包者把它当作标配。配置上最核心的是alertmanager.yml,这段 YAML 里定义了解析告警的route和receivers。离线环境下,Alertmanager 的功能依然是对外发送到 Webhook、Email、企业微信等渠道,像收到的告警怎么分组、怎么收敛、怎么静默,都写在里面。
一个典型的配置包含全局配置、路由树和接收器,注意 Alertmanager 对配置缩进极其敏感,tab 和空格混用会导致配置加载失败,而且它的启动日志里只有一句Loading configuration file failed,没有具体行号,排查起来非常费劲。我一般会先本地启动一次 Alertmanager,确认配置能被加载:
# 在解压目录或配置目录执行语法检查 amtool check-config alertmanager.ymlamtool check-config是个被低估的命令,它返回的 exit code 是 0 还是 1,直接告诉你配置能不能用。如果报错,它会给出具体的 YAML 解析错误定位,比直接启动容器看日志快很多。这个命令在资源里的amtool编译包已经包含,不需要装任何额外东西。
在写路由时,一个经典误区是试图在route里写group_by: ['instance'],但真正会用到的场景是告警太多时先按alertname分组,同组告警只发一次,然后用group_wait控制首次告警的通知延迟。我看到很多初写这个配置的人,一个服务一个 receiver,三十几个服务写了三十几段 receiver,结果告警轰炸根本控制不住。正确思路是先用一套默认 receiver,把continue: true的节点夹在中间,特殊服务走特殊的match分支,其余全部落到默认分支。配置里routes的顺序是自上而下匹配,这一点特别重要,先匹配的先生效。
4.2 amtool 模拟告警与验证路由
amtool不只是检查配置,它更厉害的用法是直接模拟一条告警发进 Alertmanager,用来验证路由是否真的把告警送到了预期 receiver。这种方式在 Alertmanager 容器里做最合适,因为amtool的二进制可以直接在容器外执行,但--alertmanager.url要指向 K8S 的 Service 地址。
假设 Alertmanager 部署在monitoring命名空间,Service 名是alertmanager-svc,端口 9093,可以这样验证:
# 模拟一条 test 告警 amtool --alertmanager.url=http://alertmanager-svc.monitoring:9093 alert add --alertmanager.url=http://alertmanager-svc.monitoring:9093 \ alertname=TestAlert severity=critical instance=test-01 job=demo # 查看当前活跃告警 amtool --alertmanager.url=http://alertmanager-svc.monitoring:9093 alert list这里加了两次--alertmanager.url是典型的命令行写法冗余,实际上第一个参数已经作用于整体,后面重复只是防止误用。验证逻辑是:alert add模拟一条告警进来,走一遍route匹配,如果有 receiver 响应,可以在 Alertmanager 的 Web UI 的status页看到这条告警及标签。如果配了 Webhook receiver,后端接口会收到 JSON 推送,这条链路就完全打通了。
amtool alert list的输出会显示告警名称、标签、状态。如果alert add之后列表里看不到告警,原因多半是 Alertmanager 的集群模式节点间同步延迟,或者--alertmanager.url的地址没走到真实节点,这种情况用 Service 的 ClusterIP 访问比用节点 IP 访问更稳定,尤其是多个副本时。我在一次排查中就遇到过 Pod IP 漂移导致 amtool 连接失败的问题,后来强制走 headless service,问题解决。
除了模拟告警,amtool silence也是前期调试离不开的命令。告警联调时最怕一个深渊告警反复触发,把自己真实的告警通道刷爆,提前把某个标签静默掉,再去做验证:
# 静默所有 severity=critical 的告警 15 分钟 amtool --alertmanager.url=http://alertmanager-svc.monitoring:9093 silence add --duration=15m 'severity="critical"'参数含义是明确的:--duration是持续时间,单引号内是匹配标签的 matcher 字符串,这个语法和 PromQL 的 label matcher 一致。加静默之后alert add的验证结果会被静默吞掉,不影响实时数据,是联调阶段不可缺少的保护伞。等到验证完毕,再用amtool silence expire过期掉这个静默,防止误接告警。
5. 避坑与常见问题:镜像加载与监控部署的七个典型踩坑记录
5.1 ctr 导入后 K8S 还是报镜像不存在
现象:在节点上执行ctr -n k8s.io images import prometheus.tar.gz成功,镜像列表能看到,但 K8S 创建 Pod 时仍然提示Failed to pull image xxx: image not found。
原因:这个是我遇到过最多的场景,常见原因有两种:一是 YAML 里 image 地址带了某个私有仓库域名,比如harbor.example.com/monitoring/prometheus:v2.45.0,但你导入的镜像保存名是prometheus:latest,两边地址完全不匹配;二是导入到命名空间的时候没带-n k8s.io,镜像落到 default 空间,K8S 根本不去那边找。
解决:用kubectl edit deploy修改 image 字段,改成ctr images list里面的完整名字;如果 YAML 里的地址恰好是目标仓库地址,那就直接把镜像重打标签再导入一遍。注意条件允许时还可以用crictl images list查看 crictl 视角的镜像列表,这个工具的读取路径和 kubelet 一致,不经过 containerd 的 client 直连,能反映真实情况。
5.2 docker load 成功但 Pod 一直 CrashLoopBackOff
现象:用docker load -i grafana.tar.gz成功导入,docker images能看到,K8S 也成功调度到了节点,但 Pod 反复重启,日志只有Error: failed to start container。
原因:根本原因基本不是镜像损坏,而是运行时环境变量不兼容。Grafana 容器在启动时需要写/var/lib/grafana目录,如果宿主机目录权限不足,容器直接启动失败。还有一个常见情况是镜像里包含/etc/grafana/ldap.toml这类配置文件,但挂载 ConfigMap 时覆盖了整个/etc/grafana目录,把默认配置覆盖没了,启动时缺少必要配置。
解决:先看日志定位,kubectl logs如果出现mkdir /var/lib/grafana: permission denied就chmod 777目录;如果是配置缺失问题,把 ConfigMap 的挂载路径细分到/etc/grafana/grafana.ini而不是整个目录,或者把 provisioning 挂载到/etc/grafana/provisioning。在我的经验里,宁可多写几个细粒度挂载,也不要直接挂整个目录,那是自寻烦恼。
5.3 Alertmanager 容器起来但告警推不出去
现象:Alertmanager Pod Running,Web UI 能打开,告警列表里也有数据,但 receiver 始终收不到推送,日志里没有任何明显报错。
原因:这属于 Alertmanager 的高优排查场景,但要区分两种可能:一是 route 配置把告警分组后没有匹配到任何 receiver,Alertmanager 会丢弃该告警且无日志;二是配置里 receiver 写的是邮箱,但 SMTP 服务器鉴权信息缺失,Alertmanager 在日志里只会提示Failed to send notification,不会在 UI 里显示。
解决:先用amtool config routes查看当前配置解析出来的实际路由树,再用amtool alert add模拟一条告警,观察这条告警最终走到哪个 receiver。如果走完仍没有推送,去 Alertmanager 容器里/alertmanager目录下找日志文件,或者用kubectl logs alertmanager-pod集中看 notification 相关日志。我在项目里发现 SMTP 配置的auth_password必须用引号括起来,如果密码里有特殊字符没加引号,YAML 解析报错要到重启容器时才显形。
5.4 多个 tar 包解压后包含重复的 prometheus.tar.gz 文件
现象:下载的镜像合集里居然有四个同名prometheus.tar.gz,而且文件大小相同,解压之后内容一致。
原因:这多半是打包者上传时重复上传了,或者是自动生成清单时的拷贝残留。不是资源损坏,只是冗余。
解决:用md5sum校验四个文件的哈希值,相同的留一个就行;不同的按大小和下载时间区分。部署时按原清单里列出的顺序加载,不要全盘导入,以免多个 tag 互相覆盖。这个坑看似无害,但如果不小心把旧的导入覆盖了新的 tag,后续排查镜像版本对不上时非常让人头疼。
5.5 Grafana 里 Prometheus 数据源配置错误
现象:Grafana 界面能打开,仪表盘加载出来但所有图表显示No data,检查数据源却显示Success,但查询返回为空。
原因:数据源连通但查询无数据,通常不是网络问题,而是 Prometheus 数据的namespace或job选择器不对。比如你部署了node-exporter,但抓取目标的 job 名是kubernetes-service-endpoints(kube-prometheus-stack 默认名),Grafana 默认面板用的 job 名是node-exporter,对不上自然没数据。
解决:先去 Prometheus 的/api/v1/targets看 targets 的 job 标签和 health 状态,然后把 Grafana 面板的 job 变量改成 targets 里实际的名字。这套资源如果带的是通用面板,大概率默认变量是node-exporter,但是在 kube-prometheus-stack 环境中实际 job 名会变成kubernetes-service-endpoints,要把 dashboard 的 job label 值改成匹配项,这个改了数据立马就出来了。如果你的集群里跑了多个 Prometheus,还要确认抓取路径里的honor_labels配置,否则标签会被覆盖掉。
6. 进阶用法:把镜像合集改造成离线监控产物并固化流程
拿到这套 K8S 集群监控资源镜像清单及镜像合集,只完成导入部署属于基础操作,真正有价值的是把它变成一套可以复用的交付物。我通常会在导入完成后,把三件事写入固定的操作脚本,避免每到一个新环境重新踩一遍坑。
第一件是创建一份deploy-all.sh,里面把ctr images import、kubectl apply -f、amtool check-config串起来,并且强制在 apply 之前做配置校验,防止错误配置直接上生产。第二件是生成一个images.list文件,把所有镜像的完整仓库名和 tag 记录下来,后续打补丁包或者升级组件时,全靠这个清单来核对。第三件是把alertmanager.yml和 Prometheus 的alert-rules.yaml放到 Git 仓库管理,任何改动都留下变更记录,不然上线三个月后根本不知道规则是哪天改的。
实际操作层面,在部署完成之后最好先跑一遍验证命令组合,按顺序确认链路完整性:
# 检查 Prometheus target 是否健康 curl -s http://prometheus-svc:9090/api/v1/targets | jq '.data[].health' # 检查 Alertmanager 配置是否合法 amtool check-config /etc/alertmanager/alertmanager.yml # 模拟一条测试告警并观察推送 amtool --alertmanager.url=http://alertmanager-svc.monitoring:9093 alert add alertname=BootstrapTest severity=info这一套三步走完,核心链路就全部确认过了。如果你面对的是一批新节点,建议把这些命令固化到 Ansible 或 shell 脚本里。环境变多了以后,人会累、会遗忘,脚本不会。
说到最后一条习惯,其实是我自己吃过亏才养成的:第一次拿到这套镜像合集时,我直接在节点上docker load完就开始部署,只验证了镜像能显示,没有验证 tag 是否与 YAML 一致,结果一个 StatefulSet 调度到第二个节点时拉镜像失败,排查了三个多小时才意识到节点上没有这个镜像。从那以后,我每次部署完镜像,都会强制走一遍ctr images list加kubectl run临时 Pod 的验证流程,再做后续编排。这套流程用到现在没再翻过车,也算是一点拿得出手的血泪经验,希望帮到你。
本文还有配套的精品资源,点击获取