【无标题】

markdown 复制代码
# 实战排坑:Flask 应用接入 Prometheus 监控,从 404 到 UP 的全过程

> 最近给自己的校园失物招领平台接入了 Prometheus 监控,本想优雅地暴露 `/metrics` 端点,结果却踩了一连串的坑。  
> 从依赖缺失、pip 超时、代码缩进混乱,到数据库连接失败、容器网络不通......  
> 本文详细记录了我排查和解决每一个问题的完整思路,希望能帮你避开同样的弯路。

## 一、背景与架构

- **应用**:基于 Flask 的校园失物招领平台,使用 Docker Compose 部署(Flask + MySQL)。
- **监控目标**:
  - **Prometheus** 抓取 Flask 应用业务指标(请求数、响应时间等)
  - **node_exporter** 采集服务器 CPU、内存、磁盘等硬件指标
  - **Grafana** 可视化展示
- **预期结果**:访问 `http://<服务器IP>:8000/metrics` 返回 Prometheus 格式的指标,Prometheus Targets 中三个 job 均为 **UP**。

现实很骨感:`/metrics` 始终返回 **404**,Prometheus 中 `campus_app` 也是 **DOWN**。

## 二、整体排错思路

1. **先确保应用自身能正常暴露 `/metrics`**(在宿主机 `curl localhost:8000/metrics` 能返回数据)。
2. **再解决 Prometheus 与各 exporter 之间的网络连通问题**(容器网络模式、DNS 解析)。
3. **最后验证 Grafana 可视化**。

下面按排查顺序逐一记录。

## 三、第一轮:Flask 应用的 `/metrics` 一直 404

### 3.1 网站本身能正常访问吗?
在浏览器打开 `www.anxun.xyz`,页面正常,说明应用容器在运行,且端口映射正确。

### 3.2 第一次排错:检查容器日志

