Моніторинг мертвий? — Хай живе моніторинг

Моніторинг мертвий? — Хай живе моніторинг

Наша компанія з 2008 року займається переважно управлінням інфраструктурами та цілодобовою технічною підтримкою веб-проектів: у нас понад 400 клієнтів, це близько 15% електронної комерції Росії. Відповідно, на підтримці дуже різноманітна архітектура. Якщо щось падає, ми повинні протягом 15 протоколу це відремонтувати. Але щоби зрозуміти, що аварія сталася, потрібно моніторити проект і реагувати на інциденти. Як це робити?

Вважаю, що у організації правильної системи моніторингу відбувається біда. Якби лиха не було, то мій спіч складався з однієї тези: «Встановіть, будь ласка, Prometheus + Grafana та плагіни 1, 2, 3». На жаль, наразі так не працює. І головна проблема полягає в тому, що всі продовжують вірити у щось таке, що існувало у 2008 році з погляду програмних компонентів.

Щодо організації системи моніторингу я ризикну сказати, що проектів з грамотним моніторингом не існує. І ситуація настільки погана, якщо щось впаде, є ризик, що це залишиться непоміченим — адже всі впевнені, що «все моніториться».
Можливо, все моніториться. Але як?

Всі ми стикалися з історією на кшталт наступної: працює девопс, якийсь адмін, до них приходить команда розробників і каже — «ми зарелізувалися, тепер замонітор». Що за монітор? Як це працює?

Ок. Моніторимо по-старому. А воно вже змінюється, і з'ясовується, що ти моніторив сервіс А, який став сервісом B, який взаємодіє з сервісом C. Але команда розробників тобі каже: "Постав софт, він же повинен все замоніторити!"

То що змінилося? - Все змінилось!

2008 рік. Все чудово

Є кілька розробників, один сервер, один сервер БД. Звідси все йде. У нас є інфа, ми ставимо zabbix, Nagios, cacti. І далі виставляємо зрозумілі алерти на ЦПУ, роботу дисків, місце на дисках. Ще робимо пару ручних перевірок, що сайт відповідає, що замовлення до бази приходять. І все – ми більш-менш захищені.

Якщо порівнювати обсяг роботи, яку тоді робив адмін для забезпечення моніторингу, то на 98% вона була автоматичною: людина, яка займається моніторингом, має зрозуміти, як поставити Zabbix, як його налаштувати та налаштувати алерти. І 2% - на зовнішні перевірки: що сайт відповідає і робить запит до бази, що нові замовлення прийшли.

Моніторинг мертвий? — Хай живе моніторинг

2010 рік. Зростає навантаження

Ми починаємо скейлити інтернет, додаємо пошуковий двигун. Ми хочемо бути впевнені, що каталог товарів містить усі товари. І що пошук товарів працює. Що база працює, що замовлення робляться, що сайт відповідає зовні та відповідає з двох серверів і користувача не викидає із сайту, доки він перебалансується на інший сервер, і т.д. Сутностей стає більше.

Причому сутність, пов'язана з інфраструктурою, як і раніше, залишається найбільшою в голові менеджера. Як і раніше, в голові сидить ідея, що людина, яка займається моніторингом, — це людина, яка поставить zabbix і зможе її конфігурувати.

Але при цьому з'являються роботи з проведення зовнішніх перевірок, створення набору скриптів запитів індексатора пошуку, набору скриптів для перевірки того, що пошук змінюється в процесі індексації, набору скриптів, які перевіряють, що службі доставці передаються товари, і т.д. і т.п.

Моніторинг мертвий? — Хай живе моніторинг

Зауважте: я тричі написав «набір скриптів». Тобто, відповідальний за моніторинг — більше не той, хто просто встановлює zabbix. Це людина, яка починає кодити. Але в головах у команди поки що нічого не змінюється.

Зате змінюється світ, ускладнюючись дедалі більше. Додають шар віртуалізації, кілька нових систем. Вони починають взаємодіяти між собою. Хто сказав «пахає мікросервісами?» Але кожен сервіс все ще окремо виглядає як сайт. Ми можемо до нього звернутись і зрозуміти, що він видає необхідну інформацію і сам по собі працює. І якщо ти адмін, котрий постійно займається проектом, який розвивається 5-7-10 років, у тебе ці знання накопичуються: з'являється новий рівень — ти його усвідомив, з'являється ще один рівень — ти його усвідомив…

