Obično se komercijalni proizvodi ili gotove alternative otvorenog koda, kao što su Prometheus + Grafana, koriste za praćenje i analizu rada Nginxa. Ovo je dobra opcija za praćenje ili analitiku u stvarnom vremenu, ali nije baš zgodna za povijesnu analizu. Na bilo kojem popularnom resursu količina podataka iz nginx zapisa brzo raste, a za analizu velike količine podataka logično je koristiti nešto specijaliziranije.
U ovom članku ću vam reći kako možete koristiti
TL:DR;
Za prikupljanje informacija koje koristimo
Prikupljanje Nginx zapisa
Prema zadanim postavkama, Nginx zapisi izgledaju otprilike ovako:
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" "-"
Mogu se analizirati, ali puno je lakše ispraviti Nginx konfiguraciju tako da proizvodi zapise u JSON-u:
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 za skladištenje
Za pohranjivanje zapisa koristit ćemo S3. To vam omogućuje pohranu i analizu zapisa na jednom mjestu, budući da Athena može izravno raditi s podacima u S3. Kasnije u članku ću vam reći kako ispravno dodati i obraditi zapise, ali prvo nam je potrebna čista kanta u S3, u kojoj se ništa drugo neće pohraniti. Vrijedno je unaprijed razmisliti u kojoj regiji ćete kreirati svoju kantu jer Athena nije dostupna u svim regijama.
Izrada sklopa u Athena konzoli
Kreirajmo tablicu u Atheni za zapise. Potreban je i za pisanje i za čitanje ako planirate koristiti Kinesis Firehose. Otvorite Athena konzolu i napravite tablicu:
Izrada SQL tablice
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');
Stvaranje Kinesis Firehose Stream
Kinesis Firehose će podatke primljene od Nginxa zapisati u S3 u odabranom formatu, dijeleći ih u direktorije u formatu GGGG/MM/DD/HH. Ovo će vam dobro doći prilikom čitanja podataka. Možete, naravno, pisati izravno u S3 iz fluentda, ali u ovom slučaju morat ćete pisati JSON, a to je neučinkovito zbog velike veličine datoteka. Osim toga, kada koristite PrestoDB ili Athena, JSON je najsporiji format podataka. Dakle, otvorite Kinesis Firehose konzolu, kliknite "Create delivery stream", odaberite "direct PUT" u polju "delivery":
U sljedećoj kartici odaberite "Pretvorba formata zapisa" - "Omogućeno" i odaberite "Apache ORC" kao format snimanja. Prema nekim istraživanjima
Odaberemo S3 za pohranu i kantu koju smo ranije izradili. Aws Glue Crawler, o kojem ću govoriti malo kasnije, ne može raditi s prefiksima u S3 kanti, pa je važno ostaviti je praznu.
Preostale opcije mogu se mijenjati ovisno o vašem opterećenju; ja obično koristim zadane. Imajte na umu da S3 kompresija nije dostupna, ali ORC prema zadanim postavkama koristi izvornu kompresiju.
Tečno
Sada kada smo konfigurirali pohranjivanje i primanje zapisa, moramo konfigurirati slanje. Koristit ćemo se
Prvo nam je potrebna konfiguracijska datoteka fluent.conf. Stvorite ga i dodajte izvor:
port 24224
vezati 0.0.0.0
Sada možete pokrenuti Fluentd poslužitelj. Ako trebate napredniju konfiguraciju, idite 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
Ova konfiguracija koristi stazu /fluentd/log
u predmemoriju zapisa prije slanja. Možete i bez ovoga, ali kada ponovno pokrenete, možete izgubiti sve što je predmemorirano uz mukotrpan rad. Također možete koristiti bilo koji port; 24224 je zadani Fluentd port.
Sada kada Fluentd radi, možemo poslati Nginx zapisnike tamo. Obično pokrećemo Nginx u Docker spremniku, u kojem slučaju Docker ima nativni upravljački program za bilježenje za 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
Ako Nginx pokrenete na drugačiji način, možete koristiti log datoteke, Fluentd ima
Dodajmo prethodno konfigurirano parsiranje dnevnika konfiguraciji Fluenta:
<filter YOUR-NGINX-TAG.*>
@type parser
key_name log
emit_invalid_record_to_error false
<parse>
@type json
</parse>
</filter>
I slanje zapisa u Kinesis pomoću
<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>
Atina
Ako ste sve ispravno konfigurirali, nakon nekog vremena (prema zadanim postavkama Kinesis bilježi primljene podatke svakih 10 minuta) trebali biste vidjeti datoteke dnevnika u S3. U izborniku "monitoring" Kinesis Firehose možete vidjeti koliko je podataka snimljeno u S3, kao i pogreške. Ne zaboravite ulozi Kinesis dati pristup pisanju u S3 spremnik. Ako Kinesis ne može analizirati nešto, dodat će pogreške u istu kantu.
Sada možete vidjeti podatke u Atheni. Pronađimo najnovije zahtjeve za koje smo vratili pogreške:
SELECT * FROM "db_name"."table_name" WHERE status > 499 ORDER BY created_at DESC limit 10;
Skeniranje svih zapisa za svaki zahtjev
Sada su naši zapisnici obrađeni i pohranjeni u S3 u ORC-u, komprimirani i spremni za analizu. Kinesis Firehose čak ih je organizirao u direktorije za svaki sat. Međutim, sve dok tablica nije particionirana, Athena će učitati podatke svih vremena na svaki zahtjev, uz rijetke iznimke. Ovo je veliki problem iz dva razloga:
- Količina podataka stalno raste, usporavajući upite;
- Athena se naplaćuje na temelju količine skeniranih podataka, s minimalno 10 MB po zahtjevu.
Da bismo to popravili, koristimo AWS Glue Crawler, koji će indeksirati podatke u S3 i zapisati informacije o particiji u Glue Metastore. To će nam omogućiti da koristimo particije kao filtar kada postavljamo upit Atheni, a skenirat će samo direktorije navedene u upitu.
Postavljanje Amazon Glue Crawlera
Amazon Glue Crawler skenira sve podatke u S3 spremniku i stvara tablice s particijama. Stvorite Glue Crawler s AWS Glue konzole i dodajte kantu u koju pohranjujete podatke. Možete koristiti jedan alat za indeksiranje za nekoliko spremnika, u kojem slučaju će on stvoriti tablice u navedenoj bazi podataka s nazivima koji odgovaraju nazivima spremnika. Ako planirate redovito koristiti ove podatke, svakako konfigurirajte raspored pokretanja programa za indeksiranje tako da odgovara vašim potrebama. Koristimo jedan alat za indeksiranje za sve tablice, koji radi svaki sat.
Pregrađeni stolovi
Nakon prvog pokretanja alata za indeksiranje, tablice za svaku skeniranu kantu trebale bi se pojaviti u bazi podataka navedenoj u postavkama. Otvorite Athena konzolu i pronađite tablicu s Nginx zapisima. Pokušajmo nešto pročitati:
SELECT * FROM "default"."part_demo_kinesis_bucket"
WHERE(
partition_0 = '2019' AND
partition_1 = '04' AND
partition_2 = '08' AND
partition_3 = '06'
);
Ovaj će upit odabrati sve zapise primljene između 6 i 7 sati 8. travnja 2019. Ali koliko je ovo učinkovitije od samog čitanja iz neparticionirane tablice? Otkrijmo i odaberimo iste zapise, filtrirajući ih prema vremenskoj oznaci:
3.59 sekundi i 244.34 megabajta podataka u skupu podataka sa samo tjedan dana zapisa. Pokušajmo s filtrom po particiji:
Malo brže, ali što je najvažnije - samo 1.23 megabajta podataka! Bilo bi mnogo jeftinije da nije minimalno 10 megabajta po zahtjevu u cijeni. Ali još uvijek je mnogo bolje, a na velikim skupovima podataka razlika će biti mnogo impresivnija.
Izrada nadzorne ploče pomoću Cube.js
Za sastavljanje nadzorne ploče koristimo Cube.js analitički okvir. Ima dosta funkcija, ali nas zanimaju dvije: mogućnost automatske upotrebe particijskih filtara i predagregacija podataka. Koristi podatkovnu shemu
Kreirajmo novu Cube.js aplikaciju. Budući da već koristimo AWS stack, logično je koristiti Lambda za implementaciju. Možete koristiti ekspresni predložak za generiranje ako planirate postaviti pozadinu Cube.js u Heroku ili Docker. Dokumentacija opisuje druge
$ npm install -g cubejs-cli
$ cubejs create nginx-log-analytics -t serverless -d athena
Varijable okruženja koriste se za konfiguriranje pristupa bazi podataka u cube.js. Generator će stvoriti .env datoteku u kojoj možete navesti svoje ključeve za
Sada trebamo
U imeniku schema
, stvorite datoteku Logs.js
. Evo primjera modela podataka za nginx:
Kod modela
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`
}
}
});
Ovdje koristimo varijablu
Također postavljamo metriku i parametre koje želimo prikazati na nadzornoj ploči i specificiramo predagregacije. Cube.js će izraditi dodatne tablice s unaprijed agregiranim podacima i automatski ažurirati podatke kako pristignu. To ne samo da ubrzava upite, već i smanjuje troškove korištenja Athene.
Dodajmo ove informacije u datoteku sheme podataka:
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`
)
}
}
}
U ovom modelu navodimo da je potrebno unaprijed agregirati podatke za sve korištene metrike i koristiti particioniranje po mjesecima.
Sada možemo sastaviti kontrolnu ploču!
Cube.js backend pruža
Poslužitelj Cube.js prihvaća zahtjev u
{
"measures": ["Logs.errorCount"],
"timeDimensions": [
{
"dimension": "Logs.createdAt",
"dateRange": ["2019-01-01", "2019-01-07"],
"granularity": "day"
}
]
}
Instalirajmo Cube.js klijent i biblioteku komponenti React putem NPM-a:
$ npm i --save @cubejs-client/core @cubejs-client/react
Uvozimo komponente cubejs
и QueryRenderer
za preuzimanje podataka i prikupljanje nadzorne ploče:
Kod nadzorne ploče
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>
);
}}
/>
)
}
Izvori nadzorne ploče dostupni su na
Izvor: www.habr.com