Nginx kirjaa analytiikkaa käyttämällä Amazon Athenaa ja Cube.js:ää

Tyypillisesti Nginxin toiminnan seurantaan ja analysointiin käytetään kaupallisia tuotteita tai valmiita avoimen lähdekoodin vaihtoehtoja, kuten Prometheus + Grafana. Tämä on hyvä vaihtoehto seurantaan tai reaaliaikaiseen analytiikkaan, mutta ei kovin kätevä historialliseen analyysiin. Missä tahansa suositussa resurssissa nginx-lokien tietojen määrä kasvaa nopeasti, ja suuren tietomäärän analysointiin on loogista käyttää jotain erikoisempaa.

Tässä artikkelissa kerron sinulle, kuinka voit käyttää Athena analysoida lokeja esimerkkinä Nginxistä, ja näytän kuinka koota analyyttinen kojelauta näistä tiedoista käyttämällä avoimen lähdekoodin cube.js-kehystä. Tässä on täydellinen ratkaisuarkkitehtuuri:

Nginx kirjaa analytiikkaa käyttämällä Amazon Athenaa ja Cube.js:ää

TL:DR;
Linkki valmiiseen kojelautaan.

Käytämme tietojen keräämiseen Sujuva, käsittelyyn - AWS Kinesis Data Firehose и AWS-liima, säilytykseen - AWS S3. Tämän paketin avulla voit tallentaa nginx-lokien lisäksi myös muita tapahtumia sekä muiden palveluiden lokeja. Voit korvata joitain osia pinossasi vastaavilla, esimerkiksi voit kirjoittaa lokit kinesikseen suoraan nginxistä ohittaen fluentd:n tai käyttää tähän logstashia.

Nginx-lokien kerääminen

Oletusarvoisesti Nginx-lokit näyttävät tältä:

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

Ne voidaan jäsentää, mutta Nginx-kokoonpanon korjaaminen on paljon helpompaa niin, että se tuottaa lokit JSONissa:

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 säilytykseen

Lokien tallentamiseen käytämme S3:a. Tämän avulla voit tallentaa ja analysoida lokeja yhdessä paikassa, koska Athena voi käsitellä tietoja suoraan S3:ssa. Myöhemmin artikkelissa kerron kuinka lokit lisätään ja käsitellään oikein, mutta ensin tarvitsemme puhtaan kauhan S3: ssa, johon mitään muuta ei tallenneta. Kannattaa harkita etukäteen, mille alueelle luot kauhan, koska Athena ei ole saatavilla kaikilla alueilla.

Piirin luominen Athena-konsolissa

Luodaan Athenaan taulukko lokeille. Sitä tarvitaan sekä kirjoittamiseen että lukemiseen, jos aiot käyttää Kinesis Firehosea. Avaa Athena-konsoli ja luo taulukko:

SQL-taulukon luominen

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

Kinesis Firehose kirjoittaa Nginxistä saadut tiedot S3:lle valitussa muodossa ja jakaa ne hakemistoihin muodossa VVVV/KK/PP/TT. Tästä on hyötyä dataa luettaessa. Voit tietysti kirjoittaa suoraan S3:een fluentd:stä, mutta tässä tapauksessa sinun on kirjoitettava JSON, mikä on tehotonta tiedostojen suuren koon vuoksi. Lisäksi PrestoDB:tä tai Athenaa käytettäessä JSON on hitain tietomuoto. Avaa siis Kinesis Firehose -konsoli, napsauta "Luo toimitusvirta", valitse "toimitus"-kentästä "suora PUT":

Nginx kirjaa analytiikkaa käyttämällä Amazon Athenaa ja Cube.js:ää

Valitse seuraavassa välilehdessä "Tallennusmuodon muuntaminen" - "Käytössä" ja valitse tallennusmuodoksi "Apache ORC". Joidenkin tutkimusten mukaan Owen O'Malley, tämä on optimaalinen muoto PrestoDB:lle ja Athenalle. Käytämme kaaviona yllä luomaamme taulukkoa. Huomaa, että voit määrittää minkä tahansa S3-paikan kinesisissä; vain taulukosta käytetään skeemaa. Mutta jos määrität toisen S3-sijainnin, et voi lukea näitä tietueita tästä taulukosta.

Nginx kirjaa analytiikkaa käyttämällä Amazon Athenaa ja Cube.js:ää

Valitsemme säilytykseen S3:n ja aiemmin luomamme kauhan. Aws Glue Crawler, josta puhun hieman myöhemmin, ei voi toimia S3-ämpärien etuliitteiden kanssa, joten on tärkeää jättää se tyhjäksi.

Nginx kirjaa analytiikkaa käyttämällä Amazon Athenaa ja Cube.js:ää

