Paprastai Nginx veikimui stebėti ir analizuoti naudojami komerciniai produktai arba paruoštos atvirojo kodo alternatyvos, tokios kaip Prometheus + Grafana. Tai geras pasirinkimas stebėjimui ar realiojo laiko analizei, bet nelabai patogus istorinei analizei. Bet kuriame populiariame šaltinyje duomenų iš nginx žurnalų kiekis sparčiai auga, o norint išanalizuoti didelį duomenų kiekį, logiška naudoti ką nors labiau specializuotą.
Šiame straipsnyje aš jums pasakysiu, kaip galite naudoti
TL:DR;
Norėdami rinkti informaciją, kurią naudojame
Nginx žurnalų rinkimas
Pagal numatytuosius nustatymus „Nginx“ žurnalai atrodo maždaug taip:
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" "-"
Juos galima išanalizuoti, tačiau daug lengviau ištaisyti Nginx konfigūraciją, kad ji sukurtų žurnalus 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 saugojimui
Žurnalams saugoti naudosime S3. Tai leidžia saugoti ir analizuoti žurnalus vienoje vietoje, nes Athena gali tiesiogiai dirbti su S3 duomenimis. Vėliau straipsnyje aš jums pasakysiu, kaip teisingai pridėti ir apdoroti žurnalus, tačiau pirmiausia mums reikia švaraus S3 kibiro, kuriame nieko daugiau nebus saugoma. Verta iš anksto pagalvoti, kuriame regione kursite kibirą, nes „Athena“ pasiekiama ne visuose regionuose.
Grandinės sukūrimas konsolėje Athena
Sukurkime „Athena“ lentelę rąstams. Jis reikalingas tiek rašant, tiek skaitant, jei planuojate naudoti Kinesis Firehose. Atidarykite „Athena“ konsolę ir sukurkite lentelę:
SQL lentelės kūrimas
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');
Kinesis Firehose Stream kūrimas
Kinesis Firehose iš Nginx gautus duomenis įrašys į S3 pasirinktu formatu, suskirstydamas juos į katalogus formatu YYYY/MM/DD/HH. Tai bus naudinga skaitant duomenis. Žinoma, galite rašyti tiesiai į S3 iš fluentd, tačiau tokiu atveju turėsite rašyti JSON, o tai neefektyvu dėl didelio failų dydžio. Be to, naudojant PrestoDB arba Athena, JSON yra lėčiausias duomenų formatas. Taigi atidarykite Kinesis Firehose pultą, spustelėkite „Sukurti pristatymo srautą“, lauke „pristatymas“ pasirinkite „tiesioginis PUT“:
Kitame skirtuke pasirinkite „Įrašymo formato konvertavimas“ - „Įjungta“ ir kaip įrašymo formatą pasirinkite „Apache ORC“. Kai kurių tyrimų duomenimis
Saugymui pasirenkame S3 ir anksčiau sukurtą kibirą. Aws Glue Crawler, apie kurį pakalbėsiu šiek tiek vėliau, negali dirbti su priešdėliais S3 kibirėlyje, todėl svarbu jį palikti tuščią.
Likusios parinktys gali būti keičiamos atsižvelgiant į jūsų apkrovą; dažniausiai naudoju numatytąsias. Atminkite, kad S3 glaudinimas nepasiekiamas, tačiau pagal numatytuosius nustatymus ORC naudoja savąjį glaudinimą.
sklandžiai
Dabar, kai sukonfigūravome žurnalų saugojimą ir gavimą, turime sukonfigūruoti siuntimą. Mes naudosime
Pirmiausia mums reikia fluent.conf konfigūracijos failo. Sukurkite jį ir pridėkite šaltinį:
uostas 24224
surišti 0.0.0.0
Dabar galite paleisti „Fluentd“ serverį. Jei jums reikia sudėtingesnės konfigūracijos, eikite į
$ 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
Ši konfigūracija naudoja kelią /fluentd/log
į talpyklą išsaugoti žurnalus prieš siunčiant. Galite apsieiti ir be šito, bet tada, kai paleisite iš naujo, galite prarasti viską, kas saugoma talpykloje dėl laužymo darbų. Taip pat galite naudoti bet kurį prievadą; 24224 yra numatytasis Fluentd prievadas.
Dabar, kai veikia „Fluentd“, galime ten siųsti „Nginx“ žurnalus. Paprastai „Nginx“ paleidžiame „Docker“ konteineryje, tokiu atveju „Docker“ turi savąją „Fluentd“ registravimo tvarkyklę:
$ 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
Jei naudojate „Nginx“ kitaip, galite naudoti žurnalo failus, „Fluentd“.
Pridėkime anksčiau sukonfigūruotą žurnalo analizę prie „Fluent“ konfigūracijos:
<filter YOUR-NGINX-TAG.*>
@type parser
key_name log
emit_invalid_record_to_error false
<parse>
@type json
</parse>
</filter>
Ir žurnalų siuntimas į Kinesis naudojant
<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>
Atėnė
Jei viską sukonfigūravote teisingai, po kurio laiko (pagal numatytuosius nustatymus Kinesis gautus duomenis įrašo kartą per 10 minučių) turėtumėte matyti žurnalo failus S3. Kinesis Firehose "stebėjimo" meniu galite pamatyti, kiek duomenų įrašyta S3, taip pat klaidas. Nepamirškite suteikti „Kinesis“ vaidmens rašymo prieigos prie S3 segmento. Jei Kinesis nepavyko ko nors išanalizuoti, jis įtrauks klaidas į tą patį segmentą.
Dabar galite peržiūrėti duomenis „Athena“. Suraskime paskutines užklausas, kuriose pateikėme klaidų:
SELECT * FROM "db_name"."table_name" WHERE status > 499 ORDER BY created_at DESC limit 10;
Nuskaitomi visi įrašai pagal kiekvieną užklausą
Dabar mūsų žurnalai buvo apdoroti ir saugomi S3 ORC, suspausti ir paruošti analizei. Kinesis Firehose netgi suskirstė juos į katalogus kiekvienai valandai. Tačiau tol, kol lentelė nėra skaidoma, „Athena“ įkels visų laikų duomenis pagal kiekvieną užklausą, išskyrus retas išimtis. Tai didelė problema dėl dviejų priežasčių:
- Duomenų apimtis nuolat auga, stabdo užklausas;
- „Athena“ apmokestinama atsižvelgiant į nuskaitytų duomenų kiekį, mažiausiai 10 MB už užklausą.
Norėdami tai išspręsti, naudojame „AWS Glue Crawler“, kuri nuskaitys duomenis S3 ir įrašys skaidinio informaciją į „Glue Metastore“. Tai leis mums naudoti skaidinius kaip filtrą užklausant Athena ir nuskaitys tik užklausoje nurodytus katalogus.
„Amazon Glue Crawler“ nustatymas
„Amazon Glue Crawler“ nuskaito visus S3 kibiro duomenis ir sukuria lenteles su skaidiniais. Sukurkite klijų skaitytuvą naudodami AWS klijų konsolę ir pridėkite kibirą, kuriame saugosite duomenis. Galite naudoti vieną skaitytuvą keliems segmentams, tokiu atveju jis sukurs lenteles nurodytoje duomenų bazėje su pavadinimais, atitinkančiais segmentų pavadinimus. Jei planuojate reguliariai naudoti šiuos duomenis, būtinai sukonfigūruokite tikrinimo programos paleidimo tvarkaraštį, kad jis atitiktų jūsų poreikius. Visiems stalams naudojame vieną skaitytuvą, kuris veikia kas valandą.
Skirstytos lentelės
Pirmą kartą paleidus tikrintuvą, nustatymuose nurodytoje duomenų bazėje turėtų atsirasti kiekvieno nuskaityto segmento lentelės. Atidarykite „Athena“ konsolę ir raskite lentelę su „Nginx“ žurnalais. Pabandykime ką nors perskaityti:
SELECT * FROM "default"."part_demo_kinesis_bucket"
WHERE(
partition_0 = '2019' AND
partition_1 = '04' AND
partition_2 = '08' AND
partition_3 = '06'
);
Ši užklausa atrinks visus įrašus, gautus nuo 6 iki 7 val. 8 m. balandžio 2019 d. Tačiau kiek tai yra efektyviau nei tiesiog skaitymas iš neskirstytos lentelės? Išsiaiškinkime ir pasirinkite tuos pačius įrašus, filtruodami juos pagal laiko žymą:
3.59 sekundės ir 244.34 megabaitų duomenų duomenų rinkinyje, kuriame yra tik savaitės žurnalų. Išbandykime filtrą pagal skaidinį:
Šiek tiek greičiau, bet svarbiausia – tik 1.23 megabaitų duomenų! Būtų daug pigiau, jei ne minimalūs 10 megabaitų užklausai kainodaroje. Tačiau tai vis tiek daug geriau, o dideliuose duomenų rinkiniuose skirtumas bus daug įspūdingesnis.
Informacijos suvestinės kūrimas naudojant Cube.js
Norėdami surinkti prietaisų skydelį, naudojame Cube.js analitinę sistemą. Jis turi gana daug funkcijų, tačiau mus domina dvi: galimybė automatiškai naudoti skaidinių filtrus ir išankstinis duomenų agregavimas. Jis naudoja duomenų schemą
Sukurkime naują Cube.js programą. Kadangi jau naudojame AWS steką, logiška diegti naudoti Lambda. Galite naudoti greitąjį šabloną generavimui, jei planuojate priglobti Cube.js pagrindinę programą Heroku arba Docker. Dokumentuose aprašomi kiti
$ npm install -g cubejs-cli
$ cubejs create nginx-log-analytics -t serverless -d athena
Aplinkos kintamieji naudojami duomenų bazės prieigai cube.js konfigūruoti. Generatorius sukurs .env failą, kuriame galėsite nurodyti savo raktus
Dabar mums reikia
Kataloge schema
, sukurkite failą Logs.js
. Štai nginx duomenų modelio pavyzdys:
Modelio kodas
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`
}
}
});
Čia mes naudojame kintamąjį
Taip pat nustatome metriką ir parametrus, kuriuos norime rodyti prietaisų skydelyje, ir nurodome išankstinius sujungimus. Cube.js sukurs papildomas lenteles su iš anksto sukauptais duomenimis ir automatiškai atnaujins gautus duomenis. Tai ne tik pagreitina užklausas, bet ir sumažina Athena naudojimo išlaidas.
Pridėkime šią informaciją į duomenų schemos failą:
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`
)
}
}
}
Šiame modelyje nurodome, kad būtina iš anksto sukaupti visų naudojamų metrikų duomenis ir naudoti skirstymą pagal mėnesius.
Dabar galime surinkti prietaisų skydelį!
Cube.js backend suteikia
Cube.js serveris priima užklausą
{
"measures": ["Logs.errorCount"],
"timeDimensions": [
{
"dimension": "Logs.createdAt",
"dateRange": ["2019-01-01", "2019-01-07"],
"granularity": "day"
}
]
}
Įdiegkime Cube.js klientą ir React komponentų biblioteką per NPM:
$ npm i --save @cubejs-client/core @cubejs-client/react
Importuojame komponentus cubejs
и QueryRenderer
Norėdami atsisiųsti duomenis ir rinkti prietaisų skydelį:
Prietaisų skydelio kodas
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>
);
}}
/>
)
}
Informacijos suvestinės šaltiniai pasiekiami adresu
Šaltinis: www.habr.com