Аналитика логов Nginx с помощью Amazon Athena и Cube.js

Zakonisht, për monitorimin dhe analizën e funksionimit të Nginx, përdoren produkte komerciale ose alternativa open-source, siç janë Prometheus + Grafana. Ky është një opsion i mirë për monitoring ose analizë në kohë reale, por nuk është shumë praktik për analizën historike. Në çdo burim të njohur, volumi i të dhënave nga log-et e nginx rritet shpejt, dhe për të analizuar një sasi të madhe të dhënash, është logjikë që të përdoret diçka më të specializuar.

Në këtë artikull, do t'ju tregoj se si mund të përdorim Athena për analizën e log-eve, duke marrë si shembull Nginx, dhe do të tregoj se si mund të ndërtoni një panel analitik nga këto të dhëna, duke përdorur framework-un open-source cube.js. Këtu është arkitektura e plotë e zgjidhjes:

Аналитика логов Nginx с помощью Amazon Athena и Cube.js

TL:DR;
Linku për panelin e gatshëm.

Për të mbledhur informacionin, ne përdorim Fluentd, për procesimin — AWS Kinesis Data Firehose и AWS Glue, për ruajtjen — AWS S3. Me këtë lidhje, mund të ruhen jo vetëm log-et e nginx, por edhe ngjarje të tjera, si dhe log-e të shërbimeve të tjera. Ju mund të zëvendësoni disa pjesë me të ngjashme për stek-in tuaj, për shembull, mund të shkruani log-e direkt në kinesis nga nginx, duke anashkaluar fluentd, ose të përdorni logstash për këtë.

Mbledhim log-et e Nginx

Sipas parazgjedhjes, log-et e Nginx duken kështu:

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

Ata mund të parse-ohen, por është shumë më e lehtë të korrigjosh konfiguracionin e Nginx për t'i dhënë log-et në formatin 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 log-et, do të përdorim S3. Kjo lejon ruajtjen dhe analizimin e log-eve në një vend, pasi Athena mund të punojë me të dhënat në S3 në mënyrë të drejtpërdrejtë. Më vonë në artikull do të tregoj si t'i organizoni dhe procesoni log-et në mënyrë të duhur, por fillimisht na nevojitet një bucket i pastër në S3, në të cilin nuk do të ruhet asgjë tjetër. Është e mençur të mendoni paraprakisht se në cilin rajon do të krijoni bucket-in, sepse Athena nuk është e disponueshme në të gjitha rajonet.

Krijojmë një skemë në konsolën Athena

Do të krijojmë një tabelë në Athena për log-et. Ajo nevojitet si për shkrim, ashtu edhe për lexim, nëse planifikoni të përdorni Kinesis Firehose. Hapni konsolën Athena dhe krijoni tabelën:

SQL për krijimin e tabelës

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://'
TBLPROPERTIES ('has_encrypted_data'='false');

Krijojmë Kinesis Firehose Stream

Kinesis Firehose do të regjistrojë të dhënat e marra nga Nginx në S3 në formatin e zgjedhur, duke i ndarë në direktoriume në formatin YYYY/MM/DD/HH. Kjo do të jetë e dobishme për leximin e të dhënave. Sigurisht, mund të shkruani drejtpërdrejt në S3 nga fluentd, por në këtë rast do të duhej të shkruanit JSON, dhe kjo nuk është efikase për shkak të madhësisë së madhe të skedarëve. Gjithashtu, kur përdorni PrestoDB ose Athena, JSON është formati më i ngadalshëm i të dhënave. Prandaj, hapim konsolën Kinesis Firehose, klikoni "Create delivery stream" dhe zgjidhni "direct PUT" në fushën "delivery":

Аналитика логов Nginx с помощью Amazon Athena и Cube.js

Në skedën tjetër, zgjidhni "Record format conversion" — "Enabled" dhe zgjidhni "Apache ORC" si format për shkrim. Sipas hulumtimeve të disa Owen O’Malley, ky është formati optimal për PrestoDB dhe Athena. Si skemë, tregoni tabelën që krijuam më sipër. Vini re se lokacioni i S3 në kinesis mund të jetë çfarëdo, vetëm skema përdoret nga tabela. Por nëse tregoni një lokacion tjetër S3, atëherë nuk do të mund të lexoni këto regjistrime nga kjo tabelë.

Аналитика логов Nginx с помощью Amazon Athena и Cube.js

Zgjidhni S3 për ruajtje dhe rezervuari që krijuam më parë. Aws Glue Crawler, për të cilin do të flas pak më vonë, nuk di të punojë me prefikset në rezervuarin S3, prandaj është e rëndësishme ta mbani të zbrazët.

Аналитика логов Nginx с помощью Amazon Athena и Cube.js

Opsionet e tjera mund të ndryshojnë në varësi të ngarkesës tuaj, unë zakonisht përdor ato default. Vini re se kompresimi në S3 nuk është i disponueshëm, por ORC përdor kompresimin e vet në mënyrë default.

