Як оновити Ruby для сервісу з 10+ тісно пов'язаними пакетами — без спричинення простоїв?
Задача: Один інтерпретатор Ruby для складного сервісу
Ось в чому справа:
Наш Ruby-сервіс включає понад 10 взаємопов'язаних пакетів. Всі вони працюють разом під єдиним інтерпретатором Ruby 2 в контейнері ECS.
Ми хочемо оновити кожен пакет у системі до Ruby 3, при цьому зберігаючи всю систему працездатною на Ruby 2.
Будь-яка помилка може порушити роботу сервісу.
Нам потрібен був обережний, поетапний підхід.
Поетапний підхід
Ось як ми це зробили:
1. Почати з незалежних пакетів
- Ми почали з утилітних пакетів, які не мають залежностей.
- Потім оновлювали залежні пакети, залишаючи основний пакет для останнього.
2. Забезпечити сумісність обох версій
- Ми оновили кожен пакет, щоб він працював як з Ruby 2, так і з Ruby 3.
- Це дозволило нам зберегти роботу системи на Ruby 2, поки ми по черзі оновлювали кожен пакет.
3. Перевірка кожного пакету
- Компіляція: Ми переконалися, що кожен пакет компілюється як для Ruby 2, так і для Ruby 3.
- Юніт-тести: Ruby — це інтерпретована мова, тому для перевірки правильності коду ми повинні були виконати тести. Ми запускали тести для обох версій Ruby, щоб виявити помилки під час виконання.
- Інтеграційні тести: Після оновлення кожного пакету ми запускали інтеграційні тести на Ruby 2, щоб переконатися, що система працює як очікується.
4. Останні кроки
- Коли всі пакети стали сумісними з Ruby 3, ми переключили сервіс на Ruby 3.
- Протягом місяця ми моніторили логи сервісу, зберігаючи підтримку Ruby 2, на випадок, якщо доведеться відкотити зміни.
- Після стабільного місяця ми видалили підтримку Ruby 2.
Цей процес дозволив здійснити плавне оновлення.
Уроки, які ми винесли
Ось що спрацювало для нас:
- Юніт-тести критичні: Оскільки Ruby є інтерпретованою мовою, помилки під час виконання можуть з’явитися навіть після успішної компіляції. Комплексні юніт-тести є необхідними для виявлення помилок.
- Розділяй і володарюй: Призначивши різним інженерам незалежні пакети, ми змогли працювати паралельно та пришвидшити процес.
- Діліться знаннями: Ми створили спільний канал в Slack та документ. Це допомогло уникнути дублювання зусиль і пришвидшило пошук рішень.
Підсумок
Оновлення тісно пов'язаних систем вимагає ретельного планування.
Ось кроки, що дозволили нам зберегти працездатність сервісу під час оновлення:
- Почати з незалежних пакетів
- Забезпечити сумісність з обома версіями Ruby
- Ретельне тестування на кожному етапі
- Моніторинг після оновлення перед видаленням підтримки Ruby 2
З цим методом ми змогли здійснити плавне оновлення без простоїв.
Перекладено з: How to Upgrade Ruby Ecosystems Without Downtime: A 4-Step Framework