【前情提要】 node_exporter 装好了,机器的“体检报告”能看了。老周说:“现在装 Prometheus,让它去取报告。”小林打开 Prometheus 官网,准备下载。但他不知道,prometheus.yml 的 YAML 缩进、scrape_configs 的配置、up 指标的验证,每一个都可能让他折腾。

一、Prometheus 是“每天去体检的人”

早上到工位,小林打开浏览器,访问 http://localhost:9100/metrics,看着满屏的指标。

老周走过来:“看得挺起劲啊。”

“周哥,这些指标是挺好,但我总不能每次都手动 curl 吧?而且我关了浏览器,这些数据就没了。”

“所以你要装 Prometheus。”老周说,“node_exporter 是体检医生,它把报告放在桌上。Prometheus 是那个每天准时去取报告的人。它把报告存起来,你随时能查历史。”

“那它多久取一次?”

“默认 15 秒。”老周说,“你配好之后,它每 15 秒去 9100 端口拉一次数据,存到自己的数据库里。”

“那它存哪?”

“本地磁盘。”老周说,“默认存 15 天。超过 15 天的数据,自动删除。你也可以配更长,但数据量会大。”

小林点点头,打开 Prometheus 官网。

二、安装 Prometheus

“Ubuntu 上装 Prometheus,跟 node_exporter 一样,有两种方式。”老周说,“apt 和二进制。”

“apt 简单,用 apt。”

“这次我建议你用二进制。”老周说。

“为啥?”

“因为 apt 源里的 Prometheus 版本可能比较旧,而 Prometheus 更新快。而且 apt 装的 Prometheus 配置文件路径和服务名,跟官方文档有时候对不上。你以后工作中,Prometheus 基本都是二进制部署。”老周说,“正好练一下。”

小林打开 Prometheus 官网的下载页面,找到 Linux ARM64 版本。

“先看架构。”

uname -m

输出:

aarch64

“下 ARM64 的。”老周说。

小林下载:

wget https://github.com/prometheus/prometheus/releases/download/v2.x.x/prometheus-2.x.x.linux-arm64.tar.gz

解压:

tar -xzvf prometheus-2.x.x.linux-arm64.tar.gz

进入目录:

cd prometheus-2.x.x.linux-arm64
ls

输出:

console_libraries/
consoles/
LICENSE
NOTICE
prometheus
promtool
prometheus.yml

“看到了吗?”老周说,“prometheus 是主程序,promtool 是配置检查工具,prometheus.yml 是配置文件。就这三个你要关心的。”

“那 console_libraries 和 consoles 呢?”

“内置的 Web 控制台,基本不用管。你以后用 Grafana 展示,不用 Prometheus 自带的。”

“那我把它们放哪?”

“标准做法:程序放 /usr/local/bin/,配置放 /etc/prometheus/,数据放 /var/lib/prometheus/。”

小林照做:

sudo mv prometheus promtool /usr/local/bin/
sudo mkdir -p /etc/prometheus /var/lib/prometheus
sudo mv prometheus.yml /etc/prometheus/
sudo mv consoles console_libraries /etc/prometheus/

“验证一下。”

prometheus --version

输出:

prometheus, version 2.x.x (branch: HEAD, revision: ...)
  build user:       root@...
  build date:       ...
  go version:       go1.x.x
  platform:         linux/arm64

“成了。”小林说。

三、prometheus.yml:配置文件

“现在看配置文件。”老周说。

cat /etc/prometheus/prometheus.yml

输出:

# my global config
global:
  scrape_interval: 15s
  evaluation_interval: 15s

# Alertmanager configuration
alerting:
  alertmanagers:
    - static_configs:
        - targets:
          # - alertmanager:9093

# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
  # - "first_rules.yml"
  # - "second_rules.yml"

# A scrape configuration containing exactly one endpoint to scrape:
scrape_configs:
  - job_name: "prometheus"
    static_configs:
      - targets: ["localhost:9090"]

