Production-ready images for k8s

Ця історія про те, як ми використовуємо контейнери в продуктовому середовищі, особливо під Kubernetes. Стаття присвячена збору метрик та логів із контейнерів, а також білду образів.

Production-ready images for k8s

Ми з фінтех-компанії Exness, яка займається розробкою сервісів для онлайн-трейдингу та фінтех-продуктів для B2B та В2С. У нашому R&D багато різних команд, у відділі розробки 100+ співробітників.

Ми представляємо команду, яка відповідає за платформу для збору та запуску коду нашими розробниками. Зокрема, ми відповідаємо за збір, зберігання та надання метрик, логів та подій з додатків. В даний час ми оперуємо приблизно трьома тисячами Docker-контейнерів у продуктовому середовищі, підтримуємо наше big data-сховище на 50 Тб та надаємо архітектурні рішення, які будуються навколо нашої інфраструктури: Kubernetes, Rancher та різноманітні публічні cloud-провайдери. 

Наша мотивація

Що горить? Ніхто не може відповісти. Де осередок? Зрозуміти важко. Коли спалахнуло? З'ясувати можна, але не одразу. 

Production-ready images for k8s

Чому одні контейнери стоять, інші впали? Який контейнер став тому виною? Адже зовні контейнери однакові, а всередині у кожного свій Neo.

Production-ready images for k8s

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

агенти

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

Production-ready images for k8s

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

У нашому випадку агенти повинні надавати логи у стандартному форматі, теговані та з тротлінгом. Також вони повинні надавати нам стандартизовані метрики, що розширюються з погляду бізнес-додатків.

Під агентами також маються на увазі утиліти для експлуатації та обслуговування, які вміють працювати в різних системах оркестрування, що підтримують різні images (Debian, Alpine, Centos тощо).

Нарешті, агенти повинні підтримувати простий CI/CD, що включає Docker-файли. Інакше корабель розвалиться, бо контейнери почнуть поставлятися «кривими» рейками.

Процес складання та влаштування цільового image

Щоб все було стандартизовано та керовано, необхідно дотримуватись якогось стандартного процесу складання. Тому ми вирішили збирати контейнери контейнерами — така ось рекурсія.

Production-ready images for k8s

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

Як ми його використовуємо? Ми маємо Docker Hub, в якому лежить контейнер. Ми дзеркаємо його всередину своєї системи, щоб позбутися зовнішніх залежностей. Вийшов контейнер, позначений жовтим кольором. Ми створюємо шаблон, щоб встановити у контейнер усі необхідні нам дистрибутиви та скрипти. Після цього ми збираємо готовий до експлуатації образ: розробники кладуть код і якісь свої особливі залежності. 

Чим добрий такий підхід? 

  • По-перше, повний версійний контроль інструментів складання – контейнер складання, версії скриптів та дистрибутивів. 
  • По-друге, ми домоглися стандартизації: однак створюємо шаблони, проміжні і готові до експлуатації image. 
  • По-третє, контейнери забезпечують нам портованість. Сьогодні ми використовуємо Gitlab, а завтра перейдемо на TeamCity або Jenkins і так само зможемо запускати наші контейнери. 
  • По-четверте, мінімізація залежностей. Ми невипадково поклали в контейнер дистрибутиви, адже це дозволяє не завантажувати їх щоразу з Інтернету. 
  • По-п'яте, підвищилася швидкість складання - наявність локальних копій образів дозволяють не витрачати час на скачування, оскільки є локальний образ. 

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

Як працює наша процедура збирання

Production-ready images for k8s

Складання запускається однією командою, процес виконується в образі (виділений червоним). Розробник має Docker-файл (виділений жовтим), ми його рендеримо, підмінюючи змінні значеннями. І попутно додаємо header'и та footer'и – це наші агенти. 

Header додає дистрибутиви із відповідних образів. А footer встановлює всередину наші сервіси, налаштовує запуск робочого навантаження, логування та інших агентів, підміняє entrypoint і т.д. 

Production-ready images for k8s

Ми довго думали, чи ставити супервізор. Зрештою вирішили, що він нам потрібний. Вибрали S6. Супервізор забезпечує керування контейнером: дозволяє підключатися до нього у разі падіння основного процесу та забезпечує ручне керування контейнером без його перестворення. Логи та метрики - це процеси, що виконуються всередині контейнера. Їх теж треба якось контролювати, і ми це робимо за допомогою супервізора. Нарешті, S6 бере на себе виконання housekeeping, обробку сигналів та інші завдання.

Оскільки у нас застосовуються різні системи оркестрації, після складання та запуску контейнер повинен зрозуміти, в якому середовищі він знаходиться, та діяти за ситуацією. Наприклад:
Це дозволяє нам зібрати один образ і запускати його в різних системах оркестрації, причому він буде запускатися з урахуванням специфіки цієї системи оркестрації.

 Production-ready images for k8s

