2. Докладчик?
• PHP-программист
• Администратор баз данных
• Эксплуатационщик
• Архитектор серверных приложений
^ WTF?
• Добрый землянин
• Занимает место в профессии
4. Цели
• Разрушить мифы
• Столкнуть с парохода современности
• Я никого не хочу победить
• «Не едь за мной – ты там не проедешь»
(ворона птица сильная и на голову наглухо
ушибленная)
5. Disclaimer
• Все мертвые проекты мертвы одинаково, все
живые проекты живы по-разному
• «Кто не умеет работать – учит»
– «Кодекс это набор рекомендаций»
• Самое важное – верно определить граничные
условия
– А нужно ли вам именно это? А зачем?
– А, может, ну его этот веб и в Тибет?
6. Большой проект
• Купонный сервис, входит в top 3 в России
• Что он должен уметь технически?
– Рассылать много почты
– Предоставлять пользователям возможность
регистрироваться и покупать
– Предоставлять продавцам возможность
регистрироваться и управлять акциями
– Предоставлять менеджерам возможность
создавать аналитические отчеты
7. Моя роль в проекте
• Системный администратор
• Мне больше нравится слово
«эксплуатационщик», но мне его не
произнести
• В свое время был привлечен для
консультаций, да так и прижился
8. Исходное состояние
• Проект на LAMP
– MySQL 5.0.95
– Drupal
• Хостинг:
– OpenVZ контейнер большого размера
– Доступа к хост-машине нет
– Установлена Cpanel
• ~20 RPS к бэкенду
9. Проблемы 1
• Необходима новая функциональность, но
– См. картинку
– По моему опыту, это верно
для многих PHP CMS
– Тем не менее, стоит
вопрос о расширении
команды
картинка взята с http://habrahabr.ru/post/144857
10. Проблемы 2
• Drupal не справлялся с нагрузкой, так как
– MySQL не справлялся с нагрузкой
– Apache/PHP не справлялся с нагрузкой
• Включение кэша Drupal не спасло
– Отвалился геотаргетинг (все незалогиненные
видят одно и то же)
– Совсем отвалился геотаргетинг (выбор города
вручную в меню не помогает)
11. Решение проблем с разработкой
• Вне зоны моей ответственности, я занимался
только эксплуатацией проекта
• Если вкратце, три слова: Ruby on Rails
• Если вам интересно мое мнение
– Лучше сразу писать проект на RoR, чем сначала написать
RoR на PHP/Drupal, а на этом уже – проект
– И не забудьте про гемы, их все тоже придется написать на
PHP
– Переход выглядит более чем оправданным
12. Решение текущих проблем с
эксплуатацией 1
• Apache/PHP
– Выкинули suPHP - избавились от форков
– (Почти) выкинули cPanel (инвазивнейший монстр,
протачивающий свои ходы по всей системе)
• MySQL
– Убрали MyISAM – перестали блокировать базу в
момент дампа
– Размер буфера, размер бинлога,
flush_log_at_trx_commit – см. доклад Андрея
Аксенова
13. Решение текущих проблем с
эксплуатацией 2
• MySQL
– Включили query cache (меньшее зло)
– Настроили slow log и mk-log-parser
– Построили индексы там, где было необходимо
• Платформа
– etckeeper (спасал нас впоследствии)
– Приведение в порядок cron-файлов и других
конфигов
– Не имея доступа к хост-машине много не
натюнишь
14. Последствия
• Кэш Drupal удалось отключить
• Нагрузка на машину существенно снизилась
• Когда через пару месяцев трафик возрос в
два раза, это не вызвало никаких проблем
15. Ruby-версия: платформа
• RHEL 5.5
• Ruby 1.8.7 (REE), Rails 3
• PostgreSQL вместо MySQL
• (Единственное, на мой взгляд, преимущество
PostgreSQL для таких проектов – наличие
online alter прямо в поставке)
– Это если вы не добавляете столбец с дефолтным
значением
16. Ruby-версия: железо
• Dell R710, 24 или 48 Gb, SAS-диски
• RAID контроллеры с BBU
• Production конфигурация:
– Один сервер для базы данных
– Один сервер для RoR
– Сервер для бэкапа
– Сервера для отдачи статики, телефонии,
разработки – гораздо более скромные параметры
17. Ruby-версия: компоненты
• RVM
• Unicorn
• god для управления Unicorn’ами
• Периодические задачи – cron+rake
• Обработчики очередей
• Resque как сервер очередей
• Изначально – god для управления
обработчиками
18. Тем временем, PHP-версия
• Менеджеры хотят считать статистику – вынос
тяжелых запросов на R/O реплику, отдельное
приложение (потом для Ruby-версии будет то
же самое)
• (Кстати, эта реплика постоянно
отваливалась)
• Трафик продолжает расти
• Внезапно, DDoS
19. DDoS 1
• Быстро выделили шаблон и собрали список
«плохих» IP (~6000 адресов за первый час)
• Доступа к хост-машине нет – ipset установить
невозможно
• Передали список «плохих» IP хостеру с
просьбой сделать блокировку на их
оборудовании
• Поставили mod_evasive
20. DDoS 2
• С утра передали еще один список ~8000
адресов
• Поддержка хостера заблокировала все
адреса на нашем же iptables
• %$^#!
• Арендовали сервер в другом месте,
установили на нем ipset, сделали его
фронтэндом к старому
21. Это еще не все с PHP-версией
• Легитимный трафик со временем все больше
• PHP-версия не справляется, разработчики
вынуждены опять включить кэш Drupal’а
• Не помогает, база время от времени
«выносит» диск
• Аренда еще одного сервера у того же хостера
(сервер стоит дороже, лучше дисковая –
выносим базу на него)
22. Запуск Ruby-версии
• Запуск поэтапный, по странам
• В России больше всего клиентов – ее
запускали в последнюю очередь
• Процедура запуска: остановка платежей,
домиграция пользователей, проксирование
сайта со старого места на новое, смена
записей в DNS
23. Проблемы 1
• Миграции с первого раза не проходят – не
сходятся балансы, приходится править код и
перезапускать миграцию
• Миграции идут через Resque
• Один пользователь – одно сообщение в
очереди
• Одно сообщение в очереди – один fork()
• Fork rate ~80 forks/s
24. Проблемы 2
• Приложение для России запустили в пятницу
вечером (thank God), в субботу днем оно
начало втыкать
• Все это время мы бились за живучесть
• Не забыли ли мы о нагрузочном
тестировании?
• Не забыли, но где-то ошиблись с оценкой
25. Битва за живучесть 1
• Сразу было видно, что проблемы не на базе
данных
• Выкинули GlusterFS – не помогло
• Переехали с KVM-based виртуалки на хост –
не помогло
• Увеличили количество воркеров – не помогло
• Поставка нового сервера – 10 дней
26. Битва за живучесть 2
• А как вообще понять, где втыкает
приложение?
• Сделать профайлинг?
• Ограничение – профайлить надо прямо под
нагрузкой
• Я PHP-программист и не имел опыта
профайлинга Ruby-проектов под нагрузкой
при помощи профайлера для Ruby
• Но ясно, что нужно что-то неинвазивное
27. Битва за живучесть 3
• А как бы я профайлил Java-приложение?
• Очевидно: сделал бы сэмплинг стектрейсов
• А что останавливает в случае Ruby?
• Ничего не останавливает:
– PMP, http://poormansprofiler.org
• При помощи тривиального sh-скрипта gdb
превращается в средство записи сэмплов с заданным
интервалом
• Я писал раз в секунду, 50-600 сэмплов за один запуск
скрипта
28. PMP
• Что хорошо – RVM собирает Ruby с debug info
• Что плохо – стектрейсы приходится
анализировать и классифицировать вручную
• Сейчас я пытаюсь написать
полуавтоматический классификатор, но даже
до альфа-версии дело пока не дошло
29. Битва за живучесть - 4
• Сэмплирование показало, что втыкает GC
• Попытки настроить GC через переменные
окружения провалились
• Срочный переход REE 1.8.7 – MRI 1.9.3
• После перехода GC заработал как надо
• Тем временем, разработчики приделали
фрагментарный кэш
• Ура, белый господин разрешил нам немного
поспать!
30. А сколько нужно воркеров?
• Автор Unicorn рекомендует от 4 до 8
воркеров на ядро
• Double facepalm
• На самом деле, воркеров нужно столько,
чтобы они могли держать нагрузку
• Что же такое «держать нагрузку»?
• 50-70% сэмплов должны быть «сижу на
select(), жду запроса»
• - Почему 50-70%? – А почему было 4-8 на ядро?
31. Когда в руках сэмплирующий
профайлер
• Все вокруг выглядит программным
обеспечением
• В норме воркеры Unicorn стоят на select() и
ждут запрос от апстрима
• Не в норме:
– На вызовах GC
– На записи в лог
– На select() внутри EventMachine (вопросы не ко мне)
– На обмене с БД
32. Помните, я говорил о рассылке
почты?
• Рассылкой почты занимается внешний
провайдер, но запускает ее rake task
• При миграции Ruby с 1.8.7 до 1.9.3 возникли
проблемы с производительностью
• Сэмплер показал что уперлись в старый
добрый GC
• В этот раз удалось настроить GC при помощи
переменных окружения
33. Что еще было хорошего?
• Запрос для построения аналитической таблицы:
UPDATE purchases SET user_created_at =
us.created_at FROM purchases p
INNER JOIN users us ON us.id = p.user_id
AND p.user_id >= 2000000 AND p.user_id < 2100000;
• Как вы думаете, что он делает?
• По уму, ему нужно сделать один full scan и
успокоиться
• Есть fork bomb и archive bomb, а это – SQL bomb
34. Sinatra
• Что занесло ее в проект сейчас не узнать –
иных уж нет, а те далече
• Может, кому-то не хватало строк в резюме?
• Sinatra при ошибке приложения читает
модели, для чего переопрашивает БД на
предмет структуры базы
• Это очень эффективный способ поставить
базу на колени
35. Пруф или не было
• На графике погода в Сахаре, но можно сделать
оценочное суждение о том, что базе плохо
• Мы в эту ловушку попадали дважды с большим
промежутком, и во второй раз никто уже не помнил
суть вопроса
36. Байки из склепа
• Однажды мы забыли на продакшне mc с
поиском по большому файлу, и он занял 16
гигабайт
• Я знал, что этим кончится, поэтому не
пользуюсь mc последние лет семь
• Кстати, для расследования подобных
инцидентов у нас запущен atop
37. Немного про настройку БД
• Ежедневные отчеты pgfouine
• Если где-то нет индекса, и он может помочь - строим
• Настройка конфигурации движка:
– А чего хотим?
– Включаем log_checkpoints и следим, чтобы причиной
выполнения checkpoint’а было истечение таймаута, а не
переполнение лога
– Таймаут, кстати, поднимаем
– А также ставим checkpoint_completion_target поближе к 1
– Много сегментов хорошо для массовой записи и плохо для
аварийного рестарта (адаптируйте под ситуацию)
38. Еще немного про настройку БД
• Пробовали встроенную в 9.X hot standby
репликацию
– Все хорошо до того момента, пока данные в
реплике не окажутся не такими, как в мастере
• Пробовали Slony-I
– DDL statements в миграциях нужно оборачивать в
вызов команды, что очень неудобно
• Вернулись к hot standby, ведем наблюдение
39. И про бэкап БД
• Сначала делали pg_dumpall прямо с
продакшна
– Это быстро закончилось
• Потом – pg_dumpall с реплики
– Это тоже перестало работать
• Теперь WAL archiving при помощи pg_rman
• Хотим попробовать Amanda
40. Эволюция деплоймента
• Вручную из git
• Caplite (не спрашивайте в киосках города,
это внутренний набор скриптов)
• Capistrano
• Я раньше не думал, что приложение на
динамическом языке может деплоиться так
долго (thank SASS, etc)
41. Эволюция процесса
• Креативный хаос (очень быстро, но очень
грязно)
• Peer reviews
• Unit tests
• CI:
– Bigtuna
– <s>Bigtuna</s> Jenkins
44. Человеческий фактор 1
• Вам может показаться, что ваши коллеги не
умеют работать
• Не надо волноваться, так оно и есть
• Google, Yandex, Островок, калифорнийские
стартапы, …, … - все нанимают лучших
• Если все нанимают лучших, кому же
достаются худшие?
• Кстати, а с чего вы взяли, что умеете
работать?
45. Человеческий фактор 2
• Лучшие, худшие, дайте хоть каких-нибудь!
• ~15 Skype-собеседований, чтобы найти
одного человека в команду эксплуатации
• Люди из Yandex, Mail.Ru, Scalaxy…
• Наняли того, кто хотя бы умеет пользоваться
поисковиком (делайте выводы о качестве
остальных)
• И не ошиблись в выборе
46. Человеческий фактор 3
• Что мешает простым людям в команде:
– Микроменеджмент
– Соревнования на пустом месте (поэтому не
нанимайте в команду одних только «звезд»)
– Поиск виноватых в те моменты, когда надо чинить
упавшее
– Истерики в эти же моменты
• К счастью, все эти вопросы можно решить
47. Планы на будущее
• Стандартизация, унификация
– Больше Chef’а
– Консолидация серверов
– Больше мониторинга и графиков
• Автоматизация
– Автоматический failover
– Раннее обнаружение отказов, построение трендов
• Уменьшение количества SPOF
48. Выводы
• Who dares wins
• Большой проект это <s>сложно</s> тяжело,
но интересно
• Ruby on Rails продукт эволюции, а не
революции – стандартные практики все те же
• Понимание происходящего в системе все так
же важно, как и 10 лет назад