使用 OpenTelemetry、Flask 和 Prometheus 的 MinIO 指标

使用 OpenTelemetry、Flask 和 Prometheus 的 MinIO 指标

可观察性就是收集信息(跟踪、日志、指标),目的是提高性能、可靠性和可用性。很少只有其中一个可以查明事件的根本原因,通常是当我们将这些信息关联起来形成一个叙述时,我们才会有更好的理解。

在最近几篇关于可观察性的博客中,我们介绍了如何在 MinIO 中实现跟踪并将MinIO 日志发送到 ElasticSearch在这篇博文中,我们将讨论指标。

从一开始,MinIO 就不仅关注性能和可扩展性,还关注可观察性。MinIO 有一个内置端点/minio/v2/metrics/clusterPrometheus可以从中抓取和收集指标。您还可以将事件发布Kafka并触发警报和其他进程运行,这取决于在 MinIO 中执行的操作。最后,您可以将所有这些指标放在Grafana 上一个漂亮的仪表板中,甚至可以启动警报。

云原生 MinIO 与OpenTelemetry等云原生可观察性工具无缝集成我们将使用我们用于指标跟踪的相同 OpenTelemetry API,只是使用不同的导出器和函数集。

为了让您感受可观察性过程和可用信息的深度,我们将构建一个小型 Flask Python 应用程序并在 Grafana 中将其可视化

  1. 我们将推出 MinIO、Prometheus 和 Grafana 容器。

  2. 使用 OpenTelemetry 编写示例 Python 应用程序以收集各种类型的指标,例如计数器和直方图。

  3. 使用 MinIO SDK 对 MinIO 执行操作。

  4. 为我们编写的示例应用程序构建一个 Docker 镜像。

  5. 在我们的 Grafana 仪表板中可视化 Prometheus 抓取的指标。

我们将逐步引导您完成整个过程。

启动基础设施

与上一篇博客类似,我们将使用从头开始编写的 Python 应用程序,只是这次我们将发送指标而不是发送跟踪。

最小IO

有多种方法可以在各种环境中安装 MinIO在这篇博文中,我们将使用 Docker 启动 MinIO,但在生产中请确保在Kubernetes上的分布式设置中安装。

在本地机器上创建一个目录,MinIO 将在其中保存数据

$ mkdir -p /minio/data

使用 Docker 启动 MinIO 容器

$ docker run -d \
  -p 9000:9000 \
  -p 9001:9001 \
  --name minio \

   --hostname minio \
  -v /minio/data:/data \
  -e "MINIO_ROOT_USER=minio" \
  -e "MINIO_ROOT_PASSWORD=minioadmin" \
  quay.io/minio/minio server /data --console-address ":9001"


注意:保留上面使用的凭据的副本;稍后您将需要它们来访问 MinIO。

使用浏览器通过http://localhost:9001/使用用于启动上述容器的凭据登录,验证您是否可以访问 MinIO。


Untitled (42).png


普罗米修斯

我们从应用程序收集的指标需要存储在某个地方。在这种情况下, Prometheus就是那个商店,但 OpenTelemetry 也支持其他出口商。

首先创建一个名为prometheus.yml以下内容的文件

scrape_configs:
  - job_name: 'metrics-app'
    scrape_interval: 2s
    static_configs:
      - targets: ['metrics-app:8000']

记下- targets: ['metrics-app:8000']稍后当我们在 Docker 容器中启动应用程序时,我们会将主机名设置为metrics-app匹配目标。

启动普罗米修斯容器

docker run -d \
    -p 9090:9090 \
    --name prom \
    --hostname prom \
    -v $(pwd)/prometheus.yml:/etc/prometheus/prometheus.yml \
    prom/prometheus

记下上面的主机名和端口;稍后在 Grafana 中配置数据源时将需要这些。

格拉法纳

您知道 Grafana 最初是作为 Kibana 的一个分支开始的吗?在此示例中,我们将使用Grafana为我们使用上面的 MinIO 收集和存储的指标提供可视化。通过在 Grafana 中进行可视化,我们可以看到出现的模式,而这些模式是我们仅通过查看数字或日志看不到的。

启动 Grafana 容器

docker run -d \
  -p 3000:3000 \
  --name grafana \
  --hostname grafana \
  grafana/grafana:latest

通过浏览器访问http://localhost:3000并按照以下说明设置 Prometheus 数据源。

烧瓶应用

之前我们编写了一个 Python 应用程序来发送跟踪,但这次我们将使用 Python 编写一个 Flask Web 应用程序以更好地了解集成。Flask 是一个 Python Web 框架,可以帮助我们轻松构建简单的面向 Web 或 API 的应用程序。在这个例子中,我们只是展示了一个精简版,以便我们可以简洁地演示这些技术。

检测指标

我们将继续将整个 Flask 应用程序粘贴到此处,然后对其进行剖析以更深入地研究我们如何检测指标的代码库。

创建一个名为的文件app.py并粘贴以下代码

from flask import Flask, request, g

from prometheus_client import start_http_server

