Grafana Mimir es una base de datos de métricas compatible con Prometheus que escala horizontalmente, guarda los datos en almacenamiento de objetos y separa la información por tenants (inquilinos). Recibe las métricas por remote_write y responde a consultas PromQL, así que Prometheus y Grafana funcionan con él sin cambios. En este tutorial instalarás Mimir en modo monolítico (todos los componentes en un proceso) en Ubuntu 24.04 con un bucket S3 como backend, conectarás Prometheus y Grafana y configurarás límites por tenant.

Requisitos previos

  • Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, con al menos 4 GB de RAM y 2 vCPU. El consumo de memoria crece con el número de series activas.
  • Un usuario no root con privilegios sudo.
  • Un almacenamiento de objetos compatible con S3 (AWS S3, MinIO, Wasabi...) con tres buckets creados: mimir-blocks, mimir-ruler y mimir-alertmanager, y unas credenciales con permisos de lectura, escritura, listado y borrado sobre ellos. Los buckets deben ser distintos entre sí.
  • Un servidor Prometheus que enviará las métricas (puede ser el mismo host).
  • Opcional: Grafana para consultar los datos.

Paso 1: Instalar el binario de Mimir

Mimir se distribuye como un único binario. Consulta la última versión estable en github.com/grafana/mimir/releases y ajusta la variable. Los releases se etiquetan como mimir-X.Y.Z:

MIMIR_VERSION=2.17.0
cd /tmp
curl -fLo mimir "https://github.com/grafana/mimir/releases/download/mimir-${MIMIR_VERSION}/mimir-linux-amd64"
sudo install -m 0755 mimir /usr/local/bin/mimir

En un servidor arm64 descarga mimir-linux-arm64. Comprueba la instalación:

mimir -version

La salida muestra la versión instalada, la rama y la revisión del binario.

Crea un usuario de sistema y los directorios de configuración y datos:

sudo useradd --system --no-create-home --shell /usr/sbin/nologin mimir
sudo mkdir -p /etc/mimir /var/lib/mimir
sudo chown mimir:mimir /var/lib/mimir

Paso 2: Crear la configuración de Mimir

La configuración define el almacenamiento, los anillos de hash (que en modo monolítico solo contienen esta instancia) y los directorios locales. Mimir escribe primero las muestras en un TSDB local en los ingesters y sube los bloques al bucket cada 2 horas. Crea el archivo:

sudo nano /etc/mimir/mimir.yaml

Sustituye your_s3_endpoint, your_region, your_access_key y your_secret_key por los datos de tu proveedor:

target: all

server:
  http_listen_port: 9009
  grpc_listen_port: 9095
  log_level: info

common:
  storage:
    backend: s3
    s3:
      endpoint: your_s3_endpoint
      region: your_region
      access_key_id: your_access_key
      secret_access_key: your_secret_key

blocks_storage:
  s3:
    bucket_name: mimir-blocks
  tsdb:
    dir: /var/lib/mimir/tsdb
  bucket_store:
    sync_dir: /var/lib/mimir/tsdb-sync

ruler_storage:
  s3:
    bucket_name: mimir-ruler

alertmanager_storage:
  s3:
    bucket_name: mimir-alertmanager

compactor:
  data_dir: /var/lib/mimir/compactor
  sharding_ring:
    kvstore:
      store: memberlist

distributor:
  ring:
    instance_addr: 127.0.0.1
    kvstore:
      store: memberlist

ingester:
  ring:
    instance_addr: 127.0.0.1
    kvstore:
      store: memberlist
    replication_factor: 1

store_gateway:
  sharding_ring:
    replication_factor: 1

limits:
  max_global_series_per_user: 1000000
  compactor_blocks_retention_period: 1y

runtime_config:
  file: /etc/mimir/runtime.yaml

Puntos importantes:

  • endpoint va sin esquema, por ejemplo s3.eu-west-1.amazonaws.com o minio.your_domain:9000. Si tu MinIO no usa TLS, añade insecure: true dentro de common.storage.s3.
  • replication_factor: 1 es obligatorio con una sola instancia; en un clúster se usa 3.
  • limits fija los valores por defecto para todos los tenants: 1 millón de series activas y 1 año de retención. Sin compactor_blocks_retention_period los datos no se borran nunca.
  • runtime_config.file apunta a un archivo que Mimir relee periódicamente sin reiniciar; ahí irán los límites por tenant.

Crea el archivo de configuración en tiempo de ejecución, de momento con un override vacío:

sudo nano /etc/mimir/runtime.yaml
overrides: {}

Protege ambos archivos, ya que contienen credenciales:

sudo chown root:mimir /etc/mimir/mimir.yaml /etc/mimir/runtime.yaml
sudo chmod 640 /etc/mimir/mimir.yaml /etc/mimir/runtime.yaml

Paso 3: Ejecutar Mimir como servicio systemd

Crea la unidad:

sudo nano /etc/systemd/system/mimir.service
[Unit]
Description=Grafana Mimir
After=network-online.target
Wants=network-online.target

[Service]
User=mimir
Group=mimir
WorkingDirectory=/var/lib/mimir
ExecStart=/usr/local/bin/mimir -config.file=/etc/mimir/mimir.yaml
Restart=on-failure
RestartSec=10
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

Arranca el servicio:

sudo systemctl daemon-reload
sudo systemctl enable --now mimir
sudo systemctl status mimir --no-pager

Mimir tarda unos segundos en unir sus anillos. Comprueba que está listo:

curl -s http://localhost:9009/ready
ready

Si devuelve otro texto (por ejemplo, que el ingester aún no está activo), espera 15-30 segundos y repite. Los errores de acceso al bucket aparecen en sudo journalctl -u mimir -n 50 --no-pager.

Paso 4: Enviar métricas desde Prometheus

Mimir tiene la multi-tenancy activada por defecto: cada petición debe llevar la cabecera X-Scope-OrgID con el identificador del tenant. Aquí se usa el tenant infra. Abre la configuración de Prometheus:

sudo nano /etc/prometheus/prometheus.yml

Añade un bloque remote_write al nivel superior del archivo (sustituye localhost por la IP de Mimir si está en otro servidor):

remote_write:
  - url: http://localhost:9009/api/v1/push
    headers:
      X-Scope-OrgID: infra

Añade también una etiqueta externa que identifique a este Prometheus, dentro del bloque global:

global:
  external_labels:
    cluster: produccion

Valida y recarga Prometheus:

promtool check config /etc/prometheus/prometheus.yml
sudo systemctl restart prometheus

Espera un minuto y consulta los datos a través de la API compatible con Prometheus de Mimir, que cuelga de la ruta /prometheus:

curl -s -H "X-Scope-OrgID: infra" \
  "http://localhost:9009/prometheus/api/v1/query?query=up" | python3 -m json.tool | head -20
{
    "status": "success",
    "data": {
        "resultType": "vector",
        "result": [
            {
                "metric": {
                    "__name__": "up",
                    "cluster": "produccion",
                    "instance": "localhost:9090",
                    "job": "prometheus"
                },
...

Si repites la consulta con otro tenant (por ejemplo X-Scope-OrgID: otro), el resultado estará vacío: los datos de cada tenant están aislados.

Paso 5: Configurar límites por tenant

Los límites de mimir.yaml se aplican a todos los tenants. Para dar más capacidad o retención a uno concreto, usa el archivo de runtime:

sudo nano /etc/mimir/runtime.yaml
overrides:
  infra:
    max_global_series_per_user: 3000000
    compactor_blocks_retention_period: 2y
  equipo-web:
    max_global_series_per_user: 200000
    compactor_blocks_retention_period: 90d

No hace falta reiniciar: Mimir recarga el archivo cada 10 segundos por defecto. Comprueba que los overrides se han aplicado:

curl -s http://localhost:9009/runtime_config

La salida muestra el contenido cargado, incluidos los límites de infra y equipo-web.

Si un tenant supera max_global_series_per_user, Mimir rechaza las series nuevas con un error per-user series limit que verás en los logs de Prometheus. Es la forma de evitar que un equipo agote la memoria de los ingesters.

Paso 6: Conectar Grafana

En Grafana, ve a Connections > Data sources > Add data source y elige Prometheus. Configura:

  • Prometheus server URL: http://localhost:9009/prometheus (o la IP de Mimir).
  • En HTTP headers, añade la cabecera X-Scope-OrgID con el valor infra.
  • En Performance, selecciona Mimir como Prometheus type si tu versión de Grafana ofrece la opción.

Pulsa Save & test. Crea un data source por tenant si varios equipos comparten Mimir.

Para consultar varios tenants en una sola consulta, activa la federación en mimir.yaml:

tenant_federation:
  enabled: true

Tras reiniciar Mimir, una cabecera como X-Scope-OrgID: infra|equipo-web devuelve datos de ambos tenants.

Paso 7: Proteger el acceso

Mimir no incluye autenticación: cualquiera que alcance el puerto 9009 puede escribir o leer datos de cualquier tenant indicando la cabecera. Si Prometheus y Grafana están en otros servidores, permite solo sus IP con UFW:

sudo ufw allow from your_prometheus_ip to any port 9009 proto tcp
sudo ufw allow from your_grafana_ip to any port 9009 proto tcp

Para exponer Mimir fuera de una red privada, ponlo detrás de un proxy inverso con TLS que autentique a los clientes y fije el X-Scope-OrgID según el usuario, en lugar de confiar en el valor que envía el cliente.

Cuándo pasar al modo distribuido

El modo monolítico aguanta bien cientos de miles de series activas en un solo servidor. Cuando necesites alta disponibilidad o más capacidad, el mismo binario puede ejecutar cada componente por separado (-target=distributor, -target=ingester, -target=querier, -target=store-gateway, -target=compactor...) y escalarlos de forma independiente: más ingesters para más escritura, más queriers para más consultas. En la práctica, el despliegue distribuido se hace en Kubernetes con el chart de Helm oficial mimir-distributed del repositorio https://grafana.github.io/helm-charts, que ya configura réplicas, memberlist y cachés.

Solución de problemas

/ready nunca devuelve ready. Revisa journalctl -u mimir. Los fallos más habituales son credenciales S3 incorrectas, un bucket inexistente o compartir el mismo bucket entre bloques, ruler y alertmanager, algo que Mimir no admite.

Prometheus muestra 401 o no org id al enviar datos. Falta la cabecera X-Scope-OrgID en remote_write. Si solo tendrás un tenant, puedes desactivar la multi-tenancy con multitenancy_enabled: false al principio de mimir.yaml; los datos irán al tenant anonymous.

Errores err-mimir-sample-out-of-order o sample too old. Dos Prometheus envían las mismas series sin etiquetas externas que los distingan, o un Prometheus reenvía datos antiguos. Da a cada Prometheus un external_labels único.

Consumo de memoria alto. La memoria de los ingesters depende de las series activas. Consulta cuántas tiene cada tenant con curl -s http://localhost:9009/metrics | grep cortex_ingester_active_series y ajusta los límites por tenant.

Conclusión

Has instalado Grafana Mimir en modo monolítico con los bloques en S3, has conectado Prometheus con remote_write, has aislado los datos por tenant con límites propios y has añadido Mimir a Grafana. Como siguientes pasos, puedes importar los dashboards de la mixin oficial de Mimir para vigilar el propio servicio, cargar reglas de alerta por tenant con mimirtool y, cuando el volumen lo pida, migrar al chart mimir-distributed en Kubernetes.