CentOS 7 + Docker 部署 Prometheus 生产实践指南
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 问题”的终极手段。
更多推荐
所有评论(0)