Analitika dnevnika Nginx z uporabo Amazon Athena in Cube.js

Običajno se za spremljanje in analizo delovanja Nginxa uporabljajo komercialni izdelki ali že pripravljene odprtokodne alternative, kot je Prometheus + Grafana. To je dobra možnost za spremljanje ali analitiko v realnem času, ni pa zelo priročna za zgodovinsko analizo. Na katerem koli priljubljenem viru količina podatkov iz dnevnikov nginx hitro narašča in za analizo velike količine podatkov je logično uporabiti nekaj bolj specializiranega.

V tem članku vam bom povedal, kako lahko uporabite Athena analizirati dnevnike, pri čemer bom kot primer vzel Nginx, in pokazal bom, kako iz teh podatkov sestaviti analitično nadzorno ploščo z uporabo odprtokodnega ogrodja cube.js. Tukaj je celotna arhitektura rešitve:

Analitika dnevnika Nginx z uporabo Amazon Athena in Cube.js

TL:DR;
Povezava do končane nadzorne plošče.

Za zbiranje informacij, ki jih uporabljamo Tekoče, za obdelavo - AWS Kinesis Data FireHose и AWS lepilo, za shranjevanje - AWS S3. S tem svežnjem lahko shranjujete ne samo dnevnike nginx, ampak tudi druge dogodke, pa tudi dnevnike drugih storitev. Nekatere dele lahko zamenjate s podobnimi za svoj sklad, na primer lahko zapisujete dnevnike v kinesis neposredno iz nginxa, mimo fluentd, ali za to uporabite logstash.

Zbiranje dnevnikov Nginx

Privzeto so dnevniki Nginx videti nekako takole:

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

Lahko jih je razčleniti, vendar je veliko lažje popraviti konfiguracijo Nginx, tako da ustvarja dnevnike v JSON:

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 shranjevanje

Za shranjevanje dnevnikov bomo uporabili S3. To vam omogoča shranjevanje in analizo dnevnikov na enem mestu, saj lahko Athena neposredno dela s podatki v S3. Kasneje v članku vam bom povedal, kako pravilno dodati in obdelati dnevnike, vendar najprej potrebujemo čisto vedro v S3, v katerem ne bo shranjeno nič drugega. Vnaprej je vredno razmisliti, v kateri regiji boste ustvarili svoje vedro, ker Athena ni na voljo v vseh regijah.

Ustvarjanje vezja v konzoli Athena

Ustvarimo tabelo v Atheni za dnevnike. Potreben je za pisanje in branje, če nameravate uporabljati Kinesis Firehose. Odprite konzolo Athena in ustvarite tabelo:

Izdelava tabele SQL

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

Ustvarjanje toka Kinesis Firehose

Kinesis Firehose bo podatke, prejete iz Nginxa, zapisal v S3 v izbranem formatu in jih razdelil v imenike v formatu LLLL/MM/DD/HH. To bo prišlo prav pri branju podatkov. Seveda lahko pišete neposredno v S3 iz fluentd, vendar boste v tem primeru morali pisati JSON, to pa je neučinkovito zaradi velike velikosti datotek. Poleg tega je pri uporabi PrestoDB ali Athena JSON najpočasnejši format podatkov. Torej odprite konzolo Kinesis Firehose, kliknite »Ustvari tok dostave«, v polju »dostava« izberite »neposredna PUT«:

Analitika dnevnika Nginx z uporabo Amazon Athena in Cube.js

V naslednjem zavihku izberite »Record format conversion« - »Enabled« in izberite »Apache ORC« kot format zapisa. Po nekaterih raziskavah Owen O'Malley, je to optimalen format za PrestoDB in Athena. Tabelo, ki smo jo ustvarili zgoraj, uporabljamo kot shemo. Upoštevajte, da lahko določite katero koli lokacijo S3 v kinezisu; uporabljena je samo shema iz tabele. Če pa določite drugo lokacijo S3, teh zapisov ne boste mogli brati iz te tabele.

Analitika dnevnika Nginx z uporabo Amazon Athena in Cube.js

Izberemo S3 za shranjevanje in vedro, ki smo ga ustvarili prej. Aws Glue Crawler, o katerem bom govoril malo kasneje, ne more delovati s predponami v vedru S3, zato je pomembno, da ga pustite praznega.