Моніторинг мертвий? — Хай живе моніторинг

Але рідко хтось супроводжує проект 10 років.

Резюме моніторингу

Припустимо, ви прийшли в новий стартап, який одразу набрав 20 розробників, написав 15 мікросервісів, а ви адмін, якому кажуть: «Побудуй CI/CD. Пожаааалуйста». Ви побудували CI/CD і раптом чуєте: «Нам складно працювати з продакшном у «кубику», не розуміючи, як у ньому працюватиме програма. Зроби нам пісочницю у цьому ж «кубику».
Ви робите пісочницю у цьому кубику. Вам відразу кажуть: «Ми хочемо stage базу даних, яка оновлюється щодня з продакшну, щоб розуміти, що це працює на базі даних, але при цьому не зіпсувати продакшн базу даних».

Ви у цьому всьому живете. Залишається 2 тижні до релізу, вам кажуть: «Тепер би все це замоніторити…» Т.е. замоніторити кластерну інфраструктуру, замоніторити мікросервісну архітектуру, замоніторити роботу із зовнішніми сервісами...

А колеги дістають такі з голови звичну схему та кажуть: «Так тут же все зрозуміло! Постав програму, яка це все замоніторить». Так-так: Prometheus + Grafana + плагіни.
І додають при цьому: "У тебе тижнів два, зроби так, щоб все було надійно".

У купі проектів, які ми бачимо, на моніторинг виділяють одну людину. Уявіть, що ми хочемо на 2 тижні найняти людину, яка займеться моніторингом, і ми складаємо йому резюме. Якими навичками має володіти ця людина — якщо зважати на все сказане нами раніше?

  • Він має розуміти моніторинг та специфіку роботи залізної інфраструктури.
  • Він повинен розуміти специфіку моніторингу Kubernetes (а всі хочуть у «кубик», тому що можна абстрагуватися від усього, сховатися, адже з рештою розбереться адмін) — самого по собі, його інфраструктури, і розуміти, як моніторити програми всередині.
  • Він повинен розуміти, що послуги між собою спілкуються особливими способами, і знати специфіки взаємодії сервісів між собою. Цілком реально побачити проект, де частина сервісів спілкуються синхронно, бо інакше ніяк. Наприклад, backend йде REST, по gRPC до сервісу каталогу, отримує список товарів і повертає назад. Тут не можна чекати. А з іншими сервісами він працює асинхронно. Передати замовлення до служби доставки, надіслати листа тощо.
    Ви, мабуть, уже попливли від цього? А адмін, якому треба це моніторити, поплив ще більше.
  • Він має вміти планувати та планувати правильно — оскільки роботи стає дедалі більше.
  • Він повинен, отже, створити стратегію із створеного сервісу, щоб зрозуміти, як це конкретно замоніторити. Йому потрібне розуміння архітектури проекту та його розвитку + розуміння технологій, що використовуються у розробці.

Згадаймо абсолютно нормальний кейс: частина сервісів на PHP, частина сервісів на Go, частина сервісів на JS. Вони якось між собою працюють. Звідси й узявся термін «мікросервіс»: окремих систем стало так багато, що розробники не можуть зрозуміти проект загалом. Одна частина команди пише послуги на JS, які працюють самі по собі і не знають, як працює решта системи. Інша частина пише сервіси на Python і не лізе в те, як працюють інші сервіси, вони ізольовані у своїй галузі. Третя — пише сервіси на php чи ще щось.
Усі ці 20 осіб розділені на 15 сервісів, і є лише один адмін, який має все це зрозуміти. Стоп! ми тільки-но розбили систему на 15 мікросервісів, тому що 20 людей всю систему зрозуміти не можуть.

Зате її треба якось замоніторити.

Що зрештою? У результаті є одна людина, якій на думку входить все, що не може зрозуміти ціла команда розробників, і при цьому він ще повинен знати і вміти те, що ми вказали вище - залізну інфраструктуру, інфраструктуру Kubernetes і т.д.

Що тут сказати ... Х'юстон, у нас проблеми.

Моніторинг сучасного програмного проекту – це сам собою програмний проект

