.NET Core на Linux, DevOps на коні

Ми розвивали DevOps як могли. Нас було 8 людина, і Вася був найкрутішим за Windows. Раптом Вася пішов, а у мені постало завдання вивести новий проект, який постачає Windows-розробка. Коли я висипав на стіл весь стек Windows-розробки, то зрозумів, що ситуація — біль…

Так починається історія Олександра Сінчінова на DevOpsConf. Коли з компанії пішов провідний фахівець з Windows, Олександр запитав, що тепер робити. Переходити на Linux, звичайно ж! Олександр розповість, як йому вдалося створити прецедент і перевести частину Windows розробки на Linux на прикладі реалізованого проекту на 100 000 кінцевих користувачів.

.NET Core на Linux, DevOps на коні

Як легко і невимушено доставляти проект в RPM, використовуючи TFS, Puppet, Linux .NET core? Як підтримувати версіонування БД проекту, якщо розробка вперше чує слова Postgres і Flyway, а дедлайн післязавтра? Як інтегрувати з Docker? Як мотивувати .NET-розробників відмовитися від Windows і смузі на користь Puppet і Linux? Як вирішувати ідеологічні конфлікти, якщо обслуговувати Windows у продакшн немає ні сил, ні бажання, ні ресурсів? Про це, а також про Web Deploy, тестування, CI, про практики використання TFS у існуючих проектах, і, звичайно, про зламані милиці і працюючі рішення, в розшифровці доповіді Олександра.


Отже, Вася пішов, завдання на мені, девелопери чекають з вилами з нетерпінням. Коли я остаточно усвідомив, що Васю не повернути приступив до справи. Для початку оцінив відсоток Win VM у нашому парку. Рахунок був не на користь Windows.

.NET Core на Linux, DevOps на коні

Так як ми активно розвиваємо DevOps, я зрозумів, що потрібно щось змінювати в підході винесення нового додатка. Рішення було одне — за можливості перекласти все на Linux. Google мені допоміг - на той момент вже був портований .Net під Linux, і я зрозумів, що це рішення!

Чому .NET core у зв'язку з Linux?

На це було кілька причин. Між «платити гроші» і «не платити» більшість вибере друге— як і я. Ліцензія на MSDB коштує близько 1 $, обслуговування парку віртуальних машин Windows обчислюється сотнями доларів. Для великої компанії це є великі витрати. Тому економія - перша причина. Не найважливіша, але одна з вагомих.

Віртуальні машини Windows займають більше ресурсів, ніж їх брати з Linux вони важкі. Враховуючи масштаб великої компанії, ми обрали Linux.

Система просто вбудовується в існуючий CI. Ми вважаємо себе прогресивними DevOps'ами, використовуємо Bamboo, Jenkins і GitLab CI, тому більша частина у нас крутиться на Linux.

Остання причина— зручний супровід. Нам потрібно було знизити поріг входження для «супроводників» — хлопців, які розуміють технічну частину, забезпечують безперебійність і обслуговують сервіси з другої лінії. Вони вже були знайомі зі стеком Linux, тому їм набагато простіше зрозуміти новий продукт, підтримувати і супроводжувати, ніж витрачати додаткові ресурси, щоб розібратися з аналогічним функціоналом ПЗ для Windows платформи.

Вимоги

Перше та головне — зручність нового рішення для розробників. Не всі з них виявилися готовими до змін, особливо після вимовленого слова Linux. Розробники хочуть улюблену Visual Studio, TFS з автотестами з збірок і смузі. Як відбувається доставка в продакшн — їм не важливо. Тому ми вирішили не змінювати звичний процес і залишити для Windows-розробки все без змін.

Новий проект потрібний вбудувати в існуючий CI. Рейки вже були і всю роботу було необхідно виконати з урахуванням параметрів системи управління конфігурацією, прийнятих стандартів доставки і систем моніторингу.

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

