Amazon Athena ve Cube.js'yi kullanarak Nginx günlük analizi

Tipik olarak, Nginx'in çalışmasını izlemek ve analiz etmek için ticari ürünler veya Prometheus + Grafana gibi hazır açık kaynak alternatifleri kullanılır. Bu, izleme veya gerçek zamanlı analiz için iyi bir seçenektir ancak geçmiş analizler için pek uygun değildir. Herhangi bir popüler kaynakta, nginx günlüklerinden gelen veri hacmi hızla artıyor ve büyük miktarda veriyi analiz etmek için daha uzmanlaşmış bir şey kullanmak mantıklıdır.

Bu yazımda size nasıl kullanabileceğinizi anlatacağım. Athena Nginx'i örnek alarak günlükleri analiz etmek için açık kaynak cube.js çerçevesini kullanarak bu verilerden analitik bir kontrol panelinin nasıl oluşturulacağını göstereceğim. İşte tam çözüm mimarisi:

Amazon Athena ve Cube.js'yi kullanarak Nginx günlük analizi

TP: DR;
Bitmiş kontrol paneline bağlantı.

Kullandığımız bilgileri toplamak için akıcı, işlem için - AWS Kinesis Veri Yangın Hortumu и AWS Tutkal, depo için - AWS S3. Bu paketi kullanarak yalnızca nginx günlüklerini değil, diğer olayların yanı sıra diğer hizmetlerin günlüklerini de depolayabilirsiniz. Yığınınız için bazı parçaları benzerleriyle değiştirebilirsiniz, örneğin fluentd'yi atlayarak doğrudan nginx'ten kinesis'e log yazabilir veya bunun için logstash kullanabilirsiniz.

Nginx günlüklerini toplama

Varsayılan olarak Nginx günlükleri şuna benzer:

4/9/2019 12:58:17 PM1.1.1.1 - - [09/Apr/2019:09:58:17 +0000] "GET /sign-up HTTP/2.0" 200 9168 "https://example.com/sign-in" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36" "-"
4/9/2019 12:58:17 PM1.1.1.1 - - [09/Apr/2019:09:58:17 +0000] "GET /sign-in HTTP/2.0" 200 9168 "https://example.com/sign-up" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36" "-"

Bunlar ayrıştırılabilir, ancak Nginx yapılandırmasını JSON'da günlükler oluşturacak şekilde düzeltmek çok daha kolaydır:

log_format json_combined escape=json '{ "created_at": "$msec", '
            '"remote_addr": "$remote_addr", '
            '"remote_user": "$remote_user", '
            '"request": "$request", '
            '"status": $status, '
            '"bytes_sent": $bytes_sent, '
            '"request_length": $request_length, '
            '"request_time": $request_time, '
            '"http_referrer": "$http_referer", '
            '"http_x_forwarded_for": "$http_x_forwarded_for", '
            '"http_user_agent": "$http_user_agent" }';

access_log  /var/log/nginx/access.log  json_combined;

Depolama için S3

Günlükleri depolamak için S3'ü kullanacağız. Athena S3'teki verilerle doğrudan çalışabildiği için bu, günlükleri tek bir yerde saklamanıza ve analiz etmenize olanak tanır. Makalenin ilerleyen kısımlarında size günlükleri nasıl doğru bir şekilde ekleyeceğinizi ve işleyeceğinizi anlatacağım, ancak önce S3'te başka hiçbir şeyin saklanmayacağı temiz bir kovaya ihtiyacımız var. Athena her bölgede mevcut olmadığından kovanızı hangi bölgede oluşturacağınızı önceden düşünmeye değer.

Athena konsolunda devre oluşturma

Loglar için Athena'da bir tablo oluşturalım. Kinesis Firehose'u kullanmayı planlıyorsanız hem yazma hem de okuma için gereklidir. Athena konsolunu açın ve bir tablo oluşturun:

SQL tablosu oluşturma

CREATE EXTERNAL TABLE `kinesis_logs_nginx`(
  `created_at` double, 
  `remote_addr` string, 
  `remote_user` string, 
  `request` string, 
  `status` int, 
  `bytes_sent` int, 
  `request_length` int, 
  `request_time` double, 
  `http_referrer` string, 
  `http_x_forwarded_for` string, 
  `http_user_agent` string)
