Nginx log analitika pomoću Amazon Athene i Cube.js

Obično se komercijalni proizvodi ili gotove alternative otvorenog koda, kao što su Prometheus + Grafana, koriste za praćenje i analizu rada Nginxa. Ovo je dobra opcija za praćenje ili analitiku u stvarnom vremenu, ali nije baš zgodna za povijesnu analizu. Na bilo kojem popularnom resursu količina podataka iz nginx zapisa brzo raste, a za analizu velike količine podataka logično je koristiti nešto specijaliziranije.

U ovom članku ću vam reći kako možete koristiti Atina za analizu zapisa, uzimajući Nginx kao primjer, a ja ću pokazati kako sastaviti analitičku kontrolnu ploču iz ovih podataka pomoću okvira otvorenog koda cube.js. Ovdje je cjelovita arhitektura rješenja:

Nginx log analitika pomoću Amazon Athene i Cube.js

TL:DR;
Link na gotovu nadzornu ploču.

Za prikupljanje informacija koje koristimo Tečno, za obradu - Vatrogasno crijevo AWS Kinesis Data и AWS ljepilo, za skladištenje - AWS S3. Koristeći ovaj paket, možete pohraniti ne samo nginx zapisnike, već i druge događaje, kao i zapisnike drugih usluga. Neke dijelove možete zamijeniti sličnim za svoj stog, na primjer, možete pisati zapise u kinesis izravno iz nginxa, zaobilazeći fluentd, ili za to koristiti logstash.

Prikupljanje Nginx zapisa

Prema zadanim postavkama, Nginx zapisi izgledaju otprilike ovako:

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" "-"

Mogu se analizirati, ali puno je lakše ispraviti Nginx konfiguraciju tako da proizvodi zapise u JSON-u:

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;

S3 za skladištenje

Za pohranjivanje zapisa koristit ćemo S3. To vam omogućuje pohranu i analizu zapisa na jednom mjestu, budući da Athena može izravno raditi s podacima u S3. Kasnije u članku ću vam reći kako ispravno dodati i obraditi zapise, ali prvo nam je potrebna čista kanta u S3, u kojoj se ništa drugo neće pohraniti. Vrijedno je unaprijed razmisliti u kojoj regiji ćete kreirati svoju kantu jer Athena nije dostupna u svim regijama.

Izrada sklopa u Athena konzoli

Kreirajmo tablicu u Atheni za zapise. Potreban je i za pisanje i za čitanje ako planirate koristiti Kinesis Firehose. Otvorite Athena konzolu i napravite tablicu:

Izrada SQL tablice

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');

Stvaranje Kinesis Firehose Stream

Kinesis Firehose će podatke primljene od Nginxa zapisati u S3 u odabranom formatu, dijeleći ih u direktorije u formatu GGGG/MM/DD/HH. Ovo će vam dobro doći prilikom čitanja podataka. Možete, naravno, pisati izravno u S3 iz fluentda, ali u ovom slučaju morat ćete pisati JSON, a to je neučinkovito zbog velike veličine datoteka. Osim toga, kada koristite PrestoDB ili Athena, JSON je najsporiji format podataka. Dakle, otvorite Kinesis Firehose konzolu, kliknite "Create delivery stream", odaberite "direct PUT" u polju "delivery":

Nginx log analitika pomoću Amazon Athene i Cube.js

U sljedećoj kartici odaberite "Pretvorba formata zapisa" - "Omogućeno" i odaberite "Apache ORC" kao format snimanja. Prema nekim istraživanjima Owen O'Malley, ovo je optimalan format za PrestoDB i Athenu. Koristimo tablicu koju smo napravili gore kao shemu. Imajte na umu da možete navesti bilo koju S3 lokaciju u kinezisu; koristi se samo shema iz tablice. Ali ako navedete drugu S3 lokaciju, nećete moći čitati te zapise iz ove tablice.

Nginx log analitika pomoću Amazon Athene i Cube.js

Odaberemo S3 za pohranu i kantu koju smo ranije izradili. Aws Glue Crawler, o kojem ću govoriti malo kasnije, ne može raditi s prefiksima u S3 kanti, pa je važno ostaviti je praznu.

Nginx log analitika pomoću Amazon Athene i Cube.js

