Привет, Хабр! Это моя первая проба пера. Статья будет на тему «как разработчик из кровавого Enterprise статейник писал». Может быть, интересно либо таким же как я, либо наоборот, ребятам которые занимаются сайтами‑статейниками и подумывают в сторону работы в больших корпорациях. Словом, для всех, кто хочет посмотреть, как оно там, у других. В ходе написания статьи я буду использовать привычную терминологию и пояснять ее. Статья идет от простого к сложному, поэтому если вы из «кровавого Enterprise» — можно пролистать статью до момента деплоя (часть 2).
Когда речь заходит о разработке сайтов, можно поделить людей на 2 категории. Назовем их так (долго думал над названиями, получилось как получилось):
Вебмастер — человек, который умеет подключать и настраивать CMS (и как правило, больше чем одну), знает что‑то про SEO, про инструменты продвижения, про рекламу, и имеет какое‑то представление про Frontend/Backend. Эдакий «универсальный солдат»
Enterprise‑инженер (Frontend/Backend) — человек, который глубоко погружен в специфику своей сферы. Как правило работает над проектами, для которых нужна глубокая кастомизация, нужно писать много кода.
Для кого статья:
Вебмастер, которому интересен ход мыслей инженера из крупных компаний (я достаточно долго работал в Яндексе и нескольких зарубежных компаниях на 1000+ разработчиков). Для вебмастера больше подойдет часть 1 текущей статьи.
Enterprise‑инженер, делающий отдельно Frontend/Backend в крупной компании, которому интересно, с чем сталкивается такой же человек, пробующий запустить что‑то в продакшене вне привычной экосистемы и деплоя по кнопке. Для Enterprise‑инженера больше подойдет часть 2 текущей статьи. Здесь будут более продвинутые вещи, например, как загрузка картинки через админку приводила к downtime сайта, и как я исследовал проблему. А так же проблемы, что же делать после деплоя, и как с этим жить
Идея проекта проста: я хотел запустить сайт, который потенциально может приносить мне пассивный доход (да, я знаю про биржу сайтов, и что сайт можно купить, но хотелось сделать самому).
Часть 1. Сбор требований и выбор инструментов
Мне не хотелось тратить слишком много денег на железо, и не хотелось тратить слишком времени на поддержку в долгосрочной перспективе. Но если взлетит — хотел бы, чтобы система была масштабируемой (вдруг меня ждет успех и куча пользователей, нужно чтобы сайт работал под нагрузкой) и переносимой (вдруг я захочу запустить еще один или несколько сайтов). Отсюда вытекают так называемый «нефункциональные требования» к системе.
// Нефункциональные требования (aka non‑functional requirements) — требования, не относящиеся непосредственно к бизнес‑логике. Надежность, отказоустойчивост��, легкость внесения правок, покрытие метриками, скорость загрузки сайта и так далее
Часто сталкиваясь с выбором тех или иных вещей я подумал, что мне нравится идея сайта, который будет помогать с выбором. Например, как выбрать кофе, чай, кроссовки для бега… Так я и определился с функциональными требованиями.
// Функциональные требования (aka functional requirements) — требования к бизнес логике приложения. Какие сценарии должны решаться, какие роли должны быть в системе, что они должны уметь, и как
Функциональные требования:
Сайт должен представлять из себя набор статей на разные темы, содержать текст и картинки в статьях
Должна быть возможность быстро и удобно добавлять новые статьи
Статьи должен уметь добавлять администратор, пользователи могут только смотреть статьи
Нефункциональные требования:
Эффективность с точки зрения ресурсов (слабее железо = меньше расходов = больше прибыль)
Портируемость (иметь возможность перенести на более мощное железо, либо отделить компоненты отдельно, например, СУБД) // это, кстати, пригодилось, но об этом позже
Масштабируемость (не умереть если сайтом будет пользоваться много людей)
Скорость (пользователь не должен думать «как же туго сайт работает»)
Легкость добавления статей (нужно чтобы я мог добавлять статьи быстро, фокусируясь только на контенте, а не на том, как бы это закодить)
Я не стал идти в сторону долгостроев с React/Angular, написания собственного API… Посоветовавшись с ChatGPT и сделав несколько запросов в поисковик я остановился на Wordpress. Большое комьюнити + популярность = залог стабильности. При этом что меня смущало в Wordpress — я понимал, что сайт будет работать медленно, так как скорость — не главный конёк для Wordpress.
При этом можно оставить Wordpress, но сам сайт сделать быстро при помощи дополнительных инструментов. Один из таких инструментов — Nginx.
// Nginx — популярный веб‑сервер, часто использующийся как reverse‑proxy. Типичное применение Nginx — пользователь отправляет запросы в Nginx, тот делает запросы к другим серверам (upstream), и отдает пользователю результат. Как правило, используется стратегия round robin (Сначала Server A, потом Server B, потом Server C, потом Server A…)