Fluentd

Tani, kur kemi vendosur ruajtjen dhe marrjen e logëve, duhet të konfiguroni dërgimin. Ne do ta përdorim Fluentd, sepse më pëlqen Ruby, por mund të përdorni Logstash ose të dërgoni logët në kinesis drejtpërdrejt. Serverin fluentd mund ta nisni në disa mënyra, do të flas për docker, sepse është e thjeshtë dhe e përshtatshme.

Së pari, na nevojitet një skedë konfigurimi fluent.conf. Krijoni atë dhe shtoni source:

lloji forward
port 24224
bind 0.0.0.0

Tani tani mund të lanconi serverin Fluentd. Nëse ju nevojitet një konfigurim më i avancuar, ka Docker Hub një udhëzues të detajuar, duke përfshirë edhe si të ndërtoni imazhin tuaj.

$ docker run 
  -d 
  -p 24224:24224 
  -p 24224:24224/udp 
  -v /data:/fluentd/log 
  -v :/fluentd/etc fluentd 
  -c /fluentd/etc/fluent.conf
  fluent/fluentd:stable

Kjo konfigurim përdor rrugën /fluentd/log për ruajtjen e regjistrimeve para dërgimit. Mund të kaloni pa këtë, por atëherë mund të humbni të gjitha të dhënat e ruajtur pas rinisjes. Porti gjithashtu mund të jetë çfarëdo, 24224 është porta standarde e Fluentd.

Tani që kemi një Fluentd të nisur, mund të dërgojmë atje regjistrimet e Nginx. Ne zakonisht e drejtojmë Nginx në një kontejner Docker, dhe në këtë rast Docker ka një driver natyral regjistrimi për Fluentd:

$ docker run 
--log-driver=fluentd 
--log-opt fluentd-address=
--log-opt tag="{{.Name}}" 
-v /some/content:/usr/share/nginx/html:ro 
-d 
nginx

Nëse po e drejtoni Nginx ndryshe, mund të përdorni regjistrat e skedarëve, në Fluentd ka file tail plugin.

Të shtojmë në konfigurimin Fluent analizën e regjistrave, të konfiguruar më parë:

@type parser
  key_name log
  emit_invalid_record_to_error false
  
    @type json

Dhe dërgimin e regjistrave në Kinesis, duke përdorur kinesis firehose plugin:

@type kinesis_firehose
    region region
    delivery_stream_name 
    aws_key_id 
    aws_sec_key

Athena

Nëse keni konfiguruar gjithçka siç duhet, pas disa kohësh (në parazgjedhje Kinesis regjistron të dhënat e marrura çdo 10 minuta) do të duhet të shihni skedarët e regjistrave në S3. Në menunë "monitoring" Kinesis Firehose mund të shihni sa të dhëna janë regjistruar në S3, si edhe gabimet. Mos harroni të jepni qasje për shkruajtje në bllokun S3 për rolin Kinesis. Nëse Kinesis nuk mund të analizonte diçka, ai do të grumbullojë gabimet në të njëjtin bllok.

Tani mund të shohim të dhënat në Athena. Le të gjejmë kërkesat më të reja për të cilat kemi marrë gabime:

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

Skano çdo rekord për çdo kërkesë

Tani regjistrat tanë janë përpunuar dhe janë ruajtur në S3 në ORC, të kompresuar dhe gati për analizë. Kinesis Firehose madje i ka organizuar ato në drejtorinë për çdo orë. Megjithatë, derisa tabela të mos jetë e ndarë në pjesë, Athena do të ngarkojë të dhënat përgjatë gjithë kohës për çdo kërkesë, me disa përjashtime të rralla. Kjo është një problem i madh për dy arsye:

  • Vëllimi i të dhënave rritet vazhdimisht, duke ngadalësuar kërkesat;
  • Kostoja për Athena pezullon mbi vëllimin e të dhënave të skanuara, me një minimum prej 10 MB për çdo kërkesë.

Për të korrigjuar këtë, ne përdorim AWS Glue Crawler, i cili do të skanojë të dhënat në S3 dhe do të regjistrojë informacionin mbi partitë në Glue Metastore. Kjo do të na lejojë të përdorim partitë si një filtër gjatë pyetjeve në Athena, dhe ajo do të skanojë vetëm direktorët e specifikuar në kërkesë.

Konfigurojmë Amazon Glue Crawler

Amazon Glue Crawler skanon të gjitha të dhënat në S3 bucket dhe krijon tabela me partie. Krijoni Glue Crawler nga konsola AWS Glue dhe shtoni bucket-in në të cilin ruani të dhënat. Mund të përdorni një crawler për disa buckets, 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 buckets. Nëse planifikoni të përdorni vazhdimisht këto të dhëna, mos harroni të përcaktoni një orar për ekzekutimin e Crawler sipas nevojave tuaja. Ne përdorim një Crawler për të gjitha tabelat, i cili aktivizohet çdo orë.

Tavolina të particionuara

Pas nisjes së parë të crawler-it, në bazën e të dhënave të specifikuar në cilësime duhet të shfaqen tabelat për çdo bucket të skanuar. Hapni konsolën Athena dhe gjeni tabelën me log-et e Nginx. Le të provojmë 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'
  );

Kjo kërkesë do të zgjedhë të gjitha rekordet që janë marrë nga ora 6 deri në 7 të mëngjesit më 8 prill 2019. Por sa më efikase është kjo krahasuar me leximin nga një tabelë që nuk është e particionuar? Le të zbulojmë dhe të zgjedhim të njëjtat rekorde, duke i filtruar ato sipas timestamp-it:

Аналитика логов Nginx с помощью Amazon Athena и Cube.js

3.59 sekonda dhe 244.34 megabajt të dhënash në një dataset, për të cilin ka vetëm një javë log-esh. Le të provojmë filtrin sipas partive:

Аналитика логов Nginx с помощью Amazon Athena и Cube.js

Pak më shpejt, por më e rëndësishmja — vetëm 1.23 megabajt të dhënash! Do të ishte shumë më lirë, nëse nuk do ishin 10 megabajt minimal për kërkesë në çmim. Por gjithsesi shumë më mirë, dhe në datasetet e mëdha ndryshimi do të ishte shumë më impresionues.

Собираем дэшборд с помощью Cube.js

Për të ndërtuar një dashboard, ne përdorim kuadrin analitik Cube.js. Ai ka shumë funksione, por na interesojnë dy: mundësia për të përdorur automatikisht filtrat sipas partive dhe para-agregimin e të dhënave. Ai përdor një skemë të dhënash data schema, e shkruar në Javascript, për të gjeneruar SQL dhe për të ekzekutuar kërkesën në bazën e të dhënave. Nga ne kërkohet vetëm të tregojmë se si të përdorim filtrin sipas partive në skemën e të dhënave.

Krijojmë një aplikacion të ri Cube.js. Duke qenë se ne tashmë po përdorim një stak AWS, është e arsyeshme të përdorim Lambda për deployment. Ju mund të përdorni modelin express për të gjeneruar, nëse planifikoni të hostoni backend-in e Cube.js në Heroku ose Docker. Dokumentacioni përshkruan mënyra të tjera të hostimit.

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

Për të konfiguruar aksesin në databazën në cube.js, përdoren variablat mjedisore. Generatori do të krijojë një skedar .env, ku mund të specifikoni çelësat tuaj për Athena.

Tani na nevojitet skema e të dhënave, ku do të specifikojmë se si ruhen log-et tona. Po ashtu, mund të përshkruajmë si të llogarisim metrikat për dashboard-et.

Në drejtorinë schema, krijoni një skedar Logs.js. Ja një shembull i modelit të 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')
    `

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 variablën FILTER_PARAMS, për të gjeneruar një SQL kërkesë me filtrin mbi partitë.

Ne gjithashtu përcaktojmë metrikat dhe parametrat që dëshirojmë të shfaqim në dashboard, dhe specifikojmë para-aggregatët. Cube.js do të krijojë tabela të tjera me të dhëna të para-agreguara dhe do të përditësojë automatikisht të dhënat ndërsa ato vijnë. Kjo lejon jo vetëm që të shpejtojë kërkesat, por gjithashtu të reduktojë kostot e përdorimit të Athena.

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

Specifikojmë në këtë model se është e nevojshme të para-agregojmë të dhënat për të gjitha metrikat e përdorura, dhe të përdorim partitimin për muaj. Partitimi i para-agregatëve mund të përshpejtojë ndjeshëm mbledhjen dhe përditësimin e të dhënave.

Tani mund të krijojmë një panel!

Backend-i i Cube.js ofron REST API dhe një grup bibliotekash kliente për strukturat e njohura frontend. Ne do të përdorim versionin React të klientit për të krijuar panelin. Cube.js ofron vetëm të dhëna, kështu që na nevojitet një bibliotekë për vizualizime — më pëlqen recharts, por mund të përdorni ndonjë tjetër.

Serveri i Cube.js pranon kërkesat në formatin JSON, në të cilin specifikohen metrikat e nevojshme. Për shembull, për të llogaritur sa gabime ka dërguar Nginx për ditë, duhet të dërgoni këtë kërkesë:

{
  "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 komponenteve React përmes NPM:

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

Importojmë komponentet cubejs и QueryRenderer, për të nxjerrë të dhënat, dhe krijojmë panelin:

Kodi i panelit

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 (
     {
        if (!resultSet) {
          return 'Loading...';
        }

        return (
          
            
            
            
          
        );
      }}
    />
  )
}

Burimet e panelit janë në dispozicion në CodeSandbox.

Burimi: habr.com

Купить надежный хостинг для сайтов с защитой от DDoS, VPS VDS серверы 🔥 Купить надежный хостинг для сайтов с защитой от DDoS, VPS VDS серверы | ProHoster