Використовуємо mcrouter, щоб досягти High Availability та масштабувати memcached в продукті

Усі статті, обговорення, новини про DevOps — в одному місці. Підписуйтеся на DOU | DevOps!

Мене звати Павло Калінін, я Head of IT Infrastructure Department в одній з продуктових компаній венчур білдера SKELAR. Керую департаментом інфраструктури з чотирьох команд, в яких загально працює 20+ спеціалістів.

Працюю над розвитком продукту для креаторів та стрімерів-початківців, який створений на PHP. До речі, цей продукт з моноліта зумів розвинутися, знайти юніт-економіку та перейти до масштабування (неймовірно, але факт 😅).

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

Тому ділюся досвідом, як ми розв’язували проблему з memcached на нашому продукті.

Проблема високого навантаження на memcached

У нас були 2 дата-центри, 75+ серверів, 25 доменів, наполовину забитий гігабітний канал і нескінченна кількість алертів усіх видів та кольорів. А також ребіти, редіси, купка баз і мемкеш, що лагає.

У роботі з високонавантаженими проєктами завжди потрібен особливий підхід і використання спеціальних інструментів, особливо якщо у вас PHP у Kubernetes.

PHP-FPM щоразу створює мережеве з’єднання під час звернення до зовнішніх даних і не перевикористовує його. Це призводить до високого навантаження на мережеву підсистему серверів, затримок на створення нових з’єднань і на закриття використаних портів. Як підсумок — при високому навантаженні можна досягти граничної кількості мережевих портів і отримати 500-ті на проді.

Розв’язання цієї проблеми — використання локальних проксі, доступних для PHP-FPM за socket.

Використання mcrouter для забезпечення High Availability

Розглянемо використання mcrouter (роутер для memcached від Facebook) для вирішення трьох важливих завдань:

● агрегація з’єднань від php до memcached;
● горизонтальне масштабування;
● відмовостійкість.

1. Агрегація з’єднань від php до memcached

Проблематика цього питання полягає в реалізації багатопотоковості на PHP. Для кожного нового процесу доводиться перевідкривати всі мережеві з’єднання, які займають мережеві порти. А вони мають неприємну властивість — закінчуватися.

Розв’язання цієї проблеми: використання сайдкара mcrouter і звернення до нього з коду застосунку через unix domain socket.

2. Горизонтальне масштабування

Крім інших багатьох фіч, mcrouter вміє:

  • робити fallback на сусідні серверні групи в разі помилки;
  • реплікувати запис.

За допомогою цих можливостей можна масштабувати кількість серверів memcached до великих розмірів.

3. Відмовостійкість

У нашому випадку дуже важлива відмовостійкість і висока доступність продукту. Для цього ми розгорнули 2 продуктові кластери в різних зонах доступності GCP. Тепер ми можемо безпроблемно знімати навантаження одного з кластерів з метою проведення критичних робіт. В подах із PHP — сайдкар с mcrouter.

Схема розгортання продуктових кластерів за допомогою mcrouter.

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

{
        "pools": {
          "A": {
            "servers": [
              "mamcached-server-1:11211",
              "mamcached-server-2:11211"
            ]
          },
          "B": {
            "servers": [
              "mamcached-server-2:11211",
              "mamcached-server-1:11211"
            ]
          }
        },
        "route": {
            "operation_policies": {
              "add": "AllFastestRoute|Pool|A",
              "append": "AllFastestRoute|Pool|A",
              "decr": "AllFastestRoute|Pool|A",
              "delete": "AllFastestRoute|Pool|A",
              "get": {
                "type": "RandomRoute",
                "children": [
                  "MissFailoverRoute|Pool|A",
                  "MissFailoverRoute|Pool|B"
                ]
              },
              "gets": {
                "type": "RandomRoute",
                "children": [
                  "MissFailoverRoute|Pool|A",
                  "MissFailoverRoute|Pool|B"
                ]
              },
              "incr": "AllFastestRoute|Pool|A",
              "prepend": "AllFastestRoute|Pool|A",
              "replace": "AllFastestRoute|Pool|A",
              "set": "AllFastestRoute|Pool|A"
            },
            "type": "OperationSelectorRoute"
          }
      }

Налаштування процесу оновлення memcached і Kubernetes без втрати даних

У нас в командах є домовленість: всі ключі в memcached мають TTL менш як добу і в самому memcached дані відсутні, то застосунок бере їх із mysql.

1. Параметр AllFastestRoute для швидкості запису

Щоб запис був швидкий через mcrouter, ми використовуємо параметр AllFastestRoute. Він дозволяє надіслати той самий реквест в обидві репліки memcached одночасно і одразу повертає успішну відповідь застосунку, якщо хоч одна з memcached реплік обробила запит без помилок.

Інші репліки обробляють запити на запис в background-режимі. Це дозволяє тримати достатній рівень консистентності даних і синхронізувати майже на 100% всі репліки за добу, а також мати replication factor 2 для нашого кеша.

2. Параметр MissFailoverRoute для обробки запитів get/ gets

Для того, щоб завжди успішно обробляти запити на читання (get/gets), ми використовуємо MissFailoverRoute — параметр mcrouter, який дозволяє послідовно пінгувати по всіх репліках memcached, доки ми не отримаємо успішну відповідь.

Іншими словами: ми можемо один раз на добу повністю втрачати дані з однієї з реплік без наслідків для нашого застосунку.

3. Процес оновлення Kubernetes

Щоб оновити ноди Kubernetes, ми створюємо окремий пул з новою версією Kubernetes і переміщаємо одну з реплік memcached. Далі чекаємо добу, слідкуючи за процесом синхронізації реплік через Grafana. Для цього ми використовуємо opensource prometheus експортери для memcached і mcrouter. Якщо дані синхронізувались, переходимо до наступного етапу — міграції другої репліки memcached на новий пул.

На цьому процес оновлення Kubernetes завершено, процес оновлення memcached відбувається аналогічним чином також без простоїв.

👍ПодобаєтьсяСподобалось7
До обраногоВ обраному0
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Підписатись на коментарі