Nginx зарекомендовал себя как очень эффективный инструмент. С чем точно может помочь Nginx для статейника:
https (достаточно легко настраивается работа с сертификатами)
Cжатие. Gzip помогает существенно снизить объем передаваемых данных по сети, а значит и скорость загрузки (Если коротко и на пальцах — gzip превращает последовательность символов в код, то есть грубо говоря, перекодировать '<div class' → 1, '<a href=' → 2). Сам по себе HTML содержит большое количество повторяемых символов, следовательно, хорошо «гзипуется»
Кеширование. Nginx может запоминать ответы на какое‑то время, и вместо того, чтобы каждый раз Wordpress доставал что‑то из базы Nginx может просто отдавать закешированную страницу, что должно существенно ускорить время отклика. Здесь я видел самую главную пользу — для 10–50 статей (даже если 200 и больше) нет смысла каждый раз исполнять php код и доставать данные из mysql. Nginx может кешировать статьи неделями
Отсюда появилась базовая архитектура статейника:

В архитектуре меня все устраивало. На первом этапе я планировал развернуть все компоненты (Nginx + WordPress + MySQL) на одном сервере, но в то же время архитектура была достаточно гибкой для того, чтобы можно было масштабироваться. То есть в каком‑то отдаленном будущем система могла находиться на разных серверах и выглядеть как‑то так:

Осталось разобраться с переносимостью. Мне не хотелось бы каждый раз устанавливать заново Nginx, WordPress, MySQL на сервера, на которые мне бы пришлось «переезжать». Поэтому нужно было что‑то, что поможет мне с установкой и разворачиванием моего стека. Здесь на помощь приходит Docker.
// Если вы никогда не использовали Docker, то я бы попробовал его объяснить как архивы, которые можно запускать, и удобно скачивать. Если упростить: «возьми мне Mysql версии 8, и запусти». Docker скачает нужный образ, и запустит. Удобность докера в том, что он работает везде одинаково. Если вы отладили Dockerfile (файл с настройками), то развернуть приложение в продакшене будет можно той же командой, что и локально (вам не нужно думать о том, не забыли ли вы чего‑нибудь установить).
Итого я собрал почти все, что мне нужно. Остались метрики. Я подключил метрики на сайте от Яндекс и Google, но мне хотелось бы видеть еще потребляемые ресу��сы в моменте, некую картинку типа этой (взял со своей админки). Для сбора и отображения использовал Prometheus (база данных для метрик) и Grafana (инструмент для визуализации метрик. Умеет работать в том числе с используемым Prometheus).
Grafana UI выглядит следующим образом (можно настроить разные промежутки времени, все достаточно удобно и интуитивно). Конкретно на этом графике виден всплеск потребления CPU, на который, возможно, стоит обратить внимание:

Совсем полная схема включая инструменты для админа (меня) выглядит так:

Пользователи читают статьи. Я загружаю статьи через админку, и смотрю на метрики. Метрики читаю через Grafana, Grafana берет метрики из Prometheus. В свою очередь Prometheus берет метрики из Nginx, Wordpress, MySQL.
Итого, получился солидный стек, хоть в резюме добавляй:
Wordpress
MySQL
Nginx
Prometheus
Grafana
Docker
Git (+Github) для ведения истории разработки и релизов
Часть 2. Запуск и проблемы
Спустя некоторое время танцев с бубном я убедился локально, что:
Сайт на Wordpress запускается через Docker
Nginx запускается, проксирует запросы в Wordpress
Кеш на Nginx работает для всего, кроме админки (/wp‑admin), для того, чтобы можно было спокойно загружать контент
Следующими шагами было купить домен и https сертификат. На этих шагах останавливаться не буду, получилось без каких‑либо проблем. Во время выбора домена пришлось сменить первоначально задуманное название «easychoice» на «tvoisovet», так как «easychoice» был занят, и пришлось выдумать что‑то другое, но в то же время звучное. Так мой статейник наконец оказался в продакшене.
Я выбрал тему более‑менее приятную по внешнему виду, чтобы она была не слишком плохой, и чтобы текст можно было удобно читать.
Статьи я генерировал через chatgpt, используя заготовленный промпт: Ты пишешь статью для сайта с рекомендациями от имени экспертов. Нужно написать статью на тему «Как выбрать кофе»
Полученный текст я копировал, вставлял в сайт, и подгружал пару картинок из интернета. Местами дописывал/переписывал руками. Какие‑то куски текста убирал, какие‑то просил расширить. Но в общем и целом костяк страницы был сгенерирован, и дополнен картинками вручную. На каждую статью уходило в среднем 25–50 минут.
Долгожданная первая страница увидела свет.
Часть 2.1 downtime на загрузку картинки из админки
Достаточно быстро обнаружилось, что если добавить 2–3 картинки в статью из админки, то сайт начинает сначала заметно медленно отвечать, потом перестает работать вообще. Спустя некоторое время он начинал работать после перезапуска процессов.
«Наверное, оперативной памяти не хватает», подумал я, уверенно введя top в терминале, и обнаружил, что оперативная память в норме, и процессор не выглядит нагруженным. Здесь стоит отметить, что на этом этапе у меня еще не были реализованы метрики через Prometheus + Grafana. Я хотел запустить сайт в продакшене пораньше, и обзавестись метриками после запуска.
Простая нагрузка была непосильной для моей конфигурации, и было непонятно почему. Нужно было как‑то это исследовать. Здесь мне помогла подготовка к одному из собеседований в бигтех (который я, к слову, благополучно завалил) с уклоном в SRE(Site Reliability Engineer).
Одна из очень классных стратегий исследования системы — the USE (Utilisation, Saturation, Errors) method (https://www.brendangregg.com/usemethod.html). Ее идея достаточно прозрачна. Берем каждый из ресурсов (CPU, RAM, Network, i/o), проверяем utilization (сколько% занято), saturation (как правило, задержки, ожидание, блокировки), errors (ошибки). Есть много разных инструментов, пользуясь случаем рекомендую прекрасную статью — SRE in 60 seconds (https://netflixtechblog.com/linux‑performance‑analysis‑in-60-000-milliseconds‑accc10403c55). Она дает хорошее понимание большинства инструментов, которые вы можете использовать для отладки.
Я запустил vmstat в терминале и начал смотреть на чиселки в момент загрузки картинок. Вывод vmstat был следующим:
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 3 81900 72016 2260 120104 1 43 7320 89 0 1409 2 3 0 95 0
0 2 81900 65464 2264 126412 0 0 6300 0 0 958 3 2 0 95 0
0 2 81900 63196 2396 128436 4 0 2172 0 0 822 1 1 0 98 0
0 2 81900 57148 2468 134488 4 0 6108 60 0 986 1 0 0 99 0
0 2 81900 73036 1112 119704 0 0 3592 92 0 928 1 3 0 96 0
0 4 81900 72032 1112 120404 0 0 700 0 0 832 0 0 0 100 0
0 3 81900 69512 2068 122124 0 0 2696 0 0 918 1 2 0 97 0
0 3 81900 66488 3252 123576 0 0 2604 0 0 953 1 2 0 97 0
0 3 81900 61196 4448 127012 0 0 4600 0 0 1143 0 2 0 98 0
0 4 81900 57444 5108 128244 4 0 1872 24 0 1188 2 4 0 94 0
0 9 81900 67376 5088 118360 0 0 5608 4 0 1295 2 1 0 97 0
0 6 81900 61328 5384 123728 0 0 5636 0 0 2042 1 2 0 97 0
1 3 81900 62500 4300 121628 0 0 9408 0 0 1584 6 3 0 91 0
1 3 81900 58244 4680 125136 0 0 3348 12 0 1182 2 3 0 95 0
0 3 81900 71564 2160 114448 0 0 4268 20 0 924 1 3 0 96 0
Сразу стало расти procs→b и cpu→wa. Это значило, что ряд процессов был чем‑то заблокирован, и не мог продолжить работу. Это пролило хоть какой‑то свет на происходящее. Пройдя по ресурсам я начал смотреть на утилизацию i/o и сети. К своему большому удивлению, я обнаружил утилизацию i/o на 100%.
В целом картина сходилась. i/o не справляется с нагрузкой в то время когда мы загружаем картинку, и не может вычитывать html/css из файловой системы. Это приводит к долгому ответу (ждем пока i/o освободится, потом считываем файлы со статикой).
Я открыл хостинг, и увидел, что для хранения был выбран HDD. Из‑за малого iops (input‑output per second) он не справлялся с нагрузкой.
Переезд на SSD был достаточно безболезненным (слепок диска, новый инстанс из слепка, переезд домена на ip нового инстанса). После переезда на SSD проблем с работоспособностью больше не наблюдалось.
Для проверки «пострелял» сайт через Apache Benchmark. Проверка получилась достаточно грубой, но значительно лучше, чем никакая. В примере ниже запускаем 500 запросов на <URL> посредством 10 параллельных запросов:
ab -n 500 -c 10 <URL>
Вывод выглядел примерно так:
Concurrency Level: 10
Time taken for tests: 24.250 seconds
Complete requests: 500
Failed requests: 0
Total transferred: 34708000 bytes
HTML transferred: 34495000 bytes
Requests per second: 20.62 [#/sec] (mean)
Time per request: 484.998 [ms] (mean)
Time per request: 48.500 [ms] (mean, across all concurrent requests)
Transfer rate: 1397.72 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 223 253 17.0 251 329
Processing: 213 222 6.0 221 285
Waiting: 141 147 3.9 146 169
Total: 439 475 18.4 472 551
Я был готов к наплыву пользователей
Часть 2.2 Первые пользователи
Итак, сайт был в продакшене. Но по метрикам пользователей было грустновато:

Периодически в метриках мелькал 1 вернувшийся человек (я). Нужно было что‑то с этим делать. Для органического трафика у меня появился план, надежный, как швейцарские часы: куплю рекламу, люди посмотрят, возможно, кто‑то сохранит ссылку себе, чтобы перечитать (если контент хороший).
Рекламная кампания «Большие надежды»

Я настроил через Яндекс.Директ рекламу на свою первую страницу — «как выбрать кофе». На удивление это было достаточно легко сделать. Сервис сам предложил сгенерированные через ИИ слоганы и картинки. А также показал, какие из них кликаются больше, а какие — меньше. В ходе рекламной кампании можно было их редактировать, словом, очень удобно. Бюджет рекламной кампании был 2500 рублей (около $30 на тот момент). Забегая вперед — бюджет второй рекламной кампании был таким же.
За неделю мою страницу увидели почти 15 тыс. людей. Я увидел, что «глубина просмотра» небольшая, но средне время на сайте заметное — больше минуты. «Значит, люди читают одну страницу и уходят» решил я. Дописав больше статей и добавив перекресных ссылок я запустил вторую кампанию: «как правильно бегать».
На этот раз перед запуском рекламы я добавил перекрестных ссылок на другие статьи.
Рекламная кампания «Надежда умирает последней»

Результат был прежним: пока я готов платить — люди готовы смотреть. Спустя пару месяцев трафик на сайте вернулся к нулевому уровню. Я добавил Турбо страницы и AMP, посмотрел как можно прийти к органическому трафику, и основные рекомендации были примерно «нормально делай — нормально будет».
Я решил, что контент плохой потому‑что мне помогали LLM, и оставил свой сайт немного пожить, перед тем, как его отключить от аппарата жизнеобеспечения потушить. Сейчас подержу его для того, чтобы читатель мог открыть и посмотреть, что же я там наделал.
Главные выводы, которые я сделал для себя:
Продавать сложнее, чем реализовать (по крайней мере, для меня)
Даже если этим никто не пользуется, или ты платишь деньги, чтобы кто‑то увидел твою работу — это все равно приятно, когда ты сделал что‑то, чем можно пользоваться.
1 час в неделю достаточно для того, чтобы довести что‑то до продакшена
Я точно хочу выпустить еще какой‑нибудь проект, но уже, видимо, не статейник.
Кстати, буду рад любым советам по продвижению. Я уже не очень верю, что в него можно вдохнуть жизнь, но мало ли.
Комментарии (3)

spions
08.11.2025 14:53Как вы решили/собираетесь решить проблему с разными wp-content при масштабировании?

siarheiblr
08.11.2025 14:53Вот интересно, а классический дедовский «статический хтмл с картинками» тут бы не подошел? Вроде как по описанию подходит.
С простым добавлением пришлось бы помучаться, но вроде рендереры в статику существовали еще на заре Интернетов.
Yevvv
Турбо страницы и AMP - этож ровно обратное органическому трафику) тупо трафик остается в поисковике.