З хибної впевненості, що моніторинг - це софт, у нас з'являється віра в чудеса. А чудес, на жаль, не буває. Не можна поставити zabbix і чекати, що все запрацює. Немає сенсу поставити Grafana і сподіватися, що все буде прибл. Більшість часу піде на організацію перевірок роботи сервісів та їх взаємодії між собою, перевірок, як працюють зовнішні системи. Фактично 90% часу піде не написання скриптів, але в розробку програмного забезпечення. І займатись нею має команда, яка розуміє роботу проекту.
Якщо в цій ситуації одну людину кинути на моніторинг, то станеться лихо. Що відбувається повсюдно.

Наприклад, є кілька сервісів, які спілкуються між собою через Kafka. Надійшло замовлення, ми повідомлення про замовлення відправили до Kafka. Є сервіс, який слухає інформацію про замовлення та проводить відвантаження товару. Є сервіс, який слухає інформацію про замовлення та надсилає лист користувачеві. А потім з'являється ще купа сервісів і ми починаємо плутатися.

А якщо ви віддасте це адміну і розробникам на етапі, коли до релізу залишився короткий час, людині потрібно буде зрозуміти весь цей протокол. Тобто. проект такого масштабу займає значний час, і у розробці системи це має бути закладено.
Але дуже часто, особливо в горінні, стартапах, ми бачимо, як моніторинг відкладають на потім. «Наразі ми зробимо Proof of Concept, з ним запустимося, нехай він падає – ми готові жертвувати. А потім ми все це замоніторимо». Коли (або якщо) проект починає приносити гроші, бізнес хоче пиляти ще більше фіч - тому що воно ж почало працювати, значить потрібно накручувати далі! А ви знаходитесь у точці, де спочатку потрібно замоніторити все попереднє, що займає не 1% часу, а значно більше. І до речі, для моніторингу потрібні будуть розробники, а їх простіше пустити на нові фічі. У результаті пишуться нові фічі, все накручується, і ви в безкінечному deadlock.

То як замоніторити проект, починаючи з початку, і що робити, якщо вам дістався проект, який потрібно замоніторити, а ви не знаєте, з чого починати?

По-перше, потрібно планувати.

Ліричний відступ: часто починають з моніторингу інфраструктури. Наприклад, у нас Kubernetes. Почнемо з того, що поставимо Prometheus із Grafana, поставимо плагіни під моніторинг "кубика". Не тільки у розробників, а й у адмінів є сумна практика: «Ми поставимо цей плагін, а плагін напевно знає як це зробити». Люди люблять починати з простих та зрозумілих, а не з важливих дій. І моніторинг інфраструктури – це просто.

Спочатку вирішите, що і як хочете замоніторити, а потім підбирайте інструмент, тому що інші люди не можуть за вас подумати. Та й чи мають? Інші люди думали про себе, про універсальну систему чи взагалі не думали, коли цей плагін писали. І те, що цей плагін має 5 тисяч користувачів, не означає, що він несе якусь користь. Можливо, ви станете 5001 просто тому, що там до цього вже було 5000 чоловік.

Якщо ви почали моніторити інфраструктуру і backend вашої програми перестав відповідати, всі користувачі втратять зв'язок з мобільним додатком. Вилетить помилка. До вас прийдуть і скажуть «Додаток не працює, чим ви тут займаєтесь?» - "Ми моніторимо". — «Як ви моніторите, якщо не бачите, що програма не працює?!»

  1. Я вважаю, що моніторити треба саме з точки входу користувача. Якщо користувач не бачить, що програма працює - все, це провал. І система моніторингу має попередити про це насамперед.
  2. І лише потім ми можемо замоніторити інфраструктуру. Або зробити це паралельно. З інфраструктурою простіше - тут ми можемо просто поставити zabbix.
  3. І тепер потрібно йти в корені програми, щоб зрозуміти, де що не працює.

Головна моя думка – моніторинг має йти паралельно із процесом розробки. Якщо ви відірвали команду моніторингу на інші завдання (створення CI/CD, пісочниці, реорганізацію інфраструктури), моніторинг почне відставати і ви, можливо, вже ніколи не наздоженете розробку (рано чи пізно доведеться її зупинити).

Все за рівнями

Ось як бачу організацію системи моніторингу.

1) Рівень додатку:

  • моніторинг бізнес-логіки програми;
  • моніторинг health-метрики сервісів;
  • інтеграційний моніторинг

