$30 off During Our Annual Pro Sale. View Details »

Лимитируй это

Лимитируй это

Однажды я провел день на колоссальном девятичасовом созвоне в попытке понять «а почему продакшн не работает?» и выводы, которые я вынес из этого дебага, я хочу принести вам в этом докладе.

Что же у нас за система? Мы делаем чат, это современная event-driven архитектура, все наши бэкенды — это rest-like части на fastapi и основная часть системы базируется на kafka продюсерах/консьюмерах. Весь наш код асинхронный, а баз две штуки — postgres и keydb.

Моя история будет о том:
— как уронить keydb;
— как kafka может уничтожить ваше асинхронное приложение;
— как неправильно планировать ресурсы в кубер кластере;
— как можно покалечиться всей системой, если у тебя кривая библиотека для работы с БД;
— какие мониторинги делать обязательно;
— что такое плохой healtcheck;
— почему документация может быть очень коварной.

Доклад стыдный, но, надеюсь, полезный!

Denis Anikin

July 31, 2023
Tweet

More Decks by Denis Anikin

Other Decks in Programming

Transcript

  1. /
    Денис Аникин
    https://xfenix.ru
    Лимитируй это

    View Slide

  2. Денис Аникин
    2
    Что я делаю
    — работаю в Райфе
    — teamlead в 3 командах
    — community lead в Python Community
    — fullstack: typescript, python, devops
    — шучу шутки со средней оценкой 4 балла
    https://xfenix.ru

    View Slide

  3. Денис Аникин
    3
    Что я делаю
    — работаю в Райфе
    — teamlead в 3 командах
    — community lead в Python Community
    — fullstack: typescript, python, devops
    — шучу шутки со средней оценкой 4 балла ИЗ 100
    https://xfenix.ru

    View Slide

  4. View Slide

  5. Пару слов о
    нашей системе

    View Slide

  6. Что у нас за система?
    6
    — Распределенная
    — Чат
    — Две основных базы данных: KeyDB (redis) & PostgreSQL
    — Наш MQ это Kafka
    — Пишем на FastAPI, Starlette
    — НЕ ХАЙЛОАД (!!!11)
    — Ну конечно же любимые и ненавистные всем микросервисы (настоящие)

    View Slide

  7. Пару слов о «НЕ ХАЙЛОАД»
    7
    — Порядок: сотни RPS от пользователей
    — Система постоянной доступности (непрерывный режим работы)
    — «Последняя» линия, к нам приходят когда уже что-то сломалось

    View Slide

  8. И вот однажды
    мы упали
    8

    View Slide

  9. View Slide

  10. Моя история — о том
    как это вышло
    10

    View Slide

  11. View Slide

  12. Ещё пару слов обо мне
    12
    — У меня не очень много опыта с распределенными и особенно event
    driven системами
    — Я не идеально разбираюсь в мониторинге

    View Slide

  13. 4 золотых сигнала
    Latency, traffic, saturation, errors

    View Slide

  14. Ещё пару слов обо мне
    14
    — У меня не очень много опыта с распределенными и особенно event
    driven системами
    — Я не идеально разбираюсь в мониторинге
    — Kubernetes на момент разработки системы был для меня новой
    технологией

    View Slide

  15. Все совпадения с
    реальными людьми —
    выдуманы
    15

    View Slide

  16. Если вы беспокоетесь,
    что чего-то не знаете,
    посмотрите на меня и
    вам станет легче
    16

    View Slide

  17. Какой бывает event driven

    View Slide

  18. По полям, по полям архитектор едет к нам!
    Так как в докладе будет много хороших решений и классных практик…

    View Slide

  19. Итак, июнь 2022,
    пятница
    19

    View Slide

  20. View Slide

  21. 14 часов mob-debug в zoom

    View Slide

  22. Наша система не упала,
    а просто перестала
    работать
    22

    View Slide

  23. View Slide

  24. Если вы работаете с монолитом, то знакомо:

    View Slide

  25. Микросервисы (вы ждали этой картинки):

    View Slide

  26. Но на самом деле:

    View Slide

  27. НАКОНЕЦ о наших делах
    27

    View Slide

  28. KeyDB (redis) мёртв
    28

    View Slide

  29. View Slide

  30. Хвала failover!
    (заметили не сразу)
    30

    View Slide

  31. View Slide

  32. Стоп.
    А что происходит?
    32

    View Slide

  33. Мы бъемся головой в
    лимит соединений!
    33

    View Slide

  34. Открытие: когда KeyDB
    упирается в соединения,
    ну…
    он падает!
    34

    View Slide

  35. Давайте вынесем
    на ретро
    35

    View Slide

  36. Ошибка номер 1 —
    мы не мониторим
    соединения с KeyDB
    36

    View Slide

  37. KeyDB
    37
    И мониторинг
    — Обычно все мониторят память
    — Количество операций
    — CPU

    View Slide

  38. Сколько соединений по-умолчанию у KeyDB?
    38
    Давайте проголосуем
    — 1 000
    — 10 000
    — 30 000
    — не ограничено

    View Slide

  39. Но зачем столько соединений-то?
    39
    Ну правда!
    — Корутины!
    — Распределенная система
    — Куча реплик
    — Несколько зон доступности
    — И секретный ингредиент!

    View Slide

  40. Ошибка номер 2 —
    мы не мониторим
    topic lag
    40

    View Slide

  41. View Slide

  42. Что такое topic lag и как он выглядит

    View Slide

  43. Это был (почти)
    «добрый» topic lag
    43

    View Slide

  44. 44
    Злой выглядел бы вот как-то так:

    View Slide

  45. Страшная ситуация:
    «перезагрузка не
    помогает»
    45

    View Slide

  46. View Slide

  47. Ошибка номер 3 — плохо
    настроенный pooling &
    шутки от aioredis
    47

    View Slide

  48. 48
    4 вида сетапов redis/keydb

    View Slide

  49. Немного о пулинге
    49
    Пулинг — прекрасно, но если вы его конфигурируете правильно
    — В документации Sentinel клиента ни слова о пулинге
    — Но самый сок нас ожидает под капотом…

    View Slide

  50. 50
    Цитата из redis-py (aioredis туда вмержен)
    def __init__(

    max_connections: Optional[int] = None,

    ):
    max_connections = max_connections or 2 ** 31 # ß Добрый вечер!
    С sentinel сработает вот так

    View Slide

  51. 51
    Вот вам и Redis Cluster
    class redis.asyncio.cluster.RedisCluster(host=None, port=6379, startup_nodes=None,
    require_full_coverage=True, read_from_replicas=False, reinitialize_steps=5,
    cluster_error_retry_attempts=3, connection_error_retry_attempts=3,
    max_connections=2147483648, # ß Привет! Как дела? Спишь? Наберу?
    db=0, path=None, credential_provider=None, username=None, password=None,
    client_name=None, encoding='utf-8', encoding_errors='strict', decode_responses=False,
    health_check_interval=0, socket_connect_timeout=None, socket_keepalive=False,
    socket_keepalive_options=None, socket_timeout=None, retry=None, retry_on_error=None,
    ssl=False, ssl_ca_certs=None, ssl_ca_data=None, ssl_cert_reqs='required', ssl_certfile=None,
    ssl_check_hostname=False, ssl_keyfile=None, address_remap=None)

    View Slide

  52. Для high availability (HA), cluster
    в документации примеры
    указаны без max_connections!
    А умолчание вы видели…
    52

    View Slide

  53. Я пытаюсь сделать
    устойчивый кластер redis
    Документация
    к redis-py
    2 ** 31

    View Slide

  54. Ошибка номер 4 —
    мы очень полюбили
    concurrency с
    помощью create_task
    54

    View Slide

  55. Тут всё довольно скромно
    55
    — Наш consumer потребляет сообщение
    — Создает обработчик с помощью create_task
    — Идёт дальше

    View Slide

  56. 56
    Если вспомнить topic lag…

    View Slide

  57. Пулинг
    Очень
    много
    корутин
    Один знаменитый
    программист
    сказал: it’s get
    crashing, when I
    pull (pull из pool’а
    соединений
    имеется ввиду)

    View Slide

  58. View Slide

  59. Ошибка номер 5 —
    requests & limits
    59

    View Slide

  60. View Slide

  61. Давайте глянем как оно там без них
    61
    — По началу неплохо
    — Но когда что-то пойдет не так…
    — Пожалуйста, проставляйте реквесты и лимиты!!1111

    View Slide

  62. View Slide

  63. Ошибка номер 6 —
    мы очень очень
    любили resilience, а
    особенно реконнекты
    63

    View Slide

  64. Прежде чем говорить, предыдущие пункты
    64
    — Что-то замедляет обработку топика (возможно, краш)
    — Мы не видим топик лаг, ребутаемся
    — Корутины плодятся без всяких на то ограничений
    — Коннекшены «хватаются» из пула огромными пачками
    — Мы и об этом не знаем
    — KeyDB умирает, уничтоженный в щепки
    — В кластере плохеет нодам, ложатся соседние сервисы (не все!)

    View Slide

  65. Цитируя известного
    художника:
    «вечеринка движется к
    каннибализму»
    65

    View Slide

  66. View Slide

  67. И добиваем лоу-киком
    67
    Чтобы KeyDB не имел шанса подняться и всем
    было веселее, мы сверху полируем
    реконнектами…
    …в каждой корутине

    View Slide

  68. Ты обронил, держи
    продакшн!

    View Slide

  69. Мои архитектурные
    навыки
    Моё умение
    программировать

    View Slide

  70. Крепкого здоровья
    погибшим, остальным
    либа backoff
    70

    View Slide

  71. Ошибка номер 7 —
    71

    View Slide

  72. Я — терминатор от мира архитектуры

    View Slide

  73. Ошибка номер 7 —
    великолепные
    healtcheck’и
    73

    View Slide

  74. Как не стоить делать хелсчеки консьюмеров
    74
    — Берем асинхронный консьюмер
    — Рядом в треде запускаем асинхронный фреймворк с одной ручкой
    — Вешаем пробу на эту ручку

    View Slide

  75. 75
    Что же может пойти не так?

    View Slide

  76. 76
    Что же может пойти не так?
    Мой продакшн

    View Slide

  77. 77
    Что же может пойти не так?
    Я, пытающийся понять
    почему topiclag 10055000,
    консьюмеры мертвее моей
    архитектурной карьеры, а
    хелсчеки говорят
    Мой продакшн

    View Slide

  78. Ошибка номер 8 —
    dead letter queue
    78

    View Slide

  79. View Slide

  80. Есть такая классическая проблема в EDA
    80
    Довольно классическая
    — К нам приходит сообщение в консьюмер
    — Мы обрабатываем это сообщение, но у нас не выходит
    — Что же делать?

    View Slide

  81. 81
    Делаем DLQ (dead letter queue)
    УПС
    Попыток 10 на
    сообщение

    View Slide

  82. Как вы догадались:
    мы поддавали жару
    ещё и здесь
    82

    View Slide

  83. Подведем итог:
    у нашей системы не
    было и шанса!
    83

    View Slide

  84. Это явно я
    Ф — надежность

    View Slide

  85. Как же мы всё это
    вылечили?

    View Slide

  86. View Slide

  87. /
    Денис Аникин
    https://xfenix.ru
    https://github.com/xfenix/
    Спасибо!

    View Slide

  88. View Slide

  89. Проставили
    requests & limits
    89

    View Slide

  90. А как это делать когда ничего непонятно?
    90
    Есть пару простых идей
    — Проставьте минимальное значение для requests, ниже которого сервис не будет работать
    совсем
    — Проставьте limits в 2-3 раза выше (в зависимости от ваших возможностей)
    — Дальше двигайтесь итерационно, либо с помощью нагрузочного тестирования, либо
    докидывая limits в процессе жизни (что более нервно)
    — Не забывайте мониторить throttling!

    View Slide

  91. View Slide

  92. Что будет, если неправильно сконфигурить
    92
    Ну кроме очевидного «пока, мой любимый кластер, мне так нравилось спать в 3 ночи»
    — Наш добрый дружок ООМ
    — CPU-«голодание»
    — «Выселение» подов
    — Трата лишних ресурсов

    View Slide

  93. Сделали мониторинг
    topic lag и количества
    консьюмеров
    93

    View Slide

  94. View Slide

  95. Что можно было бы мониторить ещё
    95
    Но что мы пока не мониторим
    — Количество sent запросов
    — Количество received запросов
    — Время/продолжительность отправки
    — Время/продолжительность потребления
    — RPS

    View Slide

  96. Сделали мониторинг
    количества
    соединений KeyDB
    96

    View Slide

  97. Проставили
    max_connections для
    пулинга!
    (у Sentinel тоже можно)
    97

    View Slide

  98. Самое интересное
    было с concurrency!
    98

    View Slide

  99. КО Порождаем
    100_000
    корутин
    Берем здесь
    соединение
    из пулинга
    УПС
    В чём сложность

    View Slide

  100. Когда мы поняли, что дело в нём
    100
    А понять было ОЧЕНЬ непросто на самом деле (ведь корутины мы не мониторим)
    — Долго обсуждал
    — И в конце я вспомнил, что в asyncio есть Semaphore!

    View Slide


  101. Какой-то шаренный ресурс (типа коннекта к базе/базы)
    Корутины
    Семафор со значением 3
    Корутины

    View Slide

  102. Пару мыслей по корутинам
    102
    — Concurrency — штука коварная, имеет смысл ограничивать количество create_task
    — Semaphore и другие примитивы синхронизации вам бро!
    — Возможно, вам может быть полезен aiomonitor!

    View Slide

  103. Enterprise архитектор
    Solution архитектор
    Я, делающий
    вот это всё

    View Slide

  104. Моя любимая панацея!
    104
    — Можно просто раз в сутки ребутать все контейнеры
    — Сделать очень просто: берёте gitlab scheduled ci и делаете с помощью kubectl rollout
    и/или helm
    — Не убегайте из зала с воплями ужаса, это правда экономит кучу нервных клеток
    — Не будем забывать о chaos engineering

    View Slide

  105. Сделал ребуты и
    делаю вид, что это
    chaos engineering

    View Slide

  106. Эксперты,
    слушающие доклад

    View Slide

  107. Пару слов о том, что не полечили
    107
    — retry. Тут стоило бы затащить circuit breaker!
    — healtcheck. В интернете ноль адекватных советов на эту тему, кстати
    — Не продумали стратегию backpressure когда retry не справляется
    (eviction/drop? delay? buffer?)
    — Пока не троттлили DLQ

    View Slide

  108. Самое главное, что я
    хочу сказать:
    «лимитируй это»…
    108

    View Slide

  109. …пожалуйста!
    109

    View Slide

  110. …не будь как я
    110

    View Slide

  111. Как тебя
    архитектурить?
    Не надо меня
    архитектурить
    Откуда ты это сказал?

    View Slide

  112. View Slide

  113. Веселый был бы доклад на
    хайлоад…

    View Slide

  114. Веселый был бы доклад на
    хайлоад…
    если бы хайлоад писали на питоне

    View Slide

  115. Некоторые ссылки
    115
    Redis py коммиты (но это не все)
    — https://github.com/redis/redis-
    py/blob/2732a8553e58d9e77f16566b9132fc7205614a53/redis/asyncio/connection.py#L976
    — https://github.com/redis/redis-
    py/blob/2732a8553e58d9e77f16566b9132fc7205614a53/redis/asyncio/cluster.py#L232
    — https://github.com/redis/redis-
    py/blob/2732a8553e58d9e77f16566b9132fc7205614a53/redis/connection.py#L952

    View Slide

  116. Денис Аникин
    https://xfenix.ru
    https://github.com/xfenix/
    Спасибо!

    View Slide