“这是默认配置。”老周说,“它只监控自己。你现在要加一个目标:node_exporter。”

“怎么加?”

“在 scrape_configs 下面加一段。”

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

  - job_name: "node"
    static_configs:
      - targets: ["localhost:9100"]

“一行一行解释。”老周说。

  • job_name:任务名。你可以随便起,但要有意义。prometheus 表示监控自己,node 表示监控节点。
  • static_configs:静态配置。表示目标列表是写死的。
  • targets:目标列表。localhost:9100 就是 node_exporter。

“那如果我有十台机器呢?”

“targets 里写十行。”

targets: ["192.168.1.101:9100", "192.168.1.102:9100", "192.168.1.103:9100"]

“但这样管理麻烦。”老周说,“Prometheus 支持服务发现,可以自动发现目标。但你现在入门,用静态配置就够了。”

“那 scrape_interval 呢?”

“全局的采集间隔,默认 15 秒。你也可以在 job_name 下面单独配,覆盖全局。”

- job_name: "node"
  scrape_interval: 30s
  static_configs:
    - targets: ["localhost:9100"]

“这样 node 任务每 30 秒采集一次,其他任务还是 15 秒。”

小林记下:

  • global.scrape_interval:全局采集间隔
  • scrape_configs:采集目标列表
  • job_name:任务名
  • static_configs.targets:静态目标列表
  • 每个 job 可以覆盖全局 scrape_interval

四、YAML 缩进:Prometheus 的“第一坑”

“现在保存,启动。”老周说。

小林用 nano 编辑:

sudo nano /etc/prometheus/prometheus.yml

加完 node 任务,保存,退出。

“启动之前,先检查配置。”老周说,“用 promtool。”

promtool check config /etc/prometheus/prometheus.yml

输出:

Checking /etc/prometheus/prometheus.yml
  SUCCESS: 1 rule files found

“等等,1 个规则文件?我没配规则文件啊。”小林说。

“它把默认的算进去了。没事,SUCCESS 就是通过。”老周说,“如果你缩进写错了,它会报错。”

小林故意把 targets 那行少缩进两个空格:

scrape_configs:
  - job_name: "node"
    static_configs:
    - targets: ["localhost:9100"]

再次检查:

promtool check config /etc/prometheus/prometheus.yml

输出:

Checking /etc/prometheus/prometheus.yml
  FAILED: cannot unmarshal config: yaml: line 20: did not find expected key

“看到了吗?”老周说,“YAML 对缩进极其敏感。- targets 和 static_configs 必须对齐,或者缩进两个空格。你少一个空格,它就报错。”

小林改回来:

scrape_configs:
  - job_name: "node"
    static_configs:
      - targets: ["localhost:9100"]

再次检查:

promtool check config /etc/prometheus/prometheus.yml

输出:

SUCCESS

“记住,”老周说,“YAML 只用空格,不用 Tab。同一层级缩进必须一致。- 和上一行的键要对齐,或者多缩进两个空格。 你以后写 YAML,先 promtool check,再启动。”

小林在笔记本上写下:

  • YAML 缩进用空格,不用 Tab
  • 同一层级缩进一致
  • - 列表项和父键对齐,或多缩进
  • 改完配置先 promtool check config

五、启动 Prometheus

“配置没问题,现在启动。”老周说。

“怎么启动?systemd?”

“对。但二进制安装的话,systemd 服务文件要自己写。”老周说,“我教你写一个。”

sudo nano /etc/systemd/system/prometheus.service

写入:

[Unit]
Description=Prometheus
Wants=network-online.target
After=network-online.target

[Service]
User=prometheus
Group=prometheus
Type=simple
ExecStart=/usr/local/bin/prometheus \
    --config.file=/etc/prometheus/prometheus.yml \
    --storage.tsdb.path=/var/lib/prometheus/ \
    --web.console.templates=/etc/prometheus/consoles \
    --web.console.libraries=/etc/prometheus/console_libraries

[Install]
WantedBy=multi-user.target