from minio import Minio


from opentelemetry import metrics
from opentelemetry.exporter.prometheus import PrometheusMetricReader
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
from opentelemetry.instrumentation.flask import FlaskInstrumentor

import time

# Service name is required for most backends
resource = Resource(attributes={
    SERVICE_NAME: "minio-service"
})

# Start Prometheus client
start_http_server(port=8000, addr="0.0.0.0")
reader = PrometheusMetricReader()
provider = MeterProvider(resource=resource, metric_readers=[reader])
metrics.set_meter_provider(provider)
meter = metrics.get_meter(__name__, True)

minio_app_visits_counter = meter.create_counter(
    name="minio_app_visits", description="count the number of visit to various routes", unit="1",
)

minio_request_duration = meter.create_histogram(
    name="minio_request_duration",
    description="measures the duration of the inbound MinIO App HTTP request",
    unit="milliseconds")

config = {
  "minio_endpoint": "minio:9000",
  "minio_username": "minio",
  "minio_password": "minioadmin",
}

minio_client = Minio(config["minio_endpoint"],
              secure=False,
              access_key=config["minio_username"],
              secret_key=config["minio_password"]
              )

app = Flask(__name__)
FlaskInstrumentor().instrument_app(app)

@app.before_request
def before_request_func():
    g.start_time = round(time.time()*1000)
    minio_app_visits_counter.add(1, attributes={"request_path": request.path})

@app.after_request
def after_request_func(response):

    total_request_time = round(time.time()*1000) - g.start_time
    minio_request_duration.record(total_request_time, {"request_path": request.path})

    return response

@app.route('/')
def hello_world():
    return '<h1>Hello World MinIO!</h1>'

@app.route('/makebucket/<name>')
def make_bucket(name):
    if not minio_client.bucket_exists(name):
 
        minio_client.make_bucket(name)

    return f"""
        <html>
        <h3>Make Bucket {name} Successful</h3>
        </html>
        """

@app.route('/removebucket/<name>')
def remove_bucket(name):
    if minio_client.bucket_exists(name):
 
        minio_client.remove_bucket(name)

    return f"""
        <html>
        <h3>Removed Bucket {name} Successful</h3>
        </html>
        """

if __name__ == "__main__":
    app.run(debug=True)


Flask是服务于我们路由的web框架

from flask import Flask, request, g

为了让服务器抓取我们的指标,我们需要启动一个Prometheus客户端。现在只需导入包。

from prometheus_client import start_http_server

为了进行 MinIO 操作,minio还要导入包。

from minio import Minio

OpenTelemetry是标准化我们发送给 Prometheus 的指标的框架。我们也将导入这些包。

from opentelemetry import metrics
from opentelemetry.exporter.prometheus import PrometheusMetricReader
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
from opentelemetry.instrumentation.flask import FlaskInstrumentor

Prometheus 客户端将我们收集的指标公开给 Prometheus scraper。

start_http_server(port=8000, addr="0.0.0.0")

提醒一下,端口应与prometheus.yml.

创建一个允许我们发送整数值的度量类型计数器。在这里,我们正在计算应用程序的访问次数。计数器可以计算像

  • API访问次数

  • 每个进程的页面错误数

minio_app_visits_counter = meter.create_counter(
    name="minio_app_visits", description="count the number of visit to various routes", unit="1",
)

除了页面访问,我们还想知道请求花了多长时间;为此,我们将创建一个度量类型直方图在本例中,我们以毫秒为单位测量入站请求时间,但我们可以显示其他指标,例如

  • 上传到特定 MinIO 存储桶的对象大小

  • 上传对象所花费的时间

minio_request_duration = meter.create_histogram(
    name="minio_request_duration",
    description="measures the duration of the inbound MinIO App HTTP request",
    unit="milliseconds")

初始化 MinIO 客户端

config = {

  "minio_endpoint": "minio:9000",

  "minio_username": "minio",

  "minio_password": "minioadmin",

}


minio_client = Minio(config["minio_endpoint"],
              secure=False,
              access_key=config["minio_username"],
              secret_key=config["minio_password"]
              )

初始化 Flask App 和 OpenTelemetry 指标检测

app = Flask(__name__)
FlaskInstrumentor().instrument_app(app)

我们正在def before_request_func():做两件事

  • 每次有人访问任何路线时都会增加一个计数器功能

  • 以毫秒为单位检查加载时间的开始时间

g.start_time = round(time.time()*1000)

minio_app_visits_counter.add(1, attributes={"request_path": request.path})


def after_request_func():我们正在捕获请求的结束时间并传递增量以记录直方图

total_request_time = round(time.time()*1000) - g.start_time
minio_request_duration.record(total_request_time, {"request_path": request.path})

这些指标封装在这些之前/之后的装饰器中,以便我们保持代码干燥。您可以添加任意数量的额外路由,它们将自动检测我们的自定义指标。

@app.before_request
def before_request_func():
    ...

@app.after_request
def after_request_func(response):
  ...

    return response

