From 40fad6bae8f7e9bbb395bfa51a103c4379adb429 Mon Sep 17 00:00:00 2001 From: Manuel Vergara Date: Fri, 9 Aug 2024 01:20:19 +0200 Subject: [PATCH] Add prometheus_grafana test --- .../05_prometheus_grafana/Dockerfile | 19 ++ .../05_prometheus_grafana/README.md | 252 +++++++++++++++++- .../05_prometheus_grafana/app/__init__.py | 0 .../05_prometheus_grafana/app/main.py | 42 +++ .../05_prometheus_grafana/docker-compose.yaml | 50 ++++ .../05_prometheus_grafana/prometheus.yml | 10 + .../05_prometheus_grafana/requirements.txt | 1 + catch-all/05_infra_test/README.md | 16 +- 8 files changed, 381 insertions(+), 9 deletions(-) create mode 100644 catch-all/05_infra_test/05_prometheus_grafana/Dockerfile create mode 100644 catch-all/05_infra_test/05_prometheus_grafana/app/__init__.py create mode 100644 catch-all/05_infra_test/05_prometheus_grafana/app/main.py create mode 100644 catch-all/05_infra_test/05_prometheus_grafana/docker-compose.yaml create mode 100644 catch-all/05_infra_test/05_prometheus_grafana/prometheus.yml create mode 100644 catch-all/05_infra_test/05_prometheus_grafana/requirements.txt diff --git a/catch-all/05_infra_test/05_prometheus_grafana/Dockerfile b/catch-all/05_infra_test/05_prometheus_grafana/Dockerfile new file mode 100644 index 0000000..1795d78 --- /dev/null +++ b/catch-all/05_infra_test/05_prometheus_grafana/Dockerfile @@ -0,0 +1,19 @@ +# Usar imagen de python 3.9 slim +FROM python:3.9-slim + +# Establece el directorio de trabajo +WORKDIR /app + +# Copia el archivo requirements.txt e instala las dependencias +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copia el código fuente a /app +COPY app app + +# Variables de entorno +ENV PYTHONUNBUFFERED 1 +ENV PYTHONPATH=/app + +# Comando para ejecutar la aplicación +CMD ["python", "app/main.py"] diff --git a/catch-all/05_infra_test/05_prometheus_grafana/README.md b/catch-all/05_infra_test/05_prometheus_grafana/README.md index 3f1afc0..664beee 100644 --- a/catch-all/05_infra_test/05_prometheus_grafana/README.md +++ b/catch-all/05_infra_test/05_prometheus_grafana/README.md @@ -1 +1,251 @@ -# Prometheus - Grafana (próximamente) + +# Configuración de Grafana con Prometheus para proyectos Python usando Docker + +En este artículo, cubriremos cómo configurar el monitoreo de servicios para proyectos Python con Prometheus y Grafana utilizando contenedores Docker. + +El monitoreo de servicios nos permite analizar eventos específicos en nuestros proyectos, tales como llamadas a bases de datos, interacción con API, seguimiento del rendimiento de recursos, etc. Puedes detectar fácilmente comportamientos inusuales o descubrir pistas útiles detrás de los problemas. + +## Escenario de Caso Real + +Teníamos un servicio temporal que redirigía las solicitudes entrantes de sitios web específicos hasta que Google dejara de indexar estas páginas web. Utilizando el monitoreo de servicios, podemos ver fácilmente el conteo de redirecciones de forma regular. En un momento determinado en el futuro, el número de redirecciones disminuirá, lo que significa que el tráfico se ha migrado al sitio web objetivo, y ya no necesitaremos que este servicio esté en funcionamiento. + +## Configuración de contenedores Docker + +Vamos a ejecutar todos nuestros servicios localmente en contenedores Docker. En las grandes empresas, existe un servicio global para Prometheus y Grafana que incluye todos los proyectos de monitoreo de microservicios. Probablemente ni siquiera necesites escribir ningún pipeline de despliegue para las herramientas de monitoreo de servicios. + +### Archivo docker-compose.yml + +Comencemos creando un archivo `docker-compose.yml` con los servicios necesarios: + +```yaml +services: + prometheus: + image: prom/prometheus:latest + container_name: prometheus + ports: + - "9090:9090" + volumes: + - ${PWD}/prometheus.yml:/etc/prometheus/prometheus.yml + restart: unless-stopped + networks: + - monitoring-net + healthcheck: + test: ["CMD", "wget", "--spider", "http://localhost:9090/-/healthy"] + interval: 1m30s + timeout: 30s + retries: 3 + start_period: 30s + + grafana: + hostname: grafana + image: grafana/grafana:latest + container_name: grafana + ports: + - 3001:3000 + restart: unless-stopped + networks: + - monitoring-net + healthcheck: + test: ["CMD", "wget", "--spider", "http://localhost:3000/api/health"] + interval: 1m30s + timeout: 30s + retries: 3 + start_period: 30s + + app: + build: + context: . + dockerfile: Dockerfile + depends_on: + - prometheus + ports: + - "8080:8080" + networks: + - monitoring-net + command: ["python3", "app/main.py"] + +networks: + monitoring-net: + driver: bridge +``` + +El punto más importante de la configuración anterior es montar el archivo `prometheus.yml` desde nuestra máquina local al contenedor Docker. Este archivo incluye la configuración para obtener datos (métricas) de nuestro servicio de aplicación o proyecto Python. Sin el archivo, no podrás ver las métricas personalizadas que incluye tu proyecto. + +Por lo tanto, crea un nuevo archivo llamado `prometheus.yml` en el nivel raíz de tu proyecto. + +### Archivo prometheus.yml + +```yaml +global: + scrape_interval: 15s + +scrape_configs: + - job_name: 'app' + static_configs: + - targets: ['app:8080'] +``` + +Ahora, Prometheus obtendrá datos de nuestro proyecto. + +Todas las demás configuraciones en el archivo de composición son autoexplicativas y no son muy críticas, como mencionamos para Prometheus. + +## Crear un nuevo proyecto Python + +Ahora, vamos a crear una aplicación Python muy sencilla que creará una métrica para rastrear el tiempo empleado y las solicitudes realizadas. Crea una nueva carpeta llamada `app` en el nivel raíz del proyecto. También incluye `__init__.py` para marcarla como un paquete Python. + +A continuación, crea otro archivo llamado `main.py` que contendrá la lógica principal del programa, como se muestra a continuación: + +### Archivo app/main.py + +```python +from prometheus_client import start_http_server, Summary, Counter, Gauge, Histogram +import random +import time + +# Create metrics to track time spent, requests made, and other events. +REQUEST_TIME = Summary('request_processing_seconds', 'Time spent processing request') +UPDATE_COUNT = Counter('update_count', 'Number of updates') +ACTIVE_REQUESTS = Gauge('active_requests', 'Number of active requests') +REQUEST_LATENCY = Histogram('request_latency_seconds', 'Request latency in seconds') + +# Decorate function with metrics. +@REQUEST_TIME.time() +@REQUEST_LATENCY.time() +def process_request(t): + """A dummy function that takes some time.""" + ACTIVE_REQUESTS.inc() + time.sleep(t) + ACTIVE_REQUESTS.dec() + +def main(): + # Start up the server to expose the metrics. + start_http_server(8000) + print("[*] Starting server on port 8000...") + + # Generate some requests. + while True: + msg = random.random() + process_request(msg) + update_increment = random.randint(1, 100) + UPDATE_COUNT.inc(update_increment) + print(f'[+] Processing request: {msg:.4f} | Updates: {update_increment}') + time.sleep(random.uniform(0.5, 2.0)) # Random delay between requests + +if __name__ == '__main__': + main() +``` + +Aquí, estamos utilizando un paquete de Python llamado `prometheus_client` para interactuar con Prometheus. Permite fácilmente la creación de diferentes tipos de métricas que nuestro proyecto requiere. + +El código anterior se copió de la documentación oficial de `prometheus_client`, que simplemente crea una nueva métrica llamada `request_processing_seconds` que mide el tiempo empleado en esa solicitud en particular. Cubriremos otros tipos de métricas más adelante en este artículo. + +Ahora, vamos a crear un `Dockerfile` y un `requirements.txt` para construir nuestro proyecto. + +### Archivo Dockerfile + +```dockerfile +# Usa la imagen base oficial de Python +FROM python:3.9-slim + +# Establece el directorio de trabajo +WORKDIR /app + +# Copia los archivos de requerimientos +COPY requirements.txt . + +# Instala las dependencias +RUN pip install --no-cache-dir -r requirements.txt + +# Copia el código de la aplicación +COPY app/ /app + +# Expone el puerto de la aplicación +EXPOSE 8000 + +# Comando por defecto para ejecutar la aplicación +CMD ["python", "main.py"] +``` + +### Archivo requirements.txt + +```txt +prometheus_client +``` + +Ahora estamos listos para construir y ejecutar los servicios. + +## Ejecución de los servicios + +Para ver todo en acción, ejecuta los siguientes comandos: + +```bash +docker-compose up -d --build +``` + +Esto levantará todos los servicios definidos en `docker-compose.yml`, compilando la imagen de la aplicación en el proceso. + +## Configuración de Grafana + +En esta sección, usaremos Prometheus como fuente de datos para mostrar métricas en gráficos de Grafana. + +1. Navega a `http://localhost:3001` para ver la página de inicio de sesión de Grafana y utiliza `admin` tanto para el nombre de usuario como para la contraseña. Luego, requerirá agregar una nueva contraseña, y puedes mantenerla igual ya que estamos probando localmente. + +2. Después de iniciar sesión correctamente, deberías ver el panel predeterminado de Grafana. Luego selecciona **Data Sources** en la página. + + ![](https://res.cloudinary.com/practicaldev/image/fetch/s--dckjv7Wt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.thepylot.dev/content/images/size/w1000/2022/06/Screen-Shot-2022-06-11-at-16.51.10.png) + +3. A continuación, selecciona Prometheus como fuente de datos: + + ![](https://res.cloudinary.com/practicaldev/image/fetch/s--v5aNeycU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.thepylot.dev/content/images/size/w1000/2022/06/Screen-Shot-2022-06-11-at-16.53.47.png) + +4. Luego requerirá la URL en la que se está ejecutando el servicio de Prometheus, que será el nombre del servicio de Docker que creamos `http://prometheus:9090`. + + ![](https://res.cloudinary.com/practicaldev/image/fetch/s--cSw8NHIy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.thepylot.dev/content/images/size/w1000/2022/06/Screen-Shot-2022-06-11-at-16.56.09.png) + +5. Finalmente, haz clic en el botón **Save & Test** para verificar la conexión de la fuente de datos. + + ![](https://res.cloudinary.com/practicaldev/image/fetch/s--i4CVmg76--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto% + +2Cw_800/https://www.thepylot.dev/content/images/size/w1000/2022/06/Screen-Shot-2022-06-11-at-16.56.55.png) + +¡Genial! Ahora nuestro Grafana está listo para ilustrar las métricas que provienen de Prometheus. + +## Creación de un nuevo dashboard en Grafana + +1. Navega a `http://localhost:3001/dashboards` para crear un nuevo dashboard y agregar un nuevo panel. Haz clic en **New Dashboard** y luego en **Add New Panel** para la inicialización. + + ![](https://res.cloudinary.com/practicaldev/image/fetch/s--yZiKAdWz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.thepylot.dev/content/images/size/w1000/2022/06/Screen-Shot-2022-06-11-at-17.02.21.png) + +2. A continuación, seleccionamos código dentro del panel de consultas y escribimos `request_processing_seconds`. Podrás ver 3 tipos diferentes de sufijos con los datos de tus métricas personalizadas. Prometheus simplemente aplica diferentes tipos de cálculos a tus datos de manera predeterminada. + +3. Selecciona una de las opciones y haz clic en **Run Query** para verla en el gráfico. + + ![](https://res.cloudinary.com/practicaldev/image/fetch/s--E8k1_d3s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.thepylot.dev/content/images/size/w1000/2022/06/Screen-Shot-2022-06-11-at-17.14.32.png) + +Finalmente, podemos ver las métricas de nuestro proyecto ilustradas muy bien por Grafana. + +## Otras métricas + +Hay muchos tipos de métricas disponibles según lo que requiera el proyecto. Si queremos contar un evento específico, como actualizaciones de registros en la base de datos, podemos usar `Counter()`. + +Si tenemos una cola de mensajes como Kafka o RabbitMQ, podemos usar `Gauge()` para ilustrar el número de elementos que esperan en la cola. + +Intenta agregar otra métrica en `main.py` como se muestra a continuación y aplica los mismos pasos para conectar Prometheus con Grafana: + +### Ejemplo de una nueva métrica con Counter + +```python +UPDATE_COUNT = Counter('update_count', 'Number of updates') + +# Dentro de tu función de procesamiento +update_increment = random.randint(1, 100) +UPDATE_COUNT.inc(update_increment) +``` + +No olvides construir nuevamente la imagen de Docker para todos los servicios: + +```bash +docker-compose up -d --build +``` + +--- diff --git a/catch-all/05_infra_test/05_prometheus_grafana/app/__init__.py b/catch-all/05_infra_test/05_prometheus_grafana/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/catch-all/05_infra_test/05_prometheus_grafana/app/main.py b/catch-all/05_infra_test/05_prometheus_grafana/app/main.py new file mode 100644 index 0000000..d1cf6f4 --- /dev/null +++ b/catch-all/05_infra_test/05_prometheus_grafana/app/main.py @@ -0,0 +1,42 @@ +from prometheus_client import start_http_server, Summary, Counter, Gauge, Histogram +import random +import time + +# Create metrics to track time spent, requests made, and other events. +REQUEST_TIME = Summary('request_processing_seconds', + 'Time spent processing request') +UPDATE_COUNT = Counter('update_count', 'Number of updates') +ACTIVE_REQUESTS = Gauge('active_requests', 'Number of active requests') +REQUEST_LATENCY = Histogram( + 'request_latency_seconds', 'Request latency in seconds') + +# Decorate function with metrics. + + +@REQUEST_TIME.time() +@REQUEST_LATENCY.time() +def process_request(t): + """A dummy function that takes some time.""" + ACTIVE_REQUESTS.inc() + time.sleep(t) + ACTIVE_REQUESTS.dec() + + +def main(): + # Start up the server to expose the metrics. + start_http_server(8000) + print("[*] Starting server on port 8000...") + + # Generate some requests. + while True: + msg = random.random() + process_request(msg) + update_increment = random.randint(1, 100) + UPDATE_COUNT.inc(update_increment) + print( + f'[+] Processing request: {msg:.4f} | Updates: {update_increment}') + time.sleep(random.uniform(0.5, 2.0)) # Random delay between requests + + +if __name__ == '__main__': + main() diff --git a/catch-all/05_infra_test/05_prometheus_grafana/docker-compose.yaml b/catch-all/05_infra_test/05_prometheus_grafana/docker-compose.yaml new file mode 100644 index 0000000..a5c1452 --- /dev/null +++ b/catch-all/05_infra_test/05_prometheus_grafana/docker-compose.yaml @@ -0,0 +1,50 @@ +services: + + prometheus: + image: prom/prometheus:latest + container_name: prometheus + ports: + - "9090:9090" + volumes: + - ${PWD}/prometheus.yml:/etc/prometheus/prometheus.yml + restart: unless-stopped + networks: + - monitoring-net + healthcheck: + test: ["CMD", "wget", "--spider", "http://localhost:9090/-/healthy"] + interval: 1m30s + timeout: 30s + retries: 3 + start_period: 30s + + grafana: + hostname: grafana + image: grafana/grafana:latest + container_name: grafana + ports: + - 3001:3000 + restart: unless-stopped + networks: + - monitoring-net + healthcheck: + test: ["CMD", "wget", "--spider", "http://localhost:3000/api/health"] + interval: 1m30s + timeout: 30s + retries: 3 + start_period: 30s + + app: + build: + context: . + dockerfile: Dockerfile + depends_on: + - prometheus + ports: + - "8080:8080" + networks: + - monitoring-net + command: ["python3", "app/main.py"] + +networks: + monitoring-net: + driver: bridge diff --git a/catch-all/05_infra_test/05_prometheus_grafana/prometheus.yml b/catch-all/05_infra_test/05_prometheus_grafana/prometheus.yml new file mode 100644 index 0000000..f70560d --- /dev/null +++ b/catch-all/05_infra_test/05_prometheus_grafana/prometheus.yml @@ -0,0 +1,10 @@ + +global: + scrape_interval: 15s # when Prometheus is pulling data from exporters etc + evaluation_interval: 30s # time between each evaluation of Prometheus' alerting rules + +scrape_configs: + - job_name: app # your project name + static_configs: + - targets: + - app:8000 \ No newline at end of file diff --git a/catch-all/05_infra_test/05_prometheus_grafana/requirements.txt b/catch-all/05_infra_test/05_prometheus_grafana/requirements.txt new file mode 100644 index 0000000..9ca76ff --- /dev/null +++ b/catch-all/05_infra_test/05_prometheus_grafana/requirements.txt @@ -0,0 +1 @@ +prometheus-client \ No newline at end of file diff --git a/catch-all/05_infra_test/README.md b/catch-all/05_infra_test/README.md index 23ff641..ac3ef1b 100644 --- a/catch-all/05_infra_test/README.md +++ b/catch-all/05_infra_test/README.md @@ -6,11 +6,11 @@ -| Nombre | Descripción | Nivel | -| -------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | ---------- | -| [redis](./01_redis_flask_docker/) | Despliegue de un contenedor de redis y flask | Básico | -| [rabbit](./02_rabbitmq/README.md) | Despliegue de distintas arquitecturas para rabbitmq | Intermedio | -| [Apache Kafka](./03_kafka/README.md) | Despliegue de Apache Kafka con productor y consumidor | Intermedio | -| [Elastic stack](./04_elastic_stack/README.md) | Despliegue de Elastic Stack | Básico | -| Prometheus Grafana (proximamente) | Despliegue de un contenedor de Prometheus Grafana | Básico | -| SonarQube (proximamente) | Despliegue de un contenedor de SonarQube | Básico | +| Nombre | Descripción | Nivel | +| ----------------------------------------------------------------------- | ----------------------------------------------------- | ---------- | +| [redis](./01_redis_flask_docker/) | Despliegue de un contenedor de redis y flask | Básico | +| [rabbit](./02_rabbitmq/README.md) | Despliegue de distintas arquitecturas para rabbitmq | Intermedio | +| [Apache Kafka](./03_kafka/README.md) | Despliegue de Apache Kafka con productor y consumidor | Intermedio | +| [Elastic stack](./04_elastic_stack/README.md) | Despliegue de Elastic Stack | Básico | +| [Prometheus Grafana](./05_prometheus_grafana/README.md) | Despliegue de un contenedor de Prometheus Grafana | Básico | +| SonarQube (proximamente) | Despliegue de un contenedor de SonarQube | Básico |