У Челябінську проходять мітапи системних адміністраторів Sysadminka, і на останньому з них я робив доповідь про наше рішення для роботи додатків на 1С-Бітрікс у Kubernetes.
Бітрікс, Kubernetes, Сeph – відмінна суміш?
Розповім, як ми з усього цього зібрали діюче рішення.
Поїхали!
Мітап пройшов 18 квітня у Челябінську. Про наші мітапи можна почитати в Timepad і подивитися на ютубі.
Якщо хочете прийти до нас з доповіддю або як слухач - велкам, пишіть на [захищено електронною поштою] і в телеграмах t.me/vadimisakanov.
Рішення «Бітрікс у Kubernetes, версія Southbridge 1.0»
Я розповім про наше рішення у форматі «для чайників у Kubernetes», як це було зроблено на мітапі. Але гадаю, що слова Бітрікс, Docker, Kubernetes, Ceph вам відомі хоча б на рівні статей у Вікіпедії.
Що взагалі є готового про Бітрікс у Kubernetes?
У всьому інтернеті дуже мало інформації про роботу програм на Бітрікс у Kubernetes.
Я знайшов лише такі матеріали:
Доповідь Олександра Сербула, 1С-Бітрікс, та Антона Тузлукова з Qsoft:
Попереджаю, якість роботи рішень за посиланнями вища ми не перевіряли 🙂
До речі, під час підготовки нашого рішення я спілкувався з Олександром Сербулом, тоді його доповіді ще не було, тож у моїх слайдах є пункт «Бітрікс не користується Kubernetes».
Чи достатньо для створення повноцінного рішення для Бітрікс в Kubernetes?
Ні. Є багато проблем, які потрібно вирішити.
У чому проблеми з Бітрікс у Kubernetes?
Перша – готові образи з Dockerhub не підходять для Kubernetes
Якщо ми хочемо побудувати мікросервісну архітектуру (а в Kubernetes ми зазвичай хочемо), додаток у Kubernetes потрібно розділяти на контейнери та домагатися того, щоб кожен контейнер виконував одну маленьку функцію (і робив це добре). Чому лише одну? Якщо коротко — що простіше, то надійніше.
Якщо автентичніше - подивіться цю статтю та відео, будь ласка: https://habr.com/ru/company/southbridge/blog/426637/
Docker образи в Dockerhub переважно побудовані за принципом «все в одному», тому нам довелося все-таки робити свій велосипед і навіть образи робити з нуля.
Друга - код сайту правиться з адмін-панелі
Створили новий розділ на сайті – оновився код (додалася директорія з назвою нового розділу).
Змінили властивості компонента з адмін-панелі – змінився код.
Kubernetes "за замовчуванням" з таким працювати не вміє, контейнери повинні бути не змінними (Stateless).
Причина: кожен контейнер (під) у кластері обробляє лише частину трафіку. Якщо змінити код тільки в одному контейнері (поде), то в різних подах код буде різним, сайт працюватиме по-різному, різним користувачам будуть відображатися різні версії сайту. Так жити не можна.
Третя – треба вирішувати питання з деплоєм
Якщо у нас моноліт і один «класичний» сервер, все просто: розгортаємо нову кодову базу, проводимо міграцію БД, переключаємо трафік на нову версію коду. Перемикання відбувається миттєво.
Якщо у нас сайт у Kubernetes, розпиляний на мікросервіси, контейнерів з кодом багато — ой. Потрібно збирати контейнери з новою версією коду, викочувати їх замість старих, правильно виконувати міграцію БД і в ідеалі робити це непомітно для відвідувачів. Добре, що в цьому Kubernetes нам допомагає, підтримуючи цілу хмару різних видів деплою.
Четверта – треба вирішувати питання зі зберіганням статики
Якщо ваш сайт важить «лише» 10 гігабайт і ви розгорнете його повністю в контейнерах, ви отримаєте контейнери вагою 10 гігабайт, які будуть деплоїтися вічність.
Потрібно зберігати найважчі частини сайту поза контейнерами, і постає питання, як правильно це робити
Чого немає у нашому рішенні
Зовсім код Бітрікс на мікрофункції/мікросервіси не розрізаний (так, щоб реєстрація окремо, модуль інтернет-магазину окремо, тощо). Всю кодову базу ми зберігаємо у кожному контейнері цілком.
Базу в Kubernetes також не зберігаємо (я все ж таки реалізовував рішення з базою в Kubernetes для оточення розробників, але не для продакшн).
Адміністраторам сайту все-таки буде помітно, що сайт працює у Kubernetes. Функція "перевірка системи" працює некоректно, для редагування коду сайту з адмін-панелі потрібно спочатку натискати кнопку "хочу відредагувати код".
З проблемами визначилися, з необхідністю реалізації мікросервісності визначилися, мета зрозуміла - отримати працюючу систему для роботи додатків на Бітрікс у Kubernetes, зберігши і можливості Бітрікс, та переваги Kubernetes. Починаємо реалізацію.
Архітектура
Багато «робітників» подів із вебсервером (worker'и).
Один під з крон-тасками (обов'язково лише один).
Один upgrade під редагування коду сайту з адмін-панелі (також обов'язково тільки один).
Вирішуємо питання:
Де зберігати сесії?
Де зберігати кеш?
Де зберігати статику, не розміщувати ж гігабайти статики у купі контейнерів?
Як працюватиме база даних?
Docker образ
Починаємо зі складання Docker образу.
Ідеальний варіант - у нас один універсальний образ, на його основі ми отримуємо і worker-поди, і поди з кронтасками, і upgrade поди.
У нього включений nginx, apache/php-fpm (можна вибрати під час складання), msmtp для надсилання пошти, та cron.
При складанні образу в директорію /app копіюється повна кодова база сайту (за винятком тих частин, що ми винесемо в окремий shared storage).
Мікросервісність, сервіси
worker піди:
Контейнер з nginx + контейнер apache/php-fpm + msmtp
msmtp винести в окремий мікросервіс не вийшло, Бітрікс починає обурюватись, що не може безпосередньо надіслати пошту
У кожному контейнері є повна кодова база.
Заборона зміни коду в контейнерах.
cron під:
контейнер з apache, php, cron
в комплекті повна кодова база
заборона на зміну коду у контейнерах
upgrade під:
контейнер з nginx + контейнер apache/php-fpm + msmtp
заборони на зміну коду у контейнерах немає
сховище сесій
сховище кешу Бітрікс
Ще важливо: паролі для підключення до всього, від бази даних до пошти ми зберігаємо в kubernetes secrets. Отримуємо бонус, паролі видно лише тим, кому ми даємо доступ до секретів, а не всім, хто має доступ до кодової бази проекту.
Сховище для статики
Можна використовувати будь-що: ceph, nfs (але nfs не рекомендуємо для продакшн), network storage від «хмарних» провайдерів, etc.
Сховище потрібно буде підключати в контейнерах /upload/ директорію сайту та інші директорії зі статикою.
База даних
Для простоти радимо виносити базу за межі Kubernetes. База в Kubernetes - окреме складне завдання, вона зробить схему набагато складнішою.
Сховище сесій
Використовуємо memcached 🙂
Він добре справляється із зберіганням сесій, кластеризується, «нативно» підтримується як session.save_path у php. Така система багато разів відпрацьована ще в класичній монолітній архітектурі, коли ми будували кластери з великою кількістю веб-серверів. Для деплою ми використовуємо helm.
$ helm install stable/memcached --name session
php.ini - тут в образі задані налаштування для зберігання сесій в memcached
Ми використовували Environment Variables для передачі даних про хостів з memcached https://kubernetes.io/docs/tasks/inject-data-application/define-environment-variable-container/.
Це дозволяє використовувати той самий код в оточеннях dev, stage, test, prod (імена хостів memcached в них будуть відрізнятися, тому в кожне оточення нам потрібно передавати унікальне ім'я хостів для сесій).
Сховище кешу Бітрікс
Нам потрібне відкаузостійке сховище, в яке всі поди могли б писати і з якого могли б читати.
Також використовуємо memcached.
Це рішення рекомендується самим Бітріксом.
$ helm install stable/memcached --name cache
bitrix/.settings_extra.php — тут у Бітрікс задається, де у нас зберігається кеш
Також використовуємо Environment Variables.
Кронтаски
Є різні підходи до виконання кронтасків у Kubernetes.
окремий deployment з подом для виконання кронтасків
cronjob для виконання кронтасків (якщо це web app - з wget https://$host$cronjobname, або kubectl exec всередину одного з worker подів, і т.п.)
і т.п.
Можна сперечатися про найбільш правильне, але в цьому випадку ми вибрали варіант "окремий deployment з подами для кронтасків"
Як це зроблено:
крон-таски додаємо через ConfigMap або через файл config/addcron
в одному примірнику запускаємо контейнер, ідентичний worker-поду + дозволяємо виконання крон-таск в ньому
використовується та ж кодова база, завдяки уніфікації складання контейнера проста
Що хорошого отримуємо:
маємо працюючі кронтаски в оточенні, ідентичному оточенню розробників (docker)
кронтаски не потрібно «переписувати» для Kubernetes, вони працюють у тому ж вигляді і в тій же кодовій базі, що й раніше
крон-таски можуть додавати всі члени команди з правами commit у production гілку, а не тільки адміни
Модуль Southbridge K8SDeploy та редагування коду з адмін-панелі
Адже ми говорили про upgrade під?
А як спрямовувати туди трафік?
Ура, ми написали для цього модуль на php 🙂 Це невеликий класичний модуль для Бітрікс. Його ще немає у відкритому доступі, але ми плануємо його відкрити.
Модуль встановлюється як звичайний модуль у Бітрікс:
І виглядає ось так:
Він дозволяє встановити cookie, яка ідентифікує адміністратора сайту і дозволяє Kubernetes відправляти трафік на upgrade під.
Коли зміни завершено, потрібно натиснути git push, зміни коду будуть відправлені в git, далі система збере образ з новою версією коду і «розкотить» її кластером, замінивши старі поди.
Так, трохи милицею, але при цьому ми зберігаємо мікросервісну архітектуру і не забираємо у користувачів Бітрікс улюблену можливість поправити код з адмінки. Зрештою, це опція, можна завдання редагування коду вирішити по-іншому.
Helm чарт
Для складання додатків у Kubernetes ми зазвичай використовуємо пакетний менеджер Helm.
Для нашого рішення Бітрікс у Kubernetes Сергій Бондарєв, наш провідний системний адміністратор, написав спеціальний Helm чарт.
Він виконує складання worker, ugrade, cron подів, налаштовує ingress'и, сервіси, передає змінні з Kubernetes secrets у поди.
Код ми зберігаємо в Gitlab, і складання Helm також запускаємо з Gitlab.
Helm також дозволяє робити безшовний відкат, якщо раптом при депло щось пішло не так. Приємно, коли не ви в паніці фіксуйте код по фтп, тому що прод впав, а Kubernetes робить це автоматично, причому без даунтайму.
Деплой
Так, ми фанати Gitlab & Gitlab CI, використовуємо його 🙂
При комітті Gitlab в репозитарій проекту Gitlab запускає пайплайн, який виконує розгортання нової версії оточення.
Етапи:
build (збираємо новий Docker образ)
test (тестуємо)
clean up (видаляємо тестове оточення)
push (надсилаємо його в Docker registry)
deploy (розгортаємо програму в Kubernetes через Helm).
Ура, готово, впроваджуємо!
Ну чи ставимо запитання, якщо вони є.
Отже, що ми зробили
З технічного погляду:
докеризували Бітрікс;
«розрізали» Бітрікс на контейнери, кожен із яких виконує мінімум функцій;
досягли стабільного стану контейнерів;
вирішили проблему з оновленням Бітрікс у Kubernetes;
всі функції Бітрікс продовжили працювати (майже всі);
відпрацювали деплою в Kubernetes та відкат між версіями.
З погляду бізнесу:
відмовостійкість;
інструменти Kubernetes (проста інтеграція із Gitlab CI, безшовний деплой, etc);
паролі в секретах (видні лише тим, кому прямо надано доступ до паролів);
зручно робити додаткові оточення (для розробки, тестів та ін.) усередині єдиної інфраструктури.