ROW FORMAT SERDE 
  'org.apache.hadoop.hive.ql.io.orc.OrcSerde' 
STORED AS INPUTFORMAT 
  'org.apache.hadoop.hive.ql.io.orc.OrcInputFormat' 
OUTPUTFORMAT 
  'org.apache.hadoop.hive.ql.io.orc.OrcOutputFormat'
LOCATION
  's3://<YOUR-S3-BUCKET>'
TBLPROPERTIES ('has_encrypted_data'='false');

Kinesis Firehose Akışı Oluşturma

Kinesis Firehose, Nginx'ten alınan verileri seçilen formatta S3'e yazacak ve YYYY/AA/GG/SS formatındaki dizinlere bölecektir. Bu, verileri okurken kullanışlı olacaktır. Elbette fluentd'den doğrudan S3'e yazabilirsiniz ancak bu durumda JSON yazmanız gerekecektir ve bu, dosyaların büyük boyutundan dolayı verimsizdir. Ayrıca PrestoDB veya Athena kullanıldığında JSON en yavaş veri formatıdır. Kinesis Firehose konsolunu açın, "Teslimat akışı oluştur"a tıklayın, "teslimat" alanında "doğrudan PUT"u seçin:

Amazon Athena ve Cube.js'yi kullanarak Nginx günlük analizi

Bir sonraki sekmede “Kayıt formatı dönüştürme” - “Etkin” seçeneğini seçin ve kayıt formatı olarak “Apache ORC” seçeneğini seçin. Bazı araştırmalara göre Owen O'Malley, bu PrestoDB ve Athena için en uygun formattır. Yukarıda oluşturduğumuz tabloyu şema olarak kullanıyoruz. Kinesis'te herhangi bir S3 konumunu belirtebileceğinizi lütfen unutmayın; tablodan yalnızca şema kullanılır. Ancak farklı bir S3 konumu belirtirseniz bu tablodan bu kayıtları okuyamazsınız.

Amazon Athena ve Cube.js'yi kullanarak Nginx günlük analizi

Depolama için S3'ü ve daha önce oluşturduğumuz kovayı seçiyoruz. Biraz sonra bahsedeceğim Aws Glue Crawler, S3 kovasındaki öneklerle çalışamıyor bu yüzden boş bırakmak önemli.

Amazon Athena ve Cube.js'yi kullanarak Nginx günlük analizi

Kalan seçenekler yükünüze göre değiştirilebilir; ben genellikle varsayılan olanları kullanıyorum. S3 sıkıştırmasının mevcut olmadığını ancak ORC'nin varsayılan olarak yerel sıkıştırmayı kullandığını unutmayın.

akıcı

Artık günlüklerin saklanmasını ve alınmasını yapılandırdığımıza göre, göndermeyi yapılandırmamız gerekiyor. Kullanacağız akıcı, çünkü Ruby'yi seviyorum ama Logstash'ı kullanabilir veya günlükleri doğrudan kinesis'e gönderebilirsiniz. Fluentd sunucusu çeşitli şekillerde başlatılabilir, size docker'dan bahsedeceğim çünkü basit ve kullanışlı.

Öncelikle fluent.conf yapılandırma dosyasına ihtiyacımız var. Oluşturun ve kaynak ekleyin:

tip ileri
liman 24224
0.0.0.0'i bağla

Artık Fluentd sunucusunu başlatabilirsiniz. Daha gelişmiş bir yapılandırmaya ihtiyacınız varsa şu adrese gidin: Docker Hub'ı Resminizi nasıl birleştireceğinizi de içeren ayrıntılı bir kılavuz var.

$ docker run 
  -d 
  -p 24224:24224 
  -p 24224:24224/udp 
  -v /data:/fluentd/log 
  -v <PATH-TO-FLUENT-CONF>:/fluentd/etc fluentd 
  -c /fluentd/etc/fluent.conf
  fluent/fluentd:stable

