Συνήθως, εμπορικά προϊόντα ή έτοιμες εναλλακτικές λύσεις ανοιχτού κώδικα, όπως το Prometheus + Grafana, χρησιμοποιούνται για την παρακολούθηση και την ανάλυση της λειτουργίας του Nginx. Αυτή είναι μια καλή επιλογή για παρακολούθηση ή ανάλυση σε πραγματικό χρόνο, αλλά όχι πολύ βολική για ιστορική ανάλυση. Σε οποιονδήποτε δημοφιλές πόρο, ο όγκος των δεδομένων από τα αρχεία καταγραφής nginx αυξάνεται ραγδαία και για να αναλύσετε μεγάλο όγκο δεδομένων, είναι λογικό να χρησιμοποιήσετε κάτι πιο εξειδικευμένο.
Σε αυτό το άρθρο θα σας πω πώς μπορείτε να χρησιμοποιήσετε
TL:DR;
Για τη συλλογή πληροφοριών που χρησιμοποιούμε
Συλλογή αρχείων καταγραφής Nginx
Από προεπιλογή, τα αρχεία καταγραφής Nginx μοιάζουν κάπως έτσι:
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" "-"
Μπορούν να αναλυθούν, αλλά είναι πολύ πιο εύκολο να διορθωθεί η διαμόρφωση Nginx έτσι ώστε να παράγει αρχεία καταγραφής σε 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 για αποθήκευση
Για την αποθήκευση αρχείων καταγραφής, θα χρησιμοποιήσουμε το S3. Αυτό σας επιτρέπει να αποθηκεύετε και να αναλύετε αρχεία καταγραφής σε ένα μέρος, καθώς η Athena μπορεί να εργαστεί απευθείας με δεδομένα στο S3. Αργότερα στο άρθρο θα σας πω πώς να προσθέτετε και να επεξεργάζεστε σωστά αρχεία καταγραφής, αλλά πρώτα χρειαζόμαστε έναν καθαρό κάδο στο S3, στον οποίο δεν θα αποθηκευτεί τίποτα άλλο. Αξίζει να σκεφτείτε εκ των προτέρων σε ποια περιοχή θα δημιουργήσετε τον κάδο σας, επειδή το Athena δεν είναι διαθέσιμο σε όλες τις περιοχές.
Δημιουργία κυκλώματος στην κονσόλα Athena
Ας δημιουργήσουμε έναν πίνακα στο Athena για κούτσουρα. Χρειάζεται τόσο για γραφή όσο και για ανάγνωση εάν σκοπεύετε να χρησιμοποιήσετε το Kinesis Firehose. Ανοίξτε την κονσόλα Athena και δημιουργήστε έναν πίνακα:
Δημιουργία πίνακα 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');
Δημιουργία Kinesis Firehose Stream
Το Kinesis Firehose θα γράψει τα δεδομένα που λαμβάνονται από το Nginx στο S3 στην επιλεγμένη μορφή, χωρίζοντάς τα σε καταλόγους σε μορφή ΕΕΕΕ/ΜΜ/ΗΗ/ΩΩ. Αυτό θα σας φανεί χρήσιμο κατά την ανάγνωση δεδομένων. Μπορείτε, φυσικά, να γράψετε απευθείας στο S3 από το fluentd, αλλά σε αυτήν την περίπτωση θα πρέπει να γράψετε JSON και αυτό είναι αναποτελεσματικό λόγω του μεγάλου μεγέθους των αρχείων. Επιπλέον, όταν χρησιμοποιείτε PrestoDB ή Athena, το JSON είναι η πιο αργή μορφή δεδομένων. Ανοίξτε λοιπόν την κονσόλα Kinesis Firehose, κάντε κλικ στο «Δημιουργία ροής παράδοσης», επιλέξτε «άμεση PUT» στο πεδίο «παράδοση»:
Στην επόμενη καρτέλα, επιλέξτε "Μετατροπή μορφής εγγραφής" - "Ενεργοποιημένη" και επιλέξτε "Apache ORC" ως μορφή εγγραφής. Σύμφωνα με κάποιες έρευνες
Επιλέγουμε το S3 για αποθήκευση και τον κάδο που δημιουργήσαμε νωρίτερα. Το Aws Glue Crawler, για το οποίο θα μιλήσω λίγο αργότερα, δεν μπορεί να λειτουργήσει με προθέματα σε έναν κάδο S3, επομένως είναι σημαντικό να το αφήσετε κενό.
Οι υπόλοιπες επιλογές μπορούν να αλλάξουν ανάλογα με το φορτίο σας. Συνήθως χρησιμοποιώ τις προεπιλεγμένες. Σημειώστε ότι η συμπίεση S3 δεν είναι διαθέσιμη, αλλά το ORC χρησιμοποιεί εγγενή συμπίεση από προεπιλογή.
Άπταιστα
Τώρα που έχουμε διαμορφώσει την αποθήκευση και τη λήψη αρχείων καταγραφής, πρέπει να διαμορφώσουμε την αποστολή. Θα το χρησιμοποιησουμε
Πρώτα, χρειαζόμαστε το αρχείο διαμόρφωσης fluent.conf. Δημιουργήστε το και προσθέστε την πηγή:
λιμάνι 24224
δέσμευση 0.0.0.0
Τώρα μπορείτε να ξεκινήσετε τον διακομιστή Fluentd. Εάν χρειάζεστε μια πιο προηγμένη διαμόρφωση, μεταβείτε στο
$ 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
Αυτή η διαμόρφωση χρησιμοποιεί τη διαδρομή /fluentd/log
στην προσωρινή αποθήκευση αρχείων καταγραφής πριν από την αποστολή. Μπορείτε να το κάνετε χωρίς αυτό, αλλά στη συνέχεια, όταν κάνετε επανεκκίνηση, μπορείτε να χάσετε ό,τι είναι αποθηκευμένο στην κρυφή μνήμη με απίστευτη εργασία. Μπορείτε επίσης να χρησιμοποιήσετε οποιαδήποτε θύρα, η 24224 είναι η προεπιλεγμένη θύρα Fluentd.
Τώρα που τρέχουμε το Fluentd, μπορούμε να στείλουμε αρχεία καταγραφής Nginx εκεί. Συνήθως εκτελούμε το Nginx σε ένα κοντέινερ Docker, οπότε το Docker έχει ένα εγγενές πρόγραμμα οδήγησης καταγραφής για το 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
Εάν εκτελείτε το Nginx διαφορετικά, μπορείτε να χρησιμοποιήσετε αρχεία καταγραφής, όπως έχει το Fluentd
Ας προσθέσουμε την ανάλυση καταγραφής που διαμορφώθηκε παραπάνω στη διαμόρφωση Fluent:
<filter YOUR-NGINX-TAG.*>
@type parser
key_name log
emit_invalid_record_to_error false
<parse>
@type json
</parse>
</filter>
Και αποστολή αρχείων καταγραφής στο Kinesis χρησιμοποιώντας
<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>
Αθήνα
Εάν έχετε ρυθμίσει τα πάντα σωστά, τότε μετά από λίγο (από προεπιλογή, το Kinesis καταγράφει τα λαμβανόμενα δεδομένα μία φορά κάθε 10 λεπτά) θα πρέπει να δείτε αρχεία καταγραφής στο S3. Στο μενού «παρακολούθηση» του Kinesis Firehose μπορείτε να δείτε πόσα δεδομένα καταγράφονται στο S3, καθώς και σφάλματα. Μην ξεχάσετε να δώσετε πρόσβαση εγγραφής στον κάδο S3 στον ρόλο Kinesis. Εάν το Kinesis δεν μπορούσε να αναλύσει κάτι, θα προσθέσει τα σφάλματα στον ίδιο κάδο.
Τώρα μπορείτε να δείτε τα δεδομένα στο Athena. Ας βρούμε τα πιο πρόσφατα αιτήματα για τα οποία επιστρέψαμε σφάλματα:
SELECT * FROM "db_name"."table_name" WHERE status > 499 ORDER BY created_at DESC limit 10;
Σάρωση όλων των εγγραφών για κάθε αίτημα
Τώρα τα αρχεία καταγραφής μας έχουν υποστεί επεξεργασία και αποθηκευτεί σε S3 σε ORC, συμπιεσμένα και έτοιμα για ανάλυση. Η Kinesis Firehose μάλιστα τα οργάνωσε σε καταλόγους για κάθε ώρα. Ωστόσο, εφόσον ο πίνακας δεν είναι χωρισμένος, η Athena θα φορτώνει δεδομένα όλων των εποχών σε κάθε αίτημα, με σπάνιες εξαιρέσεις. Αυτό είναι μεγάλο πρόβλημα για δύο λόγους:
- Ο όγκος των δεδομένων αυξάνεται συνεχώς, επιβραδύνοντας τα ερωτήματα.
- Το Athena χρεώνεται με βάση τον όγκο των δεδομένων που έχουν σαρωθεί, με τουλάχιστον 10 MB ανά αίτημα.
Για να διορθωθεί αυτό, χρησιμοποιούμε το AWS Glue Crawler, το οποίο θα ανιχνεύσει τα δεδομένα στο S3 και θα γράψει τις πληροφορίες του διαμερίσματος στο Glue Metastore. Αυτό θα μας επιτρέψει να χρησιμοποιήσουμε κατατμήσεις ως φίλτρο κατά την υποβολή ερωτημάτων στο Athena και θα σαρώσει μόνο τους καταλόγους που καθορίζονται στο ερώτημα.
Ρύθμιση του Amazon Glue Crawler
Το Amazon Glue Crawler σαρώνει όλα τα δεδομένα στον κάδο S3 και δημιουργεί πίνακες με διαμερίσματα. Δημιουργήστε ένα Glue Crawler από την κονσόλα AWS Glue και προσθέστε έναν κάδο όπου αποθηκεύετε τα δεδομένα. Μπορείτε να χρησιμοποιήσετε έναν ανιχνευτή για πολλούς κάδους, οπότε θα δημιουργήσει πίνακες στην καθορισμένη βάση δεδομένων με ονόματα που ταιριάζουν με τα ονόματα των κάδων. Εάν σκοπεύετε να χρησιμοποιείτε αυτά τα δεδομένα τακτικά, φροντίστε να διαμορφώσετε το πρόγραμμα εκκίνησης του Crawler για να ταιριάζει στις ανάγκες σας. Χρησιμοποιούμε ένα Crawler για όλα τα τραπέζια, το οποίο εκτελείται κάθε ώρα.
Χωρισμένα τραπέζια
Μετά την πρώτη εκκίνηση του προγράμματος ανίχνευσης, οι πίνακες για κάθε σαρωμένο κάδο θα πρέπει να εμφανίζονται στη βάση δεδομένων που καθορίζεται στις ρυθμίσεις. Ανοίξτε την κονσόλα Athena και βρείτε τον πίνακα με αρχεία καταγραφής Nginx. Ας προσπαθήσουμε να διαβάσουμε κάτι:
SELECT * FROM "default"."part_demo_kinesis_bucket"
WHERE(
partition_0 = '2019' AND
partition_1 = '04' AND
partition_2 = '08' AND
partition_3 = '06'
);
Αυτό το ερώτημα θα επιλέξει όλες τις εγγραφές που λαμβάνονται μεταξύ 6 π.μ. και 7 π.μ. στις 8 Απριλίου 2019. Αλλά πόσο πιο αποτελεσματικό είναι αυτό από την απλή ανάγνωση από έναν πίνακα χωρίς διαμερίσματα; Ας μάθουμε και ας επιλέξουμε τις ίδιες εγγραφές, φιλτράροντάς τις κατά χρονική σήμανση:
3.59 δευτερόλεπτα και 244.34 megabyte δεδομένων σε ένα σύνολο δεδομένων με μόνο μια εβδομάδα αρχείων καταγραφής. Ας δοκιμάσουμε ένα φίλτρο ανά διαμέρισμα:
Λίγο πιο γρήγορα, αλλά το πιο σημαντικό - μόνο 1.23 megabyte δεδομένων! Θα ήταν πολύ φθηνότερο αν δεν υπήρχαν τα ελάχιστα 10 megabyte ανά αίτημα στην τιμολόγηση. Αλλά εξακολουθεί να είναι πολύ καλύτερο και σε μεγάλα σύνολα δεδομένων η διαφορά θα είναι πολύ πιο εντυπωσιακή.
Δημιουργία πίνακα ελέγχου χρησιμοποιώντας το Cube.js
Για τη συναρμολόγηση του πίνακα εργαλείων, χρησιμοποιούμε το αναλυτικό πλαίσιο Cube.js. Έχει πολλές λειτουργίες, αλλά μας ενδιαφέρουν δύο: η δυνατότητα αυτόματης χρήσης φίλτρων διαμερισμάτων και η προ-συγκέντρωση δεδομένων. Χρησιμοποιεί σχήμα δεδομένων
Ας δημιουργήσουμε μια νέα εφαρμογή Cube.js. Εφόσον χρησιμοποιούμε ήδη τη στοίβα AWS, είναι λογικό να χρησιμοποιούμε το Lambda για ανάπτυξη. Μπορείτε να χρησιμοποιήσετε το πρότυπο express για γενιά, εάν σκοπεύετε να φιλοξενήσετε το backend του Cube.js στο Heroku ή στο Docker. Η τεκμηρίωση περιγράφει άλλα
$ npm install -g cubejs-cli
$ cubejs create nginx-log-analytics -t serverless -d athena
Οι μεταβλητές περιβάλλοντος χρησιμοποιούνται για τη διαμόρφωση της πρόσβασης στη βάση δεδομένων στο cube.js. Η γεννήτρια θα δημιουργήσει ένα αρχείο .env στο οποίο μπορείτε να καθορίσετε τα κλειδιά σας
Τώρα χρειαζόμαστε
Στον κατάλογο schema
, δημιουργήστε ένα αρχείο Logs.js
. Ακολουθεί ένα παράδειγμα μοντέλου δεδομένων για το nginx:
Κωδικός μοντέλου
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`
}
}
});
Εδώ χρησιμοποιούμε τη μεταβλητή
Ορίζουμε επίσης τις μετρήσεις και τις παραμέτρους που θέλουμε να εμφανίζονται στον πίνακα ελέγχου και καθορίζουμε προ-συγκεντρώσεις. Το Cube.js θα δημιουργήσει πρόσθετους πίνακες με προ-συγκεντρωτικά δεδομένα και θα ενημερώνει αυτόματα τα δεδομένα κατά την άφιξή τους. Αυτό όχι μόνο επιταχύνει τα ερωτήματα, αλλά μειώνει και το κόστος χρήσης του Athena.
Ας προσθέσουμε αυτές τις πληροφορίες στο αρχείο σχήματος δεδομένων:
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`
)
}
}
}
Σε αυτό το μοντέλο καθορίζουμε ότι είναι απαραίτητο να προ-συγκεντρωθούν δεδομένα για όλες τις μετρήσεις που χρησιμοποιούνται και να χρησιμοποιηθεί η κατάτμηση ανά μήνα.
Τώρα μπορούμε να συναρμολογήσουμε το ταμπλό!
Το backend του Cube.js παρέχει
Ο διακομιστής Cube.js δέχεται το αίτημα
{
"measures": ["Logs.errorCount"],
"timeDimensions": [
{
"dimension": "Logs.createdAt",
"dateRange": ["2019-01-01", "2019-01-07"],
"granularity": "day"
}
]
}
Ας εγκαταστήσουμε τον πελάτη Cube.js και τη βιβλιοθήκη στοιχείων React μέσω NPM:
$ npm i --save @cubejs-client/core @cubejs-client/react
Εισάγουμε εξαρτήματα cubejs
и QueryRenderer
για λήψη των δεδομένων και συλλογή του πίνακα ελέγχου:
Κωδικός πίνακα ελέγχου
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>
);
}}
/>
)
}
Οι πηγές του πίνακα ελέγχου είναι διαθέσιμες στη διεύθυνση
Πηγή: www.habr.com