Nginx-loganalise met behulp van Amazon Athena en Cube.js

Tipies word kommersiële produkte of klaargemaakte oopbron-alternatiewe, soos Prometheus + Grafana, gebruik om die werking van Nginx te monitor en te ontleed. Dit is 'n goeie opsie vir monitering of intydse analise, maar nie baie gerieflik vir historiese analise nie. Op enige gewilde hulpbron groei die volume data van nginx-logboeke vinnig, en om 'n groot hoeveelheid data te ontleed, is dit logies om iets meer gespesialiseerd te gebruik.

In hierdie artikel sal ek jou vertel hoe jy dit kan gebruik Athena om logs te ontleed, met Nginx as 'n voorbeeld, en ek sal wys hoe om 'n analitiese dashboard uit hierdie data saam te stel deur die oopbron cube.js-raamwerk te gebruik. Hier is die volledige oplossing argitektuur:

Nginx-loganalise met behulp van Amazon Athena en Cube.js

TL:DR;
Skakel na die voltooide dashboard.

Om inligting in te samel wat ons gebruik vlot, vir verwerking - AWS Kinesis Data Brandslang и AWS-gom, vir berging - AWS S3. Deur hierdie bundel te gebruik, kan jy nie net nginx-logboeke stoor nie, maar ook ander gebeurtenisse, sowel as logs van ander dienste. Jy kan sommige dele vervang met soortgelyke dele vir jou stapel, byvoorbeeld, jy kan logs na kinesis direk vanaf nginx skryf, fluentd omseil, of logstash hiervoor gebruik.

Versamel Nginx-logboeke

By verstek lyk Nginx-logboeke so iets:

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

Hulle kan ontleed word, maar dit is baie makliker om die Nginx-konfigurasie reg te stel sodat dit logs in JSON produseer:

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

Om logs te stoor, sal ons S3 gebruik. Dit laat jou toe om logs op een plek te stoor en te ontleed, aangesien Athena direk met data in S3 kan werk. Later in die artikel sal ek jou vertel hoe om logs korrek by te voeg en te verwerk, maar eers benodig ons 'n skoon emmer in S3, waarin niks anders gestoor sal word nie. Dit is die moeite werd om vooraf te oorweeg in watter streek jy jou emmer gaan skep, want Athena is nie in alle streke beskikbaar nie.

Skep 'n stroombaan in die Athena-konsole

Kom ons skep 'n tabel in Athena vir logs. Dit is nodig vir beide skryf en lees as jy van plan is om Kinesis Firehose te gebruik. Maak die Athena-konsole oop en skep 'n tabel:

SQL-tabelskepping

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

Skep Kinesis Firehose Stream

Kinesis Firehose sal die data wat van Nginx na S3 ontvang is in die geselekteerde formaat skryf, en dit in gidse in die JJJJ/MM/DD/HH-formaat verdeel. Dit sal handig te pas kom wanneer jy data lees. Jy kan natuurlik direk vanaf fluentd na S3 skryf, maar in hierdie geval sal jy JSON moet skryf, en dit is ondoeltreffend weens die groot grootte van die lêers. Daarbenewens, wanneer u PrestoDB of Athena gebruik, is JSON die stadigste dataformaat. Maak dus die Kinesis Firehose-konsole oop, klik "Skep afleweringsstroom", kies "direct PUT" in die "aflewering"-veld:

Nginx-loganalise met behulp van Amazon Athena en Cube.js

In die volgende oortjie, kies "Rekord formaat omskakeling" - "Enabled" en kies "Apache ORC" as die opname formaat. Volgens sommige navorsing Owen O'Malley, dit is die optimale formaat vir PrestoDB en Athena. Ons gebruik die tabel wat ons hierbo geskep het as 'n skema. Neem asseblief kennis dat jy enige S3-ligging in kinesis kan spesifiseer; slegs die skema word uit die tabel gebruik. Maar as jy 'n ander S3-ligging spesifiseer, sal jy nie hierdie rekords uit hierdie tabel kan lees nie.

Nginx-loganalise met behulp van Amazon Athena en Cube.js

Ons kies S3 vir berging en die emmer wat ons vroeër geskep het. Aws Glue Crawler, waaroor ek 'n bietjie later sal praat, kan nie met voorvoegsels in 'n S3-emmer werk nie, daarom is dit belangrik om dit leeg te laat.