Bu yapılandırma yolu kullanır /fluentd/log Günlükleri göndermeden önce önbelleğe almak için. Bu olmadan da yapabilirsiniz, ancak yeniden başlattığınızda, yıpratıcı emek nedeniyle önbelleğe alınan her şeyi kaybedebilirsiniz. Ayrıca herhangi bir bağlantı noktasını da kullanabilirsiniz; 24224 varsayılan Fluentd bağlantı noktasıdır.

Artık Fluentd çalıştığına göre Nginx loglarını oraya gönderebiliriz. Genellikle Nginx'i bir Docker kapsayıcısında çalıştırırız; bu durumda Docker'ın Fluentd için yerel bir günlük kaydı sürücüsü vardır:

$ docker run 
--log-driver=fluentd 
--log-opt fluentd-address=<FLUENTD-SERVER-ADDRESS>
--log-opt tag="{{.Name}}" 
-v /some/content:/usr/share/nginx/html:ro 
-d 
nginx

Nginx'i farklı çalıştırırsanız günlük dosyalarını kullanabilirsiniz, Fluentd'in dosya kuyruğu eklentisi.

Yukarıda yapılandırılan günlük ayrıştırmayı Fluent yapılandırmasına ekleyelim:

<filter YOUR-NGINX-TAG.*>
  @type parser
  key_name log
  emit_invalid_record_to_error false
  <parse>
    @type json
  </parse>
</filter>

Ve günlükleri kullanarak Kinesis'e gönderme kinesis firehose eklentisi:

<match YOUR-NGINX-TAG.*>
    @type kinesis_firehose
    region region
    delivery_stream_name <YOUR-KINESIS-STREAM-NAME>
    aws_key_id <YOUR-AWS-KEY-ID>
    aws_sec_key <YOUR_AWS-SEC_KEY>
</match>

Athena

Her şeyi doğru yapılandırdıysanız, bir süre sonra (varsayılan olarak Kinesis kayıtları her 10 dakikada bir veri alır) S3'te günlük dosyalarını görmelisiniz. Kinesis Firehose'un "izleme" menüsünde S3'te ne kadar veri kaydedildiğini ve hataları görebilirsiniz. Kinesis rolüne S3 klasörüne yazma erişimi vermeyi unutmayın. Kinesis bir şeyi ayrıştıramazsa hataları aynı klasöre ekleyecektir.

Artık verileri Athena'da görüntüleyebilirsiniz. Hata döndürdüğümüz en son istekleri bulalım:

SELECT * FROM "db_name"."table_name" WHERE status > 499 ORDER BY created_at DESC limit 10;

Her istek için tüm kayıtları tarama

Artık günlüklerimiz ORC'de S3'te işlendi ve saklandı, sıkıştırıldı ve analize hazır hale getirildi. Kinesis Firehose bunları her saat için dizinler halinde bile düzenledi. Ancak tablo bölümlendirilmediği sürece Athena, nadir istisnalar dışında her istekte tüm zamanların verilerini yükleyecektir. Bu iki nedenden dolayı büyük bir sorundur:

  • Veri hacmi sürekli artıyor, bu da sorguları yavaşlatıyor;
  • Athena, istek başına minimum 10 MB olmak üzere, taranan veri hacmine göre faturalandırılır.

Bunu düzeltmek için S3'teki verileri tarayacak ve bölüm bilgilerini Glue Metastore'a yazacak olan AWS Glue Crawler'ı kullanıyoruz. Bu, Athena'yı sorgularken bölümleri filtre olarak kullanmamızı sağlayacak ve yalnızca sorguda belirtilen dizinleri tarayacaktır.

Amazon Glue Crawler'ı kurma

Amazon Glue Crawler, S3 klasöründeki tüm verileri tarar ve bölümlü tablolar oluşturur. AWS Glue konsolundan bir Glue Crawler oluşturun ve verileri depoladığınız yere bir klasör ekleyin. Bir tarayıcıyı birkaç paket için kullanabilirsiniz; bu durumda, belirtilen veritabanında paketlerin adlarıyla eşleşen adlara sahip tablolar oluşturulur. Bu verileri düzenli olarak kullanmayı planlıyorsanız Crawler'ın başlatma programını ihtiyaçlarınıza uygun şekilde yapılandırdığınızdan emin olun. Tüm tablolar için her saat başı çalışan bir Tarayıcı kullanıyoruz.