Preostale opcije mogu se mijenjati ovisno o vašem opterećenju; ja obično koristim zadane. Imajte na umu da S3 kompresija nije dostupna, ali ORC prema zadanim postavkama koristi izvornu kompresiju.

Tečno

Sada kada smo konfigurirali pohranjivanje i primanje zapisa, moramo konfigurirati slanje. Koristit ćemo se Tečno, jer volim Ruby, ali možete koristiti Logstash ili izravno slati zapise na kinesis. Poslužitelj Fluentd može se pokrenuti na nekoliko načina, reći ću vam o dockeru jer je jednostavan i praktičan.

Prvo nam je potrebna konfiguracijska datoteka fluent.conf. Stvorite ga i dodajte izvor:

vrsta naprijed
port 24224
vezati 0.0.0.0

Sada možete pokrenuti Fluentd poslužitelj. Ako trebate napredniju konfiguraciju, idite na Docker čvorište Postoji detaljan vodič, uključujući kako sastaviti svoju sliku.

$ 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

Ova konfiguracija koristi stazu /fluentd/log u predmemoriju zapisa prije slanja. Možete i bez ovoga, ali kada ponovno pokrenete, možete izgubiti sve što je predmemorirano uz mukotrpan rad. Također možete koristiti bilo koji port; 24224 je zadani Fluentd port.

Sada kada Fluentd radi, možemo poslati Nginx zapisnike tamo. Obično pokrećemo Nginx u Docker spremniku, u kojem slučaju Docker ima nativni upravljački program za bilježenje za Fluentd:

$ 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

Ako Nginx pokrenete na drugačiji način, možete koristiti log datoteke, Fluentd ima file tail dodatak.

Dodajmo prethodno konfigurirano parsiranje dnevnika konfiguraciji Fluenta:

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

I slanje zapisa u Kinesis pomoću kinesis dodatak za vatrogasno crijevo:

<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>

Atina

Ako ste sve ispravno konfigurirali, nakon nekog vremena (prema zadanim postavkama Kinesis bilježi primljene podatke svakih 10 minuta) trebali biste vidjeti datoteke dnevnika u S3. U izborniku "monitoring" Kinesis Firehose možete vidjeti koliko je podataka snimljeno u S3, kao i pogreške. Ne zaboravite ulozi Kinesis dati pristup pisanju u S3 spremnik. Ako Kinesis ne može analizirati nešto, dodat će pogreške u istu kantu.

Sada možete vidjeti podatke u Atheni. Pronađimo najnovije zahtjeve za koje smo vratili pogreške:

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

Skeniranje svih zapisa za svaki zahtjev

Sada su naši zapisnici obrađeni i pohranjeni u S3 u ORC-u, komprimirani i spremni za analizu. Kinesis Firehose čak ih je organizirao u direktorije za svaki sat. Međutim, sve dok tablica nije particionirana, Athena će učitati podatke svih vremena na svaki zahtjev, uz rijetke iznimke. Ovo je veliki problem iz dva razloga:

  • Količina podataka stalno raste, usporavajući upite;
  • Athena se naplaćuje na temelju količine skeniranih podataka, s minimalno 10 MB po zahtjevu.

Da bismo to popravili, koristimo AWS Glue Crawler, koji će indeksirati podatke u S3 i zapisati informacije o particiji u Glue Metastore. To će nam omogućiti da koristimo particije kao filtar kada postavljamo upit Atheni, a skenirat će samo direktorije navedene u upitu.

Postavljanje Amazon Glue Crawlera

Amazon Glue Crawler skenira sve podatke u S3 spremniku i stvara tablice s particijama. Stvorite Glue Crawler s AWS Glue konzole i dodajte kantu u koju pohranjujete podatke. Možete koristiti jedan alat za indeksiranje za nekoliko spremnika, u kojem slučaju će on stvoriti tablice u navedenoj bazi podataka s nazivima koji odgovaraju nazivima spremnika. Ako planirate redovito koristiti ove podatke, svakako konfigurirajte raspored pokretanja programa za indeksiranje tako da odgovara vašim potrebama. Koristimo jedan alat za indeksiranje za sve tablice, koji radi svaki sat.

Pregrađeni stolovi

