Docker & DevOps

    ELK Stack Kurulumu ve Log Analizi

    Elasticsearch, Logstash ve Kibana ile merkezi log analizi kurmanın adımları.

    11 dk okuma Güncellendi: 25 Ağustos 2026

    Bir hatayı ararken yirmi sunucuda grep çekmek, ilk seferinde sabır işidir, üçüncüsünde işkenceye döner. ELK Stack kurulumu bu problemi çözmek için yıllardır kullanılan en olgun yollardan biridir: tüm loglarını tek yerde toplar, tam metin arama indeksine alır ve Kibana üzerinden saniyeler içinde sorgulanabilir hale getirir. Karşılığında da ciddi bir kaynak talebi ve gerçek bir bakım yükü ister — bunu baştan bilerek kurmak, altı ay sonra pişman olmamanın tek yoludur.

    Bu rehberde ELK'nin üç bileşeninin iş bölümünü, kurulum öncesinde yapılması zorunlu sistem ayarlarını, Docker Compose ile çalışan bir kurulumu, Filebeat ile log göndermeyi, Logstash'te grok filtreleriyle ham satırları yapılandırılmış alanlara çevirmeyi, Kibana'da ilk aramayı ve en çok atlanan konu olan index yaşam döngüsü yönetimini anlatacağım. Sonunda da diski dolduran, belleği yiyen klasik hataları toplayacağım.

    ELK Nedir, Hangi Bileşen Ne Yapar#

    ELK adı üç projenin baş harflerinden gelir ve bugün genellikle dördüncü bir bileşenle, hafif toplayıcı Beats ailesiyle birlikte kullanılır. Her parçanın işi nettir:

    BileşenGöreviZorunlu mu
    ElasticsearchLogları indeksler, saklar, arama sorgularını çalıştırırEvet
    KibanaArama ve görselleştirme arayüzüEvet (pratikte)
    LogstashLogları ayrıştırır, dönüştürür, zenginleştirirHayır
    FilebeatSunuculardan log dosyalarını okuyup gönderirEvet (pratikte)

    Logstash'in isteğe bağlı olması şaşırtıcı gelebilir. Filebeat, logları doğrudan Elasticsearch'e gönderebilir ve Elasticsearch'ün kendi ingest pipeline'ları basit ayrıştırma işlerini yapabilir. Logstash'i araya koymanın gerekçesi, karmaşık dönüşümler, birden fazla kaynağı birleştirme, dış veriyle zenginleştirme ve tampon görevi görmesidir. Küçük bir kurulumda Logstash'i atlayıp Filebeat → Elasticsearch → Kibana üçlüsüyle başlamak tamamen makul bir tercihtir; ihtiyaç doğduğunda araya sonradan da eklenebilir.

    ELK'nin Loki gibi hafif alternatiflerden ayrıldığı yer, log içeriğinin tamamını indekslemesidir. "Şu kelimeyi geçen tüm satırları getir" sorgusu ELK'de anında döner, Loki'de veriyi taramak gerekir. Bunun bedeli bellek ve disktir. İki yaklaşımın karşılaştırmasını Loki ile merkezi log toplama yazısında bulabilirsin; karar vermeden önce ikisini de okumanı öneririm.

    Kurulum Öncesi Sistem Hazırlığı#

    Elasticsearch, kurulum öncesinde iki sistem ayarı ister ve bunları atlarsan konteyner başlamadan hata verip kapanır. En sık karşılaşılan mesaj şudur: max virtual memory areas vm.max_map_count [65530] is too low.

    # Elasticsearch için gereken bellek eşlem sınırı
    sudo sysctl -w vm.max_map_count=262144
    
    # Kalıcı hale getir
    echo "vm.max_map_count=262144" | sudo tee /etc/sysctl.d/99-elasticsearch.conf
    sudo sysctl --system
    
    # Doğrula
    sysctl vm.max_map_count
    # vm.max_map_count = 262144
    

    İkinci konu bellektir ve burada dürüst olmak gerekir: Elasticsearch'ü 2 GB RAM'li bir sunucuda çalıştırmayı deneme. Tek düğümlü küçük bir kurulum için gerçekçi asgari sınır 4 GB'dır, rahat çalışması için 8 GB'ı hedefle. JVM yığın boyutunu (heap) toplam belleğin yarısı olarak ayarla ve asla 32 GB'ı geçme — bu eşiğin üstünde JVM sıkıştırılmış işaretçileri bırakır ve bellek verimliliği düşer.

    Disk tarafında ise SSD neredeyse zorunludur. Elasticsearch yoğun rastgele okuma yapar; dönen diskte arama süreleri saniyelere çıkar. Ayrıca dosya tanıtıcı (file descriptor) sınırının en az 65536 olması gerekir, Docker imajları bunu genellikle kendisi ayarlar ama paketten kurulumda LimitNOFILE değerini systemd birim dosyasında kontrol et.

    Docker Compose ile ELK Kurulumu#

    Aşağıdaki yapılandırma tek düğümlü, geliştirme ve küçük üretim ortamları için uygun bir başlangıçtır:

    # docker-compose.yml
    services:
      elasticsearch:
        image: docker.elastic.co/elasticsearch/elasticsearch:8.15.0
        container_name: elasticsearch
        restart: unless-stopped
        environment:
          - discovery.type=single-node
          - ES_JAVA_OPTS=-Xms2g -Xmx2g      # sunucu belleğinin yarısı, en fazla 31g
          - xpack.security.enabled=true
          - ELASTIC_PASSWORD=guclu-bir-parola-yaz
        ulimits:
          memlock: { soft: -1, hard: -1 }
          nofile: { soft: 65536, hard: 65536 }
        volumes:
          - es-data:/usr/share/elasticsearch/data
        ports:
          - "127.0.0.1:9200:9200"
    
      kibana:
        image: docker.elastic.co/kibana/kibana:8.15.0
        container_name: kibana
        restart: unless-stopped
        environment:
          - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
          - ELASTICSEARCH_USERNAME=kibana_system
          - ELASTICSEARCH_PASSWORD=kibana-servis-parolasi
        ports:
          - "127.0.0.1:5601:5601"
        depends_on:
          - elasticsearch
    
    volumes:
      es-data:
    

    İki noktayı vurgulamak isterim. xpack.security.enabled=true ayarını kapatma isteğine direnmelisin; kimlik doğrulamasız bir Elasticsearch, internete açık olduğu anda tüm log geçmişinin herkese açık olması demektir ve bu senaryonun gerçek dünyada sayısız örneği var. ES_JAVA_OPTS içindeki -Xms ile -Xmx değerlerinin birbirine eşit olması da bir tercih değil, resmî öneridir; farklı olduklarında JVM yığını yeniden boyutlandırırken duraklar.

    Servisler ayağa kalktıktan sonra doğrula:

    # Küme sağlığı (yellow tek düğümde normaldir)
    curl -u elastic:guclu-bir-parola-yaz http://localhost:9200/_cluster/health?pretty
    
    # Yanıt örneği:
    # "status" : "yellow", "number_of_nodes" : 1, "active_shards" : 3
    

    Tek düğümde yellow durumu tamamen normaldir: kopya (replica) parçalar atanamaz çünkü atanacak ikinci bir düğüm yoktur. Panik yapmadan önce green beklentini gözden geçir. Compose dosyalarının genel yapısı hakkında tazelenmek istersen Docker Compose kullanımı yazısı işine yarar.

    Filebeat ile Logları Göndermek#

    Filebeat, her log üreten sunucuya kurulan hafif bir ajandır. Yapılandırması iki bölümden oluşur: hangi dosyaları okuyacağı ve nereye göndereceği.

    # filebeat.yml
    filebeat.inputs:
      - type: filestream
        id: nginx-loglari
        enabled: true
        paths:
          - /var/log/nginx/*.log
        fields:
          servis: nginx
          ortam: uretim
        fields_under_root: true
    
      - type: container
        paths:
          - /var/lib/docker/containers/*/*.log
        processors:
          - add_docker_metadata: ~
    
    processors:
      # Gereksiz alanları at, indeks boyutunu küçült
      - drop_fields:
          fields: ["agent.ephemeral_id", "ecs.version", "input.type"]
    
    # Doğrudan Elasticsearch'e gönder (Logstash yoksa)
    output.elasticsearch:
      hosts: ["http://elasticsearch:9200"]
      username: "elastic"
      password: "guclu-bir-parola-yaz"
      index: "loglar-%{[ortam]}-%{+yyyy.MM.dd}"
    
    setup.ilm.enabled: false
    setup.template.name: "loglar"
    setup.template.pattern: "loglar-*"
    

    filestream girdi tipi, eski log tipinin yerini alan güncel yöntemdir; dosya döndürmelerini (log rotation) daha güvenilir takip eder. add_docker_metadata işlemcisi ise konteyner loglarına otomatik olarak konteyner adı, imaj ve etiket bilgisi ekler — bu olmadan konteyner logları kimliksiz satırlar yığınına döner.

    Filebeat'i kurduktan sonra yapılandırmayı test etmeden başlatma:

    # Yapılandırma söz dizimi kontrolü
    filebeat test config -c /etc/filebeat/filebeat.yml
    
    # Çıkış hedefine bağlanabiliyor mu
    filebeat test output -c /etc/filebeat/filebeat.yml
    

    Logstash Pipeline ve Grok Filtreleri#

    Logstash'in asıl değeri, ham bir log satırını aranabilir alanlara çevirmesidir. 192.0.2.10 - - [12/Aug/2026:10:32:11 +0300] "GET /urun/42 HTTP/1.1" 200 5321 satırı tek bir metin olarak durduğunda "yanıt kodu 500 olanları getir" diyemezsin; grok ile parçalandığında ise status: 500 alanı üzerinden anında filtrelenir.

    # /etc/logstash/conf.d/nginx.conf
    input {
      beats {
        port => 5044
      }
    }
    
    filter {
      if [servis] == "nginx" {
        grok {
          match => { "message" => "%{COMBINEDAPACHELOG}" }
          tag_on_failure => ["_grokparsefailure_nginx"]
        }
        # Log zamanını olay zamanı yap (aksi halde alım zamanı kullanılır)
        date {
          match => ["timestamp", "dd/MMM/yyyy:HH:mm:ss Z"]
          target => "@timestamp"
        }
        # Sayısal alanları metin olarak bırakma
        mutate {
          convert => { "response" => "integer" }
          convert => { "bytes" => "integer" }
          remove_field => ["timestamp", "message"]
        }
        geoip {
          source => "clientip"
          target => "istemci_konum"
        }
      }
    }
    
    output {
      elasticsearch {
        hosts => ["http://elasticsearch:9200"]
        user => "elastic"
        password => "guclu-bir-parola-yaz"
        index => "nginx-%{+YYYY.MM.dd}"
      }
    }
    

    date filtresi kritik bir ayrıntıdır ve atlandığında fark edilmesi zor bir soruna yol açar: Logstash olay zamanını varsayılan olarak satırın işlendiği ana ayarlar. Bir sunucu üç gün sonra bağlanıp birikmiş logları gönderirse, üç gün önceki olaylar bugüne yazılır ve zaman çizelgen tamamen yanlış olur. date filtresi bunu düzeltir.

    Grok desenlerini yazarken deneme yanılma kaçınılmazdır; Kibana'nın Dev Tools → Grok Debugger ekranı bu iş için tasarlanmıştır. Bir desen tutmadığında Logstash olayı _grokparsefailure etiketiyle geçirir — bu etiketi taşıyan kayıtları düzenli olarak sorgulamak, sessizce ayrıştırılamayan logları yakalamanın en pratik yoludur.

    Kibana'da Arama, Index Pattern ve Dashboard#

    Kibana'ya ilk girdiğinde hiçbir veri göremezsin, çünkü hangi indeksleri okuyacağını bilmiyordur. Stack Management → Data Views → Create data view yolundan bir desen tanımlarsın: adı nginx-*, zaman alanı @timestamp. Bu adımdan sonra Discover ekranında logların akmaya başladığını görürsün.

    Discover ekranında arama dili KQL'dir ve söz dizimi sezgiseldir:

    response >= 500                          # sunucu hataları
    response >= 500 and servis: "nginx"      # belirli servis
    url: *admin* and not clientip: "10.0.0.*"  # iç ağ dışından admin istekleri
    message: "connection refused"            # tam metin arama
    

    Pratik bir ipucu: soldaki alan listesinden bir alana tıklayıp "Visualize" dediğinde Kibana o alan için hazır bir grafik üretir — en hızlı keşif yöntemi budur. Kalıcı panolar için Dashboard → Create dashboard ile panelleri bir araya getirirsin; en çok işe yarayanlar zaman içindeki hata sayısı, durum kodu dağılımı, en çok istek alan URL'ler ve coğrafi dağılım haritasıdır.

    Not: Elasticsearch'ü Grafana'da veri kaynağı olarak da tanımlayabilirsin. Metriklerin Grafana'daysa logları da orada görmek ekran değiştirme ihtiyacını ortadan kaldırır; pano tarafı için Grafana dashboard oluşturma yazısındaki panel tipi seçimleri burada da geçerlidir.

    Index Lifecycle ile Disk Yönetimi#

    ELK kurulumlarının en sık ölüm sebebi disk dolmasıdır. Günlük indeks açan bir yapı, hiçbir müdahale olmazsa sonsuza kadar birikir ve bir gün Elasticsearch diski dolduğunda tüm indeksleri salt okunur moda alır — o noktada log alımı durur ve genellikle bunu geç fark edersin.

    Çözüm, Index Lifecycle Management (ILM) politikasıdır. Mantık dört aşamalıdır: sıcak (aktif yazma), ılık (yalnızca okuma, sıkıştırılmış), soğuk (nadiren erişilen) ve silme.

    # 30 günde silen basit bir ILM politikası
    curl -u elastic:PAROLA -X PUT "localhost:9200/_ilm/policy/log-politikasi" \
      -H 'Content-Type: application/json' -d'
    {
      "policy": {
        "phases": {
          "hot": {
            "actions": {
              "rollover": { "max_primary_shard_size": "20gb", "max_age": "1d" }
            }
          },
          "warm": {
            "min_age": "2d",
            "actions": { "forcemerge": { "max_num_segments": 1 } }
          },
          "delete": {
            "min_age": "30d",
            "actions": { "delete": {} }
          }
        }
      }
    }'
    

    Disk kullanımını düzenli kontrol etmeyi alışkanlık haline getir:

    # İndeks başına boyut ve doküman sayısı
    curl -u elastic:PAROLA "localhost:9200/_cat/indices?v&s=store.size:desc&h=index,docs.count,store.size"
    
    # Düğüm disk kullanımı
    curl -u elastic:PAROLA "localhost:9200/_cat/allocation?v"
    

    Disk beklenmedik şekilde doluyorsa suçlu her zaman Elasticsearch olmayabilir; Docker'ın kendi katman ve log birikimi de ciddi yer kaplar. Docker diski doldurdu, nasıl temizlenir yazısındaki kontrol listesi bu ayrımı hızlıca yapmanı sağlar.

    Sık Yapılan Hatalar ve Tuzaklar#

    vm.max_map_count ayarını atlamak. Elasticsearch konteyneri açılışta ölür ve loglara bakmadan sebebi anlaşılmaz. Kurulumdan önce ayarla ve kalıcı yap.

    JVM yığınını yanlış boyutlandırmak. -Xms ile -Xmx eşit olmalı, toplam belleğin yarısını geçmemeli ve 31 GB üstüne çıkmamalı. Yığını çok büyük vermek, işletim sistemine dosya önbelleği için yer bırakmaz ve arama performansını düşürür.

    Güvenliği kapatmak. xpack.security.enabled=false yazıp portu açmak, log geçmişini herkese sunmak demektir. Güvenlik açık kalsın, portu yerelde tut, dışarı erişimi ters vekil ve TLS ile ver.

    ILM politikası kurmamak. Disk sessizce dolar, Elasticsearch indeksleri salt okunur yapar ve log alımı durur. En baştan bir saklama politikası tanımla.

    Tek düğümde green durumu beklemek. Kopya parçalar atanamayacağı için durum yellow kalır ve bu normaldir. Gerçekten green istiyorsan ya ikinci düğüm ekle ya da indeks şablonunda number_of_replicas: 0 ayarla.

    Her şeyi indekslemek. Debug seviyesindeki tüm logları ELK'ye göndermek, indeks boyutunu birkaç katına çıkarır ve arama hızını düşürür. Filebeat tarafında drop_event işlemcisiyle gürültüyü kaynağında ele.

    Sıkça Sorulan Sorular#

    ELK Stack kurmak için kaç GB RAM gerekir#

    Tek düğümlü bir başlangıç için gerçekçi asgari 4 GB'dır ve bu ancak düşük log hacminde rahat eder; 8 GB çoğu küçük ve orta ölçekli kurulum için doğru hedeftir. Bunun yarısı JVM yığınına, kalanı işletim sistemi dosya önbelleğine gider — Elasticsearch aramada yoğun biçimde bu önbelleğe dayanır. Kibana ve Logstash de aynı sunucudaysa her biri için birkaç yüz megabayt daha ayır.

    ELK ücretsiz mi, lisansı nasıl işliyor#

    Temel bileşenler ücretsiz kullanılabilir ancak lisans yapısı zaman içinde değişti; Elasticsearch ve Kibana artık Elastic License ve SSPL altında dağıtılıyor, bu da özellikle hizmet olarak sunma senaryolarında kısıt anlamına geliyor. Kendi altyapında kendi loglarını analiz etmek için ücretsiz sürüm fazlasıyla yeterlidir. Tamamen açık kaynak bir alternatif istiyorsan OpenSearch, Elasticsearch'ün Apache 2.0 lisanslı çatallanmış sürümüdür ve söz dizimi büyük ölçüde uyumludur.

    Logstash şart mı, Filebeat tek başına yeter mi#

    Basit senaryolarda Filebeat tek başına yeter; logları doğrudan Elasticsearch'e gönderir ve ayrıştırma işini Elasticsearch'ün ingest pipeline'larına bırakabilirsin. Logstash'e ihtiyaç duyduğun nokta, karmaşık dönüşümler, birden fazla kaynağı birleştirme, dış veritabanıyla zenginleştirme ya da alım tepe noktalarında tampon görevi görme gerektiğinde başlar. Küçük başlayıp gerektiğinde araya eklemek makul bir yoldur.

    ELK mi Loki mi tercih etmeliyim#

    Tam metin arama, karmaşık toplamalar ve derin analitik gerekiyorsa ELK'nin sunduğu esneklik hâlâ rakipsizdir. Buna karşılık yalnızca "şu servisin son bir saatlik logunda hata var mı" tarzı sorular soruyorsan Loki çok daha az kaynakla aynı işi görür ve bakımı neredeyse yoktur. Kabaca bir kural: log hacmin ve arama karmaşıklığın yüksekse ELK, kaynak bütçen dar ve Grafana zaten kuruluysa Loki.

    Kibana'da veri görünmüyor, nereden başlamalıyım#

    Sırayla üç noktayı kontrol et. Önce indeks gerçekten oluşmuş mu: curl localhost:9200/_cat/indices?v çıktısında beklediğin isimde bir satır olmalı. Varsa, Kibana'da doğru data view tanımlı mı ve zaman alanı @timestamp seçilmiş mi. Üçüncüsü zaman aralığıdır — Discover ekranında sağ üstteki pencere dar olabilir ya da date filtresi eksik olduğu için olaylar yanlış tarihe yazılmış olabilir.

    Log saklama süresini nasıl ayarlarım#

    Index Lifecycle Management politikasıyla. Bir ILM politikası tanımlar, indeks şablonuna bağlar ve yaşa göre otomatik silme kuralı verirsin; örneğin 30 günden eski indeksler otomatik olarak silinir. Politikayı kurduktan sonra mevcut indekslerin de bu politikaya bağlı olduğunu _ilm/explain uç noktasından doğrula, çünkü politika yalnızca yeni oluşan indekslere kendiliğinden uygulanır.

    Kapanış#

    ELK, log analizinde hâlâ en güçlü araç takımlarından biri ama bedava değil: bellek, disk ve düzenli bakım ister. Aklında kalması gereken dört alışkanlık şunlar: kurulumdan önce vm.max_map_count ve JVM yığın boyutunu ayarla, güvenliği asla kapatma ve portları yerelde tut, date filtresiyle olay zamanını doğru yaz, ve ilk günden bir ILM politikası tanımla. Bu dördü olmadan kurulan her ELK, birkaç ay içinde ya diski doldurur ya da güvenilmez zaman çizelgeleri üretir.

    Elasticsearch'ün bellek ve disk iştahı düşünülünce bu yığın için ayrı bir sunucu ayırmak en sağlıklısıdır; VDS ve bulut sunucu paketlerimiz SSD depolama ve esnek bellek seçenekleriyle bu iş için uygun bir zemin sunar, daha yüksek hacimler için dedicated sunucu tarafına bakabilirsin. Kurulum ve bakımı bize bırakmak isterseniz sunucu yönetimi hizmetimiz log altyapısının kurulumunu da kapsıyor.

    ElasticsearchLogstashKibana

    Uygulamaya geçmeye hazır mısınız?

    NVMe SSD, ücretsiz SSL ve %99.9 uptime garantisiyle Clou.TR hosting ve sunucu çözümleriyle projenizi hayata geçirin.