2) Рівень інфраструктури:

  • моніторинг рівня оркестрації;
  • моніторинг системного ПЗ;
  • моніторинг рівня "заліза".

3) Знов рівень програми — але вже як інженерного продукту:

  • збір та спостереження журналів додатку;
  • APM;
  • tracing.

4) Алертинг:

  • організація системи оповіщення;
  • організація системи чергувань;
  • організація «бази знань» та workflow обробки інцидентів.

Важливо: ми доходимо до алертингу не після, а відразу! Не треба запускати моніторинг і «якось потім» вигадувати, кому приходитимуть алерти. Адже в чому завдання моніторингу: зрозуміти, де в системі щось працює не так і дати про це знати потрібним людям. Якщо це залишити на кінець, то потрібні люди дізнаються про те, що щось йде не так, лише за дзвінком «у нас нічого не працює».

Рівень програми - моніторинг бізнес-логіки

Тут йдеться про перевірки самого факту того, що програма працює для користувача.

Цей рівень має бути зроблено на етапі розробки. Наприклад, у нас є умовний Prometheus: він лізе до сервера, який займається перевірками, смикає endpoint, і endpoint йде та перевіряє API.

Коли часто просять замоніторити головну сторінку, щоб переконатися, що сайт працює, програмісти дають ручку, яку можна смикати щоразу, коли треба переконатися, що API працює. А програмісти ще беруть і пишуть /api/test/helloworld
Єдиний спосіб переконатись, що все працює? - Ні!

  • Створення таких перевірок – по суті завдання розробників. Unit-тести мають писати програмісти, які пишуть код. Тому що, якщо ви злиєте це на адміна «Чувак, тобі список протоколів API всіх 25 функцій, будь ласка, замонітор все!» - нічого не вийде.
  • Якщо ви зробите print “hello world”, ніхто ніколи не дізнається про те, що API повинен і дійсно працює. Кожна зміна API повинна вести зміну перевірок.
  • Якщо у вас вже таке лихо - зупиніть фічі і виділіть розробників, які напишуть ці перевірки, або змиріться з втратами, змиріться, що нічого не перевіряється і падатиме.

Технічні поради:

  • Обов'язково організуйте зовнішній сервер для організації перевірок, ви повинні бути впевнені, що ваш проект доступний для зовнішнього світу.
  • Організуйте перевірку по всьому протоколу API, а не лише за окремими endpoint-ами.
  • Створіть prometheus-endpoint з результатами перевірок.

Рівень програми - моніторинг health-метрик

Тепер йдеться про зовнішні health-метрики сервісів.

Ми вирішили, що всі “ручки” програми моніторимо за допомогою зовнішніх перевірок, які ми викликаємо із зовнішньої системи моніторингу. Але це саме "ручки", які "бачить" користувач. Ми хочемо бути впевнені, що в нас працюють самі сервіси. Тут краща історія: у K8s є health-чеки, щоб хоча б сам «кубик» переконувався, що сервіс працює. Але половина чеків, які я бачив, — це той самий print “hello world”. Тобто. ось він смикає один раз після деплою, йому той відповів, що все гаразд — і все. А у сервісу, якщо він rest-ом видає свій API, є безліч точок входу того самого API, який теж потрібно моніторити, тому що ми хочемо знати, що він працює. І ми його моніторимо вже усередині.

Як це правильно реалізувати технічно: кожен сервіс виставляє endpoint про свою поточну працездатність, а в графіках Grafana (або будь-якої іншої програми) ми бачимо статус усіх сервісів.

  • Кожна зміна API повинна вести зміну перевірок.
  • Новий сервіс створюйте відразу з health-метриками.
  • Адмін може прийти до розробників і попросити "допишіть мені пару фіч, щоб я все розумів і до своєї системи моніторингу додав інформацію про це". Але розробники зазвичай відповідають: «За два тижні до релізу ми нічого дописувати не будемо».
    Нехай менеджери розробки знають, що будуть такі втрати, нехай керівництво менеджерів розробки теж знає. Тому що, коли все впаде, хтось все одно зателефонує і вимагатиме замоніторити «постійний сервіс» (с)
  • До речі, виділіть розробників на написання плагінів для Grafana це буде хороша допомога для адмінів.

Рівень програми — Інтеграційний моніторинг

