Zakonisht, për monitorimin dhe analizimin e funksionimit të Nginx përdoren produkte komerciale ose alternativa open-source si Prometheus + Grafana. Ky është një opsion i mirë për monitorim ose analitikë në kohë reale, por nuk është shumë i përshtatshëm për analizën historike. Në çdo burim të njohur, volumi i të dhënave nga loget e nginx shpejt rritet, dhe për analizimin e një volumi të madh të dhënash ka kuptim të përdorim diçka më të specializuar.
Në këtë artikull do të flas për mënyrën si mund të përdoren për analizimin e logeve, duke marrë si shembull Nginx, dhe do të tregoj sesi të mbledhim një panel analitik, duke përdorur kornizën open-source cube.js. Këtu është arkitektura e plotë e zgjidhjes:

TL:DR;
.
Për grumbullimin e informacionit ne përdorim , për procesimin — dhe , për ruajtjen — . Me këtë lidhje mund të ruani jo vetëm loget e nginx, por edhe ngjarje të tjera, si dhe loge nga shërbime të tjera. Mund të zëvendësoni disa pjesë me të ngjashme për strukturën tuaj, për shembull, mund të shkruani loget në kinesis direkt nga nginx, duke anashkaluar fluentd, ose të përdorni logstash për këtë.
Grumbullojmë loget e Nginx
Për default, loget e Nginx duken si më poshtë:
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, si 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, si Gecko) Chrome/73.0.3683.86 Safari/537.36" "-"Ato mund të parse-ohen, por është shumë më e lehtë të përshtatni konfigurimin e Nginx për të gjeneruar loge 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 loget, do të përdorim S3. Kjo lejon ruajtjen dhe analizimin e logeve në një vend, pasi Athena mund të punojë me të dhënat në S3 direkt. Më tej në artikull do të flas për mënyrën e duhur për të organizuar dhe procesuar loget, por fillimisht na nevojitet një bazë e pastër në S3, në të cilën nuk do të ruhet asgjë tjetër. Duhet menduar paraprakisht për cilin rajon do të krijoni bazën, sepse Athena nuk është e disponueshme në të gjitha rajonet.
Krijojmë skemën në konsolën Athena
Do të krijojmë një tabelë në Athena për loget. Ajo është e nevojshme për shkak se shërben për ruajtje dhe lexim, nëse planifikoni të përdorni Kinesis Firehose. Hapseni 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ë shkruajë të dhënat e marra nga Nginx në S3 në formatin e përzgjedhur, duke i ndarë në drejtime në formatin YYYY/MM/DD/HH. Kjo do t'i vijë në ndihmë gjatë leximit të të dhënave. Sigurisht, mund të shkruani direkt në S3 nga fluentd, por në këtë rast do të duhet të shkruani JSON, dhe kjo nuk është efikase për shkak të madhësisë së madhe të skedarëve. Për më tepër, gjatë përdorimit të PrestoDB ose Athena, JSON është formati më i ngadalshëm i të dhënave. Pra, hapni konsolën Kinesis Firehose, klikoni "Create delivery stream", 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 shkruajtur. Sipas hulumtimeve të disa , këto janë formatet optimale për PrestoDB dhe Athena. Si skemë, përcaktoni tabelën që krijuam më sipër. Keni parasysh se lokacioni në S3 në kinesis mund të jetë çdo vend, nga tabela përdoret vetëm skema. Por nëse specifikoni një lokacion tjetër në S3, nuk do të mund të lexoni këto të dhëna nga kjo tabelë.

Zgjidhni S3 për ruajtje dhe bazën që krijuam më parë. Aws Glue Crawler, për të cilin do të flas më vonë, nuk din të punojë me prefikset në bazën S3, kështu që është e rëndësishme ta lini bosh.

Opcione të tjera mund të modifikohen në varësi të ngarkesës suaj, zakonisht unë përdor ato default. Keni parasysh se kompresimi në S3 nuk është i disponueshëm, por ORC përdor kompresim vetjak si default.
Fluentd
Tani, kur kemi konfiguruar ruajtjen dhe marrjen e logeve, duhet të konfigurojmë dërgimin. Ne do të përdorim , sepse më pëlqen Ruby, por mund të përdorni Logstash ose të dërgoni loget direkt në kinesis. Serverin Fluentd mund ta nisni në disa mënyra, unë do të flas për docker, sepse është e lehtë dhe e përshtatshme.
Fillimisht, na nevojitet një skedar konfigurimi fluent.conf. Krijoni atë dhe shtoni burimin:
përpara
port 24224
bind 0.0.0.0
Tani tani mund të filloni serverin Fluentd. Nëse ju nevojitet një konfigurim më i avancuar, ka një udhëzues të detajuar, përfshirë edhe se si të krijoni 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 logeve përpara dërgimit. Mund të kaloni këtë, por atëherë mund të humbni të gjitha loget e ruajtura pasi të ri-filloni. Porti gjithashtu mund të përdoret çdo, 24224 është porti default i Fluentd.
Tani që kemi një Fluentd të aktivizuar, mund të dërgojmë loget e Nginx atje. Ne zakonisht e aktivizojmë Nginx në një kontejner Docker, dhe në këtë rast, Docker ka një driver logu natyral 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 e aktivizoni Nginx ndryshe, mund të përdorni skedarët e logeve, në Fluentd ka .
Le të shtojmë në konfigurimin e Fluent parsing-un e logeve, të cilin e kemi konfiguruar më parë:
@type parser
key_name log
emit_invalid_record_to_error false
@type jsondhe dërgimin e logeve në Kinesis, duke përdorur :
@type kinesis_firehose
region region
delivery_stream_name
aws_key_id
aws_sec_keyAthena
Nëse e keni konfiguruar gjithçka siç duhet, pas një kohe (në mënyrë default Kinesis ruan të dhënat e marra çdo 10 minuta) duhet të shihni skedarët e logeve në S3. Në menunë "monitoring" Kinesis Firehose mund të shihni sa të dhëna janë shkruar në S3, si dhe gabimet. Mos harroni të jepni akses për shkruarje në bucket S3 për rolin e Kinesis. Nëse Kinesis nuk arrin të analizojë diçka, do të ruajë gabimet në të njëjtin bucket.
Tani mund të shikoni të dhënat në Athena. Le të gjejmë kërkesat e freskëta, për të cilat kemi pasur gabime:
SELECT * FROM "db_name"."table_name" WHERE status > 499 ORDER BY created_at DESC limit 10;Skanojmë të gjitha regjistrimet për çdo kërkesë
Tani loget tona janë përpunuar dhe ruajtur në S3 në ORC, të kompaktuar dhe gati për analizë. Kinesis Firehose madje i ka vendosur ato në drejtime për çdo orë. Megjithatë, derisa tabela të mos jetë e ndarë në parti, Athena do të ngarkojë të dhënat për çdo kohë për çdo kërkesë, me raste të shkëputura. Kjo është një problem i madh për dy arsye:
- Vëllimi i të dhënave vazhdon të rritet, duke ngadalësuar kërkesat;
- Çmimi për Athena caktohet në varësi të vëllimit të të dhënave të skanuara, me minimumin 10 MB për çdo kërkesë.
Për ta rregulluar këtë, përdorim AWS Glue Crawler, i cili do të skanojë të dhënat në S3 dhe të regjistrojë informacionin për partitë në Glue Metastore. Kjo do të na lejojë të përdorim partitë si filtër gjatë kërkesave në Athena, dhe ajo do të skanojë vetëm drejtoritë e specifikuara në kërkesë.
Konfigurojmë Amazon Glue Crawler
Amazon Glue Crawler skanon të gjitha të dhënat në bucket-in S3 dhe krijon tabela me parti. Krijoni Glue Crawler nga konsola AWS Glue dhe shtoni bucket-in ku ruhen të dhënat. Mund të përdorni një crawler për disa bucketa, në këtë rast ai do të krijojë tabela në bazën e të dhënave të caktuara me emra që përputhen me emrat e bucketeve. Nëse planifikoni të përdorni vazhdimisht këto të dhëna, mos harroni të konfiguroni një plan për të aktivizuar Crawler-n sipas nevojave tuaja. Ne përdorim një Crawler për të gjitha tabela, i cili aktivizohet çdo orë.
Tabelat e ndara në parti
Pas aktivizimit të parë të crawler-it në bazën e të dhënave, që është caktuar në konfigurim, duhet të shfaqen tabela për çdo bucket të skanuar. Hapni konsolën Athena dhe gjeni tabelën me loget e Nginx. Le të provojmë të lexojmë ndonjë gjë:
SELECT * FROM "default"."part_demo_kinesis_bucket"
WHERE(
partition_0 = '2019' AND
partition_1 = '04' AND
partition_2 = '08' AND
partition_3 = '06'
);Ky kërkesë do të zgjedhë të gjitha regjistrimet, të marra nga ora 6 deri në 7 të mëngjesit më 8 prill 2019. Por sa më efikase është kjo se sa thjesht të lexosh nga një tabelë e pa ndarë në parti? Le të zbulojmë dhe zgjedhim të njëjtat regjistrime, duke i filtruar ato sipas timestamp-it:

3.59 sekonda dhe 244.34 megabajt të dhënash në një dataset, ku ka vetëm një javë log. Të provojmë filtrin sipas partive:

Pak më shpejt, por më e rëndësishmja — vetëm 1.23 megabajt të dhënash! Kjo do të ishte shumë më e lirë, nëse nuk do të ishte minimumi 10 megabajt për kërkesë në çmimin. Por prapë është shumë më mirë, dhe në datasetet e mëdha, ndryshimi do të jetë shumë më impresionues.
Собираем дэшборд с помощью Cube.js
Për të ndërtuar një dashboard, ne përdorim strukturën analitike Cube.js. Ajo ka shumë funksione, por na interesojnë dy: mundësia për të përdorur automatikisht filtrat sipas partive dhe parashikimin e të dhënave. Ajo përdor një skemë të të dhënave , të 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.
Do të krijojmë një aplikacion të ri Cube.js. Duke qenë se ne tashmë po përdorim stakun AWS, ka logjikë të përdorim Lambda për deploy. Mund të përdorni modelin express për gjenerimin 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ë bazën e të dhënave në cube.js, përdoren variabla ambienti. Gjeneratori do të krijojë skedarin .env, ku mund të specifikoni çelësat tuaj për .
Tani na nevojitet , ku do të specifikojmë se si ruhen log-ët tanë. Aty gjithashtu mund të specifikojmë si të llogaritim metrikat për dashboard-et.
Në direktorinë 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 ne përdorim variablën , për të gjeneruar një kërkesë SQL me filter për particionet.
Gjithashtu caktojmë metrikat dhe parametrat që duam të shfaqim në dashboard, dhe specifikojmë para-agregimet. Cube.js do të krijojë tabela të tjera me të dhëna të para-agreguara dhe do të rinovojë automatikisht të dhënat ndërsa ato mbërrijnë. Kjo jo vetëm që ndihmon në përshpejtimin e kërkesave, por gjithashtu ul koston e përdorimit të Athena-s.
Le të shtojmë këtë informacion në skedarin e schemë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 e specifikojmë në këtë model se është e nevojshme të para-agregohen të dhënat për të gjitha metrikat e përdorura dhe të përdorim particionimin për muaj. mund të përshpejtojë ndjeshëm mbledhjen dhe rinovimin e të dhënave.
Tani mund të ndërtosh dashboard-in!
Backend-i i Cube.js ofron dhe një grup bibliotekash klientësh për framework-e të njohura front-end. Ne do të përdorim versionin React të klientit për ndërtimin e dashboard-it. Cube.js ofron vetëm të dhëna, kështu që na nevojitet një bibliotekë për vizualizime — mua më pëlqen , por mund të përdorni çfarëdo.
Serveri i Cube.js pranon kërkesat në , ku specifikohen metrikat e nevojshme. Për shembull, për të llogaritur se sa gabime ka raportuar Nginx për ditët, duhet të dërgoni një kërkesë të tillë:
{
"measures": ["Logs.errorCount"],
"timeDimensions": [
{
"dimension": "Logs.createdAt",
"dateRange": ["2019-01-01", "2019-01-07"],
"granularity": "day"
}
]
}Të instalojmë klientin e Cube.js dhe bibliotekën e komponenteve React përmes NPM:
$ npm i --save @cubejs-client/core @cubejs-client/reactTë importojmë komponentët cubejs dhe QueryRenderer, për të ngarkuar të dhënat, dhe të ndërtosh dashboard-in:
Kodi i dashboard-it
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 dashboard-it janë të disponueshme në .
Burimi: habr.com