Nakon prvog pokretanja alata za indeksiranje, tablice za svaku skeniranu kantu trebale bi se pojaviti u bazi podataka navedenoj u postavkama. Otvorite Athena konzolu i pronađite tablicu s Nginx zapisima. Pokušajmo nešto pročitati:

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

Ovaj će upit odabrati sve zapise primljene između 6 i 7 sati 8. travnja 2019. Ali koliko je ovo učinkovitije od samog čitanja iz neparticionirane tablice? Otkrijmo i odaberimo iste zapise, filtrirajući ih prema vremenskoj oznaci:

Nginx log analitika pomoću Amazon Athene i Cube.js

3.59 sekundi i 244.34 megabajta podataka u skupu podataka sa samo tjedan dana zapisa. Pokušajmo s filtrom po particiji:

Nginx log analitika pomoću Amazon Athene i Cube.js

Malo brže, ali što je najvažnije - samo 1.23 megabajta podataka! Bilo bi mnogo jeftinije da nije minimalno 10 megabajta po zahtjevu u cijeni. Ali još uvijek je mnogo bolje, a na velikim skupovima podataka razlika će biti mnogo impresivnija.

Izrada nadzorne ploče pomoću Cube.js

Za sastavljanje nadzorne ploče koristimo Cube.js analitički okvir. Ima dosta funkcija, ali nas zanimaju dvije: mogućnost automatske upotrebe particijskih filtara i predagregacija podataka. Koristi podatkovnu shemu shema podataka, napisan u Javascriptu za generiranje SQL-a i izvršavanje upita baze podataka. Trebamo samo naznačiti kako koristiti particijski filtar u podatkovnoj shemi.

Kreirajmo novu Cube.js aplikaciju. Budući da već koristimo AWS stack, logično je koristiti Lambda za implementaciju. Možete koristiti ekspresni predložak za generiranje ako planirate postaviti pozadinu Cube.js u Heroku ili Docker. Dokumentacija opisuje druge metode hostinga.

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

Varijable okruženja koriste se za konfiguriranje pristupa bazi podataka u cube.js. Generator će stvoriti .env datoteku u kojoj možete navesti svoje ključeve za Atina.

Sada trebamo shema podataka, u kojem ćemo točno naznačiti kako su naši dnevnici pohranjeni. Tamo također možete odrediti kako izračunati metriku za nadzorne ploče.

U imeniku schema, stvorite datoteku Logs.js. Evo primjera modela podataka za nginx:

Kod modela

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`
    }
  }
});

Ovdje koristimo varijablu FILTER_PARAMSza generiranje SQL upita s particijskim filtrom.

Također postavljamo metriku i parametre koje želimo prikazati na nadzornoj ploči i specificiramo predagregacije. Cube.js će izraditi dodatne tablice s unaprijed agregiranim podacima i automatski ažurirati podatke kako pristignu. To ne samo da ubrzava upite, već i smanjuje troškove korištenja Athene.

Dodajmo ove informacije u datoteku sheme podataka:

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`
      )
    }
  }
}

U ovom modelu navodimo da je potrebno unaprijed agregirati podatke za sve korištene metrike i koristiti particioniranje po mjesecima. Particioniranje prije agregacije može znatno ubrzati prikupljanje i ažuriranje podataka.

Sada možemo sastaviti kontrolnu ploču!

Cube.js backend pruža REST API i skup klijentskih biblioteka za popularne front-end okvire. Koristit ćemo React verziju klijenta za izradu nadzorne ploče. Cube.js daje samo podatke, pa će nam trebati biblioteka za vizualizaciju - sviđa mi se recharts, ali možete koristiti bilo koji.

Poslužitelj Cube.js prihvaća zahtjev u JSON format, koji navodi potrebne metrike. Na primjer, da biste izračunali koliko je grešaka Nginx dao po danu, morate poslati sljedeći zahtjev:

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

Instalirajmo Cube.js klijent i biblioteku komponenti React putem NPM-a:

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

Uvozimo komponente cubejs и QueryRendererza preuzimanje podataka i prikupljanje nadzorne ploče:

Kod nadzorne ploče

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>
        );
      }}
    />
  )
}

Izvori nadzorne ploče dostupni su na sandbox koda.

Izvor: www.habr.com

Dodajte komentar