Інтеграційний моніторинг фокусується на моніторингу комунікації між критичними для бізнесу системами.

Наприклад, є 15 сервісів, які спілкуються між собою. Це не окремі сайти. Тобто. ми не можемо смикнути сервіс сам собою, отримати /helloworld і зрозуміти, що сервіс працює. Тому що веб-сервіс оформлення замовлення має надіслати інформацію про замовлення у шину — з шини служба роботи зі складом має отримати це повідомлення та працювати з ним далі. А служба розсилки e-mail-ів має опрацювати це якось далі, тощо.

Відповідно, ми не можемо зрозуміти, тикаючись у кожен окремий сервіс, що це все працює. Тому що у нас є якась шина, через яку все спілкується та взаємодіє.
Тому цей етап повинен позначати етап тестування сервісів по взаємодії з іншими сервісами. Не можна, замоніторівши брокер повідомлень, організувати моніторинг комунікації. Якщо є сервіс, що видає дані, та сервіс, який їх приймає, при моніторингу брокера ми побачимо лише дані, що літають з боку на бік. Якщо навіть ми якось примудрилися замоніторити взаємодію цих даних усередині - що якийсь продюсер постить дані, хтось їх читає, цей потік продовжує йти в Kafka-це все одно не дасть нам інформацію, якщо один сервіс віддав повідомлення в одній версії, а інший сервіс не очікував на цю версію і пропустив його. Ми про це не дізнаємось, тому що нам сервіси скажуть, що все працює.

Як я рекомендую робити:

  • Для синхронної комунікації: endpoint виконує запити до зв'язаних сервісів. Тобто. ми беремо цей endpoint, смикаємо скриптик усередині сервісу, який йде по всіх точках і каже «я можу там смикнути, і там смикнути, можу там смикнути…»
  • Для асинхронної комунікації: вхідні повідомлення - endpoint перевіряє шину на наявність тестових повідомлень та видає статус обробки.
  • Для асинхронної комунікації: вихідні повідомлення - endpoint відправляє тестові повідомлення на шину.

Як завжди відбувається: у нас є сервіс, який кидає дані у шину. Ми приходимо в цей сервіс і просимо розповісти про його інтеграційне здоров'я. І якщо сервіс повинен запродюсить якесь повідомлення кудись далі (WebApp), то це тестове повідомлення прод'юсить. А якщо ми смикаємо сервіс на стороні OrderProcessing, він спочатку постить те, що він може запостити незалежне, а якщо є якісь залежні штуки — він читає з шини набір тестових повідомлень, розуміє, що він може обробити їх, повідомити про це і якщо треба, постити їх далі, і про це він каже — все ок, я живий.

Дуже часто ми чуємо питання "як ми можемо це тестувати на бойових даних?" Наприклад, йдеться про ту ж службу замовлень. Замовлення надсилає повідомлення до складу, де списуються товари: ми ж не можемо протестувати це на бойових даних, бо «у мене товари списуватимуться!» Вихід: на початковому етапі запланувати цей тест. У вас є unit-тести, які роблять mock-і. Так ось, зробіть це на більш глибокому рівні, де у вас проходитиме канал комунікації, який не зашкодить роботі бізнесу.

Рівень інфраструктури

Моніторинг інфраструктури – те, що здавна вважається самим моніторингом.

  • Моніторинг інфраструктури можна і необхідно запустити як окремий процес.
  • Не варто починати з моніторингу інфраструктури на проекті, що працює, навіть якщо дуже хочеться. Це болячка для всіх девопсів. «Спочатку за монітором кластер, за монітор інфраструктуру» - тобто. спочатку замоніторить те, що лежить внизу, а додаток не полізе. Тому що програма — незрозуміла штука для девопса. Йому це злили, і він не розуміє, як це працює. А інфраструктуру він розуміє та починає з неї. Але ні — завжди спочатку потрібно моніторити програму.
  • Не переборщіть із кількістю оповіщень. Враховуючи складність сучасних систем, алерти летять постійно, і з цією купою алертів треба якось жити. І людина on-call, подивившись на сотню чергових алертів, вирішить "не хочу про це думати". Алерти мають сповіщати лише про критичні речі.

Рівень програми як бізнес-одиниці

