Tipies word kommersiële produkte of klaargemaakte oopbron-alternatiewe, soos Prometheus + Grafana, gebruik om die werking van Nginx te monitor en te ontleed. Dit is 'n goeie opsie vir monitering of intydse analise, maar nie baie gerieflik vir historiese analise nie. Op enige gewilde hulpbron groei die volume data van nginx-logboeke vinnig, en om 'n groot hoeveelheid data te ontleed, is dit logies om iets meer gespesialiseerd te gebruik.
In hierdie artikel sal ek jou vertel hoe jy dit kan gebruik
TL:DR;
Om inligting in te samel wat ons gebruik
Versamel Nginx-logboeke
By verstek lyk Nginx-logboeke so iets:
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" "-"
Hulle kan ontleed word, maar dit is baie makliker om die Nginx-konfigurasie reg te stel sodat dit logs in JSON produseer:
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 vir berging
Om logs te stoor, sal ons S3 gebruik. Dit laat jou toe om logs op een plek te stoor en te ontleed, aangesien Athena direk met data in S3 kan werk. Later in die artikel sal ek jou vertel hoe om logs korrek by te voeg en te verwerk, maar eers benodig ons 'n skoon emmer in S3, waarin niks anders gestoor sal word nie. Dit is die moeite werd om vooraf te oorweeg in watter streek jy jou emmer gaan skep, want Athena is nie in alle streke beskikbaar nie.
Skep 'n stroombaan in die Athena-konsole
Kom ons skep 'n tabel in Athena vir logs. Dit is nodig vir beide skryf en lees as jy van plan is om Kinesis Firehose te gebruik. Maak die Athena-konsole oop en skep 'n tabel:
SQL-tabelskepping
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');
Skep Kinesis Firehose Stream
Kinesis Firehose sal die data wat van Nginx na S3 ontvang is in die geselekteerde formaat skryf, en dit in gidse in die JJJJ/MM/DD/HH-formaat verdeel. Dit sal handig te pas kom wanneer jy data lees. Jy kan natuurlik direk vanaf fluentd na S3 skryf, maar in hierdie geval sal jy JSON moet skryf, en dit is ondoeltreffend weens die groot grootte van die lêers. Daarbenewens, wanneer u PrestoDB of Athena gebruik, is JSON die stadigste dataformaat. Maak dus die Kinesis Firehose-konsole oop, klik "Skep afleweringsstroom", kies "direct PUT" in die "aflewering"-veld:
In die volgende oortjie, kies "Rekord formaat omskakeling" - "Enabled" en kies "Apache ORC" as die opname formaat. Volgens sommige navorsing
Ons kies S3 vir berging en die emmer wat ons vroeër geskep het. Aws Glue Crawler, waaroor ek 'n bietjie later sal praat, kan nie met voorvoegsels in 'n S3-emmer werk nie, daarom is dit belangrik om dit leeg te laat.
Die oorblywende opsies kan verander word afhangende van jou vrag; Ek gebruik gewoonlik die verstek. Let daarop dat S3-kompressie nie beskikbaar is nie, maar ORC gebruik by verstek inheemse kompressie.
vlot
Noudat ons die stoor en ontvang van logs gekonfigureer het, moet ons stuur opstel. Ons sal gebruik
Eerstens benodig ons die fluent.conf konfigurasielêer. Skep dit en voeg bron by:
hawe 24224
bind 0.0.0.0
Nou kan jy die Fluent-bediener begin. As jy 'n meer gevorderde konfigurasie benodig, gaan na
$ 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
Hierdie konfigurasie gebruik die pad /fluentd/log
logs te kas voordat dit gestuur word. Jy kan sonder dit klaarkom, maar wanneer jy dan weer begin, kan jy alles verloor wat met terugbrekende arbeid gekas is. Jy kan ook enige poort gebruik; 24224 is die verstek Fluentd-poort.
Noudat ons Fluentd aan die gang het, kan ons Nginx-logboeke daarheen stuur. Ons gebruik Nginx gewoonlik in 'n Docker-houer, in welke geval Docker 'n inheemse aantekenbestuurder vir Fluentd het:
$ 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
As jy Nginx anders bestuur, kan jy loglêers gebruik, het Fluentd
Kom ons voeg die log-ontleding wat hierbo gekonfigureer is by die Fluent-konfigurasie:
<filter YOUR-NGINX-TAG.*>
@type parser
key_name log
emit_invalid_record_to_error false
<parse>
@type json
</parse>
</filter>
En stuur logs na Kinesis met behulp van
<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
As jy alles korrek gekonfigureer het, moet jy na 'n rukkie (by verstek, Kinesis rekords ontvangde data een keer elke 10 minute) loglêers in S3 sien. In die "monitoring"-kieslys van Kinesis Firehose kan jy sien hoeveel data in S3 aangeteken is, sowel as foute. Moenie vergeet om skryftoegang tot die S3-emmer aan die Kinesis-rol te gee nie. As Kinesis iets nie kon ontleed nie, sal dit die foute by dieselfde emmer voeg.
Nou kan jy die data in Athena sien. Kom ons vind die jongste versoeke waarvoor ons foute teruggestuur het:
SELECT * FROM "db_name"."table_name" WHERE status > 499 ORDER BY created_at DESC limit 10;
Skandeer alle rekords vir elke versoek
Nou is ons logs verwerk en in S3 in ORC gestoor, saamgepers en gereed vir ontleding. Kinesis Firehose het hulle selfs vir elke uur in gidse georganiseer. Solank die tabel egter nie gepartisioneer is nie, sal Athena alle tye data op elke versoek laai, met seldsame uitsonderings. Dit is 'n groot probleem om twee redes:
- Die volume data groei voortdurend, wat navrae vertraag;
- Athena word gefaktureer op grond van die volume data wat geskandeer is, met 'n minimum van 10 MB per versoek.
Om dit reg te stel, gebruik ons AWS Glue Crawler, wat die data in S3 sal deurkruip en die partisie-inligting na die Glue Metastore sal skryf. Dit sal ons toelaat om partisies as 'n filter te gebruik wanneer ons Athena navraag doen, en dit sal slegs die dopgehou skandeer wat in die navraag gespesifiseer is.
Die opstel van Amazon Glue Crawler
Amazon Glue Crawler skandeer al die data in die S3-emmer en skep tabelle met partisies. Skep 'n Glue Crawler vanaf die AWS Glue-konsole en voeg 'n emmer by waar jy die data stoor. Jy kan een deurkruiper vir verskeie emmers gebruik, in welke geval dit tabelle in die gespesifiseerde databasis sal skep met name wat ooreenstem met die name van die emmers. As jy van plan is om hierdie data gereeld te gebruik, maak seker dat jy Crawler se bekendstellingskedule instel om by jou behoeftes te pas. Ons gebruik een Crawler vir alle tafels, wat elke uur loop.
Verdeelde tafels
Na die eerste bekendstelling van die deurkruiser, moet tabelle vir elke geskandeerde emmer in die databasis verskyn wat in die instellings gespesifiseer is. Maak die Athena-konsole oop en vind die tabel met Nginx-logs. Kom ons probeer iets lees:
SELECT * FROM "default"."part_demo_kinesis_bucket"
WHERE(
partition_0 = '2019' AND
partition_1 = '04' AND
partition_2 = '08' AND
partition_3 = '06'
);
Hierdie navraag sal alle rekords wat tussen 6:7 en 8:2019 op XNUMX April XNUMX ontvang is, kies. Maar hoeveel meer doeltreffend is dit as om net van 'n nie-gepartisioneerde tabel af te lees? Kom ons vind uit en kies dieselfde rekords, filter hulle volgens tydstempel:
3.59 sekondes en 244.34 megagrepe se data op 'n datastel met slegs 'n week se logs. Kom ons probeer 'n filter volgens partisie:
'n Bietjie vinniger, maar die belangrikste - slegs 1.23 megagrepe se data! Dit sou baie goedkoper wees as nie vir die minimum 10 megagrepe per versoek in die prysbepaling nie. Maar dit is steeds baie beter, en op groot datastelle sal die verskil baie meer indrukwekkend wees.
Bou 'n dashboard met Cube.js
Om die dashboard saam te stel, gebruik ons die Cube.js analitiese raamwerk. Dit het nogal baie funksies, maar ons is geïnteresseerd in twee: die vermoë om partisiefilters outomaties te gebruik en data vooraf-aggregasie. Dit gebruik dataskema
Kom ons skep 'n nuwe Cube.js-toepassing. Aangesien ons reeds die AWS-stapel gebruik, is dit logies om Lambda vir ontplooiing te gebruik. Jy kan die uitdruklike sjabloon vir generasie gebruik as jy van plan is om die Cube.js-agterkant in Heroku of Docker te huisves. Die dokumentasie beskryf ander
$ npm install -g cubejs-cli
$ cubejs create nginx-log-analytics -t serverless -d athena
Omgewingsveranderlikes word gebruik om databasistoegang in cube.js op te stel. Die kragopwekker sal 'n .env-lêer skep waarin jy jou sleutels kan spesifiseer
Nou het ons nodig
In die gids schema
, skep 'n lêer Logs.js
. Hier is 'n voorbeeld datamodel vir nginx:
Model kode
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`
}
}
});
Hier gebruik ons die veranderlike
Ons stel ook die maatstawwe en parameters in wat ons op die dashboard wil vertoon en spesifiseer voorafsamevoegings. Cube.js sal bykomende tabelle skep met vooraf saamgestelde data en sal die data outomaties bywerk soos dit aankom. Dit versnel nie net navrae nie, maar verminder ook die koste van die gebruik van Athena.
Kom ons voeg hierdie inligting by die data skema lêer:
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`
)
}
}
}
Ons spesifiseer in hierdie model dat dit nodig is om data vooraf saam te stel vir alle maatstawwe wat gebruik word, en verdeling per maand te gebruik.
Nou kan ons die paneelbord saamstel!
Cube.js backend verskaf
Die Cube.js-bediener aanvaar die versoek in
{
"measures": ["Logs.errorCount"],
"timeDimensions": [
{
"dimension": "Logs.createdAt",
"dateRange": ["2019-01-01", "2019-01-07"],
"granularity": "day"
}
]
}
Kom ons installeer die Cube.js-kliënt en die React-komponentbiblioteek via NPM:
$ npm i --save @cubejs-client/core @cubejs-client/react
Ons voer komponente in cubejs
и QueryRenderer
om die data af te laai en die dashboard te versamel:
Dashboard kode
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>
);
}}
/>
)
}
Dashboardbronne is beskikbaar by
Bron: will.com