Nginx log analytics lè l sèvi avèk Amazon Athena ak Cube.js

Tipikman, pwodwi komèsyal oswa altènativ louvri-sous pare, tankou Prometheus + Grafana, yo itilize pou kontwole ak analize operasyon an nan Nginx. Sa a se yon bon opsyon pou siveyans oswa analiz an tan reyèl, men li pa trè pratik pou analiz istorik. Sou nenpòt resous popilè, volim done ki soti nan mòso bwa nginx ap grandi rapidman, epi analize yon gwo kantite done, li lojik pou itilize yon bagay ki pi espesyalize.

Nan atik sa a mwen pral di w ki jan ou ka itilize Athena analize mòso bwa, pran Nginx kòm yon egzanp, epi mwen pral montre ki jan yo rasanble yon tablodbò analyse soti nan done sa yo lè l sèvi avèk fondasyon open-source cube.js. Isit la se achitekti solisyon konplè a:

Nginx log analytics lè l sèvi avèk Amazon Athena ak Cube.js

TL:DR;
Link nan tablodbò a fini.

Pou kolekte enfòmasyon nou itilize fluentd, pou trete - AWS Kinesis Data Firehose и AWS Lakòl, pou depo - AWS S3. Sèvi ak pake sa a, ou ka estoke pa sèlman mòso bwa nginx, men tou, lòt evènman, osi byen ke mòso bwa nan lòt sèvis yo. Ou ka ranplase kèk pati ak pati ki sanble pou chemine ou a, pou egzanp, ou ka ekri mòso bwa nan kinesis dirèkteman nan nginx, kontoune fluentd, oswa itilize logstash pou sa.

Kolekte mòso bwa Nginx

Pa default, mòso bwa Nginx sanble yon bagay tankou sa a:

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

Yo ka analize, men li pi fasil pou korije konfigirasyon Nginx pou li pwodui mòso bwa nan 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 pou depo

Pou estoke mòso bwa, nou pral sèvi ak S3. Sa a pèmèt ou estoke ak analize mòso bwa nan yon sèl kote, depi Athena ka travay ak done nan S3 dirèkteman. Pita nan atik la mwen pral di w ki jan yo kòrèkteman ajoute ak trete mòso bwa, men premye nou bezwen yon bokit pwòp nan S3, nan ki pa gen anyen lòt yo pral estoke. Li vo konsidere davans nan ki rejyon ou pral kreye bokit ou a, paske Athena pa disponib nan tout rejyon yo.

Kreye yon kous nan konsole Athena

Ann kreye yon tab nan Athena pou mòso bwa. Li nesesè pou ekri ak lekti si w gen plan pou itilize Kinesis Firehose. Louvri konsole Athena a epi kreye yon tab:

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

Kreye Kinesis Firehose Stream

Kinesis Firehose pral ekri done yo resevwa nan men Nginx nan S3 nan fòma chwazi a, divize li an repèrtwar nan fòma YYYY/MM/DD/HH. Sa a pral itil lè w ap li done yo. Ou ka, nan kou, ekri dirèkteman nan S3 soti nan fluentd, men nan ka sa a ou pral oblije ekri JSON, ak sa a se rezèvwa akòz gwosè a gwo nan dosye yo. Anplis de sa, lè w ap itilize PrestoDB oswa Athena, JSON se fòma done ki pi dousman. Se konsa, louvri konsole Kinesis Firehose la, klike sou "Kreye kouran livrezon", chwazi "mete dirèk" nan jaden an "livrezon":

Nginx log analytics lè l sèvi avèk Amazon Athena ak Cube.js

Nan pwochen onglet, chwazi "Dosye fòma konvèsyon" - "Pèmèt" epi chwazi "Apache ORC" kòm fòma anrejistreman an. Dapre kèk rechèch Owen O'Malley, sa a se fòma pi bon pou PrestoDB ak Athena. Nou itilize tablo nou te kreye pi wo a kòm yon chema. Tanpri sonje ke ou ka presize nenpòt kote S3 nan kinesis; se sèlman chema a itilize nan tablo a. Men, si ou presize yon kote S3 diferan, Lè sa a, ou pa pral kapab li dosye sa yo nan tablo sa a.

Nginx log analytics lè l sèvi avèk Amazon Athena ak Cube.js

Nou chwazi S3 pou depo ak bokit la ke nou te kreye pi bonè. Aws Glue Crawler, ke mwen pral pale sou yon ti kras pita, pa ka travay ak prefiks nan yon bokit S3, kidonk li enpòtan pou kite li vid.

