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

TL:DR;
.
Për të mbledhur informacionin, ne përdorim , për procesimin — и , për ruajtjen — . 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":

Në skedën tjetër, zgjidhni "Record format conversion" — "Enabled" dhe zgjidhni "Apache ORC" si format për shkrim. Sipas hulumtimeve të disa , 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ë.

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.

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 , 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:
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 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:stableKjo 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
nginxNëse po e drejtoni Nginx ndryshe, mund të përdorni regjistrat e skedarëve, në Fluentd ka .
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 jsonDhe dërgimin e regjistrave në Kinesis, duke përdorur :
@type kinesis_firehose
region region
delivery_stream_name
aws_key_id
aws_sec_keyAthena
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:

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:

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 , 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 .
$ npm install -g cubejs-cli
$ cubejs create nginx-log-analytics -t serverless -d athenaPë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 .
Tani na nevojitet , 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 , 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. 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 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 , por mund të përdorni ndonjë tjetër.
Serveri i Cube.js pranon kërkesat në , 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/reactImportojmë 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ë .
Burimi: habr.com
