Dadansoddeg log Nginx gan ddefnyddio Amazon Athena a Cube.js

Yn nodweddiadol, defnyddir cynhyrchion masnachol neu ddewisiadau amgen parod parod, megis Prometheus + Grafana, i fonitro a dadansoddi gweithrediad Nginx. Mae hwn yn opsiwn da ar gyfer monitro neu ddadansoddeg amser real, ond nid yw'n gyfleus iawn ar gyfer dadansoddiad hanesyddol. Ar unrhyw adnodd poblogaidd, mae cyfaint y data o logiau nginx yn tyfu'n gyflym, ac i ddadansoddi llawer iawn o ddata, mae'n rhesymegol defnyddio rhywbeth mwy arbenigol.

Yn yr erthygl hon byddaf yn dweud wrthych sut y gallwch ei ddefnyddio Athena i ddadansoddi logiau, gan gymryd Nginx fel enghraifft, a byddaf yn dangos sut i gydosod dangosfwrdd dadansoddol o'r data hwn gan ddefnyddio'r fframwaith ffynhonnell agored ciwb.js. Dyma bensaernïaeth datrysiad cyflawn:

Dadansoddeg log Nginx gan ddefnyddio Amazon Athena a Cube.js

TL:DR;
Dolen i'r dangosfwrdd gorffenedig.

I gasglu gwybodaeth a ddefnyddiwn rhugl, ar gyfer prosesu - Hose Tân Data Kinesis AWS и Glud AWS, ar gyfer storio - Strategaeth Cymru Gyfan S3. Gan ddefnyddio'r bwndel hwn, gallwch storio nid yn unig logiau nginx, ond hefyd digwyddiadau eraill, yn ogystal â logiau o wasanaethau eraill. Gallwch chi ddisodli rhai rhannau â rhai tebyg ar gyfer eich pentwr, er enghraifft, gallwch chi ysgrifennu logiau i kinesis yn uniongyrchol o nginx, osgoi fluentd, neu ddefnyddio logstash ar gyfer hyn.

Casglu logiau Nginx

Yn ddiofyn, mae logiau Nginx yn edrych rhywbeth fel hyn:

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

Gellir eu dosrannu, ond mae'n llawer haws cywiro cyfluniad Nginx fel ei fod yn cynhyrchu logiau yn 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 ar gyfer storio

I storio logiau, byddwn yn defnyddio S3. Mae hyn yn caniatáu ichi storio a dadansoddi logiau mewn un lle, oherwydd gall Athena weithio gyda data yn S3 yn uniongyrchol. Yn ddiweddarach yn yr erthygl byddaf yn dweud wrthych sut i ychwanegu a phrosesu logiau yn gywir, ond yn gyntaf mae angen bwced glân yn S3, lle na fydd unrhyw beth arall yn cael ei storio. Mae'n werth ystyried ymlaen llaw ym mha ranbarth y byddwch chi'n creu eich bwced, oherwydd nid yw Athena ar gael ym mhob rhanbarth.

Creu cylched yn y consol Athena

Gadewch i ni greu tabl yn Athena ar gyfer boncyffion. Mae ei angen ar gyfer ysgrifennu a darllen os ydych chi'n bwriadu defnyddio Kinesis Firehose. Agorwch y consol Athena a chreu bwrdd:

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

Creu Kinesis Firehose Stream

Bydd Kinesis Firehose yn ysgrifennu'r data a dderbyniwyd gan Nginx i S3 yn y fformat a ddewiswyd, gan ei rannu'n gyfeiriaduron yn y fformat BBBB / MM / DD / HH. Bydd hyn yn ddefnyddiol wrth ddarllen data. Gallwch, wrth gwrs, ysgrifennu'n uniongyrchol i S3 o fluentd, ond yn yr achos hwn bydd yn rhaid i chi ysgrifennu JSON, ac mae hyn yn aneffeithlon oherwydd maint mawr y ffeiliau. Yn ogystal, wrth ddefnyddio PrestoDB neu Athena, JSON yw'r fformat data arafaf. Felly agorwch y consol Kinesis Firehose, cliciwch “Creu ffrwd ddosbarthu”, dewiswch “PUT uniongyrchol” yn y maes “cyflenwi”:

Dadansoddeg log Nginx gan ddefnyddio Amazon Athena a Cube.js

Yn y tab nesaf, dewiswch "Trosi fformat Cofnod" - "Galluogi" a dewiswch "Apache ORC" fel y fformat recordio. Yn ôl peth ymchwil Owen O'Malley, dyma'r fformat gorau posibl ar gyfer PrestoDB ac Athena. Rydym yn defnyddio'r tabl a grëwyd gennym uchod fel sgema. Sylwch y gallwch chi nodi unrhyw leoliad S3 mewn cinesis; dim ond y sgema sy'n cael ei ddefnyddio o'r tabl. Ond os byddwch yn nodi lleoliad S3 gwahanol, yna ni fyddwch yn gallu darllen y cofnodion hyn o'r tabl hwn.

Dadansoddeg log Nginx gan ddefnyddio Amazon Athena a Cube.js

Rydym yn dewis S3 ar gyfer storio a'r bwced a grëwyd gennym yn gynharach. Ni all Aws Glue Crawler, y byddaf yn siarad amdano ychydig yn ddiweddarach, weithio gyda rhagddodiaid mewn bwced S3, felly mae'n bwysig ei adael yn wag.

Dadansoddeg log Nginx gan ddefnyddio Amazon Athena a Cube.js

Gellir newid yr opsiynau sy'n weddill yn dibynnu ar eich llwyth; Fel arfer rwy'n defnyddio'r rhai diofyn. Sylwch nad yw cywasgu S3 ar gael, ond mae ORC yn defnyddio cywasgu brodorol yn ddiofyn.

rhugl

Nawr ein bod wedi ffurfweddu storio a derbyn logiau, mae angen i ni ffurfweddu anfon. Byddwn yn defnyddio rhugl, oherwydd fy mod yn caru Ruby, ond gallwch ddefnyddio Logstash neu anfon logiau i kinesis yn uniongyrchol. Gellir lansio'r gweinydd Rhugl mewn sawl ffordd, byddaf yn dweud wrthych am docwr oherwydd ei fod yn syml ac yn gyfleus.

Yn gyntaf, mae angen y ffeil ffurfweddu fluent.conf. Creu ac ychwanegu ffynhonnell:

math ymlaen
porthladd 24224
rhwym 0.0.0.0

Nawr gallwch chi gychwyn y gweinydd Fluentd. Os oes angen cyfluniad mwy datblygedig arnoch, ewch i Hwb dociwr Mae yna ganllaw manwl, gan gynnwys sut i gydosod eich delwedd.

$ 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

Mae'r cyfluniad hwn yn defnyddio'r llwybr /fluentd/log i storio logiau cyn eu hanfon. Gallwch chi wneud heb hyn, ond yna pan fyddwch chi'n ailgychwyn, gallwch chi golli popeth sydd wedi'i storio â llafur sy'n torri'n ôl. Gallwch hefyd ddefnyddio unrhyw borthladd; 24224 yw'r porthladd Fluentd rhagosodedig.

Nawr bod gennym ni Fluentd yn rhedeg, gallwn anfon logiau Nginx yno. Rydym fel arfer yn rhedeg Nginx mewn cynhwysydd Docker, ac os felly mae gan Docker yrrwr logio brodorol ar gyfer 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

Os ydych chi'n rhedeg Nginx yn wahanol, gallwch chi ddefnyddio ffeiliau log, mae gan Fluentd ategyn cynffon ffeil.

Gadewch i ni ychwanegu'r dosrannu log sydd wedi'i ffurfweddu uchod i'r cyfluniad Rhugl:

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

Ac anfon logiau i Kinesis gan ddefnyddio ategyn firehose kinesis:

<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

Os ydych chi wedi ffurfweddu popeth yn gywir, yna ar ôl ychydig (yn ddiofyn, mae Kinesis yn cofnodi data a dderbyniwyd unwaith bob 10 munud) dylech weld ffeiliau log yn S3. Yn newislen “monitro” Kinesis Firehose gallwch weld faint o ddata sy'n cael ei gofnodi yn S3, yn ogystal â gwallau. Peidiwch ag anghofio rhoi mynediad ysgrifenedig i fwced S3 i rôl Kinesis. Os na allai Kinesis ddosrannu rhywbeth, bydd yn ychwanegu'r gwallau at yr un bwced.

Nawr gallwch chi weld y data yn Athena. Dewch i ni ddod o hyd i'r ceisiadau diweddaraf y gwnaethom ddychwelyd gwallau ar eu cyfer:

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

Sganio pob cofnod ar gyfer pob cais

Nawr mae ein logiau wedi'u prosesu a'u storio yn S3 yn ORC, wedi'u cywasgu ac yn barod i'w dadansoddi. Fe wnaeth Kinesis Firehose hyd yn oed eu trefnu'n gyfeiriaduron am bob awr. Fodd bynnag, cyn belled nad yw'r tabl wedi'i rannu, bydd Athena yn llwytho data amser llawn ar bob cais, gydag eithriadau prin. Mae hon yn broblem fawr am ddau reswm:

  • Mae maint y data yn cynyddu'n gyson, gan arafu ymholiadau;
  • Mae Athena yn cael ei bilio ar sail swm y data a sganiwyd, gydag o leiaf 10 MB fesul cais.

I drwsio hyn, rydym yn defnyddio AWS Glue Crawler, a fydd yn cropian y data yn S3 ac yn ysgrifennu'r wybodaeth rhaniad i'r Glue Metastore. Bydd hyn yn caniatáu i ni ddefnyddio rhaniadau fel hidlydd wrth ymholi Athena, a bydd ond yn sganio'r cyfeiriaduron a nodir yn yr ymholiad.

Sefydlu Amazon Glue Crawler

Mae Amazon Glue Crawler yn sganio'r holl ddata yn y bwced S3 ac yn creu tablau gyda rhaniadau. Creu Crawler Glud o'r consol Glud AWS ac ychwanegu bwced lle rydych chi'n storio'r data. Gallwch ddefnyddio un ymlusgo ar gyfer sawl bwced, ac os felly bydd yn creu tablau yn y gronfa ddata benodol gydag enwau sy'n cyfateb i enwau'r bwcedi. Os ydych chi'n bwriadu defnyddio'r data hwn yn rheolaidd, gwnewch yn siŵr eich bod chi'n ffurfweddu amserlen lansio Crawler i weddu i'ch anghenion. Rydyn ni'n defnyddio un Crawler ar gyfer pob bwrdd, sy'n rhedeg bob awr.

Byrddau rhanedig

Ar ôl lansiad cyntaf y crawler, dylai tablau ar gyfer pob bwced wedi'i sganio ymddangos yn y gronfa ddata a nodir yn y gosodiadau. Agorwch y consol Athena a dewch o hyd i'r bwrdd gyda logiau Nginx. Gadewch i ni geisio darllen rhywbeth:

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

Bydd yr ymholiad hwn yn dewis yr holl gofnodion a dderbyniwyd rhwng 6 a.m. a 7 am ar Ebrill 8, 2019. Ond faint yn fwy effeithlon yw hyn na darllen o dabl heb raniad yn unig? Dewch i ni ddarganfod a dewis yr un cofnodion, gan eu hidlo yn ôl y stamp amser:

Dadansoddeg log Nginx gan ddefnyddio Amazon Athena a Cube.js

3.59 eiliad a 244.34 megabeit o ddata ar set ddata gyda dim ond wythnos o logiau. Gadewch i ni roi cynnig ar hidlydd yn ôl rhaniad:

Dadansoddeg log Nginx gan ddefnyddio Amazon Athena a Cube.js

Ychydig yn gyflymach, ond yn bwysicaf oll - dim ond 1.23 megabeit o ddata! Byddai'n llawer rhatach os nad am yr isafswm o 10 megabeit fesul cais yn y prisiau. Ond mae'n llawer gwell o hyd, ac ar setiau data mawr bydd y gwahaniaeth yn llawer mwy trawiadol.

Adeiladu dangosfwrdd gan ddefnyddio Cube.js

I gydosod y dangosfwrdd, rydym yn defnyddio fframwaith dadansoddol Cube.js. Mae ganddo lawer iawn o swyddogaethau, ond mae gennym ddiddordeb mewn dau: y gallu i ddefnyddio hidlwyr rhaniad yn awtomatig a rhag-gasglu data. Mae'n defnyddio sgema data sgema data, wedi'i ysgrifennu mewn Javascript i gynhyrchu SQL a gweithredu ymholiad cronfa ddata. Nid oes ond angen i ni nodi sut i ddefnyddio'r hidlydd rhaniad yn y sgema data.

Gadewch i ni greu cymhwysiad Cube.js newydd. Gan ein bod eisoes yn defnyddio pentwr AWS, mae'n rhesymegol defnyddio Lambda i'w ddefnyddio. Gallwch ddefnyddio'r templed cyflym ar gyfer cynhyrchu os ydych yn bwriadu cynnal yr backend Cube.js yn Heroku neu Docker. Mae'r ddogfennaeth yn disgrifio eraill dulliau cynnal.

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

Defnyddir newidynnau amgylchedd i ffurfweddu mynediad cronfa ddata yn cube.js. Bydd y generadur yn creu ffeil .env y gallwch chi nodi'ch allweddi ar ei chyfer Athena.

Nawr mae angen sgema data, lle byddwn yn nodi'n union sut mae ein logiau'n cael eu storio. Yno, gallwch hefyd nodi sut i gyfrifo metrigau ar gyfer dangosfyrddau.

Yn y cyfeiriadur schema, creu ffeil Logs.js. Dyma enghraifft o fodel data ar gyfer nginx:

Cod model

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

Yma rydym yn defnyddio'r newidyn FILTER_PARAMSi gynhyrchu ymholiad SQL gyda hidlydd rhaniad.

Rydym hefyd yn gosod y metrigau a'r paramedrau yr ydym am eu harddangos ar y dangosfwrdd ac yn nodi rhag-gasglu. Bydd Cube.js yn creu tablau ychwanegol gyda data wedi'i gydgrynhoi ymlaen llaw a bydd yn diweddaru'r data yn awtomatig wrth iddo gyrraedd. Mae hyn nid yn unig yn cyflymu ymholiadau, ond hefyd yn lleihau cost defnyddio Athena.

Gadewch i ni ychwanegu'r wybodaeth hon at y ffeil sgema data:

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

Rydym yn nodi yn y model hwn bod angen rhag-gyfuno data ar gyfer yr holl fetrigau a ddefnyddir, a defnyddio rhaniad fesul mis. Rhaniad cyn-agregu yn gallu cyflymu casglu a diweddaru data yn sylweddol.

Nawr gallwn ni gydosod y dangosfwrdd!

Mae backend Cube.js yn darparu REST API a set o lyfrgelloedd cleientiaid ar gyfer fframweithiau pen blaen poblogaidd. Byddwn yn defnyddio fersiwn React y cleient i adeiladu'r dangosfwrdd. Dim ond data y mae Cube.js yn ei ddarparu, felly bydd angen llyfrgell ddelweddu arnom - rwy'n ei hoffi rechars, ond gallwch ddefnyddio unrhyw.

Mae gweinydd Cube.js yn derbyn y cais i mewn Fformat JSON, sy'n nodi'r metrigau gofynnol. Er enghraifft, i gyfrifo faint o wallau a roddodd Nginx yn ystod y dydd, mae angen i chi anfon y cais canlynol:

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

Gadewch i ni osod y cleient Cube.js a'r llyfrgell cydrannau React trwy NPM:

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

Rydym yn mewnforio cydrannau cubejs и QueryRendereri lawrlwytho'r data, a chasglu'r dangosfwrdd:

Cod dangosfwrdd

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

Mae ffynonellau dangosfwrdd ar gael yn blwch tywod cod.

Ffynhonnell: hab.com

Ychwanegu sylw