Loput vaihtoehdot voidaan muuttaa kuormituksestasi riippuen; käytän yleensä oletusasetuksia. Huomaa, että S3-pakkaus ei ole käytettävissä, mutta ORC käyttää oletusarvoisesti alkuperäistä pakkausta.

Sujuva

Nyt kun olemme määrittäneet lokien tallentamisen ja vastaanottamisen, meidän on määritettävä lähetys. Me käytämme Sujuva, koska rakastan Rubya, mutta voit käyttää Logstashia tai lähettää lokit suoraan kinesikseen. Fluentd-palvelin voidaan käynnistää useilla tavoilla, kerron sinulle dockerista, koska se on yksinkertainen ja kätevä.

Ensin tarvitsemme fluent.conf-määritystiedoston. Luo se ja lisää lähde:

tyyppi eteenpäin
portti 24224
sitoa 0.0.0.0

Nyt voit käynnistää Fluentd-palvelimen. Jos tarvitset kehittyneempiä määrityksiä, siirry osoitteeseen Docker-napa Siellä on yksityiskohtainen opas, joka sisältää kuvan kokoamisen.

$ 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

Tämä kokoonpano käyttää polkua /fluentd/log lokit välimuistiin ennen lähettämistä. Voit pärjätä ilman tätä, mutta kun käynnistät uudelleen, voit menettää kaiken välimuistissa olevan työvoiman. Voit myös käyttää mitä tahansa porttia; 24224 on oletusarvoinen Fluentd-portti.

Nyt kun Fluentd on käynnissä, voimme lähettää Nginx-lokit sinne. Käytämme yleensä Nginxiä Docker-säiliössä, jolloin Dockerilla on natiivi lokiohjain Fluentdille:

$ 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

Jos käytät Nginxiä eri tavalla, voit käyttää lokitiedostoja, Fluentd on tehnyt file tail plugin.

Lisätään Fluent-kokoonpanoon yllä määritetty lokin jäsennys:

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

Ja lokien lähettäminen Kinesikseen käyttäen kinesis firehose -laajennus:

<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

Jos olet määrittänyt kaiken oikein, jonkin ajan kuluttua (oletusarvoisesti Kinesis tallentaa vastaanotetut tiedot 10 minuutin välein) sinun pitäisi nähdä lokitiedostot S3:ssa. Kinesis Firehosen ”seuranta”-valikossa näet kuinka paljon tietoja S3:ssa on tallennettu, sekä virheet. Älä unohda antaa S3-ämpäriin kirjoitusoikeutta Kinesis-roolille. Jos Kinesis ei voinut jäsentää jotain, se lisää virheet samaan ryhmään.

Nyt voit tarkastella tietoja Athenassa. Etsitään viimeisimmät pyynnöt, joille olemme palauttaneet virheitä:

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

Kaikkien tietueiden skannaus jokaista pyyntöä varten

Nyt lokimme on käsitelty ja tallennettu S3:een ORC:ssa, pakattuina ja valmiina analysoitavaksi. Kinesis Firehose jopa järjesti ne hakemistoihin jokaiselle tunnille. Kuitenkin niin kauan kuin taulukkoa ei ole osioitu, Athena lataa kaikkien aikojen tiedot jokaisesta pyynnöstä, harvoja poikkeuksia lukuun ottamatta. Tämä on suuri ongelma kahdesta syystä:

  • Tietojen määrä kasvaa jatkuvasti, mikä hidastaa kyselyitä;
  • Athena laskutetaan skannatun tiedon määrän perusteella, vähintään 10 Mt per pyyntö.

Korjaamme tämän käyttämällä AWS Glue Crawleria, joka indeksoi tiedot S3:ssa ja kirjoittaa osiotiedot Glue Metastoreen. Tämä antaa meille mahdollisuuden käyttää osioita suodattimena tehdessämme kyselyä Athenasta, ja se tarkistaa vain kyselyssä määritetyt hakemistot.

Amazon Glue Crawlerin määrittäminen

Amazon Glue Crawler skannaa kaikki S3-säihön tiedot ja luo taulukoita osioineen. Luo AWS Glue -konsolista Glue Crawler ja lisää ämpäri, johon tallennat tiedot. Voit käyttää yhtä indeksointirobottia useille ryhmille, jolloin se luo määritettyyn tietokantaan taulukoita, joiden nimet vastaavat ryhmien nimiä. Jos aiot käyttää näitä tietoja säännöllisesti, muista määrittää indeksointirobotin käynnistysaikataulu tarpeidesi mukaan. Käytämme yhtä indeksointirobottia kaikille pöydille, joka käy tunnin välein.

Osioidut pöydät