Ключові моменти:

  • ELK. Це індустріальний стандарт. Якщо з якоїсь причини ви не агрегує логи, терміново почніть це робити.
  • APM. Зовнішні APM як спосіб швидко закрити моніторинг програми (NewRelic, BlackFire, Datadog). Ви можете тимчасово поставити цю річ, щоб хоч якось зрозуміти, що у вас відбувається.
  • Tracing. У десятках мікросервісів ви повинні трейсити все, тому що запит уже не живе сам собою. Дописати пізніше дуже складно, тому краще відразу запланувати tracing у розробці – це робота та утиліта розробників. Якщо ще не запровадили – впроваджуйте! Див Jaeger/Zipkin

Алертінг

  • Організація системи оповіщень: за умов моніторингу купи речей має бути єдина система розсилки оповіщень. Можна у Grafana. На Заході всі використовують PagerDuty. Оповіщення мають бути зрозумілими (наприклад, звідки вони прийшли…). І бажано контролювати, що оповіщення взагалі доходять
  • Організація системи чергувань: алерти не повинні приходити всім (або всі натовпом реагувати, або ніхто не реагуватиме). Oncall потрібно бути і розробникам: обов'язково визначте зони відповідальності, зробіть чітку інструкцію і пропишіть в ній, кому конкретно дзвонити в понеділок і середу, а кому - у вівторок і п'ятницю (інакше не будуть нікому дзвонити навіть у разі великої біди - побояться розбудити, потривожити : люди взагалі не люблять дзвонити та будити інших людей, особливо вночі) І поясніть, що звернення за допомогою – не показник некомпетентності («я прошу допомоги – значить я поганий працівник»), заохочуйте прохання про допомогу.
  • Організація «бази знань» та workflow обробки інцидентів: за кожним серйозним інцидентом має бути запланований постмортем, як тимчасовий захід мають бути зафіксовані дії, які вирішать інцидент. І заведіть практику, що повторювані алерти - це гріх; їх треба фіксувати у коді чи інфраструктурних роботах.

Технологічний стек

Уявімо, що стек у нас наступний:

  • збір даних - Prometheus + Grafana;
  • аналіз логів - ELK;
  • для APM або Tracing - Jaeger (Zipkin).

Моніторинг мертвий? — Хай живе моніторинг

Вибір варіантів некритичний. Тому що, якщо ви спочатку зрозуміли, як замоніторити систему і розписали план, далі ви вже починаєте вибирати інструментарій під свої вимоги. Питання, що ви вибрали моніторити спочатку. Тому що, можливо, інструмент, який ви вибрали спочатку, він взагалі не підходить під ваші вимоги.

Декілька технічних моментів, які я бачу скрізь останнім часом:

Prometheus пхають всередину Kubernetes - хто це придумав?! Якщо у вас впаде кластер, що ви робитимете? Якщо у вас складний кластер усередині, то має працювати якась система моніторингу всередині кластера, і якась зовні, яка збиратиме дані зсередини кластера.

Усередині кластера ми збираємо логи та все інше. Але система моніторингу має бути зовні. Дуже часто в кластері, де є Promtheus, який поставили всередину, там же стоять системи, які виконують зовнішні перевірки роботи сайту. А якщо у вас з'єднання у зовнішній світ впали і програма не працює? Виходить, усередині все добре, але користувачам від цього не легше.

Висновки

  • Розробка моніторингу - не встановлення утиліт, а розробка програмного продукту. 98% сьогоднішнього моніторингу – це кодинг. Кодинг у сервісах, кодинг зовнішніх перевірок, перевірки зовнішніх сервісів, і всього-на-всього.
  • Не шкодуйте часу розробників на моніторинг: це може зайняти до 30% їхньої роботи, але варте того.
  • Девопси, не хвилюйтеся, що вам не виходить щось замоніторити, тому що деякі речі - це взагалі інший склад розуму. Ви не були програмістом, а робота моніторингу – саме їхня робота.
  • Якщо проект вже працює і не замоніторний (а ви менеджер), виділіть ресурси на моніторинг.
  • Якщо продукт вже у продакшні, а ви девопс, якому сказали «налаштувати моніторинг», спробуйте пояснити керівництву те, про що я все це написав.

Це є розширена версія доповіді на конференції Saint Highload++.

Якщо вам цікаві мої ідеї та роздуми на it і близько того-теми, то ось можна почитати канал 🙂

Джерело: habr.com

Додати коментар або відгук