Analitika dnevnika Nginx z uporabo Amazon Athena in Cube.js

Preostale možnosti je mogoče spremeniti glede na vašo obremenitev; običajno uporabljam privzete. Upoštevajte, da stiskanje S3 ni na voljo, vendar ORC privzeto uporablja izvorno stiskanje.

Tekoče

Zdaj, ko smo konfigurirali shranjevanje in prejemanje dnevnikov, moramo konfigurirati pošiljanje. Bomo uporabili Tekoče, ker obožujem Ruby, vendar lahko uporabite Logstash ali pošljete dnevnike neposredno na kinesis. Strežnik Fluentd je mogoče zagnati na več načinov, povedal vam bom o dockerju, ker je preprost in priročen.

Najprej potrebujemo konfiguracijsko datoteko fluent.conf. Ustvarite ga in dodajte vir:

tip naprej
vrata 24224
vezati 0.0.0.0

Zdaj lahko zaženete strežnik Fluentd. Če potrebujete naprednejšo konfiguracijo, pojdite na Dock pesto Obstaja podroben vodnik, vključno s tem, kako sestaviti svojo sliko.

$ 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

Ta konfiguracija uporablja pot /fluentd/log za predpomnjenje dnevnikov pred pošiljanjem. Lahko storite brez tega, vendar potem, ko znova zaženete, lahko izgubite vse, kar je predpomnjeno, z mukotrpnim delom. Uporabite lahko tudi katera koli vrata; 24224 so privzeta vrata Fluentd.

Zdaj, ko imamo Fluentd v teku, lahko tja pošljemo dnevnike Nginx. Običajno izvajamo Nginx v vsebniku Docker, v tem primeru ima Docker izvorni gonilnik za beleženje 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

Če Nginx zaženete drugače, lahko uporabite dnevniške datoteke, Fluentd jih ima vtičnik file tail.

Dodajmo zgoraj konfigurirano razčlenjevanje dnevnika konfiguraciji Fluent:

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

In pošiljanje dnevnikov v Kinesis z uporabo vtičnik kinesis firehose:

<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

Če ste vse pravilno konfigurirali, bi morali čez nekaj časa (privzeto Kinesis beleži prejete podatke vsakih 10 minut) videti dnevniške datoteke v S3. V meniju »nadzor« Kinesis Firehose lahko vidite, koliko podatkov je zabeleženih v S3, kot tudi napake. Ne pozabite vlogi Kinesis dati dostopa za pisanje v vedro S3. Če Kinesis nečesa ne more razčleniti, bo napake dodal v isto vedro.

Zdaj si lahko ogledate podatke v Atheni. Poiščimo zadnje zahteve, za katere smo vrnili napake:

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

Pregledovanje vseh zapisov za vsako zahtevo

Zdaj so naši dnevniki obdelani in shranjeni v S3 v ORC, stisnjeni in pripravljeni za analizo. Kinesis Firehose jih je celo organiziral v imenike za vsako uro. Dokler pa tabela ni particionirana, bo Athena naložila podatke vseh časov na vsako zahtevo, z redkimi izjemami. To je velika težava iz dveh razlogov:

  • Obseg podatkov nenehno narašča, kar upočasnjuje poizvedbe;
  • Athena se zaračuna glede na količino skeniranih podatkov, z najmanj 10 MB na zahtevo.

Da bi to odpravili, uporabimo AWS Glue Crawler, ki bo preiskal podatke v S3 in zapisal informacije o particiji v Glue Metastore. To nam bo omogočilo uporabo particij kot filtra pri poizvedovanju Athene, ta pa bo pregledala samo imenike, določene v poizvedbi.

Nastavitev Amazon Glue Crawler

Amazon Glue Crawler skenira vse podatke v vedru S3 in ustvari tabele s particijami. Ustvarite Glue Crawler iz konzole AWS Glue in dodajte vedro, kamor shranjujete podatke. En pajek lahko uporabite za več veder, v tem primeru bo ustvaril tabele v določeni bazi podatkov z imeni, ki se ujemajo z imeni veder. Če nameravate te podatke redno uporabljati, ne pozabite konfigurirati razporeda zagona pajka tako, da bo ustrezal vašim potrebam. Za vse tabele uporabljamo enega pajka, ki deluje vsako uro.

Predelne mize