```bash
docker logs campus_crowdfunding-campus_app-1

关键错误:

复制代码
NameError: name 'PrometheusMetrics' is not defined

结论 :代码中使用了 PrometheusMetrics 但没有导入,或者依赖未安装。

验证依赖

bash 复制代码
docker exec campus_crowdfunding-campus_app-1 pip list | grep prometheus
# 无输出

说明 prometheus-flask-exporter 未安装。检查 requirements.txt,果然没有这一行。

尝试:添加依赖并重新构建

requirements.txt 中添加:

复制代码
prometheus-flask-exporter

重新构建:

bash 复制代码
docker compose build --no-cache campus_app

结果 :构建失败,pip 下载超时(Read timed out),因为访问 PyPI 官方源不稳定。

解决:配置国内镜像源

编辑 Dockerfile,在 COPY requirements.txt . 之后,RUN pip install 之前添加一行:

dockerfile 复制代码
RUN pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/

重新构建成功,依赖安装完成。

3.3 第二次排错:PrometheusMetrics 初始化顺序错误

构建成功后,容器启动仍报同样的 NameError

再次查看日志,发现虽然已导入 from prometheus_flask_exporter import PrometheusMetrics,但 metrics = PrometheusMetrics(app) 这行代码放在了 app = Flask(__name__) 之前 ,导致 app 未定义。

为什么会出现这个错误?

因为我最初是在代码开头直接写 metrics = PrometheusMetrics(app),而 app 还没有创建。

正确位置 :在 app = Flask(__name__) 之后立即初始化。

修改后重新构建,NameError 消失,但 /metrics 依然 404。

3.4 第三次排错:手动添加 /metrics 路由

为什么怀疑自动注册失败?
PrometheusMetrics(app) 理论上会自动注册 /metrics 路由,但既然没有,我猜测可能被其他蓝图或错误处理器干扰,于是决定手动添加作为后备方案。

app.py 末尾(return app 之前)添加:

python 复制代码
from prometheus_flask_exporter import generate_latest
@app.route('/metrics')
def metrics_endpoint():
    return generate_latest()

结果:容器启动失败,日志显示:

复制代码
cannot import name 'generate_latest' from 'prometheus_flask_exporter'

原因prometheus_flask_exporter 并没有直接提供 generate_latest 函数,它实际上依赖 prometheus_client

解决 :改用 prometheus_clientgenerate_latest,并传入 metrics.registrymetricsPrometheusMetrics 实例):

python 复制代码
from prometheus_client import generate_latest
@app.route('/metrics')
def metrics_endpoint():
    return generate_latest(metrics.registry)

为什么第一个不行,第二个可以?

  • prometheus_flask_exporter 是一个封装库,它不直接导出 generate_latest
  • prometheus_client 是底层的指标库,generate_latest 是它的原生函数,需要传入一个注册表(registry)来生成所有指标。
  • PrometheusMetrics(app) 内部会维护一个注册表,可以通过 metrics.registry 访问。

3.5 第四次排错:缩进混乱导致容器不断重启

修改代码后重新构建,容器不断重启,日志显示:

复制代码
IndentationError: expected an indented block
TabError: inconsistent use of tabs and spaces

原因 :手动添加代码时混用了 Tab 和空格,并且 return app 被意外放在了 except 块内部。

解决

  • 统一使用 4 个空格 缩进。
  • 确保 return appcreate_app 函数末尾且无条件执行(不要缩进到 ifexcept 里)。

整理后,应用启动成功,但 /metrics 仍然返回 404。

3.6 第五次排错:根本原因 -- 数据库连接失败

再次查看容器日志,发现大量:

复制代码
pymysql.err.OperationalError: (2003, "Can't connect to MySQL server on 'localhost'")

应用在 db.create_all() 时崩溃,导致 /metrics 路由根本未被注册。

排查过程

  1. 检查容器内环境变量:

    bash 复制代码
    docker exec campus_crowdfunding-campus_app-1 env | grep DATABASE_URL

    输出:

    复制代码
    DEV_DATABASE_URL=mysql+pymysql://root:root@localhost/campus_crowdfunding
    DATABASE_URL=mysql+pymysql://root:root@mysql/campus_crowdfunding
  2. 查看 config.py

    • DevelopmentConfig 使用 DEV_DATABASE_URL(默认 localhost)
    • ProductionConfig 使用 DATABASE_URL(正确指向 MySQL 服务名 mysql
  3. 当前应用启动时 APP_CONFIG 环境变量未设置,默认使用 development 配置,因此试图连接 localhost 的 MySQL(容器内没有 MySQL 服务,导致拒绝连接)。

解决方案

docker-compose.yml 中为 campus_app 添加环境变量:

yaml 复制代码
environment:
  - APP_CONFIG=production
  - DATABASE_URL=mysql+pymysql://root:root@mysql/campus_crowdfunding

重启容器后,数据库连接成功,/metrics 端点终于返回了 Prometheus 格式的指标!🎉

四、第二轮:Prometheus 抓取问题

4.1 子问题 1:node_exporter 指标抓取失败

现象 :Prometheus Targets 中 node_exporter 状态为 DOWN ,错误 connection refused。但宿主机 curl localhost:9100/metrics 正常。

排查过程

  • node_exporter 容器使用默认 bridge 网络,Prometheus 也使用 bridge 网络(有端口映射),两者不在同一网络,且均非 host 模式,导致 Prometheus 无法通过 localhost:9100 访问 node_exporter。

第一次试错 :将 node_exporter 改为 host 网络模式(删除容器并重新创建,加上 --network host)。
结果 :Prometheus 仍然无法抓取,因为 Prometheus 不在 host 网络,容器内的 localhost 指向自身。

第二次试错 :统一所有监控容器为 host 网络模式。

修改 /opt/monitoring/docker-compose.yml,为 prometheusnode_exportergrafana 都添加 network_mode: host,并删除 ports 映射。

重新创建容器后,node_exporter 变为 UP

4.2 子问题 2:Prometheus 无法抓取 Flask 应用的 /metrics

现象campus_app 状态为 DOWN ,错误 lookup campus_crowdfunding-campus_app-1 on ...: no such host

原因 :Flask 容器在自定义网络 campus_crowdfunding_default 中,而 Prometheus 现在使用 host 网络,无法通过容器名 DNS 解析。

尝试修复:将 Prometheus 加入 Flask 的网络:

bash 复制代码
docker network connect campus_crowdfunding_default prometheus

报错:

复制代码
container sharing network namespace with another container or host cannot be connected to any other network

host 网络的容器不能加入其他网络。

最终解决方案 :利用 Flask 容器的端口映射(0.0.0.0:8000->8000/tcp),让 Prometheus 通过 localhost:8000 抓取。

修改 /opt/monitoring/prometheus.yml

yaml 复制代码
- job_name: 'campus_app'
  static_configs:
    - targets: ['localhost:8000']

重载配置:

bash 复制代码
docker exec prometheus kill -HUP 1

campus_app 状态变为 UP,抓取成功。

五、额外问题:Grafana 密码忘记

现象 :访问 Grafana 登录页,默认 admin/admin 无法登录。

解决 :使用 grafana-cli 重置密码:

bash 复制代码
docker exec -it grafana grafana cli admin reset-admin-password <新密码>
docker restart grafana

六、最终状态

组件 状态 访问方式
Flask 应用 正常 http://localhost:8000/metrics
Prometheus 正常 http://公网IP:9090/targets
node_exporter 正常 通过 Prometheus 抓取
Grafana 正常 http://公网IP:3000

所有 Targets 均为 UP,监控大盘完美展示。

七、踩坑总结与避坑指南

  1. 依赖安装:务必使用国内镜像源,避免 pip 超时。
  2. 代码缩进:Python 项目统一使用 4 个空格,不要混用 Tab。
  3. 环境变量 :区分开发/生产配置,容器化部署时显式设置 APP_CONFIG=production
  4. 网络模式 :监控组件尽量统一使用 host 网络模式,或全部放在同一自定义网络中。
  5. 手动路由后备 :当自动注册失败时,可以手动暴露 /metrics,注意使用正确的库函数。
  6. 日志先行 :90% 的问题都能从 docker logs 中找到线索。
  7. 逐步验证 :先保证应用自身 curl/metrics,再解决 Prometheus 抓取问题。

八、附:完整配置示例

Flask 应用的 app.py 关键片段

python 复制代码
from prometheus_flask_exporter import PrometheusMetrics
from prometheus_client import generate_latest

def create_app():
    app = Flask(__name__)
    metrics = PrometheusMetrics(app)
    
    # ... 其他初始化 ...
    
    # 手动添加 /metrics 路由作为保险
    @app.route('/metrics')
    def metrics_endpoint():
        return generate_latest(metrics.registry)
    
    return app

Prometheus 配置文件 prometheus.yml

yaml 复制代码
global:
  scrape_interval: 15s

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

  - job_name: 'node_exporter'
    static_configs:
      - targets: ['localhost:9100']

  - job_name: 'campus_app'
    static_configs:
      - targets: ['localhost:8000']

Docker Compose 监控服务(host 网络模式)

yaml 复制代码
version: '3.8'
services:
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    network_mode: host
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'

  node_exporter:
    image: prom/node-exporter:latest
    container_name: node_exporter
    network_mode: host
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command:
      - '--path.procfs=/host/proc'
      - '--path.sysfs=/host/sys'
      - '--path.rootfs=/rootfs'

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    network_mode: host
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    volumes:
      - grafana_data:/var/lib/grafana

volumes:
  prometheus_data:
  grafana_data:

希望这篇排坑记录能帮你节省时间,早日打通 Flask + Prometheus 监控链路。如果遇到其他问题,欢迎留言交流!

复制代码
相关推荐
2301_旺仔10 小时前
【prometheus】监控linux/windows
linux·windows·prometheus
BullSmall1 天前
Prometheus 如何配置监控 SSL 证书即将过期
网络协议·ssl·prometheus
BullSmall1 天前
Prometheus 可以监控docker 部署的Nginx 吗?
nginx·docker·prometheus
鬼先生_sir2 天前
Spring Cloud 微服务监控实战:SkyWalking + Prometheus+Grafana 全栈解决方案
运维·spring cloud·grafana·prometheus·skywalking
zs宝来了3 天前
Prometheus 监控体系原理:Pull 模式与 TSDB 时序数据库
prometheus·时序数据库·监控·tsdb·pull模式
何中应4 天前
Alertmanager设置邮件通知
运维·自动化·prometheus
安审若无4 天前
运维监控及可视化工具Prometheus和grafana
运维·grafana·prometheus
却话巴山夜雨时i4 天前
互联网大厂Java面试实录:从Spring Boot到Kafka的场景应用深度解析
spring boot·kafka·prometheus·微服务架构·java面试·技术解析·互联网大厂
却话巴山夜雨时i5 天前
Java面试实录:从Spring Boot到Kafka的技术探讨
spring boot·微服务·kafka·grafana·prometheus·java面试