Bölümlenmiş tablolar

Tarayıcının ilk başlatılmasından sonra, taranan her pakete ilişkin tablolar, ayarlarda belirtilen veritabanında görünmelidir. Athena konsolunu açın ve Nginx günlüklerinin bulunduğu tabloyu bulun. Bir şeyler okumaya çalışalım:

SELECT * FROM "default"."part_demo_kinesis_bucket"
WHERE(
  partition_0 = '2019' AND
  partition_1 = '04' AND
  partition_2 = '08' AND
  partition_3 = '06'
  );

Bu sorgu, 6 Nisan 7 sabah 8 ile sabah 2019 arasında alınan tüm kayıtları seçecektir. Ancak bu, bölümlenmemiş bir tablodan okumaktan ne kadar daha verimli? Aynı kayıtları bulup seçelim ve bunları zaman damgasına göre filtreleyelim:

Amazon Athena ve Cube.js'yi kullanarak Nginx günlük analizi

Yalnızca bir haftalık günlüklerden oluşan bir veri kümesinde 3.59 saniye ve 244.34 megabayt veri. Bölüme göre filtrelemeyi deneyelim:

Amazon Athena ve Cube.js'yi kullanarak Nginx günlük analizi

Biraz daha hızlı ama en önemlisi yalnızca 1.23 megabayt veri! Fiyatlandırmada istek başına minimum 10 megabayt olmasaydı çok daha ucuz olurdu. Ancak yine de çok daha iyi ve büyük veri kümelerinde fark çok daha etkileyici olacak.

Cube.js kullanarak bir kontrol paneli oluşturma

Kontrol panelini oluşturmak için Cube.js analitik çerçevesini kullanıyoruz. Oldukça fazla işlevi var ama biz ikisiyle ilgileniyoruz: bölüm filtrelerini otomatik olarak kullanma yeteneği ve veri ön toplama. Veri şemasını kullanır veri şemasıSQL oluşturmak ve bir veritabanı sorgusu yürütmek için Javascript ile yazılmıştır. Veri şemasında sadece bölüm filtresinin nasıl kullanılacağını belirtmemiz gerekiyor.

Yeni bir Cube.js uygulaması oluşturalım. Zaten AWS yığınını kullandığımızdan dağıtım için Lambda'yı kullanmak mantıklıdır. Cube.js arka ucunu Heroku veya Docker'da barındırmayı planlıyorsanız, oluşturma için ekspres şablonu kullanabilirsiniz. Belgeler diğerlerini açıklamaktadır barındırma yöntemleri.

$ npm install -g cubejs-cli
$ cubejs create nginx-log-analytics -t serverless -d athena

Ortam değişkenleri cube.js'de veritabanı erişimini yapılandırmak için kullanılır. Oluşturucu, anahtarlarınızı belirtebileceğiniz bir .env dosyası oluşturacaktır. Athena.

Şimdi ihtiyacımız var veri şemasıGünlüklerimizin tam olarak nasıl saklandığını göstereceğiz. Burada ayrıca kontrol panellerine ilişkin metriklerin nasıl hesaplanacağını da belirleyebilirsiniz.

dizinde schema, bir dosya oluştur Logs.js. İşte nginx için örnek bir veri modeli:

Model kodu

const partitionFilter = (from, to) => `
    date(from_iso8601_timestamp(${from})) <= date_parse(partition_0 || partition_1 || partition_2, '%Y%m%d') AND
    date(from_iso8601_timestamp(${to})) >= date_parse(partition_0 || partition_1 || partition_2, '%Y%m%d')
    `

cube(`Logs`, {
  sql: `
  select * from part_demo_kinesis_bucket
  WHERE ${FILTER_PARAMS.Logs.createdAt.filter(partitionFilter)}
  `,

  measures: {
    count: {
      type: `count`,
    },

    errorCount: {
      type: `count`,
      filters: [
        { sql: `${CUBE.isError} = 'Yes'` }
      ]
    },

    errorRate: {
      type: `number`,
      sql: `100.0 * ${errorCount} / ${count}`,
      format: `percent`
    }
  },

  dimensions: {
    status: {
      sql: `status`,
      type: `number`
    },

    isError: {
      type: `string`,
      case: {
        when: [{
          sql: `${CUBE}.status >= 400`, label: `Yes`
        }],
        else: { label: `No` }
      }
    },

    createdAt: {
      sql: `from_unixtime(created_at)`,
      type: `time`
    }
  }
});

