WP-CLI для Multisite: Массовые операции и автоматизация сети сайтов

Управлять одним сайтом WordPress через админку — нормально. Управлять пятью — терпимо. Управлять пятьюдесятью — мучение. Каждый клик в сетевой админке превращается в отдельную операцию: зайти на сайт, обновить плагин, выйти, зайти на следующий. Умножьте на 50 сайтов, на 10 плагинов — получите 500 кликов только для одного обновления.

Знаете, что отличает опытного администратора сетей от новичка? Первый никогда не заходит в админку для рутинных операций. Он открывает терминал, набирает одну команду — и через 30 секунд все 50 сайтов обновлены. Это WP-CLI.

WP-CLI — это командная строка для WordPress. А для Multisite это не просто удобный инструмент, а необходимость. Без него сеть из 20+ сайтов превращается в источник постоянной головной боли. С ним — в управляемый, предсказуемый механизм.

В этом руководстве разберём все ключевые операции: массовые обновления, управление плагинами и темами, работа с пользователями, бэкапы и автоматизация через cron. Всё с реальными командами, которые можно копировать и применять.

Что такое WP-CLI и почему он обязателен для Multisite

WP-CLI (WordPress Command Line Interface) — официальный инструмент от команды WordPress для управления сайтом через терминал. Делает всё то же, что и админка, но в десятки раз быстрее.

Почему для Multisite он критичен:

  • Масштаб: одна команда работает сразу на всех сайтах сети
  • Скорость: массовое обновление 50 сайтов — 30 секунд вместо 30 минут
  • Автоматизация: команды можно встраивать в cron-скрипты и CI/CD
  • Надёжность: нет таймаутов, как при обновлениях через браузер
  • Доступ к скрытым функциям: массовый поиск, регенерация миниатюр, очистка транзентов

Базовая установка (если ещё не установлен):

# Скачиваем phar-файл
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar

# Делаем исполняемым
chmod +x wp-cli.phar

# Перемещаем в системную папку
sudo mv wp-cli.phar /usr/local/bin/wp

# Проверяем
wp --info

Все команды в этом руководстве выполняются из корня WordPress-установки. Если вы в другой директории, добавьте флаг --path=/var/www/wordpress.

Базовые команды: навигация по сети

Прежде чем делать массовые операции, нужно понимать структуру сети. WP-CLI даёт полный контроль над списком сайтов.

Получить список всех сайтов:

# Все сайты с базовой информацией
wp site list

# С конкретными полями
wp site list --fields=blog_id,url,registered,last_updated

# Только публичные сайты
wp site list --field=url --public=1

# В формате CSV (удобно для импорта в таблицы)
wp site list --format=csv > sites.csv

Переход между сайтами:

Любая команда WP-CLI по умолчанию работает на основном сайте (blog_id=1). Чтобы выполнить её на конкретном подсайте, используйте флаг --url:

# Получить опции конкретного сайта
wp option get siteurl --url=blog.custom-code.ru

# Посмотреть активных пользователей сайта
wp user list --url=shop.custom-code.ru --role=administrator

А знаете, в чём настоящая магия? Флаг --network выполняет команду на всех сайтах сети одновременно:

# Обновить все опции "blogname" во всей сети
wp option update blogname "Custom Code Network" --network

Массовые обновления: ядро, плагины, темы

Это самая частая операция. В админке Multisite обновления выглядят просто, но на практике обновление 10 плагинов на 30 сайтах через браузер — это час кликов и祈祷, чтобы ничего не сломалось.

Обновление ядра WordPress для всей сети:

# Обновить ядро
wp core update

# Обновить базу данных для всех сайтов сети
wp core update-db --network

Флаг --network здесь критичен. Без него обновится только база основного сайта, а остальные 29 останутся со старой схемой. И начнут падать с ошибками.

Обновление плагинов на всех сайтах:

# Обновить все плагины на всех сайтах
wp plugin update --all --network

# Обновить конкретный плагин на всех сайтах
wp plugin update woocommerce --network