Nginx log analytics lè l sèvi avèk Amazon Athena ak Cube.js

Opsyon ki rete yo ka chanje depann sou chaj ou a; anjeneral mwen itilize sa yo default. Remake byen ke konpresyon S3 pa disponib, men ORC itilize konpresyon natif natal pa default.

fluentd

Kounye a ke nou te configuré estoke ak resevwa mòso bwa, nou bezwen konfigirasyon voye. Nou pral itilize fluentd, paske mwen renmen Ruby, men ou ka itilize Logstash oswa voye mòso bwa nan kinesis dirèkteman. Sèvè Fluentd la ka lanse nan plizyè fason, mwen pral di w sou docker paske li senp ak pratik.

Premyèman, nou bezwen fichye konfigirasyon fluent.conf la. Kreye li epi ajoute sous:

kalite pou pi devan
pò 24224
mare 0.0.0.0

Koulye a, ou ka kòmanse sèvè Fluentd la. Si ou bezwen yon konfigirasyon ki pi avanse, ale nan Hub Docker Gen yon gid detaye, ki gen ladan ki jan yo rasanble imaj ou.

$ 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

Konfigirasyon sa a sèvi ak chemen an /fluentd/log nan kachèt mòso bwa anvan yo voye. Ou ka fè san sa a, men Lè sa a, lè ou rekòmanse, ou ka pèdi tout bagay kachèt ak travay tounen-kraze. Ou ka itilize nenpòt pò tou; 24224 se pò Fluentd default la.

Kounye a ke nou gen Fluentd kouri, nou ka voye mòso bwa Nginx la. Nou anjeneral kouri Nginx nan yon veso Docker, nan ka sa a Docker gen yon chofè natif natal pou 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

Si ou kouri Nginx yon fason diferan, ou ka itilize dosye log, Fluentd genyen dosye ke plugin.

Ann ajoute parsaj boutèy demi lit ki configuré pi wo a nan konfigirasyon Fluent la:

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

Epi voye mòso bwa nan Kinesis lè l sèvi avèk 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>

Athena

Si ou te configuré tout bagay kòrèkteman, Lè sa a, apre yon ti tan (pa default, Kinesis dosye resevwa done yon fwa chak 10 minit) ou ta dwe wè dosye log nan S3. Nan meni "siveyans" Kinesis Firehose ou ka wè konbyen done ki anrejistre nan S3, osi byen ke erè. Pa bliye bay aksè ekri nan bokit S3 a nan wòl Kinesis la. Si Kinesis pa t 'kapab analize yon bagay, li pral ajoute erè yo nan menm bokit la.

Koulye a, ou ka wè done yo nan Athena. Ann jwenn dènye demann nou te retounen erè yo:

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

Analize tout dosye pou chak demann

Koulye a, mòso bwa nou yo te trete ak estoke nan S3 nan ORC, konprese ak pare pou analiz. Kinesis Firehose menm òganize yo nan anyè pou chak èdtan. Sepandan, osi lontan ke tab la pa divize, Athena pral chaje done tout tan sou chak demann, ak eksepsyon ki ra. Sa a se yon gwo pwoblèm pou de rezon:

  • Volim done yo toujou ap grandi, ralanti demann yo;
  • Athena faktire dapre volim done yo analize, ak yon minimòm de 10 MB pou chak demann.

Pou repare sa a, nou itilize AWS Glue Crawler, ki pral rale done yo nan S3 epi ekri enfòmasyon patisyon yo nan Glue Metastore la. Sa a pral pèmèt nou sèvi ak patisyon kòm yon filtè lè w ap chèche Athena, epi li pral sèlman eskane repèrtwar ki espesifye nan rechèch la.

Mete kanpe Amazon Glue Crawler

Amazon Glue Crawler analize tout done ki nan bokit S3 la epi li kreye tab ak patisyon yo. Kreye yon Glue Crawler nan konsole AWS Glue a epi ajoute yon bokit kote ou estoke done yo. Ou ka itilize yon sèl krole pou plizyè bokit, nan ka sa a li pral kreye tab nan baz done espesifye ak non ki matche ak non bokit yo. Si w gen plan pou itilize done sa yo regilyèman, asire w ke w configured orè lansman Crawler a pou adapte w ak bezwen w yo. Nou itilize yon sèl Crawler pou tout tab, ki kouri chak èdtan.

