jueves, 1 de agosto de 2024

Monitoreo y Observabilidad: Instalando Prometheus - Parte 1



Como parte del ciclo CI/CD en la implementación de la cultura DevOps, después de realizar el Deploy necesitamos tener visibilidad de lo que está pasando dentro de nuestro cluster de kubernetes, tanto a nivel de hardware (uso de CPU, Memoria, Red, etc) sino también a nivel de aplicaciones que se están ejecutando en el cluster, podemos ver los hits que está teniendo o la memoria y CPU que está usando, esto nos puede servir para determinar cuándo escalar la aplicación y que se vuelva elástica, lo que nos ahorra dinero, así como para lanzar alertas cuando algo "pasa" y toca enviar una alarte para levantar al que está de guardia, incluso puedes integrar las alertas con una central telefónica y que llame al número del equipo que está de guardia/oncall, pero eso ya es una integración que te toca hacer a tí.
Antes de entrar en materia aquí te dejo el video en Youtube para que lo sigas conmigo:

### Prometheus

Empecemos por el principio, ¿Qué es [Prometheus](https://prometheus.io/)? Lo podemos definir como un sistema de monitoreo y alertas de código abierto. Esta escrito en Go. Todos los datos y métricas se almacenan como series temporales.




### Arquitectura de Prometheus

Consta de componentes principales:

 - **Servidor:** Cuenta con un sistema de monitoreo y alertas de código abierto. Es el encargado de llevar a cabo la administración.
    
 - **Librería de Cliente:** Cuenta con un sistema de monitoreo y alertas de código abierto. Es el encargado de llevar a cabo la lectura de los datos y exponer las métricas.
    
 - **Gestor de Alertas:** Cuenta con un sistema de monitoreo y alertas de código abierto. Es el encargado de llevar a cabo la escritura de las alertas.

**Nota:** Aunque no es parte de su arquitectura principal Prometheus también cuenta con un componente llamado **exporters**, básicamente sirve para recolectar datos, por ejemplo de bases de datos (MySQL, Mongo, etc), e incluso de otro tipo de hardware como un IBM Z.

Aquí te dejo una imágen de los componentes:
Aquí te dejo una imágen de la integración de Prometheus con otros sistemas.

### El duo dinámico Prometheus y Grafana

**Grafana** es una aplicación que se utiliza para visualizar los datos que recolecta Prometheus, creando dashboards para comprender lo que está pasando en el sistema que se está monitoreando. En el siguite post veremos como instalar y configurar.

Prometheus se encarga de recolectar, mientras que grafana de interpretar y mostrar gráficas (dashboards).

### Instalación de Prometheus

Vamos a crear un namespace llamado **monitoring** donde instalaremos Prometheus.

 **01-namespace.yaml**
 
    
    ---
    apiVersion: v1
    kind: Namespace
    metadata:
      name: monitoring
    ---
    
Vamos a necesitar crear un rol de solo lectura para poder ver, listar y obtener las méstricas de los recursos (nodos, services, pods, endpoints, etc) de Kubernetes, para eso es el siguiente yaml 02.

 **02-clusterRole.yaml**
 
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      name: prometheus
    rules:
    - apiGroups: [""]
      resources:
      - nodes
      - nodes/proxy
      - services
      - endpoints
      - pods
      verbs: ["get", "list", "watch"]
    - apiGroups:
      - extensions
      resources:
      - ingresses
      verbs: ["get", "list", "watch"]
    - nonResourceURLs: ["/metrics"]
      verbs: ["get"]
    ----
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: prometheus
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: ClusterRole
      name: prometheus
    subjects:
    - kind: ServiceAccount
      name: default
      namespace: monitoring
    ---

El siguiente yaml **03**, vamos a configurar alertas demo, por ejemplo vamos crear una alerta que se llama **High Pod Memory**, nos va alertar cuando un Pod tiene un consumo de memoria de más de 1 bytes por más de 1 minuto. Si bien no pienso profundizar mucho en la configuración de las alertas, te recuerdo que estas las puedes configurar una vez hayamos instalado Prometheus y accedas a él desde su administrador web, y siempre empieces de las más fáciles a las más complejas ya que puedes monitorear todo lo que ocurre en tu cluster de kubernetes. Poder llegar a la configuración óptima para tu cluster es un trabajo muy artesanal.

 **03-prometheus-configmap.yaml**
 
    ---
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: prometheus-server-conf
      labels:
        name: prometheus-server-conf
      namespace: monitoring
    data:
      prometheus.rules: |-
        groups:
        - name: devopscube demo alert
          rules:
          - alert: High Pod Memory
            expr: sum(container_memory_usage_bytes) > 1
            for: 1m
            labels:
              severity: slack
            annotations:
              summary: High Memory Usage
      prometheus.yml: |-
        global:
          scrape_interval: 5s
          evaluation_interval: 5s
        rule_files:
          - /etc/prometheus/prometheus.rules
        alerting:
          alertmanagers:
          - scheme: http
            static_configs:
            - targets:
              - "alertmanager.monitoring.svc:9093"
        scrape_configs:
          - job_name: 'node-exporter'
            kubernetes_sd_configs:
              - role: endpoints
            relabel_configs:
            - source_labels: [__meta_kubernetes_endpoints_name]
              regex: 'node-exporter'
              action: keep
          - job_name: 'kubernetes-apiservers'
            kubernetes_sd_configs:
            - role: endpoints
            scheme: https
            tls_config:
              ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
            bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
            relabel_configs:
            - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
              action: keep
              regex: default;kubernetes;https
          - job_name: 'kubernetes-nodes'
            scheme: https
            tls_config:
              ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
            bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
            kubernetes_sd_configs:
            - role: node
            relabel_configs:
            - action: labelmap
              regex: __meta_kubernetes_node_label_(.+)
            - target_label: __address__
              replacement: kubernetes.default.svc:443
            - source_labels: [__meta_kubernetes_node_name]
              regex: (.+)
              target_label: __metrics_path__
              replacement: /api/v1/nodes/${1}/proxy/metrics
          - job_name: 'kubernetes-pods'
            kubernetes_sd_configs:
            - role: pod
            relabel_configs:
            - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
              action: keep
              regex: true
            - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
              action: replace
              target_label: __metrics_path__
              regex: (.+)
            - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
              action: replace
              regex: ([^:]+)(?::\d+)?;(\d+)
              replacement: $1:$2
              target_label: __address__
            - action: labelmap
              regex: __meta_kubernetes_pod_label_(.+)
            - source_labels: [__meta_kubernetes_namespace]
              action: replace
              target_label: kubernetes_namespace
            - source_labels: [__meta_kubernetes_pod_name]
              action: replace
              target_label: kubernetes_pod_name
          - job_name: 'kube-state-metrics'
            static_configs:
              - targets: ['kube-state-metrics.kube-system.svc.cluster.local:8080']
          - job_name: 'kubernetes-cadvisor'
            scheme: https
            tls_config:
              ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
            bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
            kubernetes_sd_configs:
            - role: node
            relabel_configs:
            - action: labelmap
              regex: __meta_kubernetes_node_label_(.+)
            - target_label: __address__
              replacement: kubernetes.default.svc:443
            - source_labels: [__meta_kubernetes_node_name]
              regex: (.+)
              target_label: __metrics_path__
              replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor
          - job_name: 'kubernetes-service-endpoints'
            kubernetes_sd_configs:
            - role: endpoints
            relabel_configs:
            - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
              action: keep
              regex: true
            - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]
              action: replace
              target_label: __scheme__
              regex: (https?)
            - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
              action: replace
              target_label: __metrics_path__
              regex: (.+)
            - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port]
              action: replace
              target_label: __address__
              regex: ([^:]+)(?::\d+)?;(\d+)
              replacement: $1:$2
            - action: labelmap
              regex: __meta_kubernetes_service_label_(.+)
            - source_labels: [__meta_kubernetes_namespace]
              action: replace
              target_label: kubernetes_namespace
            - source_labels: [__meta_kubernetes_service_name]
              action: replace
              target_label: kubernetes_name
    ---