# Обновить плагины только на одном сайте
wp plugin update --all --url=shop.custom-code.ru

Обновление тем:

# Все темы во всей сети
wp theme update --all --network

# Конкретная тема
wp theme update flavor --network

Проверка статуса обновлений:

Перед обновлением полезно посмотреть, что вообще требует обновления:

# Проверка ядра
wp core check-update

# Проверка плагинов
wp plugin list --update=available --network

# Проверка тем
wp theme list --update=available --network

Массовое управление плагинами

В Multisite есть два уровня активации плагинов: сетевая (на всех сайтах сразу) и индивидуальная (на конкретном сайте). WP-CLI умеет оба.

Активация для всей сети:

# Установить плагин
wp plugin install redis-cache

# Активировать для всей сети
wp plugin activate redis-cache --network

# Деактивировать для всей сети
wp plugin deactivate redis-cache --network

Активация на конкретных сайтах:

# Активировать WooCommerce только на сайтах-магазинах
wp site list --field=url | grep shop | while read url; do
    wp plugin activate woocommerce --url=$url
done

Этот скрипт находит все сайты с «shop» в URL и активирует WooCommerce только на них. Полезно, когда в сети смешаны блоги и магазины.

Удаление плагина со всех сайтов:

# Сначала деактивируем везде
wp plugin deactivate bad-plugin --network

# Потом удаляем
wp plugin delete bad-plugin

Важный нюанс: в Multisite плагин физически хранится в одном месте (wp-content/plugins/), поэтому удаляется один раз. Но деактивировать его нужно на каждом сайте отдельно — иначе останутся «висящие» активации в базе.

Массовое управление пользователями

В Multisite пользователи общие для всей сети, но права у них разные на разных сайтах. Админка показывает плоский список, и разобраться, кто куда имеет доступ, — тот ещё квест.

Просмотр пользователей сети:

# Все пользователи сети
wp user list --network

# Super Admins
wp super-admin list

# Пользователи конкретного сайта
wp user list --url=blog.custom-code.ru

# Поиск по email
wp user get user@example.com --network

Массовое добавление пользователей:

# Создать пользователя
wp user create editor editor@example.com --role=editor

# Добавить существующего пользователя на все сайты сети
wp site list --field=url | while read url; do
    wp user add-role editor@example.com editor --url=$url
done

Массовое удаление спам-аккаунтов:

Если в сети открыта регистрация, рано или поздно набегают боты. Чистим их одним скриптом:

# Найти пользователей без постов и комментариев
wp user list --field=user_login --network | while read user; do
    posts=$(wp db query "SELECT COUNT(*) FROM wp_posts WHERE post_author = (SELECT ID FROM wp_users WHERE user_login = '$user')" --skip-column-names)
    if [ "$posts" -eq 0 ]; then
        echo "Deleting spam user: $user"
        wp user delete $user --network --yes
    fi
done

⚠️ Осторожно: этот скрипт удаляет пользователей навсегда. Сначала запустите его без команды wp user delete — просто выведите список. Проверьте, что не попали реальные пользователи.

Массовая работа с контентом

Контент в Multisite хранится в отдельных таблицах для каждого сайта (wp_2_posts, wp_3_posts). Но WP-CLI абстрагирует это — вы работаете с ним как с обычным сайтом.

Поиск и замена текста на всех сайтах:

Классическая задача: компания сменила название, и нужно обновить его во всех статьях. Через админку — сотни ручных правок. Через WP-CLI — одна команда:

# Поиск и замена с учётом сериализации
wp site list --field=url | while read url; do
    wp search-replace 'Старое Название' 'Новое Название' --url=$url --all-tables
done

Флаг --all-tables гарантирует, что замена пройдёт во всех таблицах сайта, включая кастомные.

Экспорт контента со всех сайтов:

# Экспорт каждого сайта в отдельный XML
mkdir -p /backup/exports
wp site list --field=url | while read url; do
    name=$(echo $url | sed 's/https\?:\/\///' | tr '.' '_')
    wp export --url=$url --dir=/backup/exports --filename_format="${name}-%Y-%m-%d.xml"
