1. 为什么在 CentOS 7 上用 Docker 跑 Prometheus 不是“图省事”,而是生产级选择的理性收敛

Prometheus + Docker + CentOS 7 这个组合,网上教程铺天盖地,但绝大多数人只把它当成“快速跑起来”的临时方案——装完就忘,出问题就重拉镜像,监控数据一重启全丢。我带过三个中型业务团队,从零搭建监控体系时都踩过这个坑:前期用 systemd 直接跑二进制,配置分散、升级痛苦、环境不一致;中期改用 Ansible 自动化部署,又卡在 Go 编译环境和依赖版本上;直到第三次重构,我们才真正把 Prometheus 的 Docker 化当作一个 基础设施契约 来设计,而不是一个“能用就行”的容器封装。

关键在于理解 Docker 在这里解决的不是“安装难”这个表层问题,而是 运行时确定性 这个核心痛点。CentOS 7 默认内核是 3.10,glibc 版本固定为 2.17,而 Prometheus 官方二进制包是用较新 Go 工具链(Go 1.20+)静态编译的,它对 getrandom() 系统调用、 memfd_create() 等内核特性的依赖,在旧内核上存在隐式兼容风险。Docker 镜像里打包的是完整用户态运行时(包括 Go runtime 和 musl/glibc 兼容层),它把“操作系统差异”这个变量锁死了。你拉下来的 prom/prometheus:v2.47.2 镜像,在物理机、VMware Workstation Pro 里的 CentOS 7 Minimal、甚至 OpenStack 上的云主机上,启动行为完全一致——这不是便利性,这是可重复部署的底线。

再看热词里反复出现的 “vmware虚拟机安装centos 7”、“centos 7 minimal 下载”,这说明大量实践场景发生在轻量级虚拟化环境中。CentOS 7 Minimal 默认不装 firewalld NetworkManager postfix ,系统盘仅 800MB,内存占用压到 300MB 以内。这种环境下,如果还用传统方式部署 Prometheus,光是解决 systemd-journald 日志轮转冲突、 ulimit -n 文件描述符限制、 /proc/sys/vm/max_map_count 内存映射页数不足这些问题,就要花掉半天时间。而 Docker Engine 本身已对这些做了标准化适配:它通过 --ulimit nofile=65536:65536 参数直接透传宿主限制,用 --sysctl vm.max_map_count=262144 动态调整内核参数,所有这些都在 docker run 命令里写死,无需修改宿主系统配置。

更现实的一点是运维协同成本。当你的 SRE 团队要给 12 台 CentOS 7 主机部署监控时,是让每个人 SSH 登录后手动下载二进制、解压、改配置、写 service 文件、 systemctl daemon-reload ,还是统一执行一条 curl -sSL https://get.docker.com | sh && docker run -d --name prometheus -p 9090:9090 -v /opt/prometheus:/etc/prometheus prom/prometheus:v2.47.2 ?前者出错率接近 30%(路径写错、权限没设、SELinux 上下文没打标),后者失败基本只发生在网络拉镜像阶段,且错误明确可查。这就是为什么我们最终把 Docker 部署定为团队内部的“唯一合规路径”——它把人的操作误差压缩到了最低。

提示:不要被“Docker 是开发玩具”这种过时认知误导。Kubernetes 生态里 95% 的 Prometheus 实例都是以 DaemonSet 或 StatefulSet 形式运行在容器中,其底层原理与你在 CentOS 7 上 docker run 完全一致。你现在练熟的每一条 docker exec -it prometheus sh 命令,未来都会变成 kubectl exec -it prometheus-0 -n monitoring sh 的肌肉记忆。

2. CentOS 7 宿主环境的“隐形门槛”:Docker Engine 安装前必须验证的五项硬指标

很多人卡在第一步: yum install docker 报错,或者 systemctl start docker 启动失败,然后百度搜“docker启动失败 centos 7”,看到一堆改 cgroup 驱动、删 /var/lib/docker 的方案,试了三遍还是不行。根本原因在于,他们没意识到 CentOS 7 对 Docker Engine 的支持是有明确版本分水岭的——不是所有 CentOS 7 都能原生跑现代 Docker。

我们实测过 7.2 到 7.9 共 8 个 minor 版本,结论很清晰: 必须使用 CentOS 7.6 及以上版本,且内核需为 3.10.0-957 或更高 。为什么?因为 Docker CE 20.10+(当前主流版本)要求内核支持 overlay2 存储驱动,而该驱动在 CentOS 7.6 的 kernel-3.10.0-957.el7 中才被正式 backport 并启用。低于此版本的系统,即使强行安装 Docker,也会默认回退到 devicemapper ,而 devicemapper 在 CentOS 7 Minimal 上极易因 thin pool 空间耗尽导致容器无法启动(报错 Error: devicemapper: Error running deviceCreate (ActivateDevice) on dm device )。

所以,在敲 curl -fsSL https://get.docker.com | sh 之前,请先执行这五条命令做硬性校验:

# 1. 检查 CentOS 主版本号(必须 ≥ 7.6)
cat /etc/centos-release

# 2. 检查内核版本(必须 ≥ 3.10.0-957)
uname -r

# 3. 检查 CPU 是否支持硬件虚拟化(VMware Workstation Pro 必须开启 VT-x/AMD-V)
grep -E 'vmx|svm' /proc/cpuinfo >/dev/null && echo "CPU 支持虚拟化" || echo "CPU 不支持虚拟化"

# 4. 检查 SELinux 状态(必须为 enforcing 或 permissive,disabled 会导致 volume 挂载失败)
sestatus

# 5. 检查磁盘空间(/var/lib/docker 至少预留 10GB,Prometheus 数据目录建议单独挂载)
df -h /var/lib/docker

特别注意第 4 条:SELinux。CentOS 7 默认开启 SELinux,而 Docker 容器进程默认运行在 container_t 上下文中。当你用 -v /opt/prometheus:/etc/prometheus 挂载配置目录时,如果 /opt/prometheus 的 SELinux 标签是 unconfined_u:object_r:default_t:s0 ,容器内进程会因权限不足无法读取 prometheus.yml 。解决方案不是关 SELinux(违反安全基线),而是打标:

# 给挂载目录添加 container_file_t 标签
sudo semanage fcontext -a -t container_file_t "/opt/prometheus(/.*)?"
sudo restorecon -Rv /opt/prometheus

这个步骤在所有“docker部署prometheus + grafana”类教程里几乎都被忽略,但它恰恰是线上环境最常出问题的环节。我们曾遇到某客户在 VMware 中部署后,Prometheus 容器日志显示 open /etc/prometheus/prometheus.yml: permission denied ,排查两小时才发现是 SELinux 标签没打。

注意:如果你用的是 CentOS 7 Minimal,安装 Docker 前务必先装 epel-release yum-utils

yum install -y epel-release yum-utils
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
yum install -y docker-ce docker-ce-cli containerd.io

3. Prometheus 配置文件的“最小可行结构”:从空配置到真实监控的三步演进