Nginx-loganalise met behulp van Amazon Athena en Cube.js

Die oorblywende opsies kan verander word afhangende van jou vrag; Ek gebruik gewoonlik die verstek. Let daarop dat S3-kompressie nie beskikbaar is nie, maar ORC gebruik by verstek inheemse kompressie.

vlot

Noudat ons die stoor en ontvang van logs gekonfigureer het, moet ons stuur opstel. Ons sal gebruik vlot, want ek is lief vir Ruby, maar jy kan Logstash gebruik of logs direk na kinesis stuur. Die Fluentd-bediener kan op verskeie maniere bekendgestel word, ek sal jou vertel van docker, want dit is eenvoudig en gerieflik.

Eerstens benodig ons die fluent.conf konfigurasielêer. Skep dit en voeg bron by:

tipe vorentoe
hawe 24224
bind 0.0.0.0

Nou kan jy die Fluent-bediener begin. As jy 'n meer gevorderde konfigurasie benodig, gaan na Docker-spilpunt Daar is 'n gedetailleerde gids, insluitend hoe om jou beeld saam te stel.

$ 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

Hierdie konfigurasie gebruik die pad /fluentd/log logs te kas voordat dit gestuur word. Jy kan sonder dit klaarkom, maar wanneer jy dan weer begin, kan jy alles verloor wat met terugbrekende arbeid gekas is. Jy kan ook enige poort gebruik; 24224 is die verstek Fluentd-poort.

Noudat ons Fluentd aan die gang het, kan ons Nginx-logboeke daarheen stuur. Ons gebruik Nginx gewoonlik in 'n Docker-houer, in welke geval Docker 'n inheemse aantekenbestuurder vir Fluentd het:

$ 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

As jy Nginx anders bestuur, kan jy loglêers gebruik, het Fluentd lêerstert-inprop.

Kom ons voeg die log-ontleding wat hierbo gekonfigureer is by die Fluent-konfigurasie:

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

En stuur logs na Kinesis met behulp van kinesis brandslang-inprop:

<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

As jy alles korrek gekonfigureer het, moet jy na 'n rukkie (by verstek, Kinesis rekords ontvangde data een keer elke 10 minute) loglêers in S3 sien. In die "monitoring"-kieslys van Kinesis Firehose kan jy sien hoeveel data in S3 aangeteken is, sowel as foute. Moenie vergeet om skryftoegang tot die S3-emmer aan die Kinesis-rol te gee nie. As Kinesis iets nie kon ontleed nie, sal dit die foute by dieselfde emmer voeg.

Nou kan jy die data in Athena sien. Kom ons vind die jongste versoeke waarvoor ons foute teruggestuur het:

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

Skandeer alle rekords vir elke versoek

Nou is ons logs verwerk en in S3 in ORC gestoor, saamgepers en gereed vir ontleding. Kinesis Firehose het hulle selfs vir elke uur in gidse georganiseer. Solank die tabel egter nie gepartisioneer is nie, sal Athena alle tye data op elke versoek laai, met seldsame uitsonderings. Dit is 'n groot probleem om twee redes:

  • Die volume data groei voortdurend, wat navrae vertraag;
  • Athena word gefaktureer op grond van die volume data wat geskandeer is, met 'n minimum van 10 MB per versoek.

Om dit reg te stel, gebruik ons ​​AWS Glue Crawler, wat die data in S3 sal deurkruip en die partisie-inligting na die Glue Metastore sal skryf. Dit sal ons toelaat om partisies as 'n filter te gebruik wanneer ons Athena navraag doen, en dit sal slegs die dopgehou skandeer wat in die navraag gespesifiseer is.

Die opstel van Amazon Glue Crawler

Amazon Glue Crawler skandeer al die data in die S3-emmer en skep tabelle met partisies. Skep 'n Glue Crawler vanaf die AWS Glue-konsole en voeg 'n emmer by waar jy die data stoor. Jy kan een deurkruiper vir verskeie emmers gebruik, in welke geval dit tabelle in die gespesifiseerde databasis sal skep met name wat ooreenstem met die name van die emmers. As jy van plan is om hierdie data gereeld te gebruik, maak seker dat jy Crawler se bekendstellingskedule instel om by jou behoeftes te pas. Ons gebruik een Crawler vir alle tafels, wat elke uur loop.

Verdeelde tafels