done

Через 5 минут у вас будет папка с XML-файлами всех сайтов сети. Идеально для бэкапов или миграции.

Регенерация миниатюр:

Если изменили размеры миниатюр в теме или установили плагин для оптимизации изображений, нужно перегенерировать миниатюры для всех сайтов:

wp site list --field=url | while read url; do
    echo "Regenerating for $url..."
    wp media regenerate --url=$url --yes
done

Оптимизация базы данных

Multisite создаёт отдельные таблицы для каждого сайта. При 50 сайтах это тысячи таблиц. Со временем они обрастают мусором: ревизиями, транзентами, спам-комментариями.

Очистка ревизий на всех сайтах:

# Оставить только 5 последних ревизий для каждого поста
wp site list --field=url | while read url; do
    wp post delete $(wp post list --post_type=revision --url=$url --format=ids | awk '{for(i=6;i<=NF;i++) print $i}') --url=$url --force 2>/dev/null
done

Более простой вариант — через прямой SQL-запрос:

# Удалить все ревизии старше 30 дней во всей сети
wp db query "DELETE FROM wp_posts WHERE post_type = 'revision' AND post_modified < DATE_SUB(NOW(), INTERVAL 30 DAY);"

Очистка транзентов:

# Удалить все просроченные транзенты на всех сайтах
wp transient delete --expired --network

# Удалить все транзенты (аккуратно!)
wp transient delete --all --network

Оптимизация всех таблиц:

# Оптимизация таблиц базы данных
wp db optimize

# Проверка целостности
wp db check

Автоматизация через скрипты и cron

Ручное выполнение команд — это хорошо для разовых задач. Но настоящая сила WP-CLI раскрывается в автоматизации. Создаём скрипты и вешаем их на cron.

Скрипт ежедневного обслуживания:

Создаём файл /usr/local/bin/wp-multisite-maintenance.sh:

#!/bin/bash

# Конфигурация
WP_PATH="/var/www/wordpress"
LOG_FILE="/var/log/wp-maintenance.log"
DATE=$(date +%Y-%m-%d_%H-%M)

cd $WP_PATH

echo "[$DATE] Starting multisite maintenance..." >> $LOG_FILE

# 1. Очистка просроченных транзентов
echo "[$DATE] Cleaning expired transients..." >> $LOG_FILE
wp transient delete --expired --network --path=$WP_PATH >> $LOG_FILE 2>&1

# 2. Удаление спам-комментариев
echo "[$DATE] Deleting spam comments..." >> $LOG_FILE
wp site list --field=url --path=$WP_PATH | while read url; do
    wp comment delete $(wp comment list --status=spam --url=$url --path=$WP_PATH --format=ids) --url=$url --path=$WP_PATH --force 2>/dev/null
done

# 3. Оптимизация базы данных
echo "[$DATE] Optimizing database..." >> $LOG_FILE
wp db optimize --path=$WP_PATH >> $LOG_FILE 2>&1

# 4. Проверка обновлений
echo "[$DATE] Checking for updates..." >> $LOG_FILE
wp core check-update --path=$WP_PATH >> $LOG_FILE 2>&1
wp plugin list --update=available --network --path=$WP_PATH >> $LOG_FILE 2>&1

# 5. Отправка отчёта в Telegram
REPORT=$(tail -n 50 $LOG_FILE)
curl -s -X POST "https://api.telegram.org/bot$BOT_TOKEN/sendMessage" \
    -d chat_id="$CHAT_ID" \
    -d text="🔧 WP Multisite Maintenance Report ($DATE):
$REPORT"

echo "[$DATE] Maintenance completed" >> $LOG_FILE

Делаем исполняемым:

chmod +x /usr/local/bin/wp-multisite-maintenance.sh

Добавление в cron:

# Запуск каждый день в 3 часа ночи
0 3 * * * /usr/local/bin/wp-multisite-maintenance.sh

Скрипт безопасного обновления:

