Nginx log-ի վերլուծություն՝ օգտագործելով Amazon Athena-ն և Cube.js-ը

Սովորաբար, առևտրային արտադրանքները կամ պատրաստի բաց կոդով այլընտրանքները, ինչպիսիք են Prometheus + Grafana-ն, օգտագործվում են Nginx-ի աշխատանքը վերահսկելու և վերլուծելու համար: Սա լավ տարբերակ է մոնիտորինգի կամ իրական ժամանակի վերլուծության համար, բայց այնքան էլ հարմար չէ պատմական վերլուծության համար: Ցանկացած հանրաճանաչ ռեսուրսի վրա nginx տեղեկամատյանների տվյալների ծավալը արագորեն աճում է, և մեծ քանակությամբ տվյալներ վերլուծելու համար տրամաբանական է օգտագործել ավելի մասնագիտացված բան:

Այս հոդվածում ես ձեզ կասեմ, թե ինչպես կարող եք օգտագործել Athena տեղեկամատյանները վերլուծելու համար՝ որպես օրինակ վերցնելով Nginx-ը, և ես ցույց կտամ, թե ինչպես կարելի է հավաքել վերլուծական վահանակ այս տվյալներից՝ օգտագործելով բաց կոդով cube.js շրջանակը: Ահա լուծման ամբողջական ճարտարապետությունը.

Nginx log-ի վերլուծություն՝ օգտագործելով Amazon Athena-ն և Cube.js-ը

TL:DR;
Հղում դեպի պատրաստի վահանակ.

Տեղեկատվություն հավաքելու համար մենք օգտագործում ենք Սահուն, մշակման համար - AWS Kinesis Data Firehose и AWS սոսինձպահեստավորման համար - AWS S3. Օգտագործելով այս փաթեթը, դուք կարող եք պահել ոչ միայն nginx տեղեկամատյանները, այլ նաև այլ իրադարձություններ, ինչպես նաև այլ ծառայությունների գրանցամատյաններ: Դուք կարող եք որոշ մասեր փոխարինել նմանատիպերով ձեր stack-ի համար, օրինակ՝ կարող եք գրել logs 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-ը: Սա թույլ է տալիս պահել և վերլուծել տեղեկամատյանները մեկ տեղում, քանի որ Athena-ն կարող է ուղղակիորեն աշխատել S3-ի տվյալների հետ: Հետագայում հոդվածում ես ձեզ կասեմ, թե ինչպես ճիշտ ավելացնել և մշակել տեղեկամատյանները, բայց նախ մեզ անհրաժեշտ է մաքուր դույլ S3-ում, որում այլ բան չի պահվի: Արժե նախօրոք մտածել, թե որ տարածաշրջանում եք ստեղծելու ձեր դույլը, քանի որ 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 հոսքի ստեղծում

Kinesis Firehose-ը Nginx-ից ստացված տվյալները կգրի S3-ին ընտրված ձևաչափով` բաժանելով դրանք YYYY/MM/DD/HH ձևաչափով դիրեկտորիաների: Սա օգտակար կլինի տվյալները կարդալիս: Դուք, իհարկե, կարող եք ուղղակիորեն գրել S3-ին fluentd-ից, բայց այս դեպքում ստիպված կլինեք գրել JSON, և դա անարդյունավետ է ֆայլերի մեծ չափի պատճառով: Բացի այդ, երբ օգտագործում եք PrestoDB կամ Athena, JSON-ը տվյալների ամենադանդաղ ձևաչափն է: Այսպիսով, բացեք Kinesis Firehose վահանակը, կտտացրեք «Ստեղծել առաքման հոսք», «առաքում» դաշտում ընտրեք «ուղիղ PUT».

Nginx log-ի վերլուծություն՝ օգտագործելով Amazon Athena-ն և Cube.js-ը

Հաջորդ ներդիրում ընտրեք «Ձայնագրման ձևաչափի փոխարկում» - «Միացված է» և որպես ձայնագրման ձևաչափ ընտրեք «Apache ORC»: Որոշ հետազոտությունների համաձայն Օուեն Օ'Մելի, սա PrestoDB-ի և Athena-ի օպտիմալ ձևաչափն է: Մենք օգտագործում ենք վերևում ստեղծված աղյուսակը որպես սխեմա: Խնդրում ենք նկատի ունենալ, որ դուք կարող եք նշել ցանկացած S3 տեղանք կինեզիսում, միայն սխեման օգտագործվում է աղյուսակից: Բայց եթե դուք նշեք մեկ այլ S3 տեղ, ապա չեք կարողանա կարդալ այս գրառումները այս աղյուսակից:

Nginx log-ի վերլուծություն՝ օգտագործելով Amazon Athena-ն և Cube.js-ը

Մենք ընտրում ենք S3 պահեստավորման համար և այն դույլը, որը մենք ստեղծել ենք ավելի վաղ: Aws Glue Crawler-ը, որի մասին ես կխոսեմ մի փոքր ուշ, չի կարող աշխատել նախածանցներով S3 դույլով, ուստի կարևոր է այն դատարկ թողնել:

Nginx log-ի վերլուծություն՝ օգտագործելով Amazon Athena-ն և Cube.js-ը

Մնացած տարբերակները կարող են փոխվել՝ կախված ձեր բեռնվածությունից, ես սովորաբար օգտագործում եմ լռելյայն տարբերակները: Նկատի ունեցեք, որ S3 սեղմումը հասանելի չէ, բայց ORC-ն լռելյայն օգտագործում է բնիկ սեղմում:

Սահուն

Այժմ, երբ մենք կարգավորել ենք տեղեկամատյանների պահպանումն ու ստացումը, մենք պետք է կարգավորենք ուղարկումը: Մենք կօգտագործենք Սահուն, քանի որ ես սիրում եմ Ruby-ին, բայց դուք կարող եք օգտագործել Logstash կամ ուղարկել տեղեկամատյանները ուղղակիորեն kinesis-ին: Fluentd սերվերը կարող է գործարկվել մի քանի ձևով, ես ձեզ կասեմ docker-ի մասին, քանի որ այն պարզ է և հարմար:

Նախ, մեզ անհրաժեշտ է fluent.conf կազմաձևման ֆայլը: Ստեղծեք այն և ավելացրեք աղբյուրը.

տիպ առաջ
նավահանգիստ 24224
կապել 0.0.0.0

Այժմ կարող եք գործարկել Fluentd սերվերը: Եթե ​​Ձեզ անհրաժեշտ է ավելի առաջադեմ կազմաձևում, գնացեք Docker հանգույց Կա մանրամասն ուղեցույց, ներառյալ՝ ինչպես հավաքել ձեր պատկերը:

$ 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-ը այլ կերպ, կարող եք օգտագործել log ֆայլերը, Fluentd-ն ունի ֆայլի tail plugin.

Եկեք ավելացնենք վերևում կազմաձևված տեղեկամատյանների վերլուծությունը Fluent-ի կազմաձևին.

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

Եվ ուղարկելով տեղեկամատյաններ Kinesis-ին, օգտագործելով kinesis firehose plugin:

<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

Եթե ​​ամեն ինչ ճիշտ եք կարգավորել, ապա որոշ ժամանակ անց (լռելյայն, Kinesis-ը գրանցում է ստացված տվյալները 10 րոպեն մեկ անգամ) դուք պետք է տեսնեք log ֆայլերը 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 ՄԲ:

Դա շտկելու համար մենք օգտագործում ենք 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-ը: Բայց որքանո՞վ է սա ավելի արդյունավետ, քան պարզապես չբաժանված աղյուսակից կարդալը: Եկեք պարզենք և ընտրենք նույն գրառումները՝ զտելով դրանք ըստ ժամանակացույցի.

Nginx log-ի վերլուծություն՝ օգտագործելով Amazon Athena-ն և Cube.js-ը

3.59 վայրկյան և 244.34 մեգաբայթ տվյալներ տվյալների բազայի վրա՝ ընդամենը մեկ շաբաթվա գրանցամատյաններով: Փորձենք զտել ըստ բաժանման.

Nginx log-ի վերլուծություն՝ օգտագործելով Amazon Athena-ն և Cube.js-ը

Մի փոքր ավելի արագ, բայց ամենակարևորը` ընդամենը 1.23 մեգաբայթ տվյալ: Դա շատ ավելի էժան կլիներ, եթե գնագոյացման մեջ չլինի նվազագույնը 10 մեգաբայթ մեկ խնդրանքով: Բայց դա դեռ շատ ավելի լավ է, և տվյալների մեծ հավաքածուներում տարբերությունը շատ ավելի տպավորիչ կլինի:

Կառուցեք վահանակ՝ օգտագործելով Cube.js

Վահանակը հավաքելու համար մենք օգտագործում ենք Cube.js վերլուծական շրջանակը: Այն ունի բավականին շատ գործառույթներ, բայց մեզ հետաքրքրում է երկուսը. Այն օգտագործում է տվյալների սխեման տվյալների սխեման, գրված է Javascript-ով SQL ստեղծելու և տվյալների բազայի հարցումը կատարելու համար։ Մենք միայն պետք է նշենք, թե ինչպես օգտագործել բաժանման ֆիլտրը տվյալների սխեմայում:

Եկեք ստեղծենք նոր Cube.js հավելված: Քանի որ մենք արդեն օգտագործում ենք AWS ստեկը, տրամաբանական է օգտագործել Lambda-ն տեղակայման համար: Դուք կարող եք օգտագործել էքսպրես ձևանմուշը սերնդի համար, եթե նախատեսում եք հյուրընկալել Cube.js backend-ը Heroku-ում կամ Docker-ում: Փաստաթղթերը նկարագրում են ուրիշներին հոստինգի մեթոդներ.

$ npm install -g cubejs-cli
$ cubejs create nginx-log-analytics -t serverless -d athena

Շրջակա միջավայրի փոփոխականները օգտագործվում են cube.js-ում տվյալների բազայի հասանելիությունը կարգավորելու համար: Գեներատորը կստեղծի .env ֆայլ, որտեղ դուք կարող եք նշել ձեր բանալիները Athena.

Հիմա մեզ պետք է տվյալների սխեման, որում մենք հստակ կնշենք, թե ինչպես են պահվում մեր տեղեկամատյանները: Այնտեղ կարող եք նաև նշել, թե ինչպես կարելի է հաշվարկել չափիչները վահանակների համար:

Գրացուցակում 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-ը կստեղծի լրացուցիչ աղյուսակներ՝ նախապես հավաքված տվյալներով և ինքնաբերաբար կթարմացնի տվյալները, երբ դրանք հասնեն: Սա ոչ միայն արագացնում է հարցումները, այլև նվազեցնում է 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`
      )
    }
  }
}

Այս մոդելում մենք նշում ենք, որ անհրաժեշտ է նախապես համախմբել տվյալները բոլոր օգտագործված չափանիշների համար և օգտագործել բաժանումը ըստ ամիսների: Pre-agregation partitioning կարող է զգալիորեն արագացնել տվյալների հավաքագրումն ու թարմացումը:

Այժմ մենք կարող ենք հավաքել վահանակը:

Cube.js backend-ը ապահովում է REST API և հաճախորդների գրադարանների մի շարք հանրաճանաչ front-end շրջանակների համար: Վահանակի ստեղծման համար մենք կօգտագործենք հաճախորդի 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>
        );
      }}
    />
  )
}

Վահանակի աղբյուրները հասանելի են այստեղ ծածկագրի ավազատուփ.

Source: www.habr.com

Добавить комментарий