عادة، تُستخدم المنتجات التجارية أو البدائل الجاهزة مفتوحة المصدر، مثل Prometheus + Grafana، لمراقبة وتحليل تشغيل Nginx. يعد هذا خيارًا جيدًا للمراقبة أو التحليلات في الوقت الفعلي، ولكنه ليس مناسبًا جدًا للتحليل التاريخي. في أي مورد شائع، ينمو حجم البيانات من سجلات nginx بسرعة، ولتحليل كمية كبيرة من البيانات، من المنطقي استخدام شيء أكثر تخصصًا.
في هذه المقالة سأخبرك كيف يمكنك استخدامها
ليرة تركية: دكتور؛
لجمع المعلومات التي نستخدمها
جمع سجلات 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 غير متوفر في جميع المناطق.
إنشاء دائرة في وحدة تحكم أثينا
لنقم بإنشاء جدول في أثينا للسجلات. إنه ضروري لكل من الكتابة والقراءة إذا كنت تخطط لاستخدام 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
سيقوم Kinesis Firehose بكتابة البيانات المستلمة من Nginx إلى S3 بالتنسيق المحدد، وتقسيمها إلى أدلة بتنسيق YYYY/MM/DD/HH. سيكون هذا مفيدًا عند قراءة البيانات. يمكنك، بالطبع، الكتابة مباشرة إلى 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 من تحليل شيء ما، فسيضيف الأخطاء إلى نفس المجموعة.
الآن يمكنك عرض البيانات في أثينا. فلنبحث عن أحدث الطلبات التي قمنا بإرجاع أخطاء لها:
SELECT * FROM "db_name"."table_name" WHERE status > 499 ORDER BY created_at DESC limit 10;
مسح كافة السجلات لكل طلب
تمت الآن معالجة سجلاتنا وتخزينها في S3 في ORC، وتم ضغطها وجاهزة للتحليل. حتى أن Kinesis Firehose قام بتنظيمها في أدلة لكل ساعة. ومع ذلك، طالما أن الجدول غير مقسم، ستقوم Athena بتحميل البيانات الدائمة لكل طلب، مع استثناءات نادرة. وهذه مشكلة كبيرة لسببين:
- يتزايد حجم البيانات باستمرار، مما يؤدي إلى إبطاء الاستعلامات؛
- تتم محاسبة Athena بناءً على حجم البيانات الممسوحة ضوئيًا، بحد أدنى 10 ميجابايت لكل طلب.
لإصلاح ذلك، نستخدم AWS Glue Crawler، الذي سيقوم بالزحف إلى البيانات في S3 وكتابة معلومات القسم إلى Glue Metastore. سيسمح لنا هذا باستخدام الأقسام كمرشح عند الاستعلام عن Athena، وسيقوم فقط بفحص الدلائل المحددة في الاستعلام.
إعداد Amazon Glue Crawler
يقوم Amazon Glue Crawler بمسح جميع البيانات الموجودة في حاوية S3 وإنشاء جداول تحتوي على أقسام. قم بإنشاء Glue Crawler من وحدة تحكم AWS Glue وأضف مجموعة حيث تقوم بتخزين البيانات. يمكنك استخدام زاحف واحد لعدة مجموعات، وفي هذه الحالة سيقوم بإنشاء جداول في قاعدة البيانات المحددة بأسماء تطابق أسماء المجموعات. إذا كنت تخطط لاستخدام هذه البيانات بانتظام، فتأكد من تكوين جدول تشغيل الزاحف ليناسب احتياجاتك. نحن نستخدم زاحفًا واحدًا لجميع الطاولات، والذي يعمل كل ساعة.
جداول مقسمة
بعد الإطلاق الأول للزاحف، يجب أن تظهر الجداول الخاصة بكل مجموعة تم مسحها ضوئيًا في قاعدة البيانات المحددة في الإعدادات. افتح وحدة تحكم 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 ميجابايت من البيانات في مجموعة بيانات مع أسبوع واحد فقط من السجلات. لنجرب التصفية حسب القسم:
أسرع قليلاً، ولكن الأهم من ذلك - 1.23 ميجابايت فقط من البيانات! سيكون أرخص بكثير إن لم يكن الحد الأدنى 10 ميغابايت لكل طلب في التسعير. لكنه لا يزال أفضل بكثير، وفي مجموعات البيانات الكبيرة سيكون الفرق أكثر إثارة للإعجاب.
إنشاء لوحة تحكم باستخدام Cube.js
لتجميع لوحة المعلومات، نستخدم الإطار التحليلي Cube.js. يحتوي على الكثير من الوظائف، لكننا مهتمون بوظيفتين: القدرة على استخدام مرشحات الأقسام والتجميع المسبق للبيانات تلقائيًا. ويستخدم مخطط البيانات
لنقم بإنشاء تطبيق Cube.js جديد. وبما أننا نستخدم بالفعل مكدس AWS، فمن المنطقي استخدام Lambda للنشر. يمكنك استخدام القالب السريع للإنشاء إذا كنت تخطط لاستضافة الواجهة الخلفية 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`
)
}
}
}
نحدد في هذا النموذج أنه من الضروري تجميع البيانات مسبقًا لجميع المقاييس المستخدمة واستخدام التقسيم حسب الشهر.
الآن يمكننا تجميع لوحة القيادة!
توفر الواجهة الخلفية 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