Обновления — рискованная операция. Создаём скрипт, который делает бэкап, обновляет и проверяет работоспособность:

#!/bin/bash

WP_PATH="/var/www/wordpress"
BACKUP_DIR="/backup/multisite"
DATE=$(date +%Y-%m-%d)

cd $WP_PATH

# 1. Бэкап базы данных
echo "Creating backup..."
wp db export $BACKUP_DIR/multisite-$DATE.sql

# 2. Обновление ядра
echo "Updating core..."
wp core update
wp core update-db --network

# 3. Обновление плагинов
echo "Updating plugins..."
wp plugin update --all --network

# 4. Обновление тем
echo "Updating themes..."
wp theme update --all --network

# 5. Проверка работоспособности каждого сайта
echo "Verifying sites..."
wp site list --field=url | while read url; do
    status=$(curl -s -o /dev/null -w "%{http_code}" $url)
    if [ "$status" != "200" ]; then
        echo "⚠️ Site $url returned HTTP $status"
        # Отправить алерт
        curl -s -X POST "https://api.telegram.org/bot$BOT_TOKEN/sendMessage" \
            -d chat_id="$CHAT_ID" \
            -d text="🚨 ALERT: $url returned HTTP $status after update"
    fi
done

echo "Update completed"

Мониторинг здоровья сети

Multisite из десятков сайтов сложно мониторить через обычный аптайм-чекер. Нужны специализированные проверки.

Скрипт проверки всех сайтов:

#!/bin/bash

# Проверка доступности всех сайтов
wp site list --field=url | while read url; do
    # HTTP-код ответа
    http_code=$(curl -s -o /dev/null -w "%{http_code}" $url --max-time 10)
    
    # Время ответа
    response_time=$(curl -s -o /dev/null -w "%{time_total}" $url --max-time 10)
    
    # Размер ответа
    size=$(curl -s -o /dev/null -w "%{size_download}" $url --max-time 10)
    
    echo "$url | HTTP $http_code | ${response_time}s | ${size} bytes"
    
    # Алерт при проблемах
    if [ "$http_code" != "200" ] || (( $(echo "$response_time > 5" | bc -l) )); then
        curl -s -X POST "https://api.telegram.org/bot$BOT_TOKEN/sendMessage" \
            -d chat_id="$CHAT_ID" \
            -d text="⚠️ $url: HTTP $http_code, ${response_time}s"
    fi
done

Поиск "мёртвых" сайтов:

В больших сетях часто есть заброшенные сайты, которые никто не обновляет годами. Находим их:

# Сайты, которые не обновлялись больше года
wp site list --fields=url,last_updated | awk '
NR > 1 {
    split($2, a, "-");
    last_update = mktime(a[1] " " a[2] " " a[3] " 0 0 0");
    year_ago = systime() - 365 * 24 * 60 * 60;
    if (last_update < year_ago) print "Inactive: " $1
}'

Такие сайты — кандидаты на архивацию или удаление. Они создают нагрузку, но не приносят пользы.

Продвинутые техники

Параллельное выполнение команд:

Последовательное выполнение команд на 50 сайтах может занять час. Используем xargs с параллелизацией:

# Обновить плагины на всех сайтах параллельно (10 потоков)
wp site list --field=url | xargs -P 10 -I {} wp plugin update --all --url={}

# Проверить все сайты параллельно
wp site list --field=url | xargs -P 20 -I {} curl -s -o /dev/null -w "{}: %{http_code}\n" {}

Флаг -P 10 означает 10 параллельных процессов. Не ставьте больше, чем у вас ядер CPU × 2 — получите обратный эффект из-за переключения контекста.

Клонирование сайта в сети:

Иногда нужно создать новый сайт по образу существующего. WP-CLI позволяет экспортировать контент и импортировать его на новый сайт:

# 1. Создать новый сайт
wp site create --slug=newblog --title="New Blog" --email=admin@example.com

# 2. Экспортировать контент с шаблона
wp export --url=template.custom-code.ru --dir=/tmp

