Analiza e regjistrave Nginx duke përdorur Amazon Athena dhe Cube.js

Në mënyrë tipike, produktet komerciale ose alternativat e gatshme me burim të hapur, si Prometheus + Grafana, përdoren për të monitoruar dhe analizuar funksionimin e Nginx. Ky është një opsion i mirë për monitorim ose analiza në kohë reale, por jo shumë i përshtatshëm për analiza historike. Në çdo burim popullor, vëllimi i të dhënave nga regjistrat nginx po rritet me shpejtësi, dhe për të analizuar një sasi të madhe të dhënash, është logjike të përdoret diçka më e specializuar.

Në këtë artikull do t'ju tregoj se si mund ta përdorni Athinë për të analizuar regjistrat, duke marrë si shembull Nginx, dhe unë do të tregoj se si të mbledh një tabelë analitike nga këto të dhëna duke përdorur kornizën me burim të hapur cube.js. Këtu është arkitektura e plotë e zgjidhjes:

Analiza e regjistrave Nginx duke përdorur Amazon Athena dhe Cube.js

TL:DR;
Lidhja me pultin e përfunduar.

Për të mbledhur informacion që ne përdorim I rrjedhshëm, për përpunim - AWS Kinesis Data Firehose и Ngjitës AWS, për ruajtje - AWS S3. Duke përdorur këtë paketë, ju mund të ruani jo vetëm regjistrat nginx, por edhe ngjarje të tjera, si dhe regjistrat e shërbimeve të tjera. Ju mund të zëvendësoni disa pjesë me të ngjashme për stivën tuaj, për shembull, mund të shkruani regjistra në kinesis direkt nga nginx, duke anashkaluar fluentd, ose të përdorni logstash për këtë.

Mbledhja e regjistrave Nginx

Si parazgjedhje, regjistrat e Nginx duken diçka si kjo:

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

Ato mund të analizohen, por është shumë më e lehtë të korrigjosh konfigurimin Nginx në mënyrë që të prodhojë regjistrat në 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 për ruajtje

Për të ruajtur regjistrat, ne do të përdorim S3. Kjo ju lejon të ruani dhe analizoni regjistrat në një vend, pasi Athena mund të punojë drejtpërdrejt me të dhënat në S3. Më vonë në artikull do t'ju tregoj se si të shtoni dhe përpunoni saktë regjistrat, por së pari na duhet një kovë e pastër në S3, në të cilën nuk do të ruhet asgjë tjetër. Vlen të merret parasysh paraprakisht se në cilin rajon do të krijoni kovën tuaj, sepse Athena nuk është e disponueshme në të gjitha rajonet.

Krijimi i një qarku në tastierën Athena

Le të krijojmë një tabelë në Athena për shkrimet. Është i nevojshëm si për shkrim ashtu edhe për lexim nëse planifikoni të përdorni Kinesis Firehose. Hapni konsolën Athena dhe krijoni një tabelë:

Krijimi i tabelës 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');

Krijimi i Kinesis Firehose Stream

Kinesis Firehose do të shkruajë të dhënat e marra nga Nginx në S3 në formatin e zgjedhur, duke i ndarë ato në drejtori në formatin YYYY/MM/DD/HH. Kjo do të jetë e dobishme kur lexoni të dhëna. Sigurisht, mund të shkruani direkt në S3 nga fluentd, por në këtë rast do t'ju duhet të shkruani JSON, dhe kjo është joefikase për shkak të madhësisë së madhe të skedarëve. Për më tepër, kur përdorni PrestoDB ose Athena, JSON është formati më i ngadaltë i të dhënave. Pra, hapni tastierën Kinesis Firehose, klikoni "Krijo transmetimin e dorëzimit", zgjidhni "PUT direkt" në fushën "dorëzimi":

Analiza e regjistrave Nginx duke përdorur Amazon Athena dhe Cube.js

Në skedën tjetër, zgjidhni "Konvertimi i formatit të regjistrimit" - "Aktivizuar" dhe zgjidhni "Apache ORC" si formatin e regjistrimit. Sipas disa hulumtimeve Owen O'Malley, ky është formati optimal për PrestoDB dhe Athena. Ne përdorim tabelën që krijuam më sipër si një skemë. Ju lutemi vini re se mund të specifikoni çdo vendndodhje S3 në kinesis; vetëm skema përdoret nga tabela. Por nëse specifikoni një vendndodhje tjetër S3, atëherë nuk do të jeni në gjendje t'i lexoni këto regjistrime nga kjo tabelë.

Analiza e regjistrave Nginx duke përdorur Amazon Athena dhe Cube.js

Ne zgjedhim S3 për ruajtje dhe kovën që krijuam më parë. Aws Glue Crawler, për të cilin do të flas pak më vonë, nuk mund të funksionojë me parashtesa në një kovë S3, kështu që është e rëndësishme ta lini bosh.

