О насБлогКонтакты
DevOps27 октября 2011 г. 4 мин 19

Хабраэффект 2011: как пост на Хабре положил наш сервер и что мы с этим сделали

AunimedaAunimeda
📋 Содержание

Хабраэффект 2011: как пост на Хабре положил наш сервер и что мы с этим сделали

Это было в четверг около 11 утра. Наш инструмент для работы с задачами — небольшой SaaS для разработчиков — попал в посты дня на Хабрахабр. Через 11 минут после публикации Zabbix начал слать алерты. Через 18 минут сайт не открывался.

Хабраэффект — это не городская легенда русскоязычного интернета. Это конкретное явление с конкретными техническими причинами. Аудитория Хабра в 2011 году — технари, открывающие ссылку сразу. Никакого медленного нарастания, как с соцсетями. Одновременный резкий пик — и всё.


Что упало и в каком порядке

Сервер: VDS на 2 ядра, 2 ГБ RAM, Ubuntu 10.04, Apache + mod_php, MySQL 5.1, без кэша.

Хронология:

  • T+0: пост опубликован, первые 50 посетителей
  • T+8 мин: 600 одновременных посетителей, Apache упёрся в MaxClients 150
  • T+11 мин: очередь Apache переполнена, новые запросы получают 503
  • T+14 мин: MySQL достиг max_connections (100 по умолчанию), PHP-ошибки
  • T+17 мин: ошибки пишутся в /var/log/apache2/error.log, диск 100%
  • T+18 мин: MySQL крашнулся из-за невозможности записать бинлог (диск полон)
  • T+19 мин: сервер не отвечает на SSH

Всё это — за 19 минут. Наш сайт был недоступен 2 часа 40 минут в пике хабраэффекта.


Вскрытие: три слоя проблем

Слой 1: Apache держал соединение пока PHP не отработал. При медленных запросах (а запросы к БД без кэша были медленными) Apache-воркер блокировался на всё время выполнения. 150 воркеров × среднее время ответа 800мс = пропускная способность ~190 запросов/сек. На практике — меньше, потому что при нагрузке MySQL начинал тормозить.

Слой 2: каждый запрос — полный рендер из БД. Главная страница делала 34 SQL-запроса. Никакого кэша. При 150 одновременных посетителях: 5100 SQL-запросов в секунду к одному MySQL без query cache.

Слой 3: логирование без ротации и лимита. PHP-ошибки (которые сыпались из-за перегруженного MySQL) писались без ограничений. За 4 минуты error.log вырос с 2 МБ до 18 ГБ. Диск кончился.


Что мы сделали после восстановления

Nginx как reverse proxy и статика

Заменили Apache на схему nginx → PHP-FPM. Nginx отдаёт статику сам, к PHP-FPM идут только динамические запросы:

server {
    listen 80;

    # Статика — сразу, без PHP
    location ~* \.(css|js|png|jpg|woff|ico)$ {
        root /var/www/app/public;
        expires 30d;
        access_log off;
    }

    location / {
        try_files $uri /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php5-fpm.sock;
        include fastcgi_params;
        fastcgi_read_timeout 30;
    }
}

При хабраэффекте статика (CSS, JS, картинки) составляет ~70% запросов. Nginx держит тысячи таких соединений без каких-либо воркеров PHP.

Memcached на каждом дорогом запросе

Правило: если SQL-запрос выполняется дольше 20 мс — он идёт через кэш.

class CacheQuery {
    private $mc;
    
    public function get(string $key, int $ttl, callable $loader): mixed {
        $cached = $this->mc->get($key);
        if ($cached !== false) return $cached;
        
        $result = $loader();
        $this->mc->set($key, $result, 0, $ttl);
        return $result;
    }
}

// Главная страница: было 34 запроса, стало 3 (только авторизация и счётчики)
$projects = $cache->get("user:{$userId}:projects", 120, fn() => 
    Project::whereUserId($userId)->with('stats')->get()
);

После внедрения: главная страница — 3 SQL-запроса вместо 34. При хабраэффекте кэш держит нагрузку, MySQL получает запросы только на кэш-промахах.

Ограничение логов и ротация

# /etc/logrotate.d/apache2
/var/log/nginx/error.log {
    daily
    missingok
    rotate 7
    compress
    size 100M          # Ротация при достижении 100 МБ, не только по дате
    notifempty
    sharedscripts
}

И в PHP:

// Лимит на размер error_log
ini_set('log_errors_max_len', 1024);
ini_set('error_log', '/var/log/php/error.log');

// Ротация через cron каждые 6 часов при нагрузке

Тест на хабраэффект (синтетический)

После всех изменений мы прогнали Apache Bench с профилем, имитирующим хабраэффект — 1000 одновременных соединений, 10 000 запросов:

ab -n 10000 -c 1000 -k https://ourapp.com/

# До оптимизации: 503 на 84% запросов, сервер лёг на 2000 запросов
# После: 0 ошибок, среднее время ответа 340мс, p99 — 1100мс

Что хабраэффект изменил в нашем подходе

После 2011 года у нас появилось правило: каждый новый проект перед запуском проходит нагрузочный тест с профилем ×50 от ожидаемого пика. Не ×2, не ×5. Хабр и Reddit не предупреждают.

Второе правило: мониторинг диска — первый алерт, не последний. Полный диск валит больше сервисов, чем кончившаяся RAM.

Хабраэффект 2011 обошёлся нам потерей ~3 часов трафика в пике и репутационным ущербом среди аудитории, которая пришла по ссылке и увидела 502. Это был дорогой, но необходимый урок о разнице между «работает в обычный день» и «выдержит нагрузку».

Читайте также

Как хранить сессии в Redis для нескольких серверов PHP (2016)aunimeda
DevOps

Как хранить сессии в Redis для нескольких серверов PHP (2016)

При горизонтальном масштабировании PHP на 2+ серверов сессии перестают работать: пользователь авторизован на сервере 1, запрос попадает на сервер 2 — сессия не найдена. Решение: хранить сессии в Redis, к которому имеют доступ все серверы. Настройка PHP, nginx upstream, Redis.

Как настроить CI/CD для PHP-проекта в GitLab в 2016 годуaunimeda
DevOps

Как настроить CI/CD для PHP-проекта в GitLab в 2016 году

GitLab CI стал бесплатным и встроенным в GitLab CE в 2015. В 2016 мы перешли с ручных деплоев по FTP на автоматическую сборку и деплой при каждом push в master. Точная конфигурация .gitlab-ci.yml: тесты PHPUnit, статический анализ, деплой по SSH без downtime.

Как установить Let's Encrypt на nginx — первые дни после запуска (декабрь 2015)aunimeda
DevOps

Как установить Let's Encrypt на nginx — первые дни после запуска (декабрь 2015)

Let's Encrypt открылся для всех 3 декабря 2015 года. Бесплатные SSL-сертификаты с автопродлением — это изменило веб. В первые недели работы Certbot (тогда letsencrypt-auto) имел баги. Вот что работало и что нет в декабре 2015 на Ubuntu 14.04 + nginx.

Нужна IT-разработка для вашего бизнеса?

Разрабатываем сайты, мобильные приложения и AI-решения для бизнеса в России. Бесплатная консультация.

Получить консультацию Все статьи