Indeksointirobotin ensimmäisen käynnistyksen jälkeen jokaisen skannatun segmentin taulukoiden pitäisi näkyä asetuksissa määritettyyn tietokantaan. Avaa Athena-konsoli ja etsi Nginx-lokit sisältävä taulukko. Yritetään lukea jotain:

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

Tämä kysely valitsee kaikki tietueet, jotka on vastaanotettu 6. huhtikuuta 7 kello 8–2019. Mutta kuinka paljon tehokkaampaa tämä on kuin pelkkä lukeminen osittamattomasta taulukosta? Selvitetään ja valitaan samat tietueet suodattamalla ne aikaleiman mukaan:

Nginx kirjaa analytiikkaa käyttämällä Amazon Athenaa ja Cube.js:ää

3.59 sekuntia ja 244.34 megatavua dataa tietojoukossa, jossa on vain viikon lokit. Kokeillaan suodatinta osion mukaan:

Nginx kirjaa analytiikkaa käyttämällä Amazon Athenaa ja Cube.js:ää

Hieman nopeampi, mutta mikä tärkeintä - vain 1.23 megatavua dataa! Se olisi paljon halvempaa, ellei hinnoittelussa olisi vähintään 10 megatavua per pyyntö. Mutta se on silti paljon parempi, ja suurilla tietojoukoilla ero on paljon vaikuttavampi.

Hallintapaneelin rakentaminen Cube.js:n avulla

Käytämme kojelaudan kokoamiseen Cube.js-analyyttistä kehystä. Siinä on melko paljon toimintoja, mutta olemme kiinnostuneita kahdesta: kyky käyttää automaattisesti osiosuodattimia ja tietojen esikokoaminen. Se käyttää dataskeemaa datakaavio, kirjoitettu Javascriptillä SQL:n luomiseksi ja tietokantakyselyn suorittamiseksi. Meidän tarvitsee vain osoittaa, kuinka osiosuodatinta käytetään tietoskeemassa.

Luodaan uusi Cube.js-sovellus. Koska käytämme jo AWS-pinoa, on loogista käyttää Lambdaa käyttöönottoon. Voit käyttää pikamallia sukupolven luomiseen, jos aiot isännöidä Cube.js-taustaohjelmaa Herokussa tai Dockerissa. Dokumentaatio kuvaa muita isännöintimenetelmiä.

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

Ympäristömuuttujia käytetään määrittämään tietokannan käyttö cube.js:ssä. Generaattori luo .env-tiedoston, jossa voit määrittää avaimesi Athena.

Nyt tarvitsemme dataskeema, jossa ilmoitamme tarkalleen kuinka lokit tallennetaan. Siellä voit myös määrittää, kuinka mittaristot lasketaan kojelaudoille.

Hakemistossa schema, luo tiedosto Logs.js. Tässä on esimerkki nginx-tietomallista:

Mallikoodi

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

Tässä käytämme muuttujaa FILTER_PARAMSSQL-kyselyn luomiseen osiosuodattimella.

Asetamme myös mittarit ja parametrit, jotka haluamme näyttää kojelaudassa, ja määritämme esikoontit. Cube.js luo lisää taulukoita, joissa on esikoottuja tietoja, ja päivittää tiedot automaattisesti niiden saapuessa. Tämä ei vain nopeuttaa kyselyitä, vaan myös vähentää Athenen käyttökustannuksia.

Lisätään nämä tiedot dataskeematiedostoon:

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

Määritämme tässä mallissa, että kaikkien käytettyjen mittareiden tiedot on esikoottava etukäteen ja käytettävä kuukausittaista osiointia. Esikokoamista edeltävä osiointi voi merkittävästi nopeuttaa tiedonkeruuta ja päivittämistä.

Nyt voimme koota kojelaudan!

Cube.js-taustaohjelma tarjoaa REST API ja joukko asiakaskirjastoja suosittuja käyttöliittymärakenteita varten. Käytämme asiakkaan React-versiota kojelaudan rakentamiseen. Cube.js tarjoaa vain dataa, joten tarvitsemme visualisointikirjaston - pidän siitä kartoituksia, mutta voit käyttää mitä tahansa.

Cube.js-palvelin hyväksyy pyynnön JSON-muoto, joka määrittää vaaditut tiedot. Esimerkiksi laskeaksesi kuinka monta virhettä Nginx antoi päivässä, sinun on lähetettävä seuraava pyyntö:

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

Asennetaan Cube.js-asiakas ja React-komponenttikirjasto NPM:n kautta:

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

Tuomme komponentteja maahan cubejs и QueryRendererladataksesi tiedot ja kerätäksesi kojetaulun:

Kojelaudan koodi

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

Kojelaudan lähteet ovat saatavilla osoitteessa koodi hiekkalaatikko.

Lähde: will.com

Lisää kommentti