Tablo divize

Apre premye lansman krole a, tab pou chak bokit tcheke ta dwe parèt nan baz done ki espesifye nan paramèt yo. Louvri konsole Athena a epi jwenn tab la ak mòso bwa Nginx. Ann eseye li yon bagay:

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

Rekèt sa a pral chwazi tout dosye yo resevwa ant 6 a.m. ak 7 a.m. nan dat 8 avril 2019. Men, konbyen pi efikas sa a pase jis lekti nan yon tab ki pa patisyon? Ann chèche konnen epi chwazi menm dosye yo, filtre yo pa timestamp:

Nginx log analytics lè l sèvi avèk Amazon Athena ak Cube.js

3.59 segonn ak 244.34 megabyte done sou yon seri done ak sèlman yon semèn nan mòso bwa. Ann eseye yon filtè pa patisyon:

Nginx log analytics lè l sèvi avèk Amazon Athena ak Cube.js

Yon ti kras pi vit, men sa ki pi enpòtan - sèlman 1.23 megabyte nan done! Li ta pi bon mache si se pa pou minimòm 10 megabytes pou chak demann nan pri a. Men, li toujou pi bon, ak sou gwo datasets diferans lan pral pi enpresyonan.

Bati yon tablodbò lè l sèvi avèk Cube.js

Pou rasanble tablodbò a, nou itilize fondasyon analitik Cube.js. Li gen yon anpil nan fonksyon, men nou enterese nan de: kapasite nan otomatikman itilize filtè patisyon ak done pre-agregasyon. Li itilize schéma done schema done, ekri an Javascript pou jenere SQL epi egzekite yon rechèch baz done. Nou sèlman bezwen endike kijan pou itilize filtè patisyon an nan chema done a.

Ann kreye yon nouvo aplikasyon Cube.js. Piske nou deja itilize pile AWS a, li lojik pou nou itilize Lambda pou deplwaman. Ou ka itilize modèl eksprime pou jenerasyon si w gen plan pou òganize backend Cube.js nan Heroku oswa Docker. Dokiman an dekri lòt moun metòd hosting.

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

Varyab anviwònman yo itilize pou konfigirasyon aksè baz done nan cube.js. Dèlko a pral kreye yon fichye .env kote ou ka presize kle ou pou Athena.

Kounye a nou bezwen schema done, nan ki nou pral endike egzakteman ki jan mòso bwa nou yo estoke. La ou ka presize tou kijan pou kalkile mezi pou tablodbò yo.

Nan anyè schema, kreye yon dosye Logs.js. Men yon egzanp modèl done pou nginx:

Kòd modèl

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

Isit la nou ap itilize varyab la FILTER_PARAMSjenere yon rechèch SQL ak yon filtè patisyon.

Nou menm tou nou mete mezi ak paramèt ke nou vle montre sou tablodbò a epi presize pre-agrégasyon. Cube.js pral kreye tab adisyonèl ak done pre-agrége epi yo pral otomatikman mete ajou done yo jan li rive. Sa a pa sèlman akselere demann, men tou, diminye pri pou itilize Athena.

Ann ajoute enfòmasyon sa yo nan dosye a chema done:

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

Nou presize nan modèl sa a ke li nesesè pre-agrégation done pou tout mezi yo itilize, epi itilize patisyon pa mwa. Pre-agregasyon patisyon ka siyifikativman akselere koleksyon done ak ajou.

Koulye a, nou ka rasanble tablodbò a!

Backend Cube.js bay REST API ak yon seri bibliyotèk kliyan pou kad devan-end popilè. Nou pral sèvi ak vèsyon React kliyan an pou konstwi tablodbò a. Cube.js sèlman bay done, kidonk nou pral bezwen yon bibliyotèk vizyalizasyon - mwen renmen li recharts, men ou ka itilize nenpòt.

Sèvè Cube.js la aksepte demann lan nan fòma JSON, ki presize mezi yo mande yo. Pou egzanp, pou kalkile konbyen erè Nginx te bay pa jou, ou bezwen voye demann sa a:

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

Ann enstale kliyan Cube.js la ak bibliyotèk eleman React atravè NPM:

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

Nou enpòte konpozan cubejs и QueryRendererpou telechaje done yo, epi kolekte tablodbò a:

Kòd tablodbò

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

Sous tablodbò yo disponib nan kòd sandbox.

Sous: www.habr.com

Add nouvo kòmantè