Na die eerste bekendstelling van die deurkruiser, moet tabelle vir elke geskandeerde emmer in die databasis verskyn wat in die instellings gespesifiseer is. Maak die Athena-konsole oop en vind die tabel met Nginx-logs. Kom ons probeer iets lees:

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

Hierdie navraag sal alle rekords wat tussen 6:7 en 8:2019 op XNUMX April XNUMX ontvang is, kies. Maar hoeveel meer doeltreffend is dit as om net van 'n nie-gepartisioneerde tabel af te lees? Kom ons vind uit en kies dieselfde rekords, filter hulle volgens tydstempel:

Nginx-loganalise met behulp van Amazon Athena en Cube.js

3.59 sekondes en 244.34 megagrepe se data op 'n datastel met slegs 'n week se logs. Kom ons probeer 'n filter volgens partisie:

Nginx-loganalise met behulp van Amazon Athena en Cube.js

'n Bietjie vinniger, maar die belangrikste - slegs 1.23 megagrepe se data! Dit sou baie goedkoper wees as nie vir die minimum 10 megagrepe per versoek in die prysbepaling nie. Maar dit is steeds baie beter, en op groot datastelle sal die verskil baie meer indrukwekkend wees.

Bou 'n dashboard met Cube.js

Om die dashboard saam te stel, gebruik ons ​​die Cube.js analitiese raamwerk. Dit het nogal baie funksies, maar ons is geïnteresseerd in twee: die vermoë om partisiefilters outomaties te gebruik en data vooraf-aggregasie. Dit gebruik dataskema data skema, geskryf in Javascript om SQL te genereer en 'n databasisnavraag uit te voer. Ons hoef slegs aan te dui hoe om die partisiefilter in die dataskema te gebruik.

Kom ons skep 'n nuwe Cube.js-toepassing. Aangesien ons reeds die AWS-stapel gebruik, is dit logies om Lambda vir ontplooiing te gebruik. Jy kan die uitdruklike sjabloon vir generasie gebruik as jy van plan is om die Cube.js-agterkant in Heroku of Docker te huisves. Die dokumentasie beskryf ander gasheermetodes.

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

Omgewingsveranderlikes word gebruik om databasistoegang in cube.js op te stel. Die kragopwekker sal 'n .env-lêer skep waarin jy jou sleutels kan spesifiseer Athena.

Nou het ons nodig data skema, waarin ons presies sal aandui hoe ons logs gestoor word. Daar kan jy ook spesifiseer hoe om maatstawwe vir dashboards te bereken.

In die gids schema, skep 'n lêer Logs.js. Hier is 'n voorbeeld datamodel vir nginx:

Model kode

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

Hier gebruik ons ​​die veranderlike FILTER_PARAMSom 'n SQL-navraag met 'n partisiefilter te genereer.

Ons stel ook die maatstawwe en parameters in wat ons op die dashboard wil vertoon en spesifiseer voorafsamevoegings. Cube.js sal bykomende tabelle skep met vooraf saamgestelde data en sal die data outomaties bywerk soos dit aankom. Dit versnel nie net navrae nie, maar verminder ook die koste van die gebruik van Athena.

Kom ons voeg hierdie inligting by die data skema lêer:

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

Ons spesifiseer in hierdie model dat dit nodig is om data vooraf saam te stel vir alle maatstawwe wat gebruik word, en verdeling per maand te gebruik. Pre-aggregasie partisionering kan data-insameling en -opdatering aansienlik bespoedig.

Nou kan ons die paneelbord saamstel!

Cube.js backend verskaf REST API en 'n stel kliëntbiblioteke vir gewilde front-end raamwerke. Ons sal die React-weergawe van die kliënt gebruik om die dashboard te bou. Cube.js verskaf net data, so ons sal 'n visualiseringsbiblioteek nodig hê - ek hou daarvan herkaarte, maar jy kan enige gebruik.

Die Cube.js-bediener aanvaar die versoek in JSON-formaat, wat die vereiste maatstawwe spesifiseer. Byvoorbeeld, om te bereken hoeveel foute Nginx per dag gegee het, moet jy die volgende versoek stuur:

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

Kom ons installeer die Cube.js-kliënt en die React-komponentbiblioteek via NPM:

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

Ons voer komponente in cubejs и QueryRendererom die data af te laai en die dashboard te versamel:

Dashboard kode

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

Dashboardbronne is beskikbaar by KodeSandbox.

Bron: will.com

Voeg 'n opmerking