# 3. Импортировать на новый сайт
wp import /tmp/template-*.xml --url=newblog.custom-code.ru --authors=create

Генерация отчёта по сети:

#!/bin/bash

# Собрать статистику по всей сети
echo "=== MULTISITE NETWORK REPORT ==="
echo "Generated: $(date)"
echo ""

echo "Total sites: $(wp site list --format=count)"
echo "Total users: $(wp user list --network --format=count)"
echo ""

echo "=== TOP 10 SITES BY POST COUNT ==="
wp site list --field=url | while read url; do
    count=$(wp post list --url=$url --post_type=post --post_status=publish --format=count 2>/dev/null)
    echo "$count posts: $url"
done | sort -rn | head -10

echo ""
echo "=== PLUGINS INSTALLED ==="
wp plugin list --network --fields=name,version,status --format=table

echo ""
echo "=== PENDING UPDATES ==="
wp plugin list --update=available --network --format=table
wp theme list --update=available --network --format=table

Чек-лист: автоматизация Multisite

Пройдитесь по этому списку. Если хотя бы один пункт не автоматизирован — вы тратите время впустую.

  • Массовое обновление плагинов — одна команда вместо сотни кликов
  • Обновление ядра с --network — для обновления всех баз данных
  • Ежедневная очистка транзентов — через cron
  • Регулярная очистка ревизий — раз в неделю
  • Мониторинг доступности всех сайтов — с алертами в Telegram
  • Поиск заброшенных сайтов — раз в квартал
  • Автоматические бэкапы базы данных — ежедневно
  • Отчёт о состоянии сети — раз в неделю в почту/Telegram
  • Параллельное выполнение — для ускорения массовых операций
  • Безопасные обновления с откатом — бэкап перед обновлением

Решение частых проблем

WP-CLI выдаёт «Site not found» для подсайтов

Проблема в том, что WP-CLI определяет сайт по URL из $_SERVER['HTTP_HOST']. При выполнении команды из терминала эта переменная пуста. Решение — всегда указывать --url явно:

# Неправильно
wp option get blogname

# Правильно
wp option get blogname --url=blog.custom-code.ru

Команды выполняются слишком медленно

Если операция на 50 сайтах занимает час — включите параллельное выполнение через xargs -P. Также убедитесь, что включён OPcache и Redis Object Cache — без них WP-CLI работает в 3-5 раз медленнее.

Ошибка «Allowed memory size exhausted» при массовых операциях

WP-CLI использует тот же memory_limit, что и PHP-FPM. Для массовых операций увеличьте его временно:

# Временно увеличить лимит для WP-CLI
wp plugin update --all --network --skip-themes --skip-plugins

Или в wp-cli.yml в корне сайта:

php:
  memory_limit: 1G

Не могу выполнить команду — «Permission denied»

WP-CLI должен запускаться от того же пользователя, что и веб-сервер (обычно www-data). Запуск от root создаст файлы с правами root, и веб-сервер не сможет их читать. Решение:

# Запуск от нужного пользователя
sudo -u www-data wp plugin update --all --network

# Или добавить флаг --allow-root (не рекомендуется)
wp plugin update --all --network --allow-root

Итог

WP-CLI для Multisite — это не опция, а базовая необходимость. Без него управление сетью из десятков сайтов превращается в ручной труд, который съедает часы каждую неделю. С ним те же операции занимают минуты.

Главные преимущества: массовые обновления одной командой, автоматизация через cron, параллельное выполнение для скорости, скрипты для мониторинга и отчётов. Всё это превращает Multisite из источника стресса в управляемый механизм.

Начните с простого: освойте wp site list, wp plugin update --all --network и wp core update-db --network. Это покроет 80% повседневных задач. Потом добавляйте скрипты автоматизации и мониторинг. Через месяц вы удивитесь, как раньше жили без этого.

А главное — откройте терминал прямо сейчас. Наберите wp site list. Посмотрите на свою сеть глазами администратора, а не пользователя. Это другой уровень управления. И он вам понравится.