Kafka Monitoring için Grafana ve Prometheus Kullanımı

Monitoring; sistemlerin sağlıklı çalışıp çalışmadığını anlamak, hataları tespit etmek ve performansı ölçmek için hayati öneme sahiptir. Bu yazıda, popüler açık kaynak kodlu izleme (monitoring) araçları olan Prometheus ve Grafana’nın nasıl kullanılacağından bahsedecek ve Kafka’nın izlenmesiyle ilgili docker ortamında bir örnek yapacağız.

Prometheus

Prometheus, açık kaynak kodlu bir izleme ve uyarı sistemi olup, ölçüm verilerini birleştirmek ve işlemek için çok boyutlu veri modeli ve esnek sorgu diline sahiptir. Hedeflerini, servis keşif mekanizması veya statik yapılandırma ile belirleyebilir ve bu hedeflerden zaman serisi verilerini toplar. Bu veriler, Grafana gibi görselleştirme araçlarıyla incelenebilir.

prometheus
Şekil1: Prometheus Arayüzü

Grafana

Grafana, çok çeşitli veri kaynaklarından veri görselleştirme ve analiz için kullanılan açık kaynaklı bir platformdur. Grafana, grafikler, histogramlar ve ölçekler oluşturarak veri analizi yapabilir. Ayrıca, kullanıcıların belirli metrikleri ve verileri izlemelerine olanak tanıyan bir uyarı özelliği de vardır.

grafana
Şekil2: Grafana Arayüzü

 

Araçları tanıdığımıza göre uygulama kısmına geçebiliriz.

Demo

Uygulamayı Docker ortamında gerçekleştireceğiz. Kullandığımız docker-compose dosyasını aşağıda paylaşıyorum. Proje dosyalarına GitHub üzerinden de erişebilirsiniz.

version: "3.8"
services:
  grafana:
    image: "grafana/grafana:9.5.12"
    ports:
      - "3000:3000"
    environment:
      GF_PATHS_DATA: /var/lib/grafana
      GF_SECURITY_ADMIN_PASSWORD: admin
    container_name: grafana
    depends_on:
      - prometheus
    networks:
      - monitoring

  prometheus:
    image: "prom/prometheus:v2.47.1"
    ports:
      - "9090:9090"
    volumes:
      - ./etc/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
    command: "--config.file=/etc/prometheus/prometheus.yml"
    container_name: prometheus
    networks:
      - monitoring

  jmx-kafka101:
    image: "kayademirseda/jmx_exporter:1.0"
    ports:
      - "5556:5556"
    environment:
      CONFIG_YML: "/etc/jmx_exporter/config.yml"
      JVM_OPTS: ${PROMETHEUS_JMX_AGENT_JVM_OPTS}
    volumes:
      - ./etc/jmx_exporter/config_kafka101.yml:/etc/jmx_exporter/config.yml
    container_name: jmx-kafka101
    depends_on:
      - kafka101
    networks:
      - monitoring

  jmx-kafka102:
    image: "kayademirseda/jmx_exporter:1.0"
    ports:
      - "5557:5556"
    environment:
      CONFIG_YML: "/etc/jmx_exporter/config.yml"
      JVM_OPTS: ${PROMETHEUS_JMX_AGENT_JVM_OPTS}

    volumes:
      - ./etc/jmx_exporter/config_kafka102.yml:/etc/jmx_exporter/config.yml
    container_name: jmx-kafka102
    depends_on:
      - kafka102
    networks:
      - monitoring

  jmx-kafka103:
    image: "kayademirseda/jmx_exporter:1.0"
    ports:
      - "5558:5556"
    environment:
      CONFIG_YML: "/etc/jmx_exporter/config.yml"
      JVM_OPTS: ${PROMETHEUS_JMX_AGENT_JVM_OPTS}
    volumes:
      - ./etc/jmx_exporter/config_kafka103.yml:/etc/jmx_exporter/config.yml
    container_name: jmx-kafka103
    depends_on:
      - kafka103
    networks:
      - monitoring

  kafka101:
    image: "kayademirseda/kafka:1.0"
    ports:
      - "19092:9092"
      - "9991:9991"
    container_name: kafka101
    volumes:
    - ./etc/kafka/config/kafka1/server.properties:/kafka/config/server.properties
    - ./etc/kafka/data/kafka1/:/data/kafka/
    environment:
      JMX_PORT: 9991
    deploy:
      resources:
        limits:
          memory: ${KAFKA_BROKER_MEM_LIMIT}
    networks:
      - monitoring

  kafka102:
    image: "kayademirseda/kafka:1.0"
    ports:
      - "29092:9092"
      - "9992:9992"
    container_name: kafka102
    volumes:
    - ./etc/kafka/config/kafka2/server.properties:/kafka/config/server.properties
    - ./etc/kafka/data/kafka2/:/data/kafka/
    environment:
      JMX_PORT: 9992
    deploy:
      resources:
        limits:
          memory: ${KAFKA_BROKER_MEM_LIMIT}
    networks:
      - monitoring

  kafka103:
    image: "kayademirseda/kafka:1.0"
    ports:
      - "39092:9092"
      - "9993:9993"
    container_name: kafka103
    volumes:
    - ./etc/kafka/config/kafka3/server.properties:/kafka/config/server.properties
    - ./etc/kafka/data/kafka3/:/data/kafka/
    environment:
      JMX_PORT: 9993
    deploy:
      resources:
        limits:
          memory: ${KAFKA_BROKER_MEM_LIMIT}
    networks:
      - monitoring

