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 analizu u realnom vremenu, ali nije baš zgodna za historijsku analizu. Na bilo kojem popularnom resursu, količina podataka iz nginx dnevnika 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 dnevnika
Podrazumevano, Nginx dnevnici 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 raščlaniti, ali je mnogo lakše ispraviti Nginx konfiguraciju tako da proizvodi evidencije 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 dnevnika koristit ćemo S3. Ovo vam omogućava da skladištite i analizirate logove na jednom mestu, pošto Athena može direktno da radi sa podacima u S3. Kasnije u članku ću vam reći kako pravilno dodati i obraditi dnevnike, ali prvo nam je potrebna čista kantica u S3, u kojoj ništa drugo neće biti pohranjeno. Vrijedi unaprijed razmisliti u kojoj regiji ćete kreirati svoju kantu, jer Athena nije dostupna u svim regijama.
Kreiranje kola u Athena konzoli
Kreirajmo tabelu u Atheni za dnevnike. Potreban je i za pisanje i za čitanje ako planirate koristiti Kinesis Firehose. Otvorite Athena konzolu i kreirajte tabelu:
Kreiranje 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');
Kreiranje 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 direktno u S3 iz fluentd-a, ali u ovom slučaju ćete morati pisati JSON, a to je neefikasno zbog velike veličine datoteka. Osim toga, kada koristite PrestoDB ili Athena, JSON je najsporiji format podataka. Dakle, otvorite Kinesis Firehose konzolu, kliknite na "Kreiraj prijenos isporuke", odaberite "direktan PUT" u polju "isporuka":
Na sljedećoj kartici odaberite “Konverzija formata zapisa” - “Omogućeno” i odaberite “Apache ORC” kao format snimanja. Prema nekim istraživanjima
Odabiremo S3 za pohranu i kantu koju smo kreirali ranije. Aws Glue Crawler, o kojem ću govoriti malo kasnije, ne može raditi sa prefiksima u S3 bucketu, pa je važno ostaviti ga praznim.
Preostale opcije se mogu mijenjati ovisno o opterećenju; obično koristim one zadane. Imajte na umu da S3 kompresija nije dostupna, ali ORC po defaultu koristi izvornu kompresiju.
fluentd
Sada kada smo konfigurirali pohranjivanje i primanje dnevnika, moramo konfigurirati slanje. Koristićemo
Prvo, trebamo konfiguracijski fajl fluent.conf. Kreirajte ga i dodajte izvor:
port 24224
vezati 0.0.0.0
Sada možete pokrenuti Fluentd server. Ako vam je potrebna naprednija konfiguracija, 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 putanju /fluentd/log
za keširanje dnevnika prije slanja. Možete i bez ovoga, ali onda kada ponovo pokrenete, možete izgubiti sve keširano uz naporan rad. Također možete koristiti bilo koji port; 24224 je zadani Fluentd port.
Sada kada imamo pokrenut Fluentd, možemo tamo poslati Nginx logove. Obično pokrećemo Nginx u Docker kontejneru, u kom slučaju Docker ima izvorni drajver za evidentiranje 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 pokrećete drugačije, možete koristiti datoteke evidencije, Fluentd ima
Dodajmo prethodno konfiguriranu analizu dnevnika u Fluent konfiguraciju:
<filter YOUR-NGINX-TAG.*>
@type parser
key_name log
emit_invalid_record_to_error false
<parse>
@type json
</parse>
</filter>
I slanje dnevnika u Kinesis koristeći
<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 konfigurisali, nakon nekog vremena (podrazumevano, Kinesis bilježi primljene podatke svakih 10 minuta) trebali biste vidjeti datoteke dnevnika u S3. U “monitoring” meniju Kinesis Firehose možete vidjeti koliko podataka je zabilježeno u S3, kao i greške. Ne zaboravite dati pristup za pisanje u S3 bucket ulozi Kinesis. Ako Kinesis ne može raščlaniti nešto, dodat će greške u istu kantu.
Sada možete vidjeti podatke u Ateni. Pronađimo najnovije zahtjeve za koje smo vratili greš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 dnevnici obrađeni i pohranjeni u S3 u ORC-u, komprimirani i spremni za analizu. Kinesis Firehose ih je čak organizirao u direktorije za svaki sat. Međutim, sve dok tabela nije particionisana, Athena će učitavati sve vremenske podatke na svaki zahtev, sa retkim izuzecima. Ovo je veliki problem iz dva razloga:
- Količina podataka stalno raste, usporavajući upite;
- Athena se naplaćuje na osnovu količine skeniranih podataka, sa minimalno 10 MB po zahtjevu.
Da to popravimo, koristimo AWS Glue Crawler, koji će indeksirati podatke u S3 i pisati informacije o particiji u Glue Metastore. Ovo će nam omogućiti da koristimo particije kao filter kada postavljamo upite za Athenu, a skenirat će se samo direktoriji navedeni u upitu.
Postavljanje Amazon Glue Crawlera
Amazon Glue Crawler skenira sve podatke u S3 bucketu i kreira tabele sa particijama. Kreirajte Glue Crawler iz AWS Glue konzole i dodajte kantu u koju pohranjujete podatke. Možete koristiti jedan alat za indeksiranje za nekoliko kantica, u kom slučaju će kreirati tabele u navedenoj bazi podataka s imenima koja odgovaraju nazivima bucketa. Ako planirate redovno koristiti ove podatke, obavezno konfigurirajte raspored pokretanja Crawlera tako da odgovara vašim potrebama. Koristimo jedan Crawler za sve tabele, koji radi svakog sata.
Particionirani stolovi
Nakon prvog pokretanja alata za indeksiranje, tabele za svaku skeniranu kantu trebale bi se pojaviti u bazi podataka navedenoj u postavkama. Otvorite Athena konzolu i pronađite tabelu sa Nginx evidencijama. 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 upit će odabrati sve zapise primljene između 6 i 7 sati 8. aprila 2019. Ali koliko je ovo efikasnije od pukog čitanja iz neparticionirane tablice? Hajde da saznamo i odaberemo iste zapise, filtrirajući ih po vremenskoj oznaci:
3.59 sekundi i 244.34 megabajta podataka na skupu podataka sa samo sedmicom evidencije. Hajde da probamo filter po particiji:
Malo brže, ali najvažnije - samo 1.23 megabajta podataka! Bilo bi mnogo jeftinije da nema minimalnih 10 megabajta po zahtjevu u cijeni. Ali i dalje je mnogo bolji, a na velikim skupovima podataka razlika će biti mnogo impresivnija.
Izrada kontrolne table pomoću Cube.js
Za sastavljanje kontrolne ploče koristimo Cube.js analitički okvir. Ima dosta funkcija, ali nas zanimaju dvije: mogućnost automatskog korištenja filtera particija i prethodno združivanje podataka. Koristi šemu podataka
Kreirajmo novu aplikaciju Cube.js. Pošto već koristimo AWS stack, logično je koristiti Lambda za implementaciju. Možete koristiti ekspresni predložak za generisanje ako planirate da ugostite Cube.js backend u Heroku ili Docker-u. Dokumentacija opisuje druge
$ npm install -g cubejs-cli
$ cubejs create nginx-log-analytics -t serverless -d athena
Varijable okruženja se koriste za konfiguriranje pristupa bazi podataka u cube.js. Generator će kreirati .env datoteku u kojoj možete odrediti svoje ključeve za
Sada nam treba
U imeniku schema
, kreirajte datoteku Logs.js
. Evo primjera modela podataka za nginx:
Šifra 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 metrike i parametre koje želimo da prikažemo na kontrolnoj tabli i specificiramo pre-agregacije. Cube.js će kreirati dodatne tabele sa unapred agregiranim podacima i automatski će ažurirati podatke kako stignu. Ovo ne samo da ubrzava upite, već i smanjuje troškove korištenja Athene.
Dodajmo ove informacije u datoteku šeme 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 prethodno agregirati podatke za sve korištene metrike i koristiti particioniranje po mjesecima.
Sada možemo sastaviti kontrolnu tablu!
Cube.js backend pruža
Cube.js server prihvata zahtjev u
{
"measures": ["Logs.errorCount"],
"timeDimensions": [
{
"dimension": "Logs.createdAt",
"dateRange": ["2019-01-01", "2019-01-07"],
"granularity": "day"
}
]
}
Instalirajmo Cube.js klijenta i React biblioteku komponenti preko NPM-a:
$ npm i --save @cubejs-client/core @cubejs-client/react
Uvozimo komponente cubejs
и QueryRenderer
da preuzmete podatke i prikupite kontrolnu tablu:
Kôd kontrolne table
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 na kontrolnoj tabli dostupni su na
izvor: www.habr.com