ELK'nin pratik uygulaması. Logstash'ı ayarlama

Giriş

Başka bir sistemi devreye alırken çok sayıda farklı logu işleme ihtiyacıyla karşı karşıya kaldık. Araç olarak ELK seçildi. Bu makalede bu yığını kurma konusundaki deneyimimiz tartışılacaktır.

Tüm yeteneklerini açıklamak için bir hedef koymuyoruz, ancak özellikle pratik sorunların çözümüne odaklanmak istiyoruz. Bunun nedeni, oldukça fazla miktarda dokümantasyon ve hazır görsel olmasına rağmen oldukça fazla tuzak olması, en azından bunları bulduk.

Yığını docker-compose aracılığıyla konuşlandırdık. Dahası, iyi yazılmış bir docker-compose.yml dosyamız vardı, bu da yığını neredeyse sorunsuz bir şekilde yükseltmemize olanak tanıyordu. Bize zafer zaten yakınmış gibi geldi, şimdi ihtiyaçlarımıza uyacak şekilde biraz ayarlayacağız ve bu kadar.

Ne yazık ki, sistemi uygulamamızdan günlükleri alacak ve işleyecek şekilde yapılandırma girişimi hemen başarılı olmadı. Bu nedenle, her bir bileşeni ayrı ayrı incelemeye ve ardından bağlantılarına dönmeye değer olduğuna karar verdik.

Böylece logstash ile başladık.

Ortam, dağıtım, Logstash'ın bir kapta çalıştırılması

Dağıtım için docker-compose kullanıyoruz; burada açıklanan deneyler MacOS ve Ubuntu 18.0.4 üzerinde gerçekleştirilmiştir.

Orijinal docker-compose.yml dosyamızda kayıtlı olan logstash görüntüsü docker.elastic.co/logstash/logstash:6.3.2'dir.

Deneyler için kullanacağız.

Logstash'ı çalıştırmak için ayrı bir docker-compose.yml yazdık. Elbette görüntüyü komut satırından başlatmak mümkündü, ancak her şeyi docker-compose'tan çalıştırdığımız belirli bir sorunu çözüyorduk.

Kısaca yapılandırma dosyaları hakkında

Açıklamadan da anlaşılacağı gibi, logstash ya bir kanal için başlatılabilir, bu durumda *.conf dosyasını iletmesi gerekir, ya da birkaç kanal için başlatılabilir, bu durumda pipelines.yml dosyasını iletmesi gerekir; , her kanal için .conf dosyalarına bağlantı verecektir.
Biz ikinci yolu seçtik. Bize daha evrensel ve ölçeklenebilir göründü. Bu nedenle, pipelines.yml'yi oluşturduk ve her kanal için .conf dosyalarını koyacağımız bir Pipelines dizini oluşturduk.

Kabın içinde başka bir yapılandırma dosyası daha var - logstash.yml. Dokunmuyoruz, olduğu gibi kullanıyoruz.

Yani dizin yapımız:

ELK'nin pratik uygulaması. Logstash'ı ayarlama

Giriş verilerini almak için şimdilik bunun 5046 numaralı bağlantı noktasındaki TCP olduğunu varsayalım ve çıkış için stdout'u kullanacağız.

İşte ilk başlatma için basit bir yapılandırma. Çünkü ilk görev başlatmaktır.

Yani, bu docker-compose.yml'ye sahibiz

version: '3'

networks:
  elk:

volumes:
  elasticsearch:
    driver: local

services:

  logstash:
    container_name: logstash_one_channel
    image: docker.elastic.co/logstash/logstash:6.3.2
    networks:
      	- elk
    ports:
      	- 5046:5046
    volumes:
      	- ./config/pipelines.yml:/usr/share/logstash/config/pipelines.yml:ro
	- ./config/pipelines:/usr/share/logstash/config/pipelines:ro