Yaml 04, aquí definimos el deployment, como estoy en un cluster "pequeño" usaré solamente una réplica.

 **04-prometheus-deployment.yaml**
 
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: prometheus-deployment
      namespace: monitoring
      labels:
        app: prometheus-server
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: prometheus-server
      template:
        metadata:
          labels:
            app: prometheus-server
        spec:
          containers:
            - name: prometheus
              image: prom/prometheus
              args:
                - "--config.file=/etc/prometheus/prometheus.yml"
                - "--storage.tsdb.path=/prometheus/"
              ports:
                - containerPort: 9090
              volumeMounts:
                - name: prometheus-config-volume
                  mountPath: /etc/prometheus/
                - name: prometheus-storage-volume
                  mountPath: /prometheus/
          volumes:
            - name: prometheus-config-volume
              configMap:
                defaultMode: 420
                name: prometheus-server-conf

            - name: prometheus-storage-volume
              emptyDir: {}
    ---

Yaml 05, es la creación del service:

 **05-prometheus-service.yaml**
 
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: prometheus-service
      namespace: monitoring
      annotations:
          prometheus.io/scrape: 'true'
          prometheus.io/port:   '9090'

    spec:
      selector:
        app: prometheus-server
      ports:
        - port: 8082
          targetPort: 9090
    ---

Hasta aquí puedes crear un port-forward y acceder a **Prometheus** si estás probando en un minikube o cualquier herramienta de kubernetes "casera", lo siguiente que haré es crear un loadbalancer como si estuviera en un proveedor de la nube, si no tienes metallb puedes omitir este paso y hacer el port-forward. Incluso puedes crear tu propio ingress.

Creación del loadbalancer yaml 06:

**Nota:** En mi ejemplo estoy usando metallb como load balancer, este me asigna una IP, eso lo hago para que sea un comportamiento "similar" a cuando estas en cualquier proveedor de nube. Si deseas que hable un poco más de cómo implementar metallb por favor déjame saberlo.

 **06-prometheus-metallb.yaml**
 
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: loadbalancer
      namespace: monitoring
    spec:
      ports:
        - port: 80
          targetPort: 9090
          protocol: TCP
      type: LoadBalancer
      selector:
        app: prometheus-server
    ---

En mi caso por usar metallb puedo entrar a mi cluster con la IP asignada por el puerto 80 y tengo la IP en mi /etc/hosts, no hay que introducir usuario ni password:

Acá una imagen del loadbalancer y la IP que fue asignada:
Aquí una imagen del administrador web Prometheus:
Puedes ir al menú superior a Alerts donde veremos la alerta que definimos en el **03-prometheus-configmap.yaml** que se llama **High Pod Memory**.
Si tienes una aplicación o Pod corriendo en tu cluster puedes ver el uso del CPU, mira la expresión que estoy usando, pueden ser expresiones complejas, por ejemplo:
Está de más decir que puedes poner en [Argo CD](https://www.ahioros.info/2024/07/desplegando-con-argo-cd.html) un repositorio con los yaml para instalar prometheus cada vez que salga una versión o para automatizar el despliegue cuando creas un cluster nuevo de kubernetes.

En el siguiente post veremos como instalar **Grafana** para usar los datos obtenidos de Prometheus.

Aquí está el repositorio de [Azure DevOps](https://dev.azure.com/guillermogarcialedezma/DockerHub%20Test/).

No hay comentarios.: