ΠΠ±ΠΈΠΊΠ½ΠΎΠ²Π΅Π½ΠΎ ΡΡΡΠ³ΠΎΠ²ΡΠΊΠΈ ΠΏΡΠΎΠ΄ΡΠΊΡΠΈ ΠΈΠ»ΠΈ Π³ΠΎΡΠΎΠ²ΠΈ Π°Π»ΡΠ΅ΡΠ½Π°ΡΠΈΠ²ΠΈ Ρ ΠΎΡΠ²ΠΎΡΠ΅Π½ ΠΊΠΎΠ΄, ΠΊΠ°ΡΠΎ 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 ΠΏΠΎΡΠΎΠΊ
Kinesis Firehose ΡΠ΅ Π·Π°ΠΏΠΈΡΠ΅ Π΄Π°Π½Π½ΠΈΡΠ΅, ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈ ΠΎΡ Nginx Π² S3 Π² ΠΈΠ·Π±ΡΠ°Π½ΠΈΡ ΡΠΎΡΠΌΠ°Ρ, ΠΊΠ°ΡΠΎ Π³ΠΈ ΡΠ°Π·Π΄Π΅Π»ΠΈ Π½Π° Π΄ΠΈΡΠ΅ΠΊΡΠΎΡΠΈΠΈ Π²ΡΠ² ΡΠΎΡΠΌΠ°Ρ ΠΠΠΠ/ΠΠ/ΠΠ/Π§Π§. Π’ΠΎΠ²Π° ΡΠ΅ Π±ΡΠ΄Π΅ ΠΏΠΎΠ»Π΅Π·Π½ΠΎ ΠΏΡΠΈ ΡΠ΅ΡΠ΅Π½Π΅ Π½Π° Π΄Π°Π½Π½ΠΈ. ΠΠΎΠΆΠ΅ΡΠ΅, ΡΠ°Π·Π±ΠΈΡΠ° ΡΠ΅, Π΄Π° ΠΏΠΈΡΠ΅ΡΠ΅ Π΄ΠΈΡΠ΅ΠΊΡΠ½ΠΎ Π² S3 ΠΎΡ fluentd, Π½ΠΎ Π² ΡΠΎΠ·ΠΈ ΡΠ»ΡΡΠ°ΠΉ ΡΠ΅ ΡΡΡΠ±Π²Π° Π΄Π° ΠΏΠΈΡΠ΅ΡΠ΅ JSON, Π° ΡΠΎΠ²Π° Π΅ Π½Π΅Π΅ΡΠ΅ΠΊΡΠΈΠ²Π½ΠΎ ΠΏΠΎΡΠ°Π΄ΠΈ Π³ΠΎΠ»Π΅ΠΌΠΈΡ ΡΠ°Π·ΠΌΠ΅Ρ Π½Π° ΡΠ°ΠΉΠ»ΠΎΠ²Π΅ΡΠ΅. ΠΡΠ²Π΅Π½ ΡΠΎΠ²Π°, ΠΊΠΎΠ³Π°ΡΠΎ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΡΠ΅ PrestoDB ΠΈΠ»ΠΈ Athena, JSON Π΅ Π½Π°ΠΉ-Π±Π°Π²Π½ΠΈΡΡ ΡΠΎΡΠΌΠ°Ρ Π·Π° Π΄Π°Π½Π½ΠΈ. Π’Π°ΠΊΠ° ΡΠ΅ ΠΎΡΠ²ΠΎΡΠ΅ΡΠ΅ ΠΊΠΎΠ½Π·ΠΎΠ»Π°ΡΠ° Kinesis Firehose, ΡΡΠ°ΠΊΠ½Π΅ΡΠ΅ Π²ΡΡΡ Ρ βΠ‘ΡΠ·Π΄Π°Π²Π°Π½Π΅ Π½Π° ΠΏΠΎΡΠΎΠΊ Π·Π° Π΄ΠΎΡΡΠ°Π²ΠΊΠ°β, ΠΈΠ·Π±Π΅ΡΠ΅ΡΠ΅ βΠ΄ΠΈΡΠ΅ΠΊΡΠ½ΠΎ ΠΠΠ‘Π’ΠΠΠ―ΠΠβ Π² ΠΏΠΎΠ»Π΅ΡΠΎ βΠ΄ΠΎΡΡΠ°Π²ΠΊΠ°β:
Π ΡΠ»Π΅Π΄Π²Π°ΡΠΈΡ ΡΠ°Π·Π΄Π΅Π» ΠΈΠ·Π±Π΅ΡΠ΅ΡΠ΅ βΠΡΠ΅ΠΎΠ±ΡΠ°Π·ΡΠ²Π°Π½Π΅ Π½Π° ΡΠΎΡΠΌΠ°Ρ Π½Π° Π·Π°ΠΏΠΈΡβ - βΠΠΊΡΠΈΠ²ΠΈΡΠ°Π½ΠΎβ ΠΈ ΠΈΠ·Π±Π΅ΡΠ΅ΡΠ΅ βApache ORCβ ΠΊΠ°ΡΠΎ ΡΠΎΡΠΌΠ°Ρ Π½Π° Π·Π°ΠΏΠΈΡ. Π‘ΠΏΠΎΡΠ΅Π΄ Π½ΡΠΊΠΎΠΈ ΠΈΠ·ΡΠ»Π΅Π΄Π²Π°Π½ΠΈΡ
ΠΠ·Π±ΠΈΡΠ°ΠΌΠ΅ S3 Π·Π° ΡΡΡ ΡΠ°Π½Π΅Π½ΠΈΠ΅ ΠΈ ΠΊΠΎΡΠ°ΡΠ°, ΠΊΠΎΡΡΠΎ ΡΡΠ·Π΄Π°Π΄ΠΎΡ ΠΌΠ΅ ΠΏΠΎ-ΡΠ°Π½ΠΎ. Aws Glue Crawler, Π·Π° ΠΊΠΎΠΉΡΠΎ ΡΠ΅ Π³ΠΎΠ²ΠΎΡΡ ΠΌΠ°Π»ΠΊΠΎ ΠΏΠΎ-ΠΊΡΡΠ½ΠΎ, Π½Π΅ ΠΌΠΎΠΆΠ΅ Π΄Π° ΡΠ°Π±ΠΎΡΠΈ Ρ ΠΏΡΠ΅ΡΠΈΠΊΡΠΈ Π² ΠΊΠΎΡΠ° S3, ΡΠ°ΠΊΠ° ΡΠ΅ Π΅ Π²Π°ΠΆΠ½ΠΎ Π΄Π° Ρ ΠΎΡΡΠ°Π²ΠΈΡΠ΅ ΠΏΡΠ°Π·Π½Π°.
ΠΡΡΠ°Π½Π°Π»ΠΈΡΠ΅ ΠΎΠΏΡΠΈΠΈ ΠΌΠΎΠ³Π°Ρ Π΄Π° ΡΠ΅ ΠΏΡΠΎΠΌΠ΅Π½ΡΡ Π² Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡ ΠΎΡ Π²Π°ΡΠ΅ΡΠΎ Π½Π°ΡΠΎΠ²Π°ΡΠ²Π°Π½Π΅; ΠΎΠ±ΠΈΠΊΠ½ΠΎΠ²Π΅Π½ΠΎ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌ ΡΠ΅Π·ΠΈ ΠΏΠΎ ΠΏΠΎΠ΄ΡΠ°Π·Π±ΠΈΡΠ°Π½Π΅. ΠΠΌΠ°ΠΉΡΠ΅ ΠΏΡΠ΅Π΄Π²ΠΈΠ΄, ΡΠ΅ S3 ΠΊΠΎΠΌΠΏΡΠ΅ΡΠΈΡΡΠ° Π½Π΅ Π΅ Π½Π°Π»ΠΈΡΠ½Π°, Π½ΠΎ ORC ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π° ΡΠΎΠ±ΡΡΠ²Π΅Π½Π° ΠΊΠΎΠΌΠΏΡΠ΅ΡΠΈΡ ΠΏΠΎ ΠΏΠΎΠ΄ΡΠ°Π·Π±ΠΈΡΠ°Π½Π΅.
fluentd
Π‘Π΅Π³Π°, ΡΠ»Π΅Π΄ ΠΊΠ°ΡΠΎ ΡΠΌΠ΅ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠΈΡΠ°Π»ΠΈ ΡΡΡ
ΡΠ°Π½ΡΠ²Π°Π½Π΅ΡΠΎ ΠΈ ΠΏΠΎΠ»ΡΡΠ°Π²Π°Π½Π΅ΡΠΎ Π½Π° ΡΠ΅Π³ΠΈΡΡΡΠ°ΡΠΈΠΎΠ½Π½ΠΈ ΡΠ°ΠΉΠ»ΠΎΠ²Π΅, ΡΡΡΠ±Π²Π° Π΄Π° ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠΈΡΠ°ΠΌΠ΅ ΠΈΠ·ΠΏΡΠ°ΡΠ°Π½Π΅ΡΠΎ. Π©Π΅ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌΠ΅
ΠΡΡΠ²ΠΎ, ΠΈΠΌΠ°ΠΌΠ΅ Π½ΡΠΆΠ΄Π° ΠΎΡ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΎΠ½Π½ΠΈΡ ΡΠ°ΠΉΠ» 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 ΠΌΠ΅Π³Π°Π±Π°ΠΉΡΠ° Π΄Π°Π½Π½ΠΈ Π² Π½Π°Π±ΠΎΡ ΠΎΡ Π΄Π°Π½Π½ΠΈ ΡΡΡ ΡΠ°ΠΌΠΎ Π΅Π΄Π½Π° ΡΠ΅Π΄ΠΌΠΈΡΠ° ΡΠ΅Π³ΠΈΡΡΡΠ°ΡΠΈΠΎΠ½Π½ΠΈ ΡΠ°ΠΉΠ»ΠΎΠ²Π΅. ΠΠ΅ΠΊΠ° ΠΎΠΏΠΈΡΠ°ΠΌΠ΅ ΡΠΈΠ»ΡΡΡ ΠΏΠΎ Π΄ΡΠ»:
ΠΠ°Π»ΠΊΠΎ ΠΏΠΎ-Π±ΡΡΠ·ΠΎ, Π½ΠΎ Π½Π°ΠΉ-Π²Π°ΠΆΠ½ΠΎΡΠΎ - ΡΠ°ΠΌΠΎ 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