Burada ne görüyoruz?

  1. Ağlar ve birimler orijinal docker-compose.yml'den (tüm yığının başlatıldığı yer) alınmıştır ve bunların buradaki genel resmi çok fazla etkilemediğini düşünüyorum.
  2. Docker.elastic.co/logstash/logstash:6.3.2 görüntüsünden bir logstash hizmeti/hizmetleri oluşturuyoruz ve bunu logstash_one_channel olarak adlandırıyoruz.
  3. Konteynerin içindeki 5046 numaralı bağlantı noktasını aynı dahili bağlantı noktasına iletiyoruz.
  4. Boru yapılandırma dosyamız ./config/pipelines.yml'yi konteynerin içindeki /usr/share/logstash/config/pipelines.yml dosyasıyla eşleştiririz; burada logstash onu alır ve her ihtimale karşı salt okunur hale getirir.
  5. Kanal ayarlarına sahip dosyalarımızın bulunduğu ./config/pipelines dizinini /usr/share/logstash/config/pipelines dizinine eşliyoruz ve onu da salt okunur hale getiriyoruz.

ELK'nin pratik uygulaması. Logstash'ı ayarlama

Pipelines.yml dosyası

- pipeline.id: HABR
  pipeline.workers: 1
  pipeline.batch.size: 1
  path.config: "./config/pipelines/habr_pipeline.conf"

HABR tanımlayıcısına sahip bir kanal ve yapılandırma dosyasının yolu burada açıklanmaktadır.

Ve son olarak “./config/pipelines/habr_pipeline.conf” dosyası

input {
  tcp {
    port => "5046"
   }
  }
filter {
  mutate {
    add_field => [ "habra_field", "Hello Habr" ]
    }
  }
output {
  stdout {
      
    }
  }

Şimdilik açıklamasına girmeyelim, çalıştırmayı deneyelim:

docker-compose up

Ne görüyoruz?

Konteyner başladı. Çalışmasını kontrol edebiliriz:

echo '13123123123123123123123213123213' | nc localhost 5046

Ve konteyner konsolunda yanıtı görüyoruz:

ELK'nin pratik uygulaması. Logstash'ı ayarlama

Ama aynı zamanda şunu da görüyoruz:

logstash_one_channel | [2019-04-29T11:28:59,790][ERROR][logstash.licensechecker.licensereader] Lisans bilgileri lisans sunucusundan alınamıyor {:message=>“Elasticsearch Ulaşılamıyor: [http://elasticsearch:9200/][Manticore ::ResolutionFailure] elasticsearch", ...

logstash_one_channel | [2019-04-29T11:28:59,894][BİLGİ ][logstash.pipeline ] Ardışık düzen başarıyla başlatıldı {:pipeline_id=>".monitoring-logstash", :thread=>"# "}

logstash_one_channel | [2019-04-29T11:28:59,988][BİLGİ ][logstash.agent ] Çalışan işlem hatları {:count=>2, :running_pipelines=>[:HABR, :".monitoring-logstash"], :non_running_pipelines=>[ ]}
logstash_one_channel | [2019-04-29T11:29:00,015][ERROR][logstash.inputs.metrics] X-Pack, Logstash'ta yüklü ancak Elasticsearch'te yüklü değil. İzleme özelliğini kullanmak için lütfen Elasticsearch'e X-Pack yükleyin. Başka özellikler mevcut olabilir.
logstash_one_channel | [2019-04-29T11:29:00,526][BİLGİ ][logstash.agent ] Logstash API uç noktası {:port=>9600} başarıyla başlatıldı
logstash_one_channel | [2019-04-29T11:29:04,478][BİLGİ ][logstash.outputs.elasticsearch] Elasticsearch bağlantısının çalışıp çalışmadığını görmek için durum kontrolü çalıştırılıyor {:healthcheck_url=>http://elasticsearch:9200/, :path=> "/"}
logstash_one_channel | [2019-04-29T11:29:04,487][WARN ][logstash.outputs.elasticsearch] Ölü ES örneğine bağlantı yeniden canlandırılmaya çalışıldı, ancak bir hata oluştu. {:url=>“elastik arama:9200/", :error_type=>LogStash::Outputs::ElasticSearch::HttpClient::Pool::HostUnreachableError, :error=>"Elasticsearch Unreachable: [http://elasticsearch:9200/][Manticore::ResolutionFailure] elastik arama"}
logstash_one_channel | [2019-04-29T11:29:04,704][BİLGİ ][logstash.licensechecker.licensereader] Elasticsearch bağlantısının çalışıp çalışmadığını görmek için durum kontrolü çalıştırılıyor {:healthcheck_url=>http://elasticsearch:9200/, :path=> "/"}
logstash_one_channel | [2019-04-29T11:29:04,710][WARN ][logstash.licensechecker.licensereader] Ölü ES örneğine bağlantı yeniden canlandırılmaya çalışıldı, ancak bir hata oluştu. {:url=>“elastik arama:9200/", :error_type=>LogStash::Outputs::ElasticSearch::HttpClient::Pool::HostUnreachableError, :error=>"Elasticsearch Unreachable: [http://elasticsearch:9200/][Manticore::ResolutionFailure] elastik arama"}

Ve kütüğümüz sürekli olarak sürünüyor.

Burada boru hattının başarıyla başlatıldığını belirten mesajı yeşil renkle, hata mesajını kırmızı renkle ve iletişim girişimiyle ilgili mesajı sarı renkle vurguladım. elastik arama: 9200.
Bunun nedeni, görüntüde yer alan logstash.conf dosyasının elasticsearch kullanılabilirliği için bir kontrol içermesidir. Sonuçta logstash, Elk yığınının bir parçası olarak çalıştığını varsayar, ancak biz onu ayırdık.

Çalışmak mümkün ama uygun değil.

Çözüm, bu kontrolü XPACK_MONITORING_ENABLED ortam değişkeni aracılığıyla devre dışı bırakmaktır.

docker-compose.yml dosyasında bir değişiklik yapalım ve tekrar çalıştıralım:

version: '3'

networks:
  elk:

volumes:
  elasticsearch:
    driver: local

services:

  logstash:
    container_name: logstash_one_channel
    image: docker.elastic.co/logstash/logstash:6.3.2
    networks:
      - elk
    environment:
      XPACK_MONITORING_ENABLED: "false"
    ports:
      - 5046:5046
   volumes:
      - ./config/pipelines.yml:/usr/share/logstash/config/pipelines.yml:ro
      - ./config/pipelines:/usr/share/logstash/config/pipelines:ro

Şimdi her şey yolunda. Konteyner deneylere hazır.

Bir sonraki konsola tekrar yazabiliriz:

echo '13123123123123123123123213123213' | nc localhost 5046

Ve bakın:

logstash_one_channel | {
logstash_one_channel |         "message" => "13123123123123123123123213123213",
logstash_one_channel |      "@timestamp" => 2019-04-29T11:43:44.582Z,
logstash_one_channel |        "@version" => "1",
logstash_one_channel |     "habra_field" => "Hello Habr",
logstash_one_channel |            "host" => "gateway",
logstash_one_channel |            "port" => 49418
logstash_one_channel | }

Tek kanalda çalışma

Böylece başlattık. Artık logstash'ın kendisini yapılandırmak için gerçekten zaman ayırabilirsiniz. Şimdilik pipelines.yml dosyasına dokunmayalım, tek kanalla çalışarak neler elde edebileceğimize bakalım.

Kanal yapılandırma dosyasıyla çalışmanın genel prensibinin resmi kılavuzda iyi açıklandığını söylemeliyim, burada burada
Rusça okumak istiyorsanız bunu kullandık bir makale(ancak oradaki sorgu sözdizimi eskidir, bunu dikkate almamız gerekir).

Giriş kısmından sırasıyla gidelim. TCP üzerinde yapılan çalışmaları zaten gördük. Burada başka ne ilginç olabilir?

Kalp atışını kullanarak mesajları test edin

Otomatik test mesajları oluşturmak için çok ilginç bir fırsat var.
Bunu yapmak için giriş bölümünde heartbean eklentisini etkinleştirmeniz gerekir.

input {
  heartbeat {
    message => "HeartBeat!"
   }
  } 

Açın, dakikada bir almaya başlayın

logstash_one_channel | {
logstash_one_channel |      "@timestamp" => 2019-04-29T13:52:04.567Z,
logstash_one_channel |     "habra_field" => "Hello Habr",
logstash_one_channel |         "message" => "HeartBeat!",
logstash_one_channel |        "@version" => "1",
logstash_one_channel |            "host" => "a0667e5c57ec"
logstash_one_channel | }

Daha sık almak istiyorsak aralık parametresini eklememiz gerekir.
Bu şekilde her 10 saniyede bir mesaj alacağız.

input {
  heartbeat {
    message => "HeartBeat!"
    interval => 10
   }
  }

Bir dosyadan veri alma

Ayrıca dosya moduna da bakmaya karar verdik. Dosyayla iyi çalışıyorsa, en azından yerel kullanım için belki de hiçbir aracıya gerek yoktur.

Açıklamaya göre, çalışma modu tail -f'ye benzer olmalıdır, yani. yeni satırları okur veya bir seçenek olarak dosyanın tamamını okur.

Peki ne elde etmek istiyoruz:

  1. Bir günlük dosyasına eklenen satırları almak istiyoruz.
  2. Birçok log dosyasına yazılan veriyi alırken, neyin nereden alındığını da ayırt edebilmek istiyoruz.
  3. Logstash yeniden başlatıldığında bu verileri tekrar almayacağından emin olmak istiyoruz.
  4. Logstash kapalıysa ve dosyalara veri yazılmaya devam ediyorsa, çalıştırdığımızda bu verileri alacağımızı kontrol etmek istiyoruz.

Deneyi gerçekleştirmek için docker-compose.yml dosyasına dosyaları koyduğumuz dizini açarak bir satır daha ekleyelim.

version: '3'

networks:
  elk:

volumes:
  elasticsearch:
    driver: local

services:

  logstash:
    container_name: logstash_one_channel
    image: docker.elastic.co/logstash/logstash:6.3.2
    networks:
      - elk
    environment:
      XPACK_MONITORING_ENABLED: "false"
    ports:
      - 5046:5046
   volumes:
      - ./config/pipelines.yml:/usr/share/logstash/config/pipelines.yml:ro
      - ./config/pipelines:/usr/share/logstash/config/pipelines:ro
      - ./logs:/usr/share/logstash/input

Ve habr_pipeline.conf dosyasındaki giriş bölümünü değiştirin

input {
  file {
    path => "/usr/share/logstash/input/*.log"
   }
  }

Hadi başlayalım:

docker-compose up

Günlük dosyalarını oluşturmak ve yazmak için şu komutu kullanacağız:


echo '1' >> logs/number1.log

{
logstash_one_channel |            "host" => "ac2d4e3ef70f",
logstash_one_channel |     "habra_field" => "Hello Habr",
logstash_one_channel |      "@timestamp" => 2019-04-29T14:28:53.876Z,
logstash_one_channel |        "@version" => "1",
logstash_one_channel |         "message" => "1",
logstash_one_channel |            "path" => "/usr/share/logstash/input/number1.log"
logstash_one_channel | }

Evet, işe yarıyor!

Aynı zamanda path alanını da otomatik olarak eklediğimizi görüyoruz. Bu, gelecekte kayıtları buna göre filtreleyebileceğimiz anlamına geliyor.

Tekrar deneyelim:

echo '2' >> logs/number1.log

{
logstash_one_channel |            "host" => "ac2d4e3ef70f",
logstash_one_channel |     "habra_field" => "Hello Habr",
logstash_one_channel |      "@timestamp" => 2019-04-29T14:28:59.906Z,
logstash_one_channel |        "@version" => "1",
logstash_one_channel |         "message" => "2",
logstash_one_channel |            "path" => "/usr/share/logstash/input/number1.log"
logstash_one_channel | }

Ve şimdi başka bir dosyaya:

 echo '1' >> logs/number2.log

{
logstash_one_channel |            "host" => "ac2d4e3ef70f",
logstash_one_channel |     "habra_field" => "Hello Habr",
logstash_one_channel |      "@timestamp" => 2019-04-29T14:29:26.061Z,
logstash_one_channel |        "@version" => "1",
logstash_one_channel |         "message" => "1",
logstash_one_channel |            "path" => "/usr/share/logstash/input/number2.log"
logstash_one_channel | }

Harika! Dosya alındı, yol doğru belirtildi, her şey yolunda.

Logstash'ı durdurun ve yeniden başlayın. Bekleyelim. Sessizlik. Onlar. Bu kayıtları bir daha alamıyoruz.

Ve şimdi en cesur deney.

Logstash'ı yükleyin ve çalıştırın:

echo '3' >> logs/number2.log
echo '4' >> logs/number1.log

Logstash'ı tekrar çalıştırın ve şunu görün:

logstash_one_channel | {
logstash_one_channel |            "host" => "ac2d4e3ef70f",
logstash_one_channel |     "habra_field" => "Hello Habr",
logstash_one_channel |         "message" => "3",
logstash_one_channel |        "@version" => "1",
logstash_one_channel |            "path" => "/usr/share/logstash/input/number2.log",
logstash_one_channel |      "@timestamp" => 2019-04-29T14:48:50.589Z
logstash_one_channel | }
logstash_one_channel | {
logstash_one_channel |            "host" => "ac2d4e3ef70f",
logstash_one_channel |     "habra_field" => "Hello Habr",
logstash_one_channel |         "message" => "4",
logstash_one_channel |        "@version" => "1",
logstash_one_channel |            "path" => "/usr/share/logstash/input/number1.log",
logstash_one_channel |      "@timestamp" => 2019-04-29T14:48:50.856Z
logstash_one_channel | }

Yaşasın! Her şey toplandı.

Ancak sizi şu konuda uyarmalıyız. Logstash kapsayıcısı silinirse (docker stop logstash_one_channel && docker rm logstash_one_channel), hiçbir şey alınmayacaktır. Dosyanın okunduğu konum kabın içinde saklandı. Sıfırdan çalıştırırsanız yalnızca yeni satırları kabul edecektir.

Mevcut dosyaları okuma

Diyelim ki logstash'ı ilk kez başlatıyoruz ama zaten loglarımız var ve bunları işlemek istiyoruz.
Yukarıda kullandığımız input bölümü ile logstash çalıştırırsak hiçbir şey elde edemeyiz. Logstash tarafından yalnızca yeni satırlar işlenecektir.

Mevcut dosyalardaki satırların yukarı çekilebilmesi için giriş kısmına bir satır daha eklemelisiniz:

input {
  file {
    start_position => "beginning"
    path => "/usr/share/logstash/input/*.log"
   }
  }

Üstelik bir nüans var: Bu yalnızca logstash'ın henüz görmediği yeni dosyaları etkiler. Zaten logstash'ın görüş alanında olan aynı dosyalar için, bunların boyutlarını zaten hatırlamıştır ve artık bunlara yalnızca yeni girişler alacaktır.

Burada duralım ve giriş bölümünü inceleyelim. Hala birçok seçenek var, ancak şimdilik daha sonraki deneyler için bu bizim için yeterli.

Yönlendirme ve Veri Dönüşümü

Aşağıdaki sorunu çözmeye çalışalım, diyelim ki bir kanaldan mesajlarımız var, bunların bir kısmı bilgilendirme, bir kısmı da hata mesajı. Etikete göre farklılık gösterirler. Bazıları BİLGİ, diğerleri HATA.

Çıkışta onları ayırmamız gerekiyor. Onlar. Bilgi mesajlarını bir kanala, hata mesajlarını diğerine yazıyoruz.

Bunu yapmak için giriş kısmından filtre ve çıkış kısmına geçin.

Filtre bölümünü kullanarak, gelen mesajı ayrıştırıp ondan zaten çalışabileceğimiz bir karma (anahtar-değer çiftleri) elde edeceğiz, yani. şartlara uygun olarak sökün. Çıkış kısmında ise mesajları seçip her birini kendi kanalına göndereceğiz.

Grok ile bir mesajı ayrıştırma

Metin dizelerini ayrıştırmak ve bunlardan bir dizi alan elde etmek için filtre bölümünde özel bir eklenti vardır - grok.

Kendime burada ayrıntılı bir açıklama yapma hedefi koymadan (bunun için resmi belgeler), Basit örneğimi vereceğim.

Bunu yapmak için giriş dizelerinin formatına karar vermeniz gerekir. Bende şu şekilde var:

1 BİLGİ mesajı1
2 HATA mesajı2

Onlar. Önce tanımlayıcı gelir, ardından INFO/ERROR, ardından boşluksuz bir kelime gelir.
Zor değil ama çalışma prensibini anlamak yeterli.

Bu nedenle, grok eklentisinin filtre bölümünde dizelerimizi ayrıştırmak için bir model tanımlamalıyız.

Bunun gibi görünecek:

filter {
  grok {
    match => { "message" => ["%{INT:message_id} %{LOGLEVEL:message_type} %{WORD:message_text}"] }
   }
  } 

Aslında bu bir düzenli ifadedir. INT, LOGLEVEL, WORD gibi hazır kalıplar kullanılır. Açıklamaları ve diğer modelleri burada bulabilirsiniz burada

Şimdi bu filtreden geçerek dizemiz üç alandan oluşan bir karma haline dönüşecek: message_id, message_type, message_text.

Çıkış bölümünde görüntülenecekler.

İf komutunu kullanarak mesajları çıkış bölümüne yönlendirme

Çıkış bölümünde hatırladığımız gibi mesajları iki akışa ayıracaktık. iNFO olan bazılarının çıktısı konsola verilecek ve hatalarla birlikte bir dosyaya çıktısı verilecek.

Bu mesajları nasıl ayıracağız? Sorunun durumu zaten bir çözüm öneriyor; sonuçta, yalnızca iki değer alabilen özel bir message_type alanımız zaten var: INFO ve ERROR. Bu temelde if ifadesini kullanarak bir seçim yapacağız.

if [message_type] == "ERROR" {
        # Здесь выводим в файл
       } else
     {
      # Здесь выводим в stdout
    }

Alanlar ve operatörlerle çalışmanın açıklamasını bu bölümde bulabilirsiniz resmi kılavuz.

Şimdi asıl sonuca geçelim.

Konsol çıktısı, burada her şey açık - stdout {}

Ancak çıktının bir dosyaya verilmesi - tüm bunları bir konteynerden çalıştırdığımızı unutmayın ve sonucu yazdığımız dosyaya dışarıdan erişilebilmesi için bu dizini docker-compose.yml dosyasında açmamız gerekiyor.

Toplam:

Dosyamızın çıktı bölümü şuna benzer:


output {
  if [message_type] == "ERROR" {
    file {
          path => "/usr/share/logstash/output/test.log"
          codec => line { format => "custom format: %{message}"}
         }
    } else
     {stdout {
             }
     }
  }

docker-compose.yml dosyasına çıktı için başka bir birim ekliyoruz:

version: '3'

networks:
  elk:

volumes:
  elasticsearch:
    driver: local

services:

  logstash:
    container_name: logstash_one_channel
    image: docker.elastic.co/logstash/logstash:6.3.2
    networks:
      - elk
    environment:
      XPACK_MONITORING_ENABLED: "false"
    ports:
      - 5046:5046
   volumes:
      - ./config/pipelines.yml:/usr/share/logstash/config/pipelines.yml:ro
      - ./config/pipelines:/usr/share/logstash/config/pipelines:ro
      - ./logs:/usr/share/logstash/input
      - ./output:/usr/share/logstash/output

Başlatıyoruz, deniyoruz ve iki akışa bölünmeyi görüyoruz.

Kaynak: habr.com

Yorum ekle