Burada değişkeni kullanıyoruz FILTER_PARAMSbölüm filtresiyle bir SQL sorgusu oluşturmak için.

Ayrıca kontrol panelinde görüntülemek istediğimiz metrikleri ve parametreleri ayarlıyor ve ön toplamaları belirliyoruz. Cube.js, önceden toplanmış verilerle ek tablolar oluşturacak ve veriler geldiğinde otomatik olarak güncelleyecektir. Bu yalnızca sorguları hızlandırmakla kalmaz, aynı zamanda Athena'yı kullanma maliyetini de azaltır.

Bu bilgiyi veri şeması dosyasına ekleyelim:

preAggregations: {
  main: {
    type: `rollup`,
    measureReferences: [count, errorCount],
    dimensionReferences: [isError, status],
    timeDimensionReference: createdAt,
    granularity: `day`,
    partitionGranularity: `month`,
    refreshKey: {
      sql: FILTER_PARAMS.Logs.createdAt.filter((from, to) => 
        `select
           CASE WHEN from_iso8601_timestamp(${to}) + interval '3' day > now()
           THEN date_trunc('hour', now()) END`
      )
    }
  }
}

Bu modelde, kullanılan tüm ölçümler için verilerin önceden toplanmasının ve aya göre bölümlemenin kullanılmasının gerekli olduğunu belirtiyoruz. Ön toplama bölümleme veri toplama ve güncellemeyi önemli ölçüde hızlandırabilir.

Artık kontrol panelini monte edebiliriz!

Cube.js arka ucu şunları sağlar REST API ve popüler ön uç çerçeveler için bir dizi istemci kitaplığı. Kontrol panelini oluşturmak için istemcinin React sürümünü kullanacağız. Cube.js yalnızca veri sağladığından bir görselleştirme kitaplığına ihtiyacımız olacak - beğendim yeniden çizelgeler, ancak herhangi birini kullanabilirsiniz.

Cube.js sunucusu isteği kabul eder JSON biçimi, gerekli ölçümleri belirtir. Örneğin Nginx'in gün içinde kaç hata verdiğini hesaplamak için aşağıdaki isteği göndermeniz gerekir:

{
  "measures": ["Logs.errorCount"],
  "timeDimensions": [
    {
      "dimension": "Logs.createdAt",
      "dateRange": ["2019-01-01", "2019-01-07"],
      "granularity": "day"
    }
  ]
}

Cube.js istemcisini ve React bileşen kütüphanesini NPM aracılığıyla yükleyelim:

$ npm i --save @cubejs-client/core @cubejs-client/react

Bileşenleri ithal ediyoruz cubejs и QueryRendererverileri indirmek ve kontrol panelini toplamak için:

Kontrol paneli kodu

import React from 'react';
import { LineChart, Line, XAxis, YAxis } from 'recharts';
import cubejs from '@cubejs-client/core';
import { QueryRenderer } from '@cubejs-client/react';

const cubejsApi = cubejs(
  'YOUR-CUBEJS-API-TOKEN',
  { apiUrl: 'http://localhost:4000/cubejs-api/v1' },
);

export default () => {
  return (
    <QueryRenderer
      query={{
        measures: ['Logs.errorCount'],
        timeDimensions: [{
            dimension: 'Logs.createdAt',
            dateRange: ['2019-01-01', '2019-01-07'],
            granularity: 'day'
        }]
      }}
      cubejsApi={cubejsApi}
      render={({ resultSet }) => {
        if (!resultSet) {
          return 'Loading...';
        }

        return (
          <LineChart data={resultSet.rawData()}>
            <XAxis dataKey="Logs.createdAt"/>
            <YAxis/>
            <Line type="monotone" dataKey="Logs.errorCount" stroke="#8884d8"/>
          </LineChart>
        );
      }}
    />
  )
}

Kontrol paneli kaynakları şu adreste mevcuttur: KodSandbox.

Kaynak: habr.com

Yorum ekle