Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
«Облако» в Badoo 
год спустя 
Юрий Насретдинов
О компании 
225M пользователей 
4M загрузок фото в день 
40K RPS на PHP-FPM 2K серверов 
160K регистраций в сутки 
50 языков интерфейса
О чём доклад 
• Общая архитектура: история создания, распределение 
нагрузки, отказоустойчивость. 
• Логи скриптов: сбор, индексация, различные виды 
просмотра. 
• Влияние Google App Engine — «облачный» разборщик 
очередей. 
• Планы на будущее 
• Как мы бы реализовали «облако» сейчас
Стек технологий 
• OS — SLES (SUSE Linux Enterprise Server) 
• ЯП — PHP 5.5, C/C++, Go, Java 
• Базы данных — MySQL, Tarantool, SQLite 
• Кеширование — Memcached 
• Веб-сервер — Nginx
Общая архитектура 
• «Старая система»: mcron — утилита для 
раскладки crontab по машинам 
• Общая архитектура новой системы («облака») 
• Распределение нагрузки по машинам, «попугаи» 
• Способы обеспечения отказоустойчивости
mcron 
sendSMS.php 
anonChat.php #1 
moderation.php 
config 
facebook.php 
anonChat.php #2 
errorlogs.php 
scripts1 scripts2 
translate.php 
anonChat.php #9 
cleanup.php 
scripts50 
…
mcron 
sendSMS.php 
anonChat.php #1 
moderation.php 
config 
facebook.php 
anonChat.php #2 
errorlogs.php 
scripts1 scripts2 
translate.php 
anonChat.php #9 
cleanup.php 
scripts50 
…
mcron 
sendSMS.php 
facebook.php 
anonChat.php #1 
moderation.php 
config 
google.php 
anonChat.php #2 
anonChat.php #3 
migration.php 
scripts1 scripts3 
translate.php 
errorlogs.php 
anonChat.php #9 
cleanup.php 
scripts50 
…
Недостатки старой системы 
• Ручное распределение нагрузки по серверам 
• Ручной перенос скриптов с «упавших» машин — 
очень большой downtime 
• Наличие «особенных» машин, на которых 
установлен дополнительный софт
«Облако» 
• Запуск заданий по расписанию / через API 
• Автоматическая балансировка нагрузки 
• Отсутствие «особенных» машин (на всех 
машинах стоит весь необходимый софт) 
• Отказоустойчивость к «падению» машин — 
автоматический перезапуск после таймаута
«Облако» (для разработчика)
Облако в Badoo год спустя - работа над ошибками, Юрий Насретдинов (Badoo)
«Облако» (для разработчика) 
script 
job #1 
job #2 job #3 
job #4 
job #5
Архитектура 
MySQL 
MySQL 
cloudsys1 
cloudsys2 
cloud1 
• 
• 
• 
cloudN 
Легенда: 
master 
replication 
mysql 
phproxyd 
heartbeat
«Облако» 
• Около 1 000 машин* 
• 15K SQL RPS (50/50 read/write) 
• 1 000 запусков скриптов в секунду 
• «Запускалка» на PHP, 16 процессов 
• Планировщик на go 
* Цифры приведены для 1 ДЦ, у нас их 2
Планировщик 
PHP 
Go
Балансировка нагрузки 
1000 
300 
600 
250 
2000 
230 
1000 
200 
2000 
180 
weighted round-robin
Балансировка нагрузки
Отказоустойчивость 
• MySQL — ручное (!) переключение на slave в случае 
аварии 
• Управляющая логика работает «циклами» — перед 
началом цикла берется лок в базе, по окончании цикла 
лок отпускается 
• Если машина «упала» в середине цикла, то через 
wait_timeout на сервере соединение будет разорвано 
и, соответственно, отпущен лок, давая возможность 
работать логике другой машине
Отказоустойчивость 
• По умолчанию wait_timeout составляет 8 часов… 
• Мы выставили wait_timeout = 60 сек 
• Cпецифичная для Percona настройка 
innodb_kill_idle_transaction = 60 сек 
• Таким образом, при любых проблемах с сетью 
или с машинами, максимальный простой 
управляющей логики составляет 1 минуту
Отказоустойчивость 
• «Задания» запускаются с лимитом на максимальное 
время их работы, который задает пользователь 
• При наступлении лимита скриптами присылается 
SIGTERM 
• Если машина не отвечает — скрипт сам «погибает» от 
SIGALRM, поскольку при запуске скрипта мы вызываем 
alarm(макс.время работы + 3 секунды) 
• Часы на всех машинах идут с точностью до 1 секунды
Сбор логов 
• Каждое задание получает уникальный id 
• Задание — запуск скрипта, с перенаправлением 
вывода в файлы «logs/phproxyd.<id>.(out|err).log» 
• С помощью inotify слушаем изменения в директории 
с логами и отправляем новые строки в scribe 
• С задержкой доставки scribe (несколько секунд) логи 
скапливаются на отдельной logs-машине
Просмотр логов 
• Логи для каждого скрипта складываются в отдельный 
файл 
• Файлы «ротируются» (с использованием logrotate) раз в 
неделю 
• Каждая строчка в логе содержит id запуска и hostname, 
где скрипт запускался 
• Логи «индексируются» в MyISAM-таблички для 
быстрого просмотра истории по конкретному id
Разборщик очередей 
• У нас все «важные» очереди хранятся в MySQL, 
для сохранности и транзакционности посылки 
событий 
• В MySQL довольно тяжело «правильно» 
разбирать очереди во много потоков
Разборщик очередей 
• Существует много стратегий «разбора очереди» 
в MySQL: 
• 1) SELECT … WHERE id % N = M 
• 2) UPDATE … SET instance_id = N WHERE 
instance_id IS NULL 
• 3) SELECT … WHERE shard_id = N
Разборщик очередей 
• Почти все стратегии плохо масштабируются при 
увеличении числа воркеров 
• Подход с shard_id масштабируется, но нужно 
следить за равномерностью распределения + 
требуется решардинг при смене числа воркеров 
• Решили написать обработчик очереди, используя 
API по добавлению заданий в «облако»
Разборщик очередей 
• Реализация: На каждую очередь создается 2 «скрипта»: 
• 1) мастер, который выбирает id новых событий из 
очереди (однопоточный) 
• 2) воркеры, которые обрабатывают пачки заданий 
(получают набор id заданий, которые нужно 
обработать) 
• Мастер «помнит» все id, которые он уже выдал и 
выбирает из очереди с помощью SELECT id … NOT IN(…)
Разборщик очередей 
• Мастер группирует события в «пачки» для 
большей эффективности обработки 
• Равномерное распределение по воркерам 
• Динамическое число воркеров (on demand) 
• Можно сделать такой разборщик без API, через 
fork(), со всеми воркерами на одной машине
Причины «падений» 
• Суммарный downtime системы составил 3 часа за год 
эксплуатации, что дает uptime 99,97%: 
• 1 час — Duplicate key в MySQL :) 
• 1 час — «кривой» merge (неправильно разрешены 
конфликты) — забыли прогнать тесты 
• 30 минут — «сломанный» cron на машинах (баг в 
одной из версий vixie cron) — не отправлялся heartbeat
Проблемы MySQL 
• Основные проблемы возникают из-за 
глобальных mutex’ов или однопоточных 
подсистем: 
• Медленный DROP TABLE больших таблиц — 
перед unlink() берется глобальный metadata lock 
и «висят» все транзакции
Проблемы MySQL 
• Медленный (однопоточный) purge thread — из-за 
MVCC «удаленные» записи могут очень 
медленно «пуржиться» из таблиц — в InnoDB 
возможна ситуация, когда SELECT COUNT(*) из 
«пустой» таблицы идет минуты и возвращает 0 
• Однопоточная репликация (до MySQL 5.6) — 
изменения могут не успевать применяться на 
реплике
Проблемы MySQL 
• Высокие накладные расходы на подключение — 
MySQL плохо «держит» больше N подключений, 
где N составляет 2-3 тысячи 
• В новых версиях MySQL и в MariaDB есть 
«connection pooling», причём для MySQL эта 
возможность отсутствует в Community Edition
Проблемы ядра Linux 
• Баг с выводом ps и «[migration/N]», который якобы 
«ест 100% CPU» (на самом деле не ест) 
• Очень медленный unlink() больших файлов, даже с 
ext4 (возникает из-за высокой фрагментации) 
• «Плохая» реализация inotify — если в директории 
активно создают файлы и у вас много «свободной» 
ОЗУ, inotify_add_watch() будет занимать секунды (!) 
и полностью блокировать запись в эту директорию
Планы на будущее 
• Полностью перевести управляющую логику на Go: 
иметь по одной goroutine на машину «облака» — 
одна «затупившая» машина не будет тормозить 
обработку остальных заданий 
• Перевести phproxyd на PHP (уже написан, нужно 
запустить в production) — экономия на запуске 
интерпретатора 
• Возможно, открыть исходные тексты системы
Как бы мы реализовывали сейчас 
• Управляющая логика на Go — отличный выбор, 
если вы почему-то не любите Erlang 
• Хранение текущего состояния заданий — 
Tarantool + Lua процедуры 
• Сразу написать новый демон для запуска 
заданий вместо существующего (на PHP, конечно 
же)
Вопросы 
Юрий Насретдинов 
@YNasretdinov 
y.nasretdinov@corp.badoo.com