Дедлайн — вчора.

Група Win розробки

З чим тоді працювала команда Windows?

.NET Core на Linux, DevOps на коні

Зараз я можу впевнено сказати, що IdentityServer4 — це класна безкоштовна альтернатива ADFS з аналогічними можливостями, або що Entity Framework Core — рай для розробника, де можна не заморочуватися написанням SQL скриптів, а описувати запити в БД у термінах ОВП. Але тоді, на обговоренні плану дій, я дивився на цей стек як на шумерський клинопис дізнаючись лише PostgreSQL і Git.

На той момент ми активно використовували Ляльковий як систему керування конфігурацією. У більшості наших проектів ми застосовували GitLab CI, Еластичний, балансували високонавантажені сервіси за допомогою HAProxy, стежили за всім з допомогою Zabbix, зв'язки Grafana и Прометей, Єгер, і все це крутилося на залізяках HPESXi на VMware. Всім знайомо — класика жанру.

.NET Core на Linux, DevOps на коні

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

Що було

TFS — це досить потужна система, яка не тільки доставляє код від розробника до кінцевої продакшн-машини, але також має набір для дуже гнучкої інтеграції з різними сервісами для забезпечення CI на кросплатформовому рівні.

.NET Core на Linux, DevOps на коні
Раніше це були суцільні кватирки. TFS використовував кілька Build-агентів, на яких збиралося безліч проектів. У кожному агенті по 3-4 worker-a, щоб розпаралелити завдання та оптимізувати процес. Далі, згідно з релізними планами, TFS доставляв новий Build на Windows-сервер додатків.

До чого ми хотіли прийти

Для доставки та розробки використовуємо TFS, а запускаємо додаток на Linux Application server,і між ними якась магія. Цей Чарівна коробка і є сіль майбутньої роботи. Перед тим, як розібрати його по частинах, зроблю крок убік і скажу два слова про додаток.

Проект

Додаток надає функціональність для оперування передплаченими картками.

.NET Core на Linux, DevOps на коні

Клієнт

Існували два типи користувачів. Перший отримував доступ, авторизуючись за сертифікатом SSL SHA-2. У другий був доступ за логіном і паролем.

HAProxy

Далі клієнтський запит потрапляв у HAProxy, який вирішував наступні завдання:

  • первинна авторизація;
  • термінування SSL;
  • тюнінг HTTP запитів;
  • трансляція запитів.

Перевірка сертифікату клієнта йшла по ланцюжку. Ми — влада і можемо собі таке дозволити, оскільки самі надаємо сертифікати клієнтам сервісу.

Зверніть увагу на третій пункт, трохи пізніше повернемося до нього.

Backend

Бекенд планували зробити на Linux. Бекенд взаємодіє з БД, підвантажує необхідний список привілеїв і потім, в залежності від того, якими привілеями має авторизований користувач, надає доступ для підписання фінансових документів та надсилання їх на виконання, або генерації якогось звіту.

Економія з HAProxy

Крім двох контекстів, якими ходив кожен із клієнтів, існував ще контекст identity. IdentityServer4 якраз дозволяє авторизуватися, це безкоштовний і потужний аналог для ADFS - Послуги Федерації Active Directory.

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

Другий крок — запит потрапляв на сторінку авторизації в IdentityServer, де клієнт реєструвався, а в базі даних IdentityServer з'являвся той самий довгоочікуваний токен.

Третій крок — клієнт редиректився назад на контекст, з якого він прийшов.

.NET Core на Linux, DevOps на коні

У IdentityServer4 є особливість: відповідь на зворотний запит він повертає за HTTP. Як не билися з настроюванням сервера, як не просвічувалися документацією, але ми щоразу отримували початковий запит клієнта з URL, який прийшов по HTTPS, а Identity Server повертав той самий контекст, але з HTTP. Ми були в шоці! І перевели все це через контекст identity на HAProxy, а в хедерах довелося модифікувати протокол HTTP на HTTPS.