最后但同样重要的是,一些示例路由使用 MinIO Python SDK 执行一些操作。对这些的 cURL 调用看起来像这样

  • curl http://:/makebucket/aj-test

  • curl http://:/removebucket/aj-test

@app.route('/')
def hello_world():
    return '<h1>Hello World MinIO!</h1>'

@app.route('/makebucket/<name>')
def make_bucket(name):
    if not minio_client.bucket_exists(name):
 
        minio_client.make_bucket(name)

    return f"""
        <html>
        <h3>Make Bucket {name} Successful</h3>
        </html>
        """

@app.route('/removebucket/<name>')
def remove_bucket(name):
    if minio_client.bucket_exists(name):
 
        minio_client.remove_bucket(name)

    return f"""
        <html>
        <h3>Removed Bucket {name} Successful</h3>
        </html>
        """

如您所见,我们的应用程序非常简单,这就是它的全部意义所在。我们想让您对基础有一个概念,但您可以接受它并使用它运行并添加您自己的工具。

构建和部署应用程序

我们编写了应用程序,现在我们需要将应用程序构建为 Docker 镜像,这样我们就可以将它与其他基础设施一起部署,这样一切都可以相互通信。

创建一个名为的文件Dockerfile并添加以下内容

# syntax=docker/dockerfile:1

FROM python:3.8-slim-buster

WORKDIR /metrics_app

COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt

COPY app.py app.py

CMD [ "flask", "run", "--host", "0.0.0.0"]

我们需要安装我们在应用程序中导入的所有包。requirements.txt创建一个名为以下内容的文件

minio
flask
prometheus_client
opentelemetry-api
opentelemetry-sdk
opentelemetry-exporter-prometheus
opentelemetry-instrumentation-flask

ot-prom-metrics-app使用我们稍后将用作启动容器的图像名称的名称构建应用程序。

docker build --tag ot-prom-metrics-app .

使用我们刚刚在 port 上创建的图像启动应用程序容器5000

docker run -d \
    -p 5000:5000 \
    --name metrics-app \
    --hostname metrics-app \
    ot-prom-metrics-app

通过使用以下 curl 命令制作一个桶进行测试。

$ curl http://127.0.0.1:5000/makebucket/aj-test

        <html>
        <h3>Make Bucket aj-test Successful</h3>
        </html>

现在我们已经部署了所有部分,是时候在 Grafana 中观察它们了。

观察指标

我们部署了一个可以生成一些指标的应用程序,一个用于保存这些指标的数据存储 (prometheus),以及用于历史视图和上下文的 Grafana。

Grafana 查询

登录到 Grafana 


您会注意到我们的指标都不在其中。那是因为您没有对 flask 应用程序路由进行任何调用,或者它是在五分钟前进行的。从技术上讲,如果您扩大可见性的时间范围,您应该会看到在上一节中创建的 curl 调用寄存器,但无论如何,让我们再次进行测试调用,然后进行检查。

$ curl http://127.0.0.1:5000/makebucket/aj-test

        <html>
        <h3>Make Bucket aj-test Successful</h3>
        </html>

刷新 Grafana 页面

那不是很有趣吗?让我们做一些更有趣的事情,通过多次调用但错开它们。随后的每个调用都将花费一秒钟的时间。换句话说,第一次调用需要一秒,第二次调用需要两秒,第三次调用需要三秒等等。继续并运行下面的命令。

for i in {1..10}; do curl http://127.0.0.1:5000/makebucket/aj-test && sleep $i; done;

在 Grafana 中,单击Run query上述命令执行完毕。结果应该看起来像这样,注意到最右边了吗?那是我们的 11 个请求,或者无论您提出多少请求。


Screen Shot 2022-09-21 at 5.13.02 PM.png



虽然这很有趣,而且现在我们知道了对应用程序的访问量,但我们不知道这些访问的速度是多少:它们是每秒一次吗?每秒几个?这就是范围函数派上用场的地方。


现在您有了一个简单的应用程序,您可以使用它来构建您认为合适的进一步检测。其他一些常见用例是

  • 监控上传对象的大小

  • 监控获取对象数量的速率

  • 监视下载对象的速率

这些只是几个例子;您使用的工具越多,您的可观察性就越高。

最后的想法

可观察性是多方面的;您通常需要查看跟踪、指标和日志的组合来确定根本原因。您可以使用 Gremlin、ChaosMonkey 等混沌工程工具来破坏您的系统并观察指标中的模式。

例如,也许您正在收集您的 HTTP 请求状态,通常您一直看到 200 秒,但突然间您看到 500 秒的峰值。您查看日志,发现最近进行了部署或数据库因维护而停机。或者,如果您正在监控对象存储性能指标,您可以将任何服务器端问题与此数据相关联。正是这些类型的事件通常会造成最大的痛苦,并且在这些情况下具有可见性是最重要的。

如果您想了解有关混沌工程的更多信息,或者想在您的 MinIO 应用程序中实现指标的出色实现,请在Slack上联系我们我们很想看看你有什么。



上一篇 下一篇