Як ми перемогли сутінок між тестуванням і експлуатацією

Деякий час тому ми HeadHunter виявили «сутінкову зону» при передачі нової версії сайту тестування в експлуатацію. Недостатня увага до різниці між тестовій і бойової інфраструктурою періодично призводило до падіння сайту.

Вийти з тіні

Старі тестові стенди помітно відрізнялися за внутрішнім устроєм від робочого кластера. Відрізнялися init-скрипти для запуску сервісів, відрізнялися файли конфігурації по розташуванню і змістом. Взаємодія сервісів між собою відбувалося без врахування особливостей бойового оточення.

Я покажу логіку нашого рішення, яка дозволила досягти якісно нових результатів тестування.

Ця стаття продовжує мій доповідь на SQA Days-18.

Файли конфігурації
Перший момент, який ми побачили, це різні файли конфігурації на тестових стендах і в бою. У нашому випадку це були навіть різні по розташуванню файли. Якщо говорити про їх вміст, то їх писали різні люди і різними способами. Що це означає? Це означає, що кожен пакет приносив з собою на бойові сервери файл налаштувань за замовчуванням, який лежав в /var/lib/… або /usr/shared/… Це ті параметри, які зручні в тестовому оточенні. І ті з них, які потрібно було замінити, знаходилися вже у файлі конфігурації /etc/ім'я_пакету. Налаштування туди записували сисадміни, коли отримували відповідну задачу в Jira.

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

Чому виникла така ситуація? Тому, що на тестових стендах, як ми звикли їх бачити, сервіси живуть на одному сервері. В одній і тій самій файловій системі. І якщо настройки одного з них потрібно поміняти у файлі /etc/default/jetty, то налаштування іншого доведеться міняти якось по-іншому. Для цього на тестовому стенді були написані особливі init-скрипти для управління різними сервісами. І там же, в самописних init-скриптах, були вказані конфіг-файли, які лежали в нестандартних місцях.

Вирішити конфлікт
Як же вирішити цей конфлікт? Ми вирішили, що потрібна ізоляція сервісів один від одного на рівні файлової системи. Адже в бою кожен сервіс крутиться на окремому сервері або виртуалке.

Можливо, chroot зможе вирішити завдання ізоляції сервісів на тестовому стенді на рівні файлової системи. Тоді кожен сервіс буде мати свою папку /etc і свої файли конфігурації, які розташовані там же, де вони перебувають на бойових серверах. І лог-файли будуть знаходитися в тих же папці, що і в бою. І таке рішення наближає до вирішення задачі, як використовувати ті ж конфіг-файли на тестовому стенді, що і в production environment.

Достатньо ізоляції на рівні файлової системи?
На старих тестових стендах всі сервіси слухали localhost, кожен на своєму порту. І спілкувалися між собою через localhost. Однак на цьому сайті кожен сервіс живе на своєму сервері. І, більше того, кожен сервіс запущений в 2-х і більше примірниках. Таке рішення потрібно, по-перше, для розподілу навантаження і горизонтальної масштабованості сервісу. І, по-друге, для забезпечення надійності роботи сервісу. У тих випадках, коли один з серверів повинен бути зупинений на обслуговування, інші такі ж беруть на себе його частину роботи.

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

Сисадмінам вигідно?!
І тут ми побачили ще один пункт для нашого списку завдань. Конфігурація балансувальника, ось що ми раніше ніколи не тестували! Тому ризик, пов'язаний з створенням конфігурації nginx, який і виконує роль балансувальника, повністю лежав на сисадминах. І вони не мали можливості перевірити його коректну роботу ніде, крім як на живому сайті. І іноді експерименти закінчувалися не дуже вдало…

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

Балансувальник
І на нових стендах буде балансувальник навантаження. Тоді нам буде потрібно в конфігурації nginx прописати IP і порти серверів в upstream. Можливо, буде зручніше дійсно виділити кожному сервісу по серверу, на якому він зможе працювати.
Отже, на додаток до ізоляції серверів по файловій системі, ми додали ізоляцію по IP. Крім того, допоміжні сервіси, такі як rsyslog, зможуть працювати за тим же принципом, як і на цьому сайті. Мабуть, тільки в тому випадку, якщо конфіг-файли кожного сервісу будуть ті ж, що і в бою.