У чому ж поліпшення і де заощадили?

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

Як має працювати

Отже, як я обіцяв Magic Box. Ми вже розуміємо, що гарантовано рухаємося в бік Linux. Давайте сформулюємо конкретні завдання, які вимагали вирішення.

.NET Core на Linux, DevOps на коні

Маніфести Puppet. Щоб доставляти та керувати конфігурацією сервісу та програми, потрібно було написати класні рецепти. Рулончик з олівцем красномовно показує як швидко і якісно це було зроблено.

Спосіб доставки. Стандарт - це RPM. Всі розуміють, що в Linux без нього ніяк, але сам проект після складання був набором виконуваних DLL-файлів. Їх було близько 150, проект досить важкий. Єдине гармонійне рішення - упаковати цю бінарщину в RPM і вже з неї розгортати додаток.

Версіонування. Нам потрібно було релізуватися дуже часто, і потрібно було вирішити, яким чином формувати ім'я пакета. Це питання рівня інтеграції з TFS. Build-агент у нас був на Linux. Коли TFS відправляє завдання обробнику - worker - на Build-агент, він передає йому ще і банк змінних, які потрапляють в environment процесу обробника. У цих змінних оточення передається ім'я Build, ім'я версії та інші змінні. Детальніше про цей у розділі «складання RPM-пакету».

Налаштування TFS зводилася до налаштування Pipeline. Раніше ми збирали на Windows-агентах всі Windows-проекти, а зараз з'являється Linux-агент - Build-агент, який потрібно включити в групу збірки, збагатити якимись артефактами, сказати, якого саме типу проекти будуть збиратися на цьому Build-агенті, і якось модифікувати Pipeline.

IdentityServer. ADFS не наш шлях, топимо за Open Source.

Пройдемося по компонентах.

Чарівна коробка

Складається із чотирьох частин.

.NET Core на Linux, DevOps на коні

Linux Build-агент. Linux, тому що ми під нього збираємо — логічно. Ця частина виконувалася за три кроки.

  • Налаштувати worker-и і не один, оскільки передбачалася розподілена робота над проектом.
  • Встановити .NET Core 1.х. Чому саме 1.х, коли вже доступно 2.0 в стандартному репозиторії? Тому що, коли ми починали розробку, стабільною версією була 1.09, і проект було вирішено робити під неї.
  • Git 2.x.

RPM-репозиторія. RPM-пакети потрібно було десь зберігати. Передбачалося, що ми будемо використовувати той самий корпоративний RPM-репозиторій, який доступний всім Linux хостам. Так і надійшли. На сервері репозиторію налаштовано веб-гачок який скачував із зазначеного місця необхідний RPM-пакет. Версію пакету webhook'у повідомляв Build-агент.

GitLab. Увага! GitLab тут використовується не розробниками, а відділом експлуатації для контролю версій програми, версій пакетів, контролю стану всіх Linux-машин і в ньому зберігається рецептура — всі маніфести Puppet.

Ляльковий — розрулює всі спірні моменти і доставляє саме ту конфігурацію, яку ми хочемо, з Gitlab.

Починаємо занурюватись. Як відбувається доставка DLL в RPM?

Доставка DDL в RPM

Припустимо, у нас є рок-зірка розробки на .NET. Він використовує Visual Studio і створює релізну гілку. Після цього завантажує її в Git, і Git тут - TFS-сутність, тобто це репозиторій програми, з яким працює розробник.

.NET Core на Linux, DevOps на коні

Після цього TFS бачить, що прилетів новий коміт. Який додаток? В налаштуваннях TFS є мітка, якими ресурсами володіє той чи інший Build-агент. У цьому випадку він бачить, що ми збираємо .NET Core проект і вибирає Linux Build-агент з пула.

