Как мы перевезли крупный проект на WordPress в Google Cloud и не поседели
Привет, инженеры! Сегодня я хочу поделиться с вами подробностями одного из самых масштабных, сложных и в то же время захватывающих проектов в нашей практике — миграцией гигантского WordPress-проекта. Точное название клиента находится под строжайшим NDA, но масштаб системы вы можете себе представить по одному простому факту: генеральным подрядчиком и провайдером платформы выступала сама компания Automattic (да-да, те самые создатели и главные контрибьюторы WordPress). Когда к тебе приходят ребята такого уровня, ты понимаешь, что стандартными решениями из учебников тут не обойтись. Задача казалась классической: перевезти высоконагруженные сайты на масштабируемую, отказоустойчивую и современную облачную инфраструктуру Google Cloud Platform (GCP) Platform (GCP), используя связку Cloud Run для вычислений и Cloud SQL для хранения данных. Но, как водится в нашей индустрии, дьявол кроется в деталях и легаси.
С чего все началось: аудит легаси-монстра
Когда мы впервые заглянули под капот текущей инфраструктуры клиента, у нас зашевелились волосы даже в тех местах, где их давно нет. Это был классический монолит, который годами обрастал костылями, кастомными плагинами и оптимизациями под конкретное старое железо. Трафик там был просто космический: миллионы уникальных пользователей в сутки, регулярные медийные спайки, когда посещаемость могла вырасти в 10–15 раз за несколько минут из-за хайповых новостей, и терабайты медиаконтента.
Архитектура старой площадки представляла собой распределенную группу виртуальных машин, где синхронизация медиафайлов осуществлялась через медленный и постоянно сбоящий GlusterFS, а сессии пользователей синхронизировались через перегруженный Redis. База данных трещала по швам от огромного количества таблиц (специфика работы WordPress Multisite), а время отклика сервера (TTFB) в пиковые часы зашкаливало за разумные пределы. Сразу стало очевидно, что простой перенос виртуалок методом lift-and-shift в облако не решит проблем, а лишь увеличит счет за инфраструктуру в GCP. Нам требовалось полностью переосмыслить архитектуру приложения, сделав его по-настоящему cloud-native.
Почему Cloud Run, а не Kubernetes (GKE)?
Перед нами стоял выбор целевой платформы для контейнеризации. С одной стороны, проверенный временем Google Kubernetes Engine (GKE), который дает полный контроль над кластером. С другой стороны — полностью управляемый serverless-сервис Cloud Run, работающий на базе технологии Knative. После проведения серии нагрузочных тестов и оценки стоимости эксплуатации (TCO) мы сделали выбор в пользу Cloud Run. И вот почему:
- Скорость масштабирования: Cloud Run способен поднимать новые инстансы контейнеров за считанные секунды (холодный старт практически сведен к нулю при правильной настройке), в то время как GKE требует времени на выделение новых нод в пуле, если ресурсы кластера исчерпаны.
- Отсутствие операционных накладных расходов: Клиенту не нужно держать в штате выделенную команду SRE-инженеров только для обслуживания и обновления самого кластера Kubernetes.
- Оплата за реальное потребление: В периоды низкого трафика (например, глубокой ночью) система автоматически масштабируется почти до нуля, экономя огромные бюджеты.
Однако контейнеризация WordPress — это та еще задача, поскольку эта CMS исторически создавалась как stateful-система, которая любит писать логи, кэш и медиафайлы на локальный диск.
Инженерный хардкор: База данных и тюнинг пула соединений
Первым серьезным вызовом стала миграция базы данных без остановки обслуживания пользователей. Объем базы составлял более 500 гигабайт. Мы разработали комплексную стратегию миграции с использованием кастомных скриптов на Python и Go, которые осуществляли первоначальный слепок данных, а затем докатывали изменения в реальном времени через бинарные логи. Для обеспечения безопасности и минимальной задержки мы развернули VPC Serverless Access Connector — специальный сетевой мост, позволяющий контейнерам Cloud Run общаться с базой данных Cloud SQL напрямую через приватные IP-адреса, минуя публичный интернет.
Но настоящая головная боль началась, когда мы приступили к нагрузочному тестированию. WordPress по умолчанию создает новое подключение к базе данных на каждый HTTP-запрос. Когда Cloud Run под нагрузкой мгновенно масштабировался с 10 до 300 инстансов, каждый из которых запускал десятки воркеров PHP-FPM, база данных Cloud SQL моментально утилизировала лимит подключений и уходила в глубокий нокдаун. Процессор поднимался до 100%, и пользователи видели ошибку Error Establishing a Database Connection.
Хотя классический WordPress работает на MySQL, в данном проекте по требованию архитекторов из Automattic использовалась кастомная сборка с адаптером PG4WP для базы данных PostgreSQL. Это позволило нам применить мощный инструмент пулленга соединений — PgBouncer. Мы развернули PgBouncer в качестве промежуточного слоя. Он мультиплексировал тысячи кратковременных клиентских соединений от контейнеров Cloud Run в стабильный пул из нескольких десятков постоянных соединений к самой БД.
Активные соединения с БД при автоскейлинге Cloud Run
Сравнение количества открытых коннектов к Cloud SQL с использованием PgBouncer и без него при резком росте нагрузки (инстансов Cloud Run).
Настройка файла конфигурации pgbouncer.ini потребовала тонкого тюнинга. Мы перевели пул в режим работы pool_mode = transaction, что позволило максимально эффективно переиспользовать соединения, хотя и потребовало от нас переписать несколько legacy-плагинов, которые некорректно работали с транзакционным режимом (например, использовали временные таблицы вне транзакций).
Решение проблемы stateless: Медиафайлы и сессии
Поскольку контейнеры в Cloud Run эфемерны и могут быть уничтожены или перезапущены в любой момент, мы не могли хранить загружаемые пользователями файлы на локальном диске контейнера. Для решения этой проблемы мы полностью переписали логику работы с медиафайлами. Все статические ресурсы были перенаправлены в объектное хранилище Google Cloud Storage (GCS).
Для этого мы использовали плагин WP Offload Media, предварительно оптимизировав его код для работы в высоконагруженной среде. Теперь при загрузке картинки редактором сайта она мгновенно улетала в GCS, а в базу данных записывался правильный URL, указывающий на глобальную сеть доставки контента Cloud CDN. Это не только решило проблему сохранности данных, но и сняло колоссальную нагрузку с бэкенд-серверов: статика теперь раздавалась из кэша на пограничных серверах Google (Edge Points of Presence) по всему миру с минимальным пингом.
Для кэширования объектов внутри WordPress (Object Cache) и хранения сессий пользователей мы развернули отказоустойчивый кластер Memorystore for Redis. Это позволило разгрузить базу данных от повторяющихся тяжелых SQL-запросов и обеспечило бесшовную авторизацию пользователей: даже если контейнер Cloud Run, на котором находился пользователь, уничтожался, его сессия оставалась активной, так как она хранилась в общем централизованном кэше Redis.
Сборка контейнеров и оптимизация PHP-FPM
Чтобы добиться максимальной производительности, мы ушли от стандартных Docker-образов WordPress. Мы собрали свой кастомный образ на базе чистого Alpine Linux, установили туда только необходимые расширения PHP, настроили OPcache для кэширования байт-кода в оперативной памяти и оптимизировали конфигурацию PHP-FPM.
Анти-пробки для доставки цветов и еды: Google Maps Routes API (TSP-оптимизация) против грабительских комиссий курьерских агрегаторов
В конфигурационном файле opcache.ini мы задали следующие параметры:
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0
Параметр opcache.validate_timestamps=0 критически важен для продакшена: он отключает проверку изменений файлов на диске при каждом запросе. Поскольку наш код упакован в неизменяемый контейнер, файлы не могут измениться во время работы, и эта оптимизация экономит драгоценное процессорное время и дисковые операции ввода-вывода (I/O).
Смешной эпизод с DNS на cutover
Но самое интересное и запоминающееся случилось непосредственно в ночь переключения (cutover). Те, кто занимался миграциями крупных проектов, знают это непередаваемое чувство: ночь, в созвоне Google Meet сидит около пятнадцати человек — инженеры с нашей стороны, архитекторы из Automattic, представители клиента, дежурные системные администраторы. Все сосредоточены, в воздухе висит напряжение, литры кофе уже выпиты.
Мы начинаем финальную синхронизацию базы данных, переводим старую площадку в режим read-only, проверяем, что все лаги репликации сошлись в ноль. Переключаем трафик на уровне Cloud DNS, меняя A-записи на IP-адрес нашего нового балансировщика нагрузки GCP HTTPS Load Balancer. Метрики в панели управления Cloud Monitoring начинают оживать, запросы плавно перетекают на новую инфраструктуру, все графики зеленые, ошибок нет. Полный триумф!
И тут в общий чат пишет наш джуниор-инженер, который помогал с ручным тестированием интерфейса после переключения: "Ребят, а почему у нас тестовый домен и админка до сих пор резолвятся на localhost? Я пытаюсь зайти, а мне выдает ошибку подключения!". В созвоне повисает гробовая тишина. У всех начинает холодеть внутри — неужели мы где-то прописали жесткий хардкод локального хоста в конфигурационных файлах WordPress Multisite или в базе данных, и теперь миллионы пользователей не могут зайти на сайт?
Я судорожно начинаю проверять конфигурационный файл wp-config.php, лезу в базу данных через консоль, проверяю таблицы, но там все чисто. И тут до меня доходит... Я смотрю на свой собственный терминал и понимаю, что буквально за полчаса до начала миграции я сам прописал заглушку в локальный файл /etc/hosts на своей рабочей машине, чтобы протестировать один специфический скрипт локально, и забыл ее удалить! И наш джун, который сидел со мной в одном офисе, просто скопировал мои настройки окружения.
Оказалось, что я полчаса тестировал прод локально, думая, что все идеально работает на новых серверах GCP, а джун паниковал из-за моей же забывчивости! Мы быстро удалили строчку из /etc/hosts, проверили реальный DNS через публичные резолверы по всему миру — всё обновилось без единого сучка и задоринки. Напряжение в звонке сменилось дружным хохотом, мы выдохнули, допили остывший кофе и с чувством выполненного долга пошли спать.
Результаты миграции
Результаты проделанной работы превзошли ожидания даже скептически настроенных представителей клиента. Благодаря переходу на serverless-архитектуру и тонкому тюнингу кэширования, нам удалось добиться невероятных показателей производительности и стабильности.
Среднее время отклика (Latency) до и после миграции
Динамика изменения времени загрузки страниц (в миллисекундах) на старой инфраструктуре и в новой конфигурации Cloud Run + Cloud CDN.
Помимо снижения задержки, клиент получил:
- Zero Downtime: Миграция прошла абсолютно незаметно для конечных пользователей.
- Экономию бюджета: За счет автоматического масштабирования Cloud Run затраты на инфраструктуру снизились на 35% по сравнению со старой схемой с постоянно запущенными виртуалками.
- Безопасность: Интеграция с Cloud Armor позволила настроить автоматическую защиту от DDoS-атак и попыток подбора паролей к админке WordPress (brute-force).
Гигантский проект теперь стабильно крутится на мощностях GCP к огромной радости наших коллег из Automattic, а мы получили бесценный опыт, отличный кейс в портфолио и пару новых седых волос в подарок. Cloud Run действительно рулит, если уметь его правильно готовить и не забывать очищать свои локальные конфиги! До новых встреч на страницах нашего блога!
Реальные ограничения и компромиссы решения
Инженерная честность OZAT: при внедрении решения «Как мы перевезли крупный проект на WordPress в Google Cloud и не поседели» в промышленную эксплуатацию вы обязаны учитывать следующие технологические ограничения:
- Сложность горизонтального масштабирования медиафайлов WordPress: Директория wp-content/uploads требует синхронизации через Cloud Storage FUSE или плагин GCS Offload.
- Ограничения блокировок таблиц MySQL при высоких нагрузках: База данных wp_options и transient-кэши создают конкуренцию за строки; обязателен вынос сессий в Cloud Memorystore (Redis).
- Уязвимости плагинов и тем: Открытый исходный код расширений требует сканирования уязвимостей и защиты через Cloud Armor WAF.
- Задержка холодного старта PHP-FPM контейнеров: Инициализация стека Apache/Nginx + PHP-FPM в Cloud Run требует прогрева и настройки min-instances.
💡 Совет OZAT: Готовы к внедрению? Рассчитайте архитектуру и бюджет через Scope Builder или пройдите бесплатный ИИ-аудит.

Рустам Шарафутдинов
Эксперт в области архитектуры Google Cloud и Senior Full-Stack разработчик с более чем 15-летним опытом. Специализируется на отказоустойчивых архитектурах, оптимизации высоконагруженных проектов и интеграции AI (Vertex AI).