І це – третій пункт нашого списку завдань. Як забезпечити використання тих же самих файлів конфігурації і з тим самим вмістом, як на тестових стендах, так і на бойових серверах?

Як домогтися однакових конфіги?
Якщо ми вже вирішили, що кожен сервіс буде запущено на окремому сервері, то, може бути, ми зможемо використовувати і скрипти deploy, які є у сисадмінів? І з їх допомогою розкладати на тестовий стенд ті ж файли конфігурації, що і на великому сайті? Так, ми можемо так зробити. З урахуванням того, що паролі від платіжних систем або СМС-розсилок повинні залишитися в секреті.

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

Конфіги на GitHub
Щоб сховати паролі у файлі конфігурації, ми використовуємо змінні. Тому що самі файли конфіги стали шаблонами Jinja2 для Ansible. Ми написали свою систему викладки з його використанням. І Ansible дозволяє нам мати два набори змінних, тобто дві пари папок group_vars і host_vars, де визначені значення змінних. Один набір знаходиться в папці playbooks, а інший – в папці з файлом inventory. І один з цих наборів завжди має пріоритет над іншим.

Таким чином, ми поклали в GitHub не тільки скрипти викладки і конфіг-файли, але і один набір значень змінних, якщо вони не секретні. Це можуть бути обмеження по пам'яті для додатків, кількість потоків або форкнутых процесів, і таймаут. Тобто ті значення, які розрізняються в бою і на тестовому стенді.

Зберігаємо секрети
Секретні ж значення, такі як паролі до платіжних систем і сервісів СМС-розсилок, а також паролі до бази даних, що знаходяться в приватному репозиторії сисадмінів і недоступні тестувальникам.

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

Окремий сервер для кожного стенду – марнотратно
З написаного вище ми бачимо, яким вимогам повинні відповідати ті окремі сервери для сервісів, які ми будемо запускати на тестовому стенді:

  • ізоляція файлової системи;
  • окремий IP-адресу;
  • init для запуску наших і супутніх пакетів;
  • opensshd для роботи Ansible.
Ці вимоги можуть задовольнити контейнери Linux, LXC. Додатковим плюсом при їх використанні буде те, що вони використовують спільну пам'ять, виділяючи її в міру того, як додатки всередині контейнерів її запитують. І, на відміну від віртуальних машин, не відкушують відразу великий шматок RAM. Завдяки цьому ми економимо пам'ять.

Які вигоди нашого рішення
В результаті того, що ми

  • виділили псевдосервер для кожного сервісу на тестовому стенді;
  • використовуємо ті ж скрипти викладки і ті ж параметри, що і в бою;
  • повторили на Linux-контейнері всередині стенду внутрішній балансувальник;
  • змушені спочатку збирати пакет з гілки Git, щоб викотити його на свій псевдосервер з допомогою deploy скриптів,
ми досягли певних результатів.

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

По-друге, ми стали тестувати ті самі конфіг-файли, які будуть использоаны на цьому сайті. І, значить, ми виключили людський фактор у написанні конфігів, який міг призводити до падіння сайту. Відмінності між стендом і production environment ми винесли в змінні.

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

І, по-четверте. Тепер ми можемо запустити два або більше примірника кожного сервісу. Ми не тільки можемо налагодити роботу reties в nginx, але ще й протестувати, як веде себе на сайті під час випуску нової версії.

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

Результат
Підіб'ємо підсумок. Реінжиніринг процесу тестування і рефакторинг тестових стендів дали чудовий результат. За 2 роки uptime сайту перевищив 99,9%. Це відмінний показник, що якщо перерахувати його в хвилини. За один місяць простий сайту становить менше 43 хвилин. При цьому ми в 3 рази посилили визначення downtime, з 60 500-х помилок в секунду до 20.

Для інтернет-бізнесу, яким і є HeadHunter, поліпшення uptime означає реальну економію грошей. Додайте до цього клієнтів, яких залучив hh.ru завдяки більш стабільній роботі сайту, ніж раніше. Як ви думаєте, чи є uptime сайту ключовим фактором успіху для вашого бізнесу?

Отже, 4 простих кроки:

  • виберіть кожного сервісу окремий сервер на стенді;
  • тестуйте пакет, а не гілку з кодом;
  • використовуйте скрипти викладки від експлуатації і тестуйте конфіг-файли;
  • повторіть балансувальник і тестуйте процес релізу.
Перемога світла неминуча!

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.