Di genere, i prudutti cummirciali o l'alternative open-source pronti, cum'è Prometheus + Grafana, sò usati per monitorà è analizà l'operazione di Nginx. Questa hè una bona opzione per u monitoraghju o l'analisi in tempu reale, ma micca assai cunvene per l'analisi storica. Nant'à ogni risorsa populari, u voluminu di dati da i logs nginx cresce rapidamente, è per analizà una grande quantità di dati, hè logicu di utilizà qualcosa di più specializatu.
In questu articulu vi dicu cumu pudete aduprà
TL: DR;
Per cullà l'infurmazioni chì usemu
Raccolta di logs Nginx
Per automaticamente, i logs di Nginx pareanu cusì:
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" "-"
Puderanu esse analizati, ma hè assai più faciule per correggerà a cunfigurazione Nginx in modu chì pruduce logs in 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 per u almacenamentu
Per almacenà logs, avemu aduprà S3. Stu vi permette di almacenà è analizà logs in un locu, postu chì Athena pò travaglià cù dati in S3 direttamente. In seguitu in l'articulu vi dicu cumu aghjunghje currettamente è processà i logs, ma prima avemu bisognu di un bucket pulito in S3, in quale nunda di più serà guardatu. Vale a pena cunsiderà in anticipu in quale regione creerete u vostru bucket, perchè Athena ùn hè micca dispunibule in tutte e regioni.
Crià un circuitu in a cunsola Athena
Creemu una tavola in Athena per i logs. Hè necessariu per scrive è leghje se pensa à aduprà Kinesis Firehose. Aprite a cunsola Athena è crea una tavola:
Creazione di tavule 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');
Creazione di Kinesis Firehose Stream
Kinesis Firehose scriverà i dati ricevuti da Nginx à S3 in u formatu sceltu, dividendu in cartulari in u formatu AAAA/MM/DD/HH. Questu serà utile quandu leghje e dati. Pudete, sicuru, scrive direttamente à S3 da fluentd, ma in questu casu avete da scrive JSON, è questu hè inefficiente per via di a grandezza di i schedari. Inoltre, quandu si usa PrestoDB o Athena, JSON hè u formatu di dati più lento. Allora apre a cunsola Kinesis Firehose, cliccate "Crea un flussu di consegna", selezziunate "PUT direttu" in u campu "consegna":
In a tabulazione dopu, selezziunate "Conversione di furmatu di registrazione" - "Enabled" è selezziunate "Apache ORC" cum'è furmatu di registrazione. Sicondu qualchi ricerca
Selezziemu S3 per u almacenamiento è u bucket chì avemu creatu prima. Aws Glue Crawler, chì parleraghju un pocu dopu, ùn pò micca travaglià cù prefissi in un bucket S3, per quessa hè impurtante di lascià viotu.
L'opzioni rimanenti ponu esse cambiate secondu a vostra carica, sò generalmente utilizatu quelli predeterminati. Nota chì a cumpressione S3 ùn hè micca dispunibule, ma ORC usa a cumpressione nativa per difettu.
fluentd
Avà chì avemu cunfiguratu l'almacenamiento è a ricezione di logs, avemu bisognu di cunfigurà l'invio. Avemu aduprà
Prima, avemu bisognu di u schedariu di cunfigurazione fluent.conf. Crea è aghjunghje a fonte:
portu 24224
ligà 0.0.0.0
Avà pudete inizià u servitore Fluentd. Sè avete bisognu di una cunfigurazione più avanzata, andate à
$ 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
Sta cunfigurazione usa a strada /fluentd/log
per cache i logs prima di mandà. Pudete fà senza questu, ma dopu quandu riavviate, pudete perde tuttu in cache cù u travagliu back-breaking. Pudete ancu aduprà qualsiasi portu 24224 hè u portu Fluentd predeterminatu.
Avà chì avemu Fluentd in esecuzione, pudemu mandà logs Nginx quì. Di solitu eseguimu Nginx in un containeru Docker, in quale casu Docker hà un driver di logging nativu per 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
Se eseguite Nginx diversamente, pudete aduprà i schedarii di log, Fluentd hà
Aghjunghjemu l'analisi di log cunfigurata sopra à a cunfigurazione Fluent:
<filter YOUR-NGINX-TAG.*>
@type parser
key_name log
emit_invalid_record_to_error false
<parse>
@type json
</parse>
</filter>
È mandà logs à Kinesis usendu
<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
Se avete cunfiguratu tuttu bè, dopu un pocu tempu (per difettu, Kinesis registra i dati ricevuti una volta ogni 10 minuti) duvete vede i schedarii di log in S3. In u menù di "surveglianza" di Kinesis Firehose pudete vede quantu dati sò registrati in S3, è ancu errori. Ùn vi scurdate di dà accessu à scrittura à u bucket S3 à u rolu Kinesis. Se Kinesis ùn pudia analizà qualcosa, aghjunghje l'errori à u stessu bucket.
Avà pudete vede i dati in Athena. Truvemu l'ultime dumande per quale avemu tornatu errori:
SELECT * FROM "db_name"."table_name" WHERE status > 499 ORDER BY created_at DESC limit 10;
Scanning tutti i registri per ogni dumanda
Avà i nostri logs sò stati processati è almacenati in S3 in ORC, cumpressi è pronti per l'analisi. Kinesis Firehose li hà ancu urganizatu in cartulari per ogni ora. In ogni casu, finu à chì a tavula ùn hè micca partizionata, Athena caricarà dati di tutti i tempi nantu à ogni dumanda, cù rari eccezzioni. Questu hè un grande prublema per dui motivi:
- U voluminu di dati hè in constantemente crescente, rallentendu e dumande;
- Athena hè fattura basatu annantu à u voluminu di dati scansati, cù un minimu di 10 MB per dumanda.
Per riparà questu, usemu AWS Glue Crawler, chì rastrearà e dati in S3 è scrive l'infurmazioni di partizione à u Glue Metastore. Questu ci permetterà d'utilizà partizioni cum'è un filtru quandu si dumanda à Athena, è scanserà solu i cartulari specificati in a dumanda.
Configurazione di Amazon Glue Crawler
Amazon Glue Crawler scansa tutte e dati in u bucket S3 è crea tabelle cù partizioni. Crea un Glue Crawler da a cunsola AWS Glue è aghjunghje un bucket induve guardate i dati. Pudete utilizà un crawler per parechji buckets, in quale casu, creà tabelle in a basa di dati specifica cù nomi chì currispondenu à i nomi di i buckets. Se pensa à aduprà sta dati regularmente, assicuratevi di cunfigurà u calendariu di lanciamentu di Crawler per adattà à i vostri bisogni. Avemu aduprà un Crawler per tutte e tavule, chì corre ogni ora.
Tavulini spartuti
Dopu u primu lanciamentu di u crawler, i tabelli per ogni bucket scansatu devenu apparisce in a basa di dati specificata in i paràmetri. Apertura a cunsola Athena è truvate a tavola cù logs Nginx. Pruvemu di leghje qualcosa:
SELECT * FROM "default"."part_demo_kinesis_bucket"
WHERE(
partition_0 = '2019' AND
partition_1 = '04' AND
partition_2 = '08' AND
partition_3 = '06'
);
Questa dumanda selezziunà tutti i registri ricevuti trà 6 a.m. è 7 a.m. l'8 d'aprile 2019. Ma quantu più efficaci hè questu chì solu leghje da una tavula micca partizionata? Scupritemu è selezziunate i stessi registri, filtràli per timestamp:
3.59 seconde è 244.34 megabytes di dati nantu à un dataset cù solu una settimana di logs. Pruvemu un filtru per partizione:
Un pocu più veloce, ma u più impurtante - solu 1.23 megabyte di dati! Saria assai più prezzu s'ellu ùn hè micca per u minimu 10 megabytes per dumanda in u prezzu. Ma hè sempre assai megliu, è nantu à grande datasets a diferenza serà assai più impressiunanti.
Custruì un dashboard cù Cube.js
Per assemblà u dashboard, usemu u quadru analiticu Cube.js. Havi assai funzioni, ma ci interessanu dui: a capacità di utilizà automaticamente filtri di partizioni è pre-aggregazione di dati. Si usa schema di dati
Creemu una nova applicazione Cube.js. Siccomu avemu digià aduprendu a pila AWS, hè logicu aduprà Lambda per implementazione. Pudete utilizà u mudellu espressu per a generazione se pensa à ospitu u backend Cube.js in Heroku o Docker. A documentazione descrive l'altri
$ npm install -g cubejs-cli
$ cubejs create nginx-log-analytics -t serverless -d athena
Variabili di l'ambiente sò usati per cunfigurà l'accessu à a basa di dati in cube.js. U generatore creà un schedariu .env in quale pudete specificà e vostre chjave per
Avà avemu bisognu
In u cartulare schema
, crea un schedariu Logs.js
. Eccu un esempiu di mudellu di dati per nginx:
Codice di mudellu
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`
}
}
});
Quì avemu aduprà a variàbbili
Avemu ancu stabilitu e metriche è i paràmetri chì vulemu vede nantu à u dashboard è specifichi pre-aggregazioni. Cube.js hà da creà tabelle supplementari cù dati pre-aggregati è aghjurnà automaticamente e dati cum'è ghjunghje. Questu ùn solu accelerà e dumande, ma riduce ancu u costu di l'usu di Athena.
Aghjunghjemu sta infurmazione à u schedariu di schema di dati:
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`
)
}
}
}
Spicemu in questu mudellu chì hè necessariu di pre-aggregate dati per tutti i metrici utilizati, è aduprà a partizione per mese.
Avà pudemu assemble u dashboard!
Cube.js backend furnisce
U servitore Cube.js accetta a dumanda in
{
"measures": ["Logs.errorCount"],
"timeDimensions": [
{
"dimension": "Logs.createdAt",
"dateRange": ["2019-01-01", "2019-01-07"],
"granularity": "day"
}
]
}
Stallà u cliente Cube.js è a biblioteca di cumpunenti React via NPM:
$ npm i --save @cubejs-client/core @cubejs-client/react
Importemu cumpunenti cubejs
и QueryRenderer
per scaricà i dati, è raccoglie u dashboard:
Codice dashboard
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>
);
}}
/>
)
}
I fonti di Dashboard sò dispunibili à
Source: www.habr.com