Po prvem zagonu pajka se morajo tabele za vsako skenirano vedro prikazati v bazi podatkov, določeni v nastavitvah. Odprite konzolo Athena in poiščite tabelo z dnevniki Nginx. Poskusimo nekaj prebrati:

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

Ta poizvedba bo izbrala vse zapise, prejete med 6. in 7. uro 8. aprila 2019. Toda koliko bolj učinkovito je to kot samo branje iz neparticionirane tabele? Ugotovimo in izberemo iste zapise ter jih filtriramo po časovnem žigu:

Analitika dnevnika Nginx z uporabo Amazon Athena in Cube.js

3.59 sekunde in 244.34 megabajtov podatkov v naboru podatkov s samo tednom dnevnikov. Poskusimo filter po particiji:

Analitika dnevnika Nginx z uporabo Amazon Athena in Cube.js

Malce hitreje, a kar je najpomembneje - le 1.23 megabajta podatkov! Bilo bi veliko ceneje, če v ceniku ne bi bilo najmanj 10 megabajtov na zahtevo. Vendar je še vedno veliko boljši in na velikih zbirkah podatkov bo razlika veliko bolj impresivna.

Izdelava nadzorne plošče z uporabo Cube.js

Za sestavo nadzorne plošče uporabljamo analitično ogrodje Cube.js. Ima precej funkcij, vendar nas zanimata dve: možnost samodejne uporabe particijskih filtrov in predzdruževanje podatkov. Uporablja podatkovno shemo podatkovna shema, napisan v Javascriptu za ustvarjanje SQL in izvajanje poizvedbe po bazi podatkov. Navesti moramo le, kako uporabiti particijski filter v podatkovni shemi.

Ustvarimo novo aplikacijo Cube.js. Ker že uporabljamo sklad AWS, je logično, da za uvajanje uporabimo Lambda. Če nameravate zaledje Cube.js gostiti v Heroku ali Dockerju, lahko uporabite ekspresno predlogo za generiranje. Dokumentacija opisuje druge metode gostovanja.

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

Spremenljivke okolja se uporabljajo za konfiguracijo dostopa do baze podatkov v cube.js. Generator bo ustvaril datoteko .env, v kateri lahko podate svoje ključe Athena.

Zdaj potrebujemo podatkovna shema, v katerem bomo natančno navedli, kako so shranjeni naši dnevniki. Tam lahko tudi določite, kako izračunati meritve za nadzorne plošče.

V imeniku schema, ustvarite datoteko Logs.js. Tukaj je primer podatkovnega modela za nginx:

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

Tukaj uporabljamo spremenljivko FILTER_PARAMSza ustvarjanje poizvedbe SQL s particijskim filtrom.

Nastavimo tudi metrike in parametre, ki jih želimo prikazati na nadzorni plošči, ter določimo predzdruževanja. Cube.js bo ustvaril dodatne tabele s predhodno združenimi podatki in samodejno posodobil podatke, ko bodo prispeli. To ne le pospeši poizvedbe, ampak tudi zmanjša stroške uporabe Athene.

Dodajmo te informacije v datoteko podatkovne sheme:

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

V tem modelu določamo, da je treba vnaprej združiti podatke za vse uporabljene metrike in uporabiti razdelitev po mesecih. Particioniranje pred združevanjem lahko bistveno pospeši zbiranje in posodabljanje podatkov.

Zdaj lahko sestavimo armaturno ploščo!

Zaledje Cube.js zagotavlja REST API in nabor odjemalskih knjižnic za priljubljena sprednja ogrodja. Za izdelavo nadzorne plošče bomo uporabili različico odjemalca React. Cube.js ponuja samo podatke, zato bomo potrebovali knjižnico za vizualizacijo – všeč mi je recharts, lahko pa uporabite katerokoli.

Strežnik Cube.js sprejme zahtevo v format JSON, ki določa zahtevane meritve. Če želite na primer izračunati, koliko napak je Nginx dal na dan, morate poslati naslednjo zahtevo:

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

Namestimo odjemalca Cube.js in knjižnico komponent React prek NPM:

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

Uvažamo komponente cubejs и QueryRendererza prenos podatkov in zbiranje nadzorne plošče:

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

Viri nadzorne plošče so na voljo na kodni peskovnik.

Vir: www.habr.com

Dodaj komentar