More Related Content

Облако в Badoo год спустя - работа над ошибками, Юрий Насретдинов (Badoo)

  • 1. «Облако» в Badoo год спустя Юрий Насретдинов
  • 2. О компании 225M пользователей 4M загрузок фото в день 40K RPS на PHP-FPM 2K серверов 160K регистраций в сутки 50 языков интерфейса
  • 3. О чём доклад • Общая архитектура: история создания, распределение нагрузки, отказоустойчивость. • Логи скриптов: сбор, индексация, различные виды просмотра. • Влияние Google App Engine — «облачный» разборщик очередей. • Планы на будущее • Как мы бы реализовали «облако» сейчас
  • 4. Стек технологий • OS — SLES (SUSE Linux Enterprise Server) • ЯП — PHP 5.5, C/C++, Go, Java • Базы данных — MySQL, Tarantool, SQLite • Кеширование — Memcached • Веб-сервер — Nginx
  • 5. Общая архитектура • «Старая система»: mcron — утилита для раскладки crontab по машинам • Общая архитектура новой системы («облака») • Распределение нагрузки по машинам, «попугаи» • Способы обеспечения отказоустойчивости
  • 6. mcron sendSMS.php anonChat.php #1 moderation.php config facebook.php anonChat.php #2 errorlogs.php scripts1 scripts2 translate.php anonChat.php #9 cleanup.php scripts50 …
  • 7. mcron sendSMS.php anonChat.php #1 moderation.php config facebook.php anonChat.php #2 errorlogs.php scripts1 scripts2 translate.php anonChat.php #9 cleanup.php scripts50 …
  • 8. mcron sendSMS.php facebook.php anonChat.php #1 moderation.php config google.php anonChat.php #2 anonChat.php #3 migration.php scripts1 scripts3 translate.php errorlogs.php anonChat.php #9 cleanup.php scripts50 …
  • 9. Недостатки старой системы • Ручное распределение нагрузки по серверам • Ручной перенос скриптов с «упавших» машин — очень большой downtime • Наличие «особенных» машин, на которых установлен дополнительный софт
  • 10. «Облако» • Запуск заданий по расписанию / через API • Автоматическая балансировка нагрузки • Отсутствие «особенных» машин (на всех машинах стоит весь необходимый софт) • Отказоустойчивость к «падению» машин — автоматический перезапуск после таймаута
  • 13. «Облако» (для разработчика) script job #1 job #2 job #3 job #4 job #5
  • 14. Архитектура MySQL MySQL cloudsys1 cloudsys2 cloud1 • • • cloudN Легенда: master replication mysql phproxyd heartbeat
  • 15. «Облако» • Около 1 000 машин* • 15K SQL RPS (50/50 read/write) • 1 000 запусков скриптов в секунду • «Запускалка» на PHP, 16 процессов • Планировщик на go * Цифры приведены для 1 ДЦ, у нас их 2
  • 17. Балансировка нагрузки 1000 300 600 250 2000 230 1000 200 2000 180 weighted round-robin
  • 19. Отказоустойчивость • MySQL — ручное (!) переключение на slave в случае аварии • Управляющая логика работает «циклами» — перед началом цикла берется лок в базе, по окончании цикла лок отпускается • Если машина «упала» в середине цикла, то через wait_timeout на сервере соединение будет разорвано и, соответственно, отпущен лок, давая возможность работать логике другой машине
  • 20. Отказоустойчивость • По умолчанию wait_timeout составляет 8 часов… • Мы выставили wait_timeout = 60 сек • Cпецифичная для Percona настройка innodb_kill_idle_transaction = 60 сек • Таким образом, при любых проблемах с сетью или с машинами, максимальный простой управляющей логики составляет 1 минуту
  • 21. Отказоустойчивость • «Задания» запускаются с лимитом на максимальное время их работы, который задает пользователь • При наступлении лимита скриптами присылается SIGTERM • Если машина не отвечает — скрипт сам «погибает» от SIGALRM, поскольку при запуске скрипта мы вызываем alarm(макс.время работы + 3 секунды) • Часы на всех машинах идут с точностью до 1 секунды
  • 22. Сбор логов • Каждое задание получает уникальный id • Задание — запуск скрипта, с перенаправлением вывода в файлы «logs/phproxyd.<id>.(out|err).log» • С помощью inotify слушаем изменения в директории с логами и отправляем новые строки в scribe • С задержкой доставки scribe (несколько секунд) логи скапливаются на отдельной logs-машине
  • 23. Просмотр логов • Логи для каждого скрипта складываются в отдельный файл • Файлы «ротируются» (с использованием logrotate) раз в неделю • Каждая строчка в логе содержит id запуска и hostname, где скрипт запускался • Логи «индексируются» в MyISAM-таблички для быстрого просмотра истории по конкретному id
  • 24. Разборщик очередей • У нас все «важные» очереди хранятся в MySQL, для сохранности и транзакционности посылки событий • В MySQL довольно тяжело «правильно» разбирать очереди во много потоков
  • 25. Разборщик очередей • Существует много стратегий «разбора очереди» в MySQL: • 1) SELECT … WHERE id % N = M • 2) UPDATE … SET instance_id = N WHERE instance_id IS NULL • 3) SELECT … WHERE shard_id = N
  • 26. Разборщик очередей • Почти все стратегии плохо масштабируются при увеличении числа воркеров • Подход с shard_id масштабируется, но нужно следить за равномерностью распределения + требуется решардинг при смене числа воркеров • Решили написать обработчик очереди, используя API по добавлению заданий в «облако»
  • 27. Разборщик очередей • Реализация: На каждую очередь создается 2 «скрипта»: • 1) мастер, который выбирает id новых событий из очереди (однопоточный) • 2) воркеры, которые обрабатывают пачки заданий (получают набор id заданий, которые нужно обработать) • Мастер «помнит» все id, которые он уже выдал и выбирает из очереди с помощью SELECT id … NOT IN(…)
  • 28. Разборщик очередей • Мастер группирует события в «пачки» для большей эффективности обработки • Равномерное распределение по воркерам • Динамическое число воркеров (on demand) • Можно сделать такой разборщик без API, через fork(), со всеми воркерами на одной машине
  • 29. Причины «падений» • Суммарный downtime системы составил 3 часа за год эксплуатации, что дает uptime 99,97%: • 1 час — Duplicate key в MySQL :) • 1 час — «кривой» merge (неправильно разрешены конфликты) — забыли прогнать тесты • 30 минут — «сломанный» cron на машинах (баг в одной из версий vixie cron) — не отправлялся heartbeat
  • 30. Проблемы MySQL • Основные проблемы возникают из-за глобальных mutex’ов или однопоточных подсистем: • Медленный DROP TABLE больших таблиц — перед unlink() берется глобальный metadata lock и «висят» все транзакции
  • 31. Проблемы MySQL • Медленный (однопоточный) purge thread — из-за MVCC «удаленные» записи могут очень медленно «пуржиться» из таблиц — в InnoDB возможна ситуация, когда SELECT COUNT(*) из «пустой» таблицы идет минуты и возвращает 0 • Однопоточная репликация (до MySQL 5.6) — изменения могут не успевать применяться на реплике
  • 32. Проблемы MySQL • Высокие накладные расходы на подключение — MySQL плохо «держит» больше N подключений, где N составляет 2-3 тысячи • В новых версиях MySQL и в MariaDB есть «connection pooling», причём для MySQL эта возможность отсутствует в Community Edition
  • 33. Проблемы ядра Linux • Баг с выводом ps и «[migration/N]», который якобы «ест 100% CPU» (на самом деле не ест) • Очень медленный unlink() больших файлов, даже с ext4 (возникает из-за высокой фрагментации) • «Плохая» реализация inotify — если в директории активно создают файлы и у вас много «свободной» ОЗУ, inotify_add_watch() будет занимать секунды (!) и полностью блокировать запись в эту директорию
  • 34. Планы на будущее • Полностью перевести управляющую логику на Go: иметь по одной goroutine на машину «облака» — одна «затупившая» машина не будет тормозить обработку остальных заданий • Перевести phproxyd на PHP (уже написан, нужно запустить в production) — экономия на запуске интерпретатора • Возможно, открыть исходные тексты системы
  • 35. Как бы мы реализовывали сейчас • Управляющая логика на Go — отличный выбор, если вы почему-то не любите Erlang • Хранение текущего состояния заданий — Tarantool + Lua процедуры • Сразу написать новый демон для запуска заданий вместо существующего (на PHP, конечно же)
  • 36. Вопросы Юрий Насретдинов @YNasretdinov y.nasretdinov@corp.badoo.com