Analiza e regjistrave Nginx duke përdorur Amazon Athena dhe Cube.js

Opsionet e mbetura mund të ndryshohen në varësi të ngarkesës suaj; Unë zakonisht përdor ato të paracaktuara. Vini re se kompresimi S3 nuk është i disponueshëm, por ORC përdor si parazgjedhje kompresimin vendas.

I rrjedhshëm

Tani që kemi konfiguruar ruajtjen dhe marrjen e regjistrave, duhet të konfigurojmë dërgimin. ne do të përdorim I rrjedhshëm, sepse unë e dua Ruby, por ju mund të përdorni Logstash ose të dërgoni shkrime në kinesis drejtpërdrejt. Serveri Fluentd mund të lansohet në disa mënyra, unë do t'ju tregoj për docker sepse është i thjeshtë dhe i përshtatshëm.

Së pari, na duhet skedari i konfigurimit fluent.conf. Krijojeni atë dhe shtoni burimin:

lloj përpara
porti 24224
lidh 0.0.0.0

Tani mund të nisni serverin Fluentd. Nëse keni nevojë për një konfigurim më të avancuar, shkoni te Qendër dokeri Ekziston një udhëzues i detajuar, duke përfshirë mënyrën e montimit të imazhit tuaj.

$ 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

Ky konfigurim përdor shtegun /fluentd/log për të ruajtur regjistrat para dërgimit. Ju mund të bëni pa këtë, por më pas kur të rindizni, mund të humbni gjithçka të ruajtur në memorien tuaj me punë të pakëndshme. Ju gjithashtu mund të përdorni çdo port; 24224 është porti i parazgjedhur Fluentd.

Tani që kemi funksionimin e Fluentd, ne mund të dërgojmë regjistrat e Nginx atje. Ne zakonisht e drejtojmë Nginx në një kontejner Docker, në të cilin rast Docker ka një drejtues logimi vendas për 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

Nëse e drejtoni Nginx ndryshe, mund të përdorni skedarë log, ka Fluentd shtojca e skedarit tail.

Le të shtojmë analizën e regjistrit të konfiguruar më lart në konfigurimin Fluent:

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

Dhe dërgimi i regjistrave në Kinesis duke përdorur shtojca 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>

Athinë

Nëse keni konfiguruar gjithçka në mënyrë korrekte, atëherë pas një kohe (si parazgjedhje, Kinesis regjistron të dhënat e marra një herë në 10 minuta) duhet të shihni skedarët e regjistrave në S3. Në menynë e “monitorimit” të Kinesis Firehose mund të shihni se sa të dhëna janë regjistruar në S3, si dhe gabime. Mos harroni të jepni akses shkrimi në kovën S3 në rolin Kinesis. Nëse Kinesis nuk mund të analizojë diçka, ai do të shtojë gabimet në të njëjtën kovë.

Tani mund t'i shikoni të dhënat në Athena. Le të gjejmë kërkesat më të fundit për të cilat kemi kthyer gabime:

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

Skanimi i të gjitha të dhënave për secilën kërkesë

Tani regjistrat tanë janë përpunuar dhe ruajtur në S3 në ORC, të ngjeshur dhe gati për analizë. Kinesis Firehose madje i organizoi ato në drejtori për çdo orë. Megjithatë, për sa kohë që tabela nuk është e ndarë, Athena do të ngarkojë të dhënat e të gjitha kohërave për çdo kërkesë, me përjashtime të rralla. Ky është një problem i madh për dy arsye:

  • Vëllimi i të dhënave po rritet vazhdimisht, duke ngadalësuar pyetjet;
  • Athena faturohet në bazë të vëllimit të të dhënave të skanuara, me një minimum prej 10 MB për kërkesë.

Për ta rregulluar këtë, ne përdorim AWS Glue Crawler, i cili do të zvarritet të dhënat në S3 dhe do të shkruajë informacionin e ndarjes në Glue Metastore. Kjo do të na lejojë të përdorim ndarjet si filtër kur kërkojmë Athena dhe do të skanojmë vetëm drejtoritë e specifikuara në pyetje.

Konfigurimi i Amazon Glue Crawler

Amazon Glue Crawler skanon të gjitha të dhënat në kovën S3 dhe krijon tabela me ndarje. Krijoni një Glue Crawler nga tastiera AWS Glue dhe shtoni një kovë ku ruani të dhënat. Ju mund të përdorni një zvarritës për disa kova, në këtë rast ai do të krijojë tabela në bazën e të dhënave të specifikuar me emra që përputhen me emrat e kovave. Nëse planifikoni t'i përdorni këto të dhëna rregullisht, sigurohuni që të konfiguroni orarin e nisjes së Crawler për t'iu përshtatur nevojave tuaja. Ne përdorim një Crawler për të gjitha tabelat, i cili funksionon çdo orë.

Tavolina të ndara