Для того самого контейнера ми отримуємо різні процесні дерева в Docker і Kubernetes:

Production-ready images for k8s

Корисне навантаження виконується під супервізором S6. Зверніть увагу на collector та events – це наші агенти, які відповідають за логи та метрики. У Kubernetes їх немає, а Docker є. Чому? 

Якщо подивитися специфікацію «пода» (тут і далі Kubernetes pod), то ми побачимо, що контейнер events виконується в поді, в якому є окремий контейнер collector, що виконує функцію збору метрик і логів. Ми можемо використовувати можливості Kubernetes: запуск контейнерів в одному поді, в єдиному процесному та/або мережному просторах. Фактично впроваджувати своїх агентів та виконувати якісь функції. І якщо цей же контейнер запуститься в Docker, він отримає на виході ті самі можливості, тобто зможе доставляти логи і метрики, так як агенти будуть запущені всередині. 

Метрики та логи

Доставка метрик та логів – складне завдання. Із її вирішенням пов'язано кілька аспектів.
Інфраструктура створюється до виконання корисного навантаження, а чи не масової доставки логів. Тобто цей процес має виконуватись із мінімальними вимогами до ресурсів контейнерів. Ми прагнемо допомогти нашим розробникам: "Візьміть контейнер Docker Hub, запустіть і ми зможемо доставити логи". 

Другий аспект – обмеження обсягу логів. Якщо в кількох контейнерах виникає ситуація сплеску обсягу логів (додаток у циклі виводить stack-trace), зростає навантаження на CPU, канали зв'язку, систему обробки логів, і це впливає на роботу хоста в цілому та інші контейнери на хості, то іноді це призводить до "падіння" хоста. 

Третій аспект - необхідно з коробки підтримувати якнайбільше методик збору метрик. Від читання файлів та опитування Prometheus-endpoint до використання специфічних протоколів додатків.

І останній аспект необхідно мінімізувати споживання ресурсів.

Ми вибрали open-source рішення Go під назвою Telegraf. Це універсальний конектор, який підтримує більше 140 видів вхідних каналів (input plugins) та 30 видів вихідних (output plugins). Ми його доопрацювали, і зараз ми розповімо, як він використовується у нас на прикладі Kubernetes. 

Production-ready images for k8s

Припустимо, розробник розгортає навантаження, і Kubernetes отримує запит створення пода. У цей момент для кожного пода автоматично створюється контейнер під назвою Collector (ми використовуємо mutation webhook). Collector – це наш агент. На старті цей контейнер налаштовує себе на роботу з Prometheus і системою збору логів.

  • І тому він використовує інструкції пода, й у залежність від її вмісту, створює, скажімо, кінцеву точку end-point Prometheus; 
  • На підставі специфікації пода та специфічних налаштувань контейнерів вирішує, як доставляти логи.

Логи ми збираємо через Docker API: розробникам достатньо покласти їх у stdout чи stderr, а далі Collector розбереться. Логи збираються chunk'ами з деякою затримкою, щоб запобігти можливому навантаженню хоста. 

Метрики збираються за примірниками робочого навантаження (процесів) у контейнерах. Все позначається тегами: namespace, під і так далі, а потім конвертується у формат Prometheus – і стає доступним для збору (крім логів). Також, логі, метрики та події ми відправляємо до Kafka і далі:

  • Логи доступні у Graylog (для візуального аналізу);
  • Логи, метрики, події вирушають до Clickhouse для довгострокового зберігання.

Так само все працює в AWS, тільки ми замінюємо Graylog з Kafka на Cloudwatch. Відправляємо туди логи, і все виходить дуже зручно: одразу зрозуміло, до кого кластера та контейнера вони відносяться. Те саме вірно і для Google Stackdriver. Тобто наша схема працює як on-premise з Kafka, так і у хмарі. 

Якщо ж у нас немає Kubernetes з подами, схема виходить трохи складнішою, але працює за тими самими принципами.

Production-ready images for k8s

Усередині контейнера виконуються такі самі процеси, вони оркеструються за допомогою S6. Ті самі процеси запущені всередині одного контейнера.

У підсумку

Ми створили цілісне рішення для складання та запуску образів в експлуатацію, з опціями збору та доставкою логів та метрик:

  • Розробили стандартизований підхід до збирання образів, на його основі розробили CI-шаблони;
  • Агенти для збору даних – це наші розширення Telegraf. Ми їх добре обкатали у production;
  • Застосовуємо mutation webhook для впровадження контейнерів з агентами у подах; 
  • Інтегрувалися в екосистему Kubernetes/Rancher;
  • Можемо виконувати однакові контейнери у різних системах оркестрації та отримувати очікуваний нами результат;
  • Створили повністю динамічну конфігурацію управління контейнерами. 

Співавтор: Ілля Прудніков

Джерело: habr.com

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