“这段啥意思?”小林问。

“ExecStart 是启动命令。--config.file 指定配置文件,--storage.tsdb.path 指定数据目录,--web.console.* 指定控制台模板。”老周说,“你以后改配置,改这几个参数就行。”

“那 User=prometheus 是啥?”

“哦,我忘了,得先创建 prometheus 用户。”

sudo useradd --no-create-home --shell /bin/false prometheus
sudo chown -R prometheus:prometheus /etc/prometheus /var/lib/prometheus
sudo chown prometheus:prometheus /usr/local/bin/prometheus /usr/local/bin/promtool

“现在启动。”

sudo systemctl daemon-reload
sudo systemctl start prometheus
sudo systemctl status prometheus

输出:

● prometheus.service - Prometheus
     Loaded: loaded (/etc/systemd/system/prometheus.service; disabled; vendor preset: enabled)
     Active: active (running) since ...

“active (running),成了。”小林说。

“设个开机自启。”

sudo systemctl enable prometheus

“现在验证端口。”

sudo ss -lntp | grep 9090

输出:

LISTEN 0 4096 *:9090 *:* users:(("prometheus",pid=5678,fd=7))

“9090 端口在监听。”老周说,“打开浏览器,访问 http://localhost:9090。”

小林在 Ubuntu 浏览器里打开。Prometheus 的 Web UI 出现了,顶部是查询框,下面是菜单。

“这就是 Prometheus 的界面。”老周说,“你以后配 Grafana,数据源就是它。”

六、验证 up 指标

“现在验证最重要的一件事。”老周说,“Prometheus 到底有没有拉到数据。”

“怎么看?”

“在查询框里输入 up,点 Execute。”

小林输入 up,回车。

输出:

up{instance="localhost:9090", job="prometheus"} 1
up{instance="localhost:9100", job="node"} 1

“看到了吗?”老周说,“两条 up,都是 1。1 表示目标在线,0 表示目标离线。”

“那如果 node_exporter 挂了,会怎样?”

“你试试。”老周说。

小林停掉 node_exporter:

sudo systemctl stop prometheus-node-exporter

等 15 秒,刷新 Prometheus 页面,再次查询 up。

输出:

up{instance="localhost:9090", job="prometheus"} 1
up{instance="localhost:9100", job="node"} 0

“看到了吧?node 的 up 变成 0 了。”老周说,“这就是 Prometheus 的‘目标存活检测’。你以后配告警,第一件事就是配 up == 0。”

“那它怎么知道目标挂了?”

“拉取失败。”老周说,“Prometheus 每 15 秒去 9100 拉一次,拉不到就标记为 0。连续失败几次,up 一直是 0。”

小林重新启动 node_exporter:

sudo systemctl start prometheus-node-exporter

再等 15 秒,查询 up,node 变回 1。

“这就是监控的基本闭环。”老周说,“Prometheus 拉数据,up 告诉你目标在不在,up == 0 触发告警。”

小林记下:

  • up 指标:1 在线,0 离线
  • up{job="node"}:查看 node 任务的目标状态
  • up == 0:目标离线,是告警的基础

七、排错挑战:Prometheus 启动失败

下午,小林想改一下 prometheus.yml,把 scrape_interval 从 15 秒改成 10 秒。

他改完保存,重启:

sudo systemctl restart prometheus

输出:

Job for prometheus.service failed because the control process exited with error code.
See "systemctl status prometheus.service" and "journalctl -xe" for details.

“啥?”小林懵了。

他检查状态:

sudo systemctl status prometheus

输出:

● prometheus.service - Prometheus
     Active: failed (Result: exit-code)

“启动失败了。”小林说。

“看日志。”

sudo journalctl -u prometheus -n 20

输出:

level=error msg="Error loading config" err="yaml: line 8: did not find expected key"

“YAML 第 8 行。”老周说,“你改了啥?”

小林打开配置:

sudo nano /etc/prometheus/prometheus.yml

看第 8 行:

global:
  scrape_interval: 10s
   evaluation_interval: 15s

“evaluation_interval 多了一个空格。”小林说。

“对。”老周说,“scrape_interval 和 evaluation_interval 是同层级,缩进必须一样。你多了一个空格,YAML 解析失败,Prometheus 启动失败。”

小林改回来:

global:
  scrape_interval: 10s
  evaluation_interval: 15s

“记住,改配置前先 promtool check。”

promtool check config /etc/prometheus/prometheus.yml

输出:

SUCCESS
sudo systemctl restart prometheus
sudo systemctl status prometheus

输出:

Active: active (running)

“回来了。”小林松了口气。

排错思路总结:

  1. Prometheus 启动失败,先看 systemctl status prometheus。
  2. 如果 failed,看 journalctl -u prometheus -n 20。
  3. 日志显示 Error loading config,说明 YAML 写错了。
  4. 用 promtool check config /etc/prometheus/prometheus.yml 定位错误行。
  5. 修正缩进,再 promtool check,通过后 restart。
  6. 永远先 promtool check,再 restart。

“记住,”老周说,“YAML 是 Prometheus 最常见的坑。 你以后写配置,眼睛盯着缩进,别用 Tab,改完先 promtool check。这三条做到,能少 90% 的启动失败。”

八、练习

  1. 练习一:用二进制方式安装 Prometheus,创建 systemd 服务,启动并验证 9090 端口监听。在浏览器访问 http://localhost:9090,确认 Web UI 正常显示。
  2. 练习二:修改 prometheus.yml,添加 node 任务,目标为 localhost:9100。用 promtool check config 检查通过后,重启 Prometheus。在 Web UI 查询 up,确认两个目标都是 1。
  3. 练习三:停掉 node_exporter,等待 15 秒,查询 up{job="node"},确认值为 0。重新启动 node_exporter,再次查询,确认恢复为 1。

参考答案

练习一:

wget https://github.com/prometheus/prometheus/releases/download/v2.x.x/prometheus-2.x.x.linux-arm64.tar.gz
tar -xzvf prometheus-2.x.x.linux-arm64.tar.gz
cd prometheus-2.x.x.linux-arm64

sudo mv prometheus promtool /usr/local/bin/
sudo mkdir -p /etc/prometheus /var/lib/prometheus
sudo mv prometheus.yml /etc/prometheus/
sudo mv consoles console_libraries /etc/prometheus/

sudo useradd --no-create-home --shell /bin/false prometheus
sudo chown -R prometheus:prometheus /etc/prometheus /var/lib/prometheus
sudo chown prometheus:prometheus /usr/local/bin/prometheus /usr/local/bin/promtool

sudo nano /etc/systemd/system/prometheus.service

服务文件内容参考正文。

sudo systemctl daemon-reload
sudo systemctl start prometheus
sudo systemctl enable prometheus
sudo systemctl status prometheus
sudo ss -lntp | grep 9090

浏览器访问 http://localhost:9090。

练习二:

sudo nano /etc/prometheus/prometheus.yml

添加:

  - job_name: "node"
    static_configs:
      - targets: ["localhost:9100"]
promtool check config /etc/prometheus/prometheus.yml
sudo systemctl restart prometheus

Web UI 查询 up,输出:

up{instance="localhost:9090", job="prometheus"} 1
up{instance="localhost:9100", job="node"} 1

练习三:

sudo systemctl stop prometheus-node-exporter
# 等 15 秒

Web UI 查询 up{job="node"},输出 0。

sudo systemctl start prometheus-node-exporter
# 等 15 秒

Web UI 再次查询,输出 1。

九、下集预告

Prometheus 装好了,up 指标也会看了。老周说:“现在你能看到数据了,但 PromQL 才是你真正要学的。没有 PromQL,你只能看 up。”小林打开 Prometheus 查询框,准备学 PromQL。但他不知道,rate()、sum by()、avg by(),每一个函数都有讲究。

下一集:《PromQL 入门:用“查询语言”问机器问题》