Build-агент отримує вихідники, викачує необхідні залежно c репозиторія .NET, npm і т.д. і після збирання самої програми та наступної упаковки відправляє RPM-пакет в RPM-репозиторій.

З іншого боку відбувається наступне. Інженер відділу експлуатації займається безпосередньо викочуванням проекту: змінює версії пакетів у Hiera в репозиторії, де зберігається рецептура програми, після чого Puppet тригеріт Юм, забирає новий пакет з репозиторію, і нова версія програми готова до використання.

.NET Core на Linux, DevOps на коні

На словах все просто, але що відбувається всередині на самому Build-агенті?

Упаковка DLL RPM

Отримано вихідники проекту та завдання на збірку від TFS. Build-агент запускає складання самого проекту із вихідників. Зібраний проект доступний у вигляді безлічі DLL файлів, які упаковані в zip-архів для зниження навантаження на файлову систему.

ZIP-архів викидається в директорію складання пакету RPM. Далі Bash-скрипт ініціалізує змінні оточення, знаходить версію Build, версію проекту, шлях до директорії складання, і запускає RPM-build. Після закінчення зборки пакет публікується в локальний репозиторій, який знаходиться на Build-агенті.

Далі, з Build-агента на сервер у RPM-репозиторія надсилається JSON-запит із зазначенням імені версії та білда. Webhook, про який я раніше говорив, викачує цей самий пакет з локального репозиторію на Build-агенті і робить нову збірку доступною для установки.

.NET Core на Linux, DevOps на коні

Чому саме така схема доставки пакета в репозиторій RPM? Чому не можна відразу відправити зібраний пакет у репозиторій? Справа в тому, що це умова для забезпечення безпеки. Такий сценарій обмежує можливість несанкціонованого завантаження RPM-пакетів сторонніми людьми на сервер, який доступний усім Linux-машинам.

Версіонування БД

На консиліумі з розробкою з'ясувалося, що хлопцям ближче MS SQL, але в більшості non-Windows проектів ми вже використовували PostgreSQL. Так як ми вже вирішили відмовитися від платного, то стали використовувати PostgreSQL і тут.

.NET Core на Linux, DevOps на коні

В цій частині хочу розповісти, як ми здійснювали версіонування БД і як вибирали між Flyway і Entity Framework Core. Розглянемо їх плюси та мінуси.

Мінуси

Flyway йде тільки в один бік, ми не можемо відкотитися назад - Це суттєвий мінус. Порівнювати з Entity Framework Core можна за іншими параметрами з точки зору зручності розробника. Ви вже пам'ятаєте, що ми поставили на кут, і основним критерієм було не змінити нічого для Windows-розробки.

Для Flyway нам була потрібна якась обгорткащоб хлопці не писали SQL-запити. Їм набагато ближче оперувати в термінах ОВП. Написали інструкції з роботи з об'єктами БД, сформувався SQL-запит і виконався. Нова версія БД готова, прокаталася все добре, все працює.

У Entity Framework Core є мінус при великих навантаженнях він будує неоптимальні SQL-запити, і просадка по БД може бути суттєвою. Але бо у нас не високонавантажений сервіс, ми не обчислюємо навантаження сотнями RPS, ми прийняли ці ризики і делегували проблему майбутнім нам.

Плюси

Entity Framework Core працює з коробки та зручний розробці, а Flyway легко інтегрується в існуючий CI. Але ми що робимо зручно девелоперам:)

Процедура накату

Puppet бачить, що приходить зміна версії пакетів серед яких той, що відповідає за міграцію. Спочатку встановлює пакет, де містяться міграційні скрипти і функціонал зав'язаний на БД. Після цього рестартується додаток, який працює з БД. Далі йде встановлення компонентів, що залишилися. Черговість установки пакетів і запуску додатків описані в маніфесті Puppet.

Програми використовують чутливі дані, такі як токени, паролі до БД, все це підтягується в конфіг з Puppet master, де вони зберігаються в зашифрованому вигляді.

