Nginx log analitika koristeći Amazon Athena 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 analizu u realnom vremenu, ali nije baš zgodna za historijsku analizu. Na bilo kojem popularnom resursu, količina podataka iz nginx dnevnika 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 analizirati dnevnike, uzimajući Nginx kao primjer, a ja ću pokazati kako sastaviti analitičku kontrolnu tablu od ovih podataka koristeći open-source cube.js framework. Evo kompletne arhitekture rješenja:

Nginx log analitika koristeći Amazon Athena i Cube.js

TL:DR;
Link do gotove kontrolne table.

Za prikupljanje informacija koje koristimo fluentd, za preradu - AWS Kinesis Data Firehose и AWS lepak, za skladištenje - AWS S3. Koristeći ovaj paket, možete pohraniti ne samo nginx dnevnike, već i druge događaje, kao i dnevnike drugih usluga. Možete zamijeniti neke dijelove sličnim za svoj stog, na primjer, možete pisati dnevnike u kinesis direktno iz nginxa, zaobilazeći fluentd, ili koristiti logstash za ovo.

Prikupljanje Nginx dnevnika

Podrazumevano, Nginx dnevnici 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 raščlaniti, ali je mnogo lakše ispraviti Nginx konfiguraciju tako da proizvodi evidencije 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 dnevnika koristit ćemo S3. Ovo vam omogućava da skladištite i analizirate logove na jednom mestu, pošto Athena može direktno da radi sa podacima u S3. Kasnije u članku ću vam reći kako pravilno dodati i obraditi dnevnike, ali prvo nam je potrebna čista kantica u S3, u kojoj ništa drugo neće biti pohranjeno. Vrijedi unaprijed razmisliti u kojoj regiji ćete kreirati svoju kantu, jer Athena nije dostupna u svim regijama.

Kreiranje kola u Athena konzoli

Kreirajmo tabelu u Atheni za dnevnike. Potreban je i za pisanje i za čitanje ako planirate koristiti Kinesis Firehose. Otvorite Athena konzolu i kreirajte tabelu:

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

Kreiranje 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 direktno u S3 iz fluentd-a, ali u ovom slučaju ćete morati pisati JSON, a to je neefikasno zbog velike veličine datoteka. Osim toga, kada koristite PrestoDB ili Athena, JSON je najsporiji format podataka. Dakle, otvorite Kinesis Firehose konzolu, kliknite na "Kreiraj prijenos isporuke", odaberite "direktan PUT" u polju "isporuka":

Nginx log analitika koristeći Amazon Athena i Cube.js

Na sljedećoj kartici odaberite “Konverzija formata zapisa” - “Omogućeno” i odaberite “Apache ORC” kao format snimanja. Prema nekim istraživanjima Owen O'Malley, ovo je optimalni format za PrestoDB i Athena. Koristimo tabelu koju smo kreirali iznad kao šemu. Imajte na umu da možete specificirati bilo koju S3 lokaciju u kinezi; samo se shema koristi iz tabele. Ali ako navedete drugu S3 lokaciju, tada nećete moći čitati ove zapise iz ove tabele.

Nginx log analitika koristeći Amazon Athena i Cube.js

Odabiremo S3 za pohranu i kantu koju smo kreirali ranije. Aws Glue Crawler, o kojem ću govoriti malo kasnije, ne može raditi sa prefiksima u S3 bucketu, pa je važno ostaviti ga praznim.

Nginx log analitika koristeći Amazon Athena i Cube.js

Preostale opcije se mogu mijenjati ovisno o opterećenju; obično koristim one zadane. Imajte na umu da S3 kompresija nije dostupna, ali ORC po defaultu koristi izvornu kompresiju.

fluentd

Sada kada smo konfigurirali pohranjivanje i primanje dnevnika, moramo konfigurirati slanje. Koristićemo fluentd, jer volim Ruby, ali možete koristiti Logstash ili slati zapise direktno u kinesis. Fluentd server se može pokrenuti na nekoliko načina, reći ću vam o dockeru jer je jednostavan i zgodan.

Prvo, trebamo konfiguracijski fajl fluent.conf. Kreirajte ga i dodajte izvor:

tip napred
port 24224
vezati 0.0.0.0

Sada možete pokrenuti Fluentd server. Ako vam je potrebna naprednija konfiguracija, 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 putanju /fluentd/log za keširanje dnevnika prije slanja. Možete i bez ovoga, ali onda kada ponovo pokrenete, možete izgubiti sve keširano uz naporan rad. Također možete koristiti bilo koji port; 24224 je zadani Fluentd port.

Sada kada imamo pokrenut Fluentd, možemo tamo poslati Nginx logove. Obično pokrećemo Nginx u Docker kontejneru, u kom slučaju Docker ima izvorni drajver za evidentiranje 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 pokrećete drugačije, možete koristiti datoteke evidencije, Fluentd ima file tail plugin.

Dodajmo prethodno konfiguriranu analizu dnevnika u Fluent konfiguraciju:

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

I slanje dnevnika u Kinesis koristeći kinesis firehose plugin:

<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 konfigurisali, nakon nekog vremena (podrazumevano, Kinesis bilježi primljene podatke svakih 10 minuta) trebali biste vidjeti datoteke dnevnika u S3. U “monitoring” meniju Kinesis Firehose možete vidjeti koliko podataka je zabilježeno u S3, kao i greške. Ne zaboravite dati pristup za pisanje u S3 bucket ulozi Kinesis. Ako Kinesis ne može raščlaniti nešto, dodat će greške u istu kantu.

Sada možete vidjeti podatke u Ateni. Pronađimo najnovije zahtjeve za koje smo vratili greš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 dnevnici obrađeni i pohranjeni u S3 u ORC-u, komprimirani i spremni za analizu. Kinesis Firehose ih je čak organizirao u direktorije za svaki sat. Međutim, sve dok tabela nije particionisana, Athena će učitavati sve vremenske podatke na svaki zahtev, sa retkim izuzecima. Ovo je veliki problem iz dva razloga:

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

Da to popravimo, koristimo AWS Glue Crawler, koji će indeksirati podatke u S3 i pisati informacije o particiji u Glue Metastore. Ovo će nam omogućiti da koristimo particije kao filter kada postavljamo upite za Athenu, a skenirat će se samo direktoriji navedeni u upitu.

Postavljanje Amazon Glue Crawlera

Amazon Glue Crawler skenira sve podatke u S3 bucketu i kreira tabele sa particijama. Kreirajte Glue Crawler iz AWS Glue konzole i dodajte kantu u koju pohranjujete podatke. Možete koristiti jedan alat za indeksiranje za nekoliko kantica, u kom slučaju će kreirati tabele u navedenoj bazi podataka s imenima koja odgovaraju nazivima bucketa. Ako planirate redovno koristiti ove podatke, obavezno konfigurirajte raspored pokretanja Crawlera tako da odgovara vašim potrebama. Koristimo jedan Crawler za sve tabele, koji radi svakog sata.

Particionirani stolovi

Nakon prvog pokretanja alata za indeksiranje, tabele za svaku skeniranu kantu trebale bi se pojaviti u bazi podataka navedenoj u postavkama. Otvorite Athena konzolu i pronađite tabelu sa Nginx evidencijama. 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 upit će odabrati sve zapise primljene između 6 i 7 sati 8. aprila 2019. Ali koliko je ovo efikasnije od pukog čitanja iz neparticionirane tablice? Hajde da saznamo i odaberemo iste zapise, filtrirajući ih po vremenskoj oznaci:

Nginx log analitika koristeći Amazon Athena i Cube.js

3.59 sekundi i 244.34 megabajta podataka na skupu podataka sa samo sedmicom evidencije. Hajde da probamo filter po particiji:

Nginx log analitika koristeći Amazon Athena i Cube.js

Malo brže, ali najvažnije - samo 1.23 megabajta podataka! Bilo bi mnogo jeftinije da nema minimalnih 10 megabajta po zahtjevu u cijeni. Ali i dalje je mnogo bolji, a na velikim skupovima podataka razlika će biti mnogo impresivnija.

Izrada kontrolne table pomoću Cube.js

Za sastavljanje kontrolne ploče koristimo Cube.js analitički okvir. Ima dosta funkcija, ali nas zanimaju dvije: mogućnost automatskog korištenja filtera particija i prethodno združivanje podataka. Koristi šemu podataka shema podataka, napisan u Javascriptu za generiranje SQL-a i izvršavanje upita baze podataka. Moramo samo naznačiti kako koristiti filter particije u šemi podataka.

Kreirajmo novu aplikaciju Cube.js. Pošto već koristimo AWS stack, logično je koristiti Lambda za implementaciju. Možete koristiti ekspresni predložak za generisanje ako planirate da ugostite Cube.js backend u Heroku ili Docker-u. Dokumentacija opisuje druge metode hostinga.

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

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

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

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

Šifra 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 sa filterom particija.

Također postavljamo metrike i parametre koje želimo da prikažemo na kontrolnoj tabli i specificiramo pre-agregacije. Cube.js će kreirati dodatne tabele sa unapred agregiranim podacima i automatski će ažurirati podatke kako stignu. Ovo ne samo da ubrzava upite, već i smanjuje troškove korištenja Athene.

Dodajmo ove informacije u datoteku šeme 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 prethodno agregirati podatke za sve korištene metrike i koristiti particioniranje po mjesecima. Pre-agregacijsko particioniranje može značajno ubrzati prikupljanje i ažuriranje podataka.

Sada možemo sastaviti kontrolnu tablu!

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

Cube.js server prihvata zahtjev u JSON format, koji specificira potrebne metrike. Na primjer, da biste izračunali koliko je grešaka Nginx dnevno dao, morate poslati sljedeći zahtjev:

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

Instalirajmo Cube.js klijenta i React biblioteku komponenti preko NPM-a:

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

Uvozimo komponente cubejs и QueryRendererda preuzmete podatke i prikupite kontrolnu tablu:

Kôd kontrolne table

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 na kontrolnoj tabli dostupni su na kod sandbox.

izvor: www.habr.com

Dodajte komentar