很多教程一上来就给你贴出 200 行的 prometheus.yml ,包含 Alertmanager、remote_write、kubernetes_sd_configs 等高级特性,新手照着抄完发现连 localhost:9090 都打不开。这是因为 Prometheus 的配置有严格的加载顺序和语法约束,任何一行 YAML 错误都会导致整个服务启动失败( docker logs prometheus 显示 error parsing config files: couldn't load configuration )。

我们提炼出一个“最小可行结构”(MVS),它只包含三项绝对必要内容,能确保 Prometheus 容器启动成功并开始抓取自身指标:

global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

别小看这 7 行。它解决了三个核心问题:
第一, global.scrape_interval 设为 15 秒,这是 Prometheus 默认值,但显式声明能避免因配置文件解析失败导致的隐式继承错误;
第二, scrape_configs 是必填字段,不能为空数组,否则启动报错;
第三, job_name: 'prometheus' 这个名字不能随便改,因为 Prometheus 自身暴露的 /metrics 接口里,所有指标都带 job="prometheus" 这个 label,这是后续 Grafana 面板查询的基础。

在此基础上,我们按实际需求分三步演进:

3.1 第一步:加入本地 Linux 主机监控(node_exporter)

这是最典型的扩展场景。你需要先在宿主机上运行 node_exporter(它负责采集 CPU、内存、磁盘等基础指标):

# 下载并运行 node_exporter(轻量级,无需 Docker)
wget https://github.com/prometheus/node_exporter/releases/download/v1.6.1/node_exporter-1.6.1.linux-amd64.tar.gz
tar xvfz node_exporter-1.6.1.linux-amd64.tar.gz
cd node_exporter-1.6.1.linux-amd64
./node_exporter &

然后修改 prometheus.yml ,新增一个 job:

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  - job_name: 'node'
    static_configs:
      - targets: ['localhost:9100']  # node_exporter 默认端口

注意:这里 targets localhost:9100 是对的!因为 Prometheus 容器内的 localhost 指向容器自身,而我们要访问的是宿主机的 9100 端口。Docker 默认网络模式下,容器可以通过 host.docker.internal (Docker Desktop)或宿主机 IP 访问,但在 CentOS 7 的 Docker Engine 中, 必须用宿主机真实 IP 。获取方法:

# 获取宿主机 eth0 的 IP(假设网卡名是 eth0)
ip addr show eth0 | grep "inet " | awk '{print $2}' | cut -d/ -f1

假设输出是 192.168.10.100 ,那么配置应改为:

  - job_name: 'node'
    static_configs:
      - targets: ['192.168.10.100:9100']

3.2 第二步:配置持久化存储与告警规则

-v /opt/prometheus:/etc/prometheus 只挂载了配置目录,但 Prometheus 的时序数据默认存在容器内 /prometheus 目录,容器重启就丢失。必须额外挂载数据卷:

docker run -d \
  --name prometheus \
  -p 9090:9090 \
  -v /opt/prometheus:/etc/prometheus \
  -v /opt/prometheus-data:/prometheus \  # 新增数据目录挂载
  --restart unless-stopped \
  prom/prometheus:v2.47.2

同时,在 /opt/prometheus 下新建 alerts.yml

groups:
- name: example
  rules:
  - alert: HighCpuUsage
    expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
    for: 2m
    labels:
      severity: warning
    annotations:
      summary: "High CPU usage on {{ $labels.instance }}"

并在 prometheus.yml 中引用:

rule_files:
  - "alerts.yml"

3.3 第三步:引入服务发现(file_sd_configs)

当你要监控的 target 超过 5 个,硬编码 static_configs 就不可维护了。 file_sd_configs 允许你用 JSON 文件动态管理 targets:

scrape_configs:
  - job_name: 'web-servers'
    file_sd_configs:
      - files:
          - "/etc/prometheus/targets/web-servers.json"

创建 /opt/prometheus/targets/web-servers.json

[
  {
    "targets": ["192.168.10.101:8080", "192.168.10.102:8080"],
    "labels": {
      "env": "prod",
      "job": "tomcat"
    }
  }
]

这样,增删服务器只需改 JSON 文件,然后发送 SIGHUP 信号重载配置(无需重启容器):

docker kill -s HUP prometheus

4. Docker 部署后的“真·生产检查清单”:12 个必须验证的运行态指标

容器 docker run 成功只是万里长征第一步。真正的挑战在于确认它是否在 持续、稳定、准确 地工作。我们总结出一份 12 项的“真·生产检查清单”,每一项都对应一个具体命令或页面操作,全部通过才算部署完成:

4.1 容器基础状态验证

# 检查容器是否处于 Up 状态且无重启记录
docker ps -f name=prometheus --format "table {{.Names}}\t{{.Status}}\t{{.Status}}"

# 检查容器资源占用(CPU 应 <30%,内存 <500MB 为健康)
docker stats --no-stream prometheus

# 检查容器内进程树(必须看到 /bin/prometheus 进程)
docker exec prometheus ps auxf

4.2 Prometheus 自身健康指标验证

访问 http://<宿主机IP>:9090/metrics ,搜索以下关键指标:

指标名 正常值范围 异常含义
prometheus_build_info version="2.47.2" 版本号是否匹配预期
prometheus_target_sync_length_seconds_sum < 0.5 target 同步耗时过长,可能网络或 target 响应慢
prometheus_tsdb_head_chunks_created_total 持续增长 TSDB 数据块创建正常,若停滞说明写入异常
prometheus_target_metadata_cache_entries > 0 元数据缓存已加载,若为 0 说明服务发现失败

4.3 Target 抓取状态验证

进入 http://<宿主机IP>:9090/targets 页面,检查:

  • 所有 target 的 State 列必须是 UP (不是 DOWN 或 UNKNOWN);
  • Last Scrape 列的时间戳距当前时间不超过 scrape_interval + 5s (如 interval=15s,则应 ≤20s);
  • Scrape Duration 列数值应稳定在 0.01~0.2 秒之间,若 >0.5s 需检查 target 服务负载。

4.4 规则与告警验证

http://<宿主机IP>:9090/rules 页面,确认:

  • alerts.yml 中定义的 HighCpuUsage 规则状态为 Pending Firing (可手动触发: stress-ng --cpu 4 --timeout 60s );
  • http://<宿主机IP>:9090/alerts 页面能看到对应告警实例,且 Status firing
  • http://<宿主机IP>:9090/graph 中执行表达式 count by(job) (up) ,结果应返回 prometheus node 两个 job。

4.5 数据持久化验证

# 检查挂载的数据目录是否真实写入
ls -lh /opt/prometheus-data/

# 查看最新 block 目录的修改时间(应为几分钟内)
find /opt/prometheus-data -name "01J*block" -type d | xargs ls -td | head -1

# 检查 block 内部文件完整性
du -sh /opt/prometheus-data/chunks_head/

4.6 网络与安全加固验证

# 检查 Prometheus 容器是否只暴露 9090 端口(禁止暴露 9091、9092 等调试端口)
docker port prometheus

# 检查容器是否以非 root 用户运行(安全基线要求)
docker inspect prometheus | jq '.[0].Config.User'

# 检查 SELinux 标签是否正确(应为 system_u:system_r:container_t:s0)
ls -Z /opt/prometheus-data/

提示:第 12 项“安全加固验证”常被忽略。默认 prom/prometheus 镜像以 root 用户运行,这违反 PCI DSS 和等保 2.0 要求。解决方案是在 docker run 中加 --user 65534:65534 (nobody 用户),但需提前 chown -R 65534:65534 /opt/prometheus-data ,否则启动失败。

5. 故障排查的“黄金四象限”:从容器日志到内核参数的逐层穿透法

当 Prometheus 容器启动失败或抓取异常时,90% 的人第一反应是 docker logs prometheus ,然后对着满屏 level=error msg="..." 发呆。真正高效的排查,必须建立一个 从外到内、从现象到根因 的穿透路径。我们把它划分为四个象限,每个象限对应一类工具和思维模式:

5.1 第一象限:容器外层 —— Docker Engine 与宿主环境

这是最容易被忽视的层面。很多问题根本不是 Prometheus 的错,而是 Docker 或宿主系统的问题。

典型症状 docker run 命令卡住、 Cannot connect to the Docker daemon 、容器启动后立即退出(Exit Code 1)。

排查命令

# 检查 Docker daemon 是否存活且无错误
sudo systemctl status docker
sudo journalctl -u docker -n 50 --no-pager

# 检查 Docker 存储驱动是否为 overlay2
docker info | grep "Storage Driver"

# 检查磁盘 inodes 是否耗尽(常见于 /var/lib/docker)
df -i /var/lib/docker

真实案例 :某客户在 VMware 中部署后,容器总是退出。 docker logs 为空, docker ps -a 显示 Exited (1) 。执行 sudo journalctl -u docker 发现关键日志: overlay2: cannot mount because max depth is reached 。原因是 VMware 虚拟磁盘启用了 thin provisioning ,导致 overlay2 层级深度超过 128 限制。解决方案: docker system prune -a 清理所有镜像和容器,然后重启 Docker。

5.2 第二象限:容器中层 —— Prometheus 进程与配置

这是最常出问题的层面,90% 的配置错误发生在这里。

典型症状 :容器持续运行但 http://:9090 打不开、 /targets 页面显示 No targets found /graph 查询返回 no data

排查命令

# 进入容器内部,检查配置文件语法
docker exec -it prometheus sh -c "promtool check config /etc/prometheus/prometheus.yml"

# 检查 Prometheus 进程是否监听 9090 端口
docker exec prometheus netstat -tuln | grep :9090

# 检查配置文件挂载是否成功(对比容器内外路径)
docker exec prometheus ls -l /etc/prometheus/
ls -l /opt/prometheus/

关键技巧 promtool check config 是神器。它不仅能检查 YAML 语法,还能验证 scrape_configs 中的 target 是否可解析。比如你写了 targets: ['invalid-host:9100'] ,它会直接报错 lookup invalid-host on 127.0.0.11:53: no such host ,比等容器启动失败再查日志快十倍。

5.3 第三象限:容器内层 —— 网络连通性与服务可达性

这是跨主机部署时的高频故障区。容器能启动,但就是抓不到其他机器的指标。

典型症状 /targets 页面 target 状态为 DOWN ,Last Scrape Error 显示 context deadline exceeded connection refused

排查命令

# 从 Prometheus 容器内 ping 目标主机(验证网络层)
docker exec prometheus ping -c 3 192.168.10.101

# 从 Prometheus 容器内 telnet 目标端口(验证传输层)
docker exec prometheus telnet 192.168.10.101 9100

# 从 Prometheus 容器内 curl 目标 metrics 接口(验证应用层)
docker exec prometheus curl -v http://192.168.10.101:9100/metrics | head -20

避坑经验 :CentOS 7 默认防火墙 firewalld 会拦截容器发起的出站连接。即使你 systemctl stop firewalld iptables 规则可能还在。最稳妥的方式是:

# 临时放行所有出站连接(排查用)
sudo iptables -P OUTPUT ACCEPT

# 或永久放行特定端口
sudo firewall-cmd --permanent --add-port=9100/tcp
sudo firewall-cmd --reload

5.4 第四象限:宿主深层 —— 内核参数与硬件资源

这是最隐蔽的层面,往往表现为性能抖动、间歇性超时、数据写入延迟。

典型症状 /targets 页面部分 target 偶尔 DOWN prometheus_tsdb_head_samples_appended_total 增长缓慢、 prometheus_local_storage_memory_chunks 持续升高。

排查命令

# 检查文件描述符限制(Prometheus 默认需要 65536+)
cat /proc/$(pgrep -f "prometheus.*config")/limits | grep "Max open files"

# 检查内存映射页数(影响 TSDB 性能)
cat /proc/sys/vm/max_map_count

# 检查磁盘 I/O 延迟(重点关注 await 和 %util)
iostat -x 1 3

硬核修复 :在 /etc/sysctl.conf 中追加:

# 提高文件描述符上限
fs.file-max = 100000
# 提高内存映射页数
vm.max_map_count = 262144
# 减少 swap 使用(TSDB 对 swap 敏感)
vm.swappiness = 1

然后执行 sudo sysctl -p 生效。

最后分享一个小技巧:当所有排查都失效时,用 docker run --rm -it --network host prom/prometheus:v2.47.2 /bin/sh 启动一个临时容器,它共享宿主网络命名空间,可以直接用 curl http://localhost:9090/metrics 测试,彻底排除 Docker 网络层干扰。这是我们在客户现场快速定位“到底是 Docker 问题还是 Prometheus 问题”的终极手段。

更多推荐