networks:
  monitoring:
    name: monitoring
    driver: bridge

Kısaca servislerin yapılandırmasına göz atalım.

  • Grafana: Çeşitli veri kaynaklarından verileri görselleştirmek ve analiz etmek için kullanılan bir platformdur. Bu servis 3000 numaralı porttan hizmet verir. Ayrıca, Prometheus servisine bağımlıdır.
  • Prometheus: Hedeflerden zaman serisi verilerini toplayan bir izleme ve uyarı sistemidir. Bu servis 9090 numaralı porttan hizmet verir.
  • JMX-Kafka101, JMX-Kafka102, JMX-Kafka103: Bu servisler, Prometheus’un Kafka’dan JMX sorgulamalarını gerçekleştirmesini sağlar. Her bir JMX servisi, bir Kafka servisine bağımlıdır ve her biri farklı bir porttan hizmet verir.
  • Kafka101, Kafka102, Kafka103: Bu servisler, Kafka brokerleridir.

Docker-compose dosyasını hızlıca inceledik. Şimdi de yapılandırma dosyalarını gözden geçirip görselleştirmeye başlayalım.

JMX ile Prometheus – Kafka Bağlantısını Kurma

Prometheus ile Kafka arasında bağlantı kurmak için jmx-exporter‘ı kullanıyoruz. Parametrelerin Prometheus tarafından kullanılabilmesi için konfigürasyon dosyasını jmx konteynerına eklememiz gerekmektedir.

  1. hostPort: Metriklerin alınacağı hedefin adresi ve portu belirlenir. Bu örnekte, metrikler kafka101 hostunun 9991 portundan alınır. Diğer konteynerlara erişim sağlamak için bu bölümü güncellememiz gerekmektedir.
  2. lowercaseOutputName ve lowercaseOutputLabelNames: Bu ayarlar, metrik ve etiket isimlerinin küçük harfle çıktı olarak verilmesini sağlar. Bu, tutarlılık sağlamak ve karmaşayı azaltmak amacıyla yapılmaktadır.
  3. whitelistObjectNames ve blacklistObjectNames: Bu bölümler, hangi JMX nesnelerinin (ObjectName) metriklere dahil edileceğini veya hariç bırakılacağını belirtir. whitelistObjectNames belirli nesneleri dahil ederken, blacklistObjectNames belirli nesneleri hariç tutar. Özellikle, belirli bir ön ek veya deseni içeren nesneleri filtrelemek için kullanılır.
  4. rules: Bu bölüm, toplanan metrikler üzerinde işlem yapmak için Prometheus kurallarını tanımlar. Örneğin, metrik isimlerini yeniden adlandırmak, etiketler eklemek veya kaldırmak, metrik değerlerini dönüştürmek veya toplamak gibi işlemler gerçekleştirilebilir. Her kuralın altında, bir pattern (desen) belirtilir ve bu desen, eşleşen metrikler üzerinde işlem yapmak için kullanılır. Daha sonra, işlem yapılan metriğin adı (name) ve etiketleri (labels) belirtilir. Bu örnekte, Broker Metrics ve JVM Metrics olmak üzere iki kural tanımlanmıştır. Her bir kuralın altında, desen, metrik adı ve etiketler belirtilmiştir. Örneğin, CPU kullanımı için bir kural tanımlanmış ve bu kuralın pattern‘ı java.lang<type=OperatingSystem><ProcessCpuLoad> olarak belirlenmiştir. Bu desen, CPU kullanım metriklerini temsil eder. Daha sonra, bu metriğin Prometheus’ta nasıl adlandırılacağı (name) ve hangi etiketlerle (labels) işaretleneceği belirlenmiştir.
hostPort: kafka101:9991
lowercaseOutputName: true
lowercaseOutputLabelNames: true

# Whitelist is used to reduce scrapping time
whitelistObjectNames:
  - java.lang:*
  - kafka.cluster:*
  - kafka.controller:*
  - kafka.log:*
  - kafka.server:type=KafkaServer,name=BrokerState
  - kafka.server:type=KafkaRequestHandlerPool,*
  - kafka.server:type=BrokerTopicMetrics,*
  - kafka.server:type=FetcherLagMetrics,*
  - kafka.server:type=FetcherStats,*
  - kafka.server:type=Request,*
  - kafka.server:type=Fetch,*
  - kafka.server:type=Produce,*
  - kafka.server:type=ReplicaManager,*
  - kafka.server:type=ReplicaFetcherManager,*
  - kafka.server:type=SessionExpireListener,*
  - kafka.server:type=controller-channel-metrics,*
  - kafka.server:type=socket-server-metrics,*
  - kafka.network:type=RequestChannel,*
  - kafka.network:type=Processor,*
  - kafka.network:type=SocketServer,*
  - kafka.network:type=RequestMetrics,*
  - kafka.coordinator.group:*
blacklistObjectNames:
  - java.lang:type=ClassLoading,*
  - java.lang:type=Compilation,*
  - java.lang:type=MemoryManager,*
  - kafka.utils:*
  - kafka.controller:type=ControllerChannelManager,name=QueueSize,*
  # Following metrics are exposed per topics
  - kafka.log:type=Log,name=LogEndOffset,*
  - kafka.log:type=Log,name=LogStartOffset,*
  - kafka.cluster:type=Partition,name=InSyncReplicasCount,*
  - kafka.cluster:type=Partition,name=LastStableOffsetLag,*
  - kafka.cluster:type=Partition,name=ReplicasCounts,*
  - kafka.cluster:type=Partition,name=UnderReplicated,*
  - kafka.server:type=BrokerTopicMetrics,name=TotalFetchRequestsPerSec,*
  - kafka.server:type=BrokerTopicMetrics,name=TotalProduceRequestsPerSec,*
  - kafka.server:type=BrokerTopicMetrics,name=FailedProduceRequestsPerSec,*
  - kafka.server:type=BrokerTopicMetrics,name=FailedFetchRequestsPerSec,*
  - kafka.server:type=BrokerTopicMetrics,name=BytesRejectedPerSec,*