Проблеми TFS

Після того, як ми визначилися і зрозуміли, що у нас дійсно все працює, я вирішив подивитися, що діється зі збірками в TFS в цілому для відділу Win-розробки за іншими проектами – швидко чи ні збираємося/релізимося, і виявив істотні проблеми зі швидкістю .

Один із основних проектів збирається 12-15 хвилин — це довго, так жити не можна. Швидкий аналіз показав моторошну просадку по I/O, і це на масивах.

Проаналізувавши покомпонентно, я виділив три вогнища. Перший — "Kaspersky antivirus", який на всіх Windows Build-агентах сканує вихідні коди. Другий — Windows Indexer. Він не був відключений, і на Build-агентах в реальному часі індексувалося все в процесі деплою.

Третій — NPM install. Виявилося, що в більшості Pipelines ми використовували саме цей сценарій. Чим він поганий? Процедура Npm install запускається при формуванні дерева залежностей package-lock.json, де фіксуються версії пакетів, які будуть використовуватися для збирання проекту. Мінус у тому, що Npm install щоразу підтягує актуальні версії пакетів з інтернету, а це чималий час у разі великого проекту.

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

Рішення

  • Вихідники у винятку AV.
  • Вимкнення індексації.
  • Перехід на npm ci.

Плюси npm ci, в тому, що ми збираємо дерево залежностей один раз, і  отримуємо можливість надати розробнику актуальний список пакетів, з яким він може скільки завгодно експериментувати локально. Це заощаджує час розробників, які пишуть код

Конфігурація

Нині трохи про конфігурацію репозиторію. Історично ми використовуємо нексус для управління репозиторіями, у тому числі Internal REPO. У цей внутрішній репозиторій поставляються всі компоненти, які ми використовуємо для внутрішніх цілей, наприклад, самописні моніторинги.

.NET Core на Linux, DevOps на коні

Ми також використовуємо NuGet, тому що він краще кешує в порівнянні з іншими пакетними менеджерами.

Результат

Після того, як ми оптимізували Build-агентів, середній час зборки скоротився з 12 хвилин до 7.

Якщо порахувати всі машини, які ми могли б використовувати для Windows, але переклали на Linux в цьому проекті, ми заощадили близько $ 10. І це тільки на ліцензіях, а якщо враховувати зміст більше.

Плани

На наступний квартал заклали в план роботу над оптимізацією доставки коду.

Перехід на пребілд Docker-образу. TFS — класна штука з множиною плагінів, які дозволяють інтегрувати в Pipeline, в том числі, і збірку за тригером, припустимо, Docker-образу. Цей тригер ми хочемо зробити на той самий package-lock.json. Якщо якимось чином змінюється склад компонентів, які використовуються для складання проекту - у нас збирається новий Docker-образ. Надалі він використовується для розгортання контейнера з зібраним додатком. Зараз цього немає, але плануємо перейти на мікросервісну архітектуру в Kubernetes, який активно розвивається в нашій компанії і давно обслуговує продакшн рішення.

Резюме

Закликаю всіх викинути Windows, але це не тому, що я не вмію її готувати. Причина в тому, що більша частина Opensource-рішень - це Linux-стек. Ви добре заощадите на ресурсах. На мій погляд, майбутнє за рішеннями Open Source на Linux з потужним ком'юніті.

Профіль спікера Олександра Сінчинова на GitHub.

DevOps Conf — це конференція з інтеграції процесів розробки, тестування та експлуатації для професіоналів від професіоналів. Саме тому проект, про який розповідав Олександр? реалізовано та працює, а день виступу проведено два успішні релізи. на DevOps Conf на РИТ++ 27 і 28 травня буде ще більше подібних кейсів від практиків. Ще можна схопитися в останній вагон і подати доповідь чи не поспішаючи забронювати квиток. Зустрінемось у Сколково!

Джерело: habr.com

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