Pas nisjes së parë të zvarritësit, tabelat për secilën kovë të skanuar duhet të shfaqen në bazën e të dhënave të specifikuar në cilësimet. Hapni konsolën Athena dhe gjeni tabelën me regjistrat Nginx. Le të përpiqemi të lexojmë diçka:

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

Ky pyetje do të zgjedhë të gjitha regjistrimet e marra midis orës 6 të mëngjesit dhe orës 7 të mëngjesit më 8 prill 2019. Por sa më efikase është kjo sesa thjesht leximi nga një tabelë jo e ndarë? Le të zbulojmë dhe zgjedhim të njëjtat regjistrime, duke i filtruar ato sipas vulës kohore:

Analiza e regjistrave Nginx duke përdorur Amazon Athena dhe Cube.js

3.59 sekonda dhe 244.34 megabajt të dhëna në një grup të dhënash me vetëm një javë regjistra. Le të provojmë një filtër sipas ndarjes:

Analiza e regjistrave Nginx duke përdorur Amazon Athena dhe Cube.js

Pak më shpejt, por më e rëndësishmja - vetëm 1.23 megabajt të dhëna! Do të ishte shumë më lirë nëse jo për 10 megabajt minimale për kërkesë në çmim. Por është akoma shumë më mirë, dhe në grupe të dhënash të mëdha ndryshimi do të jetë shumë më mbresëlënës.

Ndërtimi i një pulti duke përdorur Cube.js

Për të mbledhur pultin, ne përdorim kornizën analitike Cube.js. Ka mjaft funksione, por ne jemi të interesuar për dy: aftësinë për të përdorur automatikisht filtrat e ndarjeve dhe grumbullimin paraprak të të dhënave. Ai përdor skemën e të dhënave skema e të dhënave, i shkruar në Javascript për të gjeneruar SQL dhe për të ekzekutuar një pyetje të bazës së të dhënave. Ne vetëm duhet të tregojmë se si të përdorim filtrin e ndarjes në skemën e të dhënave.

Le të krijojmë një aplikacion të ri Cube.js. Meqenëse ne tashmë po përdorim pirgun AWS, është logjike të përdorim Lambda për vendosje. Mund të përdorni shabllonin ekspres për gjenerim nëse planifikoni të strehoni backend-in e Cube.js në Heroku ose Docker. Dokumentacioni përshkruan të tjerët metodat e pritjes.

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

Variablat e mjedisit përdoren për të konfiguruar aksesin në bazën e të dhënave në cube.js. Gjeneratori do të krijojë një skedar .env në të cilin mund të specifikoni çelësat tuaj Athinë.

Tani na duhet skema e të dhënave, në të cilën do të tregojmë saktësisht se si ruhen regjistrat tanë. Atje mund të specifikoni gjithashtu se si të llogaritni metrikat për panelet e kontrollit.

Në drejtori schema, krijoni një skedar Logs.js. Këtu është një shembull modeli i të dhënave për nginx:

Kodi i modelit

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

Këtu po përdorim variablin FILTER_PARAMSpër të gjeneruar një pyetje SQL me një filtër ndarjeje.

Ne gjithashtu vendosim matjet dhe parametrat që duam të shfaqim në panelin e kontrollit dhe specifikojmë grumbullimet paraprake. Cube.js do të krijojë tabela shtesë me të dhëna të grumbulluara paraprakisht dhe do të përditësojë automatikisht të dhënat kur të mbërrijnë. Kjo jo vetëm që shpejton pyetjet, por gjithashtu zvogëlon koston e përdorimit të Athena.

Le të shtojmë këtë informacion në skedarin e skemës së të dhënave:

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

Ne specifikojmë në këtë model se është e nevojshme të grumbullohen paraprakisht të dhënat për të gjitha metrikat e përdorura dhe të përdoren ndarjet sipas muajve. Ndarja para grumbullimit mund të përshpejtojë ndjeshëm mbledhjen dhe përditësimin e të dhënave.

Tani mund të montojmë pultin!

Cube.js backend ofron REST API dhe një grup bibliotekash klientësh për korniza të njohura të front-endit. Ne do të përdorim versionin React të klientit për të ndërtuar panelin e kontrollit. Cube.js ofron vetëm të dhëna, kështu që do të na duhet një bibliotekë vizualizimi - më pëlqen riformulime, por ju mund të përdorni ndonjë.

Serveri Cube.js pranon kërkesën në Formati JSON, i cili specifikon matjet e kërkuara. Për shembull, për të llogaritur sa gabime dha Nginx në ditë, duhet të dërgoni kërkesën e mëposhtme:

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

Le të instalojmë klientin Cube.js dhe bibliotekën e komponentëve React përmes NPM:

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

Ne importojmë komponentë cubejs и QueryRendererpër të shkarkuar të dhënat dhe për të mbledhur pultin:

Kodi i pultit

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

Burimet e panelit të kontrollit janë në dispozicion në sandbox kod.

Burimi: www.habr.com

Shto një koment