rules:
  #-------------------------------------------------------------------------------------------------------
  # Broker Metrics :
  #-------------------------------------------------------------------------------------------------------
  # Total Topic Count
  - pattern: kafka.server<type=BrokerTopicMetrics, name=Topics><>Value
    name: kafka_server_broker_topic_count
    labels:
      service: kafka-broker
      env: cluster-demo

  # Total Message Count
  - pattern: kafka.server<type=BrokerTopicMetrics, name=MessagesInPerSec><>OneMinuteRate
    name: kafka_server_broker_total_messages_per_sec
    labels:
      aggregate: one_minute_rate
      service: kafka-broker
      env: cluster-demo

  # Total Partition Count
  - pattern: kafka.cluster<type=Partition, name=([^,]+), topic=([^,]+), partition=([^,]+)><>Value
    name: kafka_cluster_partition_$1
    labels:
      topic: $2
      partition: $3
      service: kafka-broker
      env: cluster-demo

  #-------------------------------------------------------------------------------------------------------
  # JVM Metrics :
  #-------------------------------------------------------------------------------------------------------
  # CPU Usage
  - pattern: java.lang<type=OperatingSystem><ProcessCpuLoad>
    name: kafka_jvm_os_process_cpu_load
    labels:
      service: kafka-broker
      env: cluster-demo

  # RAM Usage
  - pattern: java.lang<type=OperatingSystem><TotalPhysicalMemorySize>
    name: kafka_jvm_os_total_physical_memory_size
    labels:
      service: kafka-broker
      env: cluster-demo

  # Heap Memory Usage
  - pattern: java.lang<type=Memory><HeapMemoryUsage>(\w*)
    name: kafka_jvm_heap_usage
    labels:
      type: $1
      service: kafka-broker
      env: cluster-demo
    attrNameSnakeCase: true

  # Non-Heap Memory Usage
  - pattern: java.lang<type=Memory><NonHeapMemoryUsage>(\w*)
    name: kafka_jvm_non_heap_usage
    labels:
      type: $1
      service: kafka-broker
      env: cluster-demo
    attrNameSnakeCase: true

Ardından, prometheus.yml dosyasını oluşturuyoruz. Bu dosya, Prometheus’un yapılandırma dosyasıdır ve Prometheus’un hangi metrikleri, ne sıklıkta ve nereden toplayacağını tanımlar.

  1. Global Ayarlar: Bu bölüm, tüm scrape işlemleri ve kural değerlendirmeleri için genel ayarları tanımlar. Örneğin, scrape aralığı (scrape_interval) ve kural değerlendirme aralığı (evaluation_interval) burada belirtilir. Örneğin, her 15 saniyede bir metrik alınması ve kuralların her 15 saniyede bir değerlendirilmesi belirtilir.
  2. Alertmanager Yapılandırması: Bu bölüm, Alertmanager yapılandırmasını belirtir. Alertmanager, Prometheus’tan gelen uyarıları yönetmek ve bunlara yanıt vermek için kullanılır. Ancak, targets bölümü boş olduğu için herhangi bir Alertmanager hedefi belirtilmemiştir.
  3. Kuralların Yüklenmesi: Bu bölüm, Prometheus’un yükleyeceği kural dosyalarını belirtir. Ancak, kural dosyaları yorumlanmış (# işaretiyle başlıyor) olduğu için herhangi bir kural dosyası yüklenmeyecektir.
  4. Scrape Yapılandırmaları: Bu bölüm, Prometheus’un metrikleri toplayacağı hedefleri belirtir. Her scrape yapılandırması, bir “job” adı altında gruplandırılmış hedefler listesi içerir. Bu örnekte, “kafka” adında bir job tanımlanmış ve bu job’un metrikleri toplayacağı hedefler (targets) belirtilmiştir. Bu hedefler, jmx-kafka101:5556, jmx-kafka102:5556 ve jmx-kafka103:5556 adreslerindeki Kafka sunucularıdır.

 

# my global config
global:
  scrape_interval:     15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
  evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
  # scrape_timeout is set to the global default (10s).

# Alertmanager configuration
alerting:
  alertmanagers:
  - static_configs:
    - targets:
      # - alertmanager:9093

# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
  # - "first_rules.yml"
  # - "second_rules.yml"

# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: 'kafka'

    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.

    static_configs:
    - targets: ['jmx-kafka101:5556','jmx-kafka102:5556', 'jmx-kafka103:5556']

Grafana Prometheus bağlantısı

Görselleştirmeleri yapabilmek için veri kaynağı olarak Prometheus eklememiz gerekiyor. Home> Administration > Data sources > Add data source adımlarını izleyerek Prometheus’u veri kaynağı olarak ekleyebilirsiniz.

 

03_grafana_prometheus_connection
Şekil3: Grafana Prometheus Bağlantısı

Bu bağlantıyı, arayüzden olduğu gibi, yapılandırma dosyası ile de gerçekleştirebiliriz. Aşağıda bir örnek yapılandırma dosyasını bulabilirsiniz. Oluşturduğumuz datasource.yaml dosyasını /etc/grafana/provisioning/datasources dizinine kopyalayarak bağlantıyı sağlayabiliriz.

  1. apiVersion: Bu, yapılandırma dosyasının format sürümünü belirtir.
  2. deleteDatasources: Bu bölüm, silinecek veri kaynaklarını belirtir.
  3. datasources: Bu bölüm, eklenecek veya güncellenecek veri kaynaklarını belirtir. Her bir veri kaynağı için aşağıdaki bilgiler gerekir:
    • name: Veri kaynağının adı.
    • type: Veri kaynağının türü.
    • access: Erişim modu.
    • orgId: Veri kaynağının ait olduğu organizasyonun ID’si.
    • url: Sunucu adresi.
    • password: Kullanılacak şifre, eğer varsa.
    • user: Kullanıcı adı, eğer varsa.
    • database: Veritabanı adı, eğer varsa.
    • basicAuth, basicAuthUser, basicAuthPassword: Temel kimlik doğrulama bilgileri, eğer kullanılacaksa.
    • withCredentials: Başlıklarda kimlik bilgilerini etkinleştir veya devre dışı bırak.
    • isDefault: Bu veri kaynağının varsayılan olup olmadığını belirtir, eğer varsa.
    • jsonData: JSON formatında saklanacak alanlar.
    • secureJsonData: Şifreli JSON verisi, örneğin TLS sertifikaları gibi.
    • version: Yapılandırma sürümü.
    • editable: Kullanıcıların UI’den veri kaynağını düzenleyip düzenleyemeyeceğini belirtir.
# config file version
apiVersion: 1

# list of datasources that should be deleted from the database
deleteDatasources:
  - name: Prometheus
    orgId: 1

# list of datasources to insert/update depending on
# what's available in the database
datasources:
  # <string, required> name of the datasource. Required
- name: Prometheus
  # <string, required> datasource type. Required
  type: prometheus
  # <string, required> access mode. proxy or direct (Server or Browser in the UI). Required
  access: proxy
  # <int> org id. will default to orgId 1 if not specified
  orgId: 1
  # <string> url
  url: http://prometheus:9090
  # <string> database password, if used
  password:
  # <string> database user, if used
  user:
  # <string> database name, if used
  database:
  # <bool> enable/disable basic auth
  basicAuth:
  # <string> basic auth username
  basicAuthUser:
  # <string> basic auth password
  basicAuthPassword:
  # <bool> enable/disable with credentials headers
  withCredentials:
  # <bool> mark as default datasource. Max one per org
  isDefault: true
  # <map> fields that will be converted to json and stored in json_data
  jsonData:
     httpMethod: POST
     manageAlerts: true
     prometheusType: Prometheus
     prometheusVersion: 2.47.0
     cacheLevel: 'High'
     tlsAuth: false
     tlsAuthWithCACert: false
  # <string> json object of data that will be encrypted.
  secureJsonData:
    tlsCACert: "..."
    tlsClientCert: "..."
    tlsClientKey: "..."
  version: 1
  # <bool> allow users to edit datasources from the UI.
  editable: true

Grafana ile Grafik Oluşturma

Grafana ile çeşitli görselleştirmeler oluşturabiliriz. Veri analizi ve izleme için ideal bir araç olan Grafana, çeşitli grafikler, histogramlar, ısı haritaları ve daha fazlasını oluşturmamıza olanak sağlar. Veriye dayalı uyarıları oluşturabilir ve bu uyarıları e-posta, Slack gibi platformlara yönlendirebiliriz. Ayrıca, karmaşık sorguları kaydederek daha sonraki kullanımlar için saklayabiliriz.

Şimdi bir grafik oluşturmayı birlikte deneyelim. İlk olarak, yeni bir dashboard oluşturmalıyız. Bu adımları takip edebiliriz: Home > Dashboards > New Dashboard. Sonra, ‘Add new panel’ seçeneğini tıklayarak yeni bir grafik başlatabiliriz. Grafik oluştururken, veri kaynağını seçmeli ve bir sorgu yazmalıyız. Sorgu, grafikte neyin gösterileceğini belirler. Örneğin, belirli bir zaman aralığındaki bir metriğin değerlerini gösterebiliriz. Daha sonra, grafik türünü seçip, grafik ayarlarını özelleştirebiliriz. Tüm bu adımlar tamamlandığında, ‘Apply’ düğmesine tıklayarak grafik panelini dashboard’a ekleyebiliriz.

04_create_dashboard
Şekil4: Dashboard Oluşturma

Örneğin, ortalama bellek kullanımını gözlemlemek için bir grafik oluşturalım. İlk olarak, grafikte göstermek istediğimiz verinin kaynağını belirleriz. Kaynağı belirledikten sonra, verinin nasıl gösterileceğini belirleyen sorguyu yazmamız gerekiyor. Sonra, oluşturduğumuz grafiğe bir isim verelim ve bu ismi sol paneldeki ilgili alana giriyoruz. Aynı panelde, hangi tür verinin nasıl bir formatla gösterileceğini belirleyen grafik türünün özelliklerini de ayarlayabilirz. Ayarlar tamamlandıktan sonra, ‘kaydet’ butonuna basıyourz burada bize hangi panoya eklemek istediğimiz soracak. Son olarak, grafiği hangi panoya eklemek istediğimizi belirleyip grafiği kayıt ediyoruz.

05_create_chart
Şekil5: Grafik Oluşturma

Bu yazıda Prometheus ve Grafana’nın karmaşık izleme görevlerini nasıl basitleştirdiğini gördük ve Kafka kümesinin izlenme sürecini anlattık ve bu araçların birlikte nasıl çalıştığını açıkladık. Herkese keyifli okumalar dilerim, bir sonraki yazıda görüşmek üzere.

Kaynaklar:

  1. Prometheus
  2. Grafana
  3. Kafka’nın İzlenmesi
  4. Unsplash: Kapak Fotoğrafı

Yazar Hakkında
Toplam 6 yazı
Seda KAYADEMİR
Seda KAYADEMİR
Üniversite yıllarında tanışmış olduğu veri dünyasına ilgisi artan ve bu alanda kariyer hedeflemiş bir süre yazılım mühendisi olarak çalışmış ve hedefi doğrultusunda kendini geliştirerek kariyerine veri mühendisi olarak devam etmektedir. İlgilendiği Alanlar; Dataops, Mlops, Bulut Teknolojileri.
Yorumlar (Yorum yapılmamış)

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

×

Bir Şeyler Ara