تجزیه و تحلیل Log Nginx با استفاده از Amazon Athena و Cube.js

به طور معمول، محصولات تجاری یا جایگزین های متن باز آماده، مانند Prometheus + Grafana، برای نظارت و تجزیه و تحلیل عملکرد Nginx استفاده می شوند. این گزینه خوبی برای نظارت یا تجزیه و تحلیل بلادرنگ است، اما برای تحلیل تاریخی چندان مناسب نیست. در هر منبع محبوب، حجم داده‌های لاگ‌های nginx به سرعت در حال رشد است و برای تجزیه و تحلیل حجم زیادی از داده‌ها، منطقی است که از چیزی تخصصی‌تر استفاده کنیم.

در این مقاله به شما خواهم گفت که چگونه می توانید از آن استفاده کنید الههء عقل و زیبایی برای تجزیه و تحلیل گزارش‌ها، Nginx را به عنوان مثال در نظر می‌گیریم، و من نحوه جمع‌آوری داشبورد تحلیلی از این داده‌ها را با استفاده از چارچوب منبع باز cube.js نشان خواهم داد. در اینجا معماری کامل راه حل است:

تجزیه و تحلیل Log Nginx با استفاده از Amazon Athena و Cube.js

TL:DR;
پیوند به داشبورد تمام شده.

برای جمع آوری اطلاعات استفاده می کنیم روان، برای پردازش - AWS Kinesis Data Firehose и چسب AWS، برای ذخیره سازی - AWS S3. با استفاده از این بسته، می‌توانید نه تنها گزارش‌های nginx، بلکه رویدادهای دیگر و همچنین گزارش‌های سایر سرویس‌ها را ذخیره کنید. شما می توانید برخی از قطعات را با قطعات مشابه برای پشته خود جایگزین کنید، به عنوان مثال، می توانید لاگ به kinesis را مستقیماً از nginx بنویسید و از fluentd عبور کنید یا برای این کار از logstash استفاده کنید.

جمع آوری لاگ های 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 استفاده می کنیم. این به شما امکان می‌دهد لاگ‌ها را در یک مکان ذخیره و تجزیه و تحلیل کنید، زیرا آتنا می‌تواند مستقیماً با داده‌ها در S3 کار کند. بعداً در مقاله به شما خواهم گفت که چگونه لاگ ها را به درستی اضافه و پردازش کنید، اما ابتدا به یک سطل تمیز در S3 نیاز داریم که هیچ چیز دیگری در آن ذخیره نشود. ارزش این را دارد که از قبل در نظر بگیرید که در کدام منطقه سطل خود را ایجاد خواهید کرد، زیرا آتنا در همه مناطق در دسترس نیست.

ایجاد مدار در کنسول آتنا

بیایید یک جدول در آتنا برای لاگ ها ایجاد کنیم. اگر قصد استفاده از Kinesis Firehose را دارید، هم برای نوشتن و هم برای خواندن لازم است. کنسول آتنا را باز کنید و یک جدول ایجاد کنید:

ایجاد جدول 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 را با فرمت انتخاب شده می نویسد و آن ها را به دایرکتوری هایی با فرمت YYYY/MM/DD/HH تقسیم می کند. این در هنگام خواندن داده ها مفید خواهد بود. البته می توانید مستقیماً از fluentd روی S3 بنویسید، اما در این صورت باید JSON بنویسید و این به دلیل حجم بالای فایل ها ناکارآمد است. علاوه بر این، هنگام استفاده از PrestoDB یا Athena، JSON کندترین فرمت داده است. بنابراین کنسول Kinesis Firehose را باز کنید، روی "Create delivery stream" کلیک کنید، "direct PUT" را در قسمت "delivery" انتخاب کنید:

تجزیه و تحلیل Log Nginx با استفاده از Amazon Athena و Cube.js

در تب بعدی، "Record format conversion" - "Enabled" را انتخاب کنید و "Apache ORC" را به عنوان فرمت ضبط انتخاب کنید. طبق برخی تحقیقات اوون اومالی، این فرمت بهینه برای PrestoDB و Athena است. از جدولی که در بالا ایجاد کردیم به عنوان طرحواره استفاده می کنیم. لطفاً توجه داشته باشید که می‌توانید هر مکان S3 را در حرکت حرکتی مشخص کنید؛ فقط طرحواره از جدول استفاده می‌شود. اما اگر مکان S3 دیگری را مشخص کنید، دیگر نمی توانید این رکوردها را از این جدول بخوانید.

تجزیه و تحلیل Log Nginx با استفاده از Amazon Athena و Cube.js

ما S3 را برای ذخیره سازی و سطلی را که قبلا ایجاد کردیم انتخاب می کنیم. Aws Glue Crawler که کمی بعداً در مورد آن صحبت خواهم کرد، نمی تواند با پیشوندها در یک سطل S3 کار کند، بنابراین مهم است که آن را خالی بگذارید.

تجزیه و تحلیل Log Nginx با استفاده از Amazon Athena و Cube.js

گزینه های باقی مانده را می توان بسته به بار شما تغییر داد؛ من معمولاً از گزینه های پیش فرض استفاده می کنم. توجه داشته باشید که فشرده سازی S3 در دسترس نیست، اما ORC به طور پیش فرض از فشرده سازی بومی استفاده می کند.

روان

اکنون که ذخیره و دریافت گزارش‌ها را پیکربندی کرده‌ایم، باید ارسال را پیکربندی کنیم. ما استفاده خواهیم کرد روان، زیرا من عاشق Ruby هستم، اما می توانید از Logstash استفاده کنید یا لاگ ها را مستقیماً به kinesis ارسال کنید. سرور Fluentd را می توان به روش های مختلفی راه اندازی کرد، من در مورد docker به شما خواهم گفت زیرا ساده و راحت است.

ابتدا به فایل پیکربندی 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 اجرا می کنیم، در این صورت داکر دارای یک درایور لاگ بومی برای 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 را متفاوت اجرا می کنید، می توانید از فایل های log استفاده کنید، Fluentd افزونه دم فایل.

بیایید تجزیه گزارش پیکربندی شده در بالا را به پیکربندی Fluent اضافه کنیم:

<filter YOUR-NGINX-TAG.*>
  @type parser
  key_name log
  emit_invalid_record_to_error false
  <parse>
    @type json
  </parse>
</filter>

و ارسال سیاهههای مربوط به Kinesis با استفاده از پلاگین kinesis firehose:

<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 داده های تمام زمان را در هر درخواست بارگیری می کند، به استثنای نادر. این یک مشکل بزرگ به دو دلیل است:

  • حجم داده ها به طور مداوم در حال رشد است و سرعت درخواست ها را کاهش می دهد.
  • صورتحساب آتنا بر اساس حجم داده های اسکن شده، با حداقل 10 مگابایت در هر درخواست صورت می گیرد.

برای رفع این مشکل، از AWS Glue Crawler استفاده می‌کنیم که داده‌ها را در S3 می‌خزد و اطلاعات پارتیشن را در Glue Metastore می‌نویسد. این به ما امکان می دهد از پارتیشن ها به عنوان فیلتر هنگام پرس و جوی Athena استفاده کنیم و فقط دایرکتوری های مشخص شده در پرس و جو را اسکن می کند.

راه اندازی Amazon Glue Crawler

Amazon Glue Crawler تمام داده های موجود در سطل S3 را اسکن می کند و جداول با پارتیشن ایجاد می کند. یک خزنده چسب از کنسول AWS Glue ایجاد کنید و یک سطل اضافه کنید که در آن داده ها را ذخیره می کنید. شما می توانید از یک خزنده برای چندین سطل استفاده کنید، در این صورت جدول هایی را در پایگاه داده مشخص شده با نام هایی که با نام سطل ها مطابقت دارند ایجاد می کند. اگر قصد دارید به طور منظم از این داده ها استفاده کنید، حتماً برنامه راه اندازی Crawler را مطابق با نیازهای خود پیکربندی کنید. ما از یک خزنده برای همه جداول استفاده می کنیم که هر ساعت اجرا می شود.

جداول پارتیشن بندی شده

پس از اولین راه اندازی خزنده، جداول برای هر سطل اسکن شده باید در پایگاه داده مشخص شده در تنظیمات ظاهر شود. کنسول آتنا را باز کنید و جدولی را با لاگ های 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 انتخاب می کند. اما این کار چقدر کارآمدتر از خواندن از روی یک جدول غیرپارتیشن بندی شده است؟ بیایید همان رکوردها را پیدا کرده و انتخاب کنیم و آنها را بر اساس زمان فیلتر کنیم:

تجزیه و تحلیل Log Nginx با استفاده از Amazon Athena و Cube.js

3.59 ثانیه و 244.34 مگابایت داده در یک مجموعه داده با تنها یک هفته گزارش. بیایید فیلتر به پارتیشن را امتحان کنیم:

تجزیه و تحلیل Log Nginx با استفاده از Amazon Athena و Cube.js

کمی سریعتر، اما مهمتر از همه - فقط 1.23 مگابایت داده! اگر حداقل 10 مگابایت در هر درخواست در قیمت گذاری نباشد، بسیار ارزان تر خواهد بود. اما هنوز خیلی بهتر است و در مجموعه داده های بزرگ این تفاوت بسیار چشمگیرتر خواهد بود.

ساخت داشبورد با استفاده از Cube.js

برای مونتاژ داشبورد از چارچوب تحلیلی Cube.js استفاده می کنیم. این توابع بسیار زیادی دارد، اما ما به دو مورد علاقه داریم: توانایی استفاده خودکار از فیلترهای پارتیشن و پیش تجمع داده ها. از طرح داده استفاده می کند طرح واره داده، در جاوا اسکریپت برای تولید SQL و اجرای کوئری پایگاه داده نوشته شده است. ما فقط باید نحوه استفاده از فیلتر پارتیشن را در طرح داده نشان دهیم.

بیایید یک برنامه جدید 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`
    }
  }
});

در اینجا ما از متغیر استفاده می کنیم FILTER_PARAMSبرای ایجاد یک پرس و جو SQL با فیلتر پارتیشن.

همچنین معیارها و پارامترهایی را که می‌خواهیم روی داشبورد نمایش دهیم تنظیم کرده و پیش‌تجمیع‌ها را مشخص می‌کنیم. Cube.js جداول اضافی با داده های از پیش انباشته شده ایجاد می کند و به صورت خودکار داده ها را به محض رسیدن به روز می کند. این امر نه تنها باعث افزایش سرعت پرس و جو می شود، بلکه هزینه استفاده از آتنا را نیز کاهش می دهد.

بیایید این اطلاعات را به فایل طرح داده اضافه کنیم:

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 فراهم می کند REST API و مجموعه ای از کتابخانه های مشتری برای فریمورک های فرانت اند محبوب. ما از نسخه React کلاینت برای ساخت داشبورد استفاده خواهیم کرد. Cube.js فقط داده ها را ارائه می دهد، بنابراین ما به یک کتابخانه تجسم نیاز داریم - من آن را دوست دارم نمودارهای مجدد، اما می توانید از هر کدام استفاده کنید.

سرور Cube.js درخواست را می پذیرد فرمت JSON، که معیارهای مورد نیاز را مشخص می کند. به عنوان مثال، برای محاسبه تعداد خطاهای Nginx در روز، باید درخواست زیر را ارسال کنید:

{
  "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

اضافه کردن نظر