Tipik olarak, Nginx'in çalışmasını izlemek ve analiz etmek için ticari ürünler veya Prometheus + Grafana gibi hazır açık kaynak alternatifleri kullanılır. Bu, izleme veya gerçek zamanlı analiz için iyi bir seçenektir ancak geçmiş analizler için pek uygun değildir. Herhangi bir popüler kaynakta, nginx günlüklerinden gelen veri hacmi hızla artıyor ve büyük miktarda veriyi analiz etmek için daha uzmanlaşmış bir şey kullanmak mantıklıdır.
Bu yazımda size nasıl kullanabileceğinizi anlatacağım.
TP: DR;
Kullandığımız bilgileri toplamak için
Nginx günlüklerini toplama
Varsayılan olarak Nginx günlükleri şuna benzer:
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" "-"
Bunlar ayrıştırılabilir, ancak Nginx yapılandırmasını JSON'da günlükler oluşturacak şekilde düzeltmek çok daha kolaydır:
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;
Depolama için S3
Günlükleri depolamak için S3'ü kullanacağız. Athena S3'teki verilerle doğrudan çalışabildiği için bu, günlükleri tek bir yerde saklamanıza ve analiz etmenize olanak tanır. Makalenin ilerleyen kısımlarında size günlükleri nasıl doğru bir şekilde ekleyeceğinizi ve işleyeceğinizi anlatacağım, ancak önce S3'te başka hiçbir şeyin saklanmayacağı temiz bir kovaya ihtiyacımız var. Athena her bölgede mevcut olmadığından kovanızı hangi bölgede oluşturacağınızı önceden düşünmeye değer.
Athena konsolunda devre oluşturma
Loglar için Athena'da bir tablo oluşturalım. Kinesis Firehose'u kullanmayı planlıyorsanız hem yazma hem de okuma için gereklidir. Athena konsolunu açın ve bir tablo oluşturun:
SQL tablosu oluşturma
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 Akışı Oluşturma
Kinesis Firehose, Nginx'ten alınan verileri seçilen formatta S3'e yazacak ve YYYY/AA/GG/SS formatındaki dizinlere bölecektir. Bu, verileri okurken kullanışlı olacaktır. Elbette fluentd'den doğrudan S3'e yazabilirsiniz ancak bu durumda JSON yazmanız gerekecektir ve bu, dosyaların büyük boyutundan dolayı verimsizdir. Ayrıca PrestoDB veya Athena kullanıldığında JSON en yavaş veri formatıdır. Kinesis Firehose konsolunu açın, "Teslimat akışı oluştur"a tıklayın, "teslimat" alanında "doğrudan PUT"u seçin:
Bir sonraki sekmede “Kayıt formatı dönüştürme” - “Etkin” seçeneğini seçin ve kayıt formatı olarak “Apache ORC” seçeneğini seçin. Bazı araştırmalara göre
Depolama için S3'ü ve daha önce oluşturduğumuz kovayı seçiyoruz. Biraz sonra bahsedeceğim Aws Glue Crawler, S3 kovasındaki öneklerle çalışamıyor bu yüzden boş bırakmak önemli.
Kalan seçenekler yükünüze göre değiştirilebilir; ben genellikle varsayılan olanları kullanıyorum. S3 sıkıştırmasının mevcut olmadığını ancak ORC'nin varsayılan olarak yerel sıkıştırmayı kullandığını unutmayın.
akıcı
Artık günlüklerin saklanmasını ve alınmasını yapılandırdığımıza göre, göndermeyi yapılandırmamız gerekiyor. Kullanacağız
Öncelikle fluent.conf yapılandırma dosyasına ihtiyacımız var. Oluşturun ve kaynak ekleyin:
liman 24224
0.0.0.0'i bağla
Artık Fluentd sunucusunu başlatabilirsiniz. Daha gelişmiş bir yapılandırmaya ihtiyacınız varsa şu adrese gidin:
$ 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
Bu yapılandırma yolu kullanır /fluentd/log
Günlükleri göndermeden önce önbelleğe almak için. Bu olmadan da yapabilirsiniz, ancak yeniden başlattığınızda, yıpratıcı emek nedeniyle önbelleğe alınan her şeyi kaybedebilirsiniz. Ayrıca herhangi bir bağlantı noktasını da kullanabilirsiniz; 24224 varsayılan Fluentd bağlantı noktasıdır.
Artık Fluentd çalıştığına göre Nginx loglarını oraya gönderebiliriz. Genellikle Nginx'i bir Docker kapsayıcısında çalıştırırız; bu durumda Docker'ın Fluentd için yerel bir günlük kaydı sürücüsü vardır:
$ 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'i farklı çalıştırırsanız günlük dosyalarını kullanabilirsiniz, Fluentd'in
Yukarıda yapılandırılan günlük ayrıştırmayı Fluent yapılandırmasına ekleyelim:
<filter YOUR-NGINX-TAG.*>
@type parser
key_name log
emit_invalid_record_to_error false
<parse>
@type json
</parse>
</filter>
Ve günlükleri kullanarak Kinesis'e gönderme
<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
Her şeyi doğru yapılandırdıysanız, bir süre sonra (varsayılan olarak Kinesis kayıtları her 10 dakikada bir veri alır) S3'te günlük dosyalarını görmelisiniz. Kinesis Firehose'un "izleme" menüsünde S3'te ne kadar veri kaydedildiğini ve hataları görebilirsiniz. Kinesis rolüne S3 klasörüne yazma erişimi vermeyi unutmayın. Kinesis bir şeyi ayrıştıramazsa hataları aynı klasöre ekleyecektir.
Artık verileri Athena'da görüntüleyebilirsiniz. Hata döndürdüğümüz en son istekleri bulalım:
SELECT * FROM "db_name"."table_name" WHERE status > 499 ORDER BY created_at DESC limit 10;
Her istek için tüm kayıtları tarama
Artık günlüklerimiz ORC'de S3'te işlendi ve saklandı, sıkıştırıldı ve analize hazır hale getirildi. Kinesis Firehose bunları her saat için dizinler halinde bile düzenledi. Ancak tablo bölümlendirilmediği sürece Athena, nadir istisnalar dışında her istekte tüm zamanların verilerini yükleyecektir. Bu iki nedenden dolayı büyük bir sorundur:
- Veri hacmi sürekli artıyor, bu da sorguları yavaşlatıyor;
- Athena, istek başına minimum 10 MB olmak üzere, taranan veri hacmine göre faturalandırılır.
Bunu düzeltmek için S3'teki verileri tarayacak ve bölüm bilgilerini Glue Metastore'a yazacak olan AWS Glue Crawler'ı kullanıyoruz. Bu, Athena'yı sorgularken bölümleri filtre olarak kullanmamızı sağlayacak ve yalnızca sorguda belirtilen dizinleri tarayacaktır.
Amazon Glue Crawler'ı kurma
Amazon Glue Crawler, S3 klasöründeki tüm verileri tarar ve bölümlü tablolar oluşturur. AWS Glue konsolundan bir Glue Crawler oluşturun ve verileri depoladığınız yere bir klasör ekleyin. Bir tarayıcıyı birkaç paket için kullanabilirsiniz; bu durumda, belirtilen veritabanında paketlerin adlarıyla eşleşen adlara sahip tablolar oluşturulur. Bu verileri düzenli olarak kullanmayı planlıyorsanız Crawler'ın başlatma programını ihtiyaçlarınıza uygun şekilde yapılandırdığınızdan emin olun. Tüm tablolar için her saat başı çalışan bir Tarayıcı kullanıyoruz.
Bölümlenmiş tablolar
Tarayıcının ilk başlatılmasından sonra, taranan her pakete ilişkin tablolar, ayarlarda belirtilen veritabanında görünmelidir. Athena konsolunu açın ve Nginx günlüklerinin bulunduğu tabloyu bulun. Bir şeyler okumaya çalışalım:
SELECT * FROM "default"."part_demo_kinesis_bucket"
WHERE(
partition_0 = '2019' AND
partition_1 = '04' AND
partition_2 = '08' AND
partition_3 = '06'
);
Bu sorgu, 6 Nisan 7 sabah 8 ile sabah 2019 arasında alınan tüm kayıtları seçecektir. Ancak bu, bölümlenmemiş bir tablodan okumaktan ne kadar daha verimli? Aynı kayıtları bulup seçelim ve bunları zaman damgasına göre filtreleyelim:
Yalnızca bir haftalık günlüklerden oluşan bir veri kümesinde 3.59 saniye ve 244.34 megabayt veri. Bölüme göre filtrelemeyi deneyelim:
Biraz daha hızlı ama en önemlisi yalnızca 1.23 megabayt veri! Fiyatlandırmada istek başına minimum 10 megabayt olmasaydı çok daha ucuz olurdu. Ancak yine de çok daha iyi ve büyük veri kümelerinde fark çok daha etkileyici olacak.
Cube.js kullanarak bir kontrol paneli oluşturma
Kontrol panelini oluşturmak için Cube.js analitik çerçevesini kullanıyoruz. Oldukça fazla işlevi var ama biz ikisiyle ilgileniyoruz: bölüm filtrelerini otomatik olarak kullanma yeteneği ve veri ön toplama. Veri şemasını kullanır
Yeni bir Cube.js uygulaması oluşturalım. Zaten AWS yığınını kullandığımızdan dağıtım için Lambda'yı kullanmak mantıklıdır. Cube.js arka ucunu Heroku veya Docker'da barındırmayı planlıyorsanız, oluşturma için ekspres şablonu kullanabilirsiniz. Belgeler diğerlerini açıklamaktadır
$ npm install -g cubejs-cli
$ cubejs create nginx-log-analytics -t serverless -d athena
Ortam değişkenleri cube.js'de veritabanı erişimini yapılandırmak için kullanılır. Oluşturucu, anahtarlarınızı belirtebileceğiniz bir .env dosyası oluşturacaktır.
Şimdi ihtiyacımız var
dizinde schema
, bir dosya oluştur Logs.js
. İşte nginx için örnek bir veri modeli:
Model kodu
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`
}
}
});
Burada değişkeni kullanıyoruz
Ayrıca kontrol panelinde görüntülemek istediğimiz metrikleri ve parametreleri ayarlıyor ve ön toplamaları belirliyoruz. Cube.js, önceden toplanmış verilerle ek tablolar oluşturacak ve veriler geldiğinde otomatik olarak güncelleyecektir. Bu yalnızca sorguları hızlandırmakla kalmaz, aynı zamanda Athena'yı kullanma maliyetini de azaltır.
Bu bilgiyi veri şeması dosyasına ekleyelim:
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`
)
}
}
}
Bu modelde, kullanılan tüm ölçümler için verilerin önceden toplanmasının ve aya göre bölümlemenin kullanılmasının gerekli olduğunu belirtiyoruz.
Artık kontrol panelini monte edebiliriz!
Cube.js arka ucu şunları sağlar
Cube.js sunucusu isteği kabul eder
{
"measures": ["Logs.errorCount"],
"timeDimensions": [
{
"dimension": "Logs.createdAt",
"dateRange": ["2019-01-01", "2019-01-07"],
"granularity": "day"
}
]
}
Cube.js istemcisini ve React bileşen kütüphanesini NPM aracılığıyla yükleyelim:
$ npm i --save @cubejs-client/core @cubejs-client/react
Bileşenleri ithal ediyoruz cubejs
и QueryRenderer
verileri indirmek ve kontrol panelini toplamak için:
Kontrol paneli kodu
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>
);
}}
/>
)
}
Kontrol paneli kaynakları şu adreste mevcuttur:
Kaynak: habr.com