А тут случилось мне поднимать один сайт на WordPress‘е в дроплете DigitalOcean с 512 мегабайтами оперативной памяти.

Сразу к сути - в 512 метрах ему очень тесно.

Итак, есть дроплет в DigitalOcean, я рулю им через SSH. В дроплете стоит Ubuntu, Apache, MySQL и лежит сайт на WordPress‘е.

Проблема

Началось с того, что вдруг вырубился MySQL и отказался стартовать обратно обратно. Хотя конфиг я не изменял и вообще ничего не трогал, просто запустил сайт для общего доступа (до этого заходил на него только я один). Дальше буду приводить все свои действия по решению.

Запускал я его такой командой:

sudo /etc/init.d/mysql restart

На что он мне выдавал:

Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (111)

Так как конфиги я не трогал, то все рецепты по их редактированию я пропускал. Если у вас ситуация другая, то посмотрите тут ещё. Через некоторое время после чтения этой статьи и форумов выяснилось, что проблема… в нехватке оперативной памяти либо места на диске. Так как места на диске завались, то остаётся оперативка.

Мониторинг потребления памяти

Информацию лучше браит непосредственно на самом сервере. Потому для мониторинга потребления оперативки на сервере, а также для получения статуса MySQL (ещё жив или уже слёг) я запилил себе отправку состояния сервера через Telegram.

Команда проверки использования памяти:

free -m

Результат (в мегабайтах) выдаётся примерно такой:

             total       used       free     shared    buffers     cached
Mem:           994        596        397         64         29        261
-/+ buffers/cache:        305        688

Вот у меня картина была такая, что из 512 метров свободно два, а то и вообще один.

Для оптимизации использования памяти попробовал следовать советам статьи Configuring Apache/PHP/MySQL for Low Memory (RAM) VPS - взял из неё значения для конфигов MySQL и Apache. Также хотел потключать модули Apache в соответствии с предложенным там списком, но он у меня после таких манипуляций просто не стартовал, потому я вернул их взад.

Вроде стало получше, но от перегрузок всё равно не спасало. Также при помощи команды top выяснилось, что память жрёт не только MySQL, но и Apache, только он-то не падает, а вот база - да.

Использование swap

Далее я создал swap по исправленной инструкции отсюда:

dd if=/dev/zero of=/swap.dat bs=1024 count=512k
mkswap /swap.dat
swapon /swap.dat

После первой команды он немного подумает, так как будет создаваться swap-файл на 512 метров.

И добавил в файл /etc/fstab строку:

/swap.dat      none    swap    sw      0       0

Не могу сказать, насколько это вообще хоть на что-то повлияло. Потому через некоторое время (после установки nginx) я его выключил:

swapoff -a
rm /swap.dat

И удалил строку из /etc/fstab.

Плагины Wordpress

Попробовал как-то оптимизировать Wordpress с помощью плагинов.

WP Super Cache

После этих манипуляций MySQL стартовал, но вскоре опять встал… Вот тут я сдался и сменил дроплет с 512 МБ на 1 ГБ. Но и с гигабайтом оперативки MySQL падал и не поднимался.

Спасением оказался плагин WP Super Cache - после его включения неистово сократилось потребление всего, и всё спокойно себе работает. До этого даже простой проверки через этот сервис хватало, чтобы всё издохло. Так что можно сказать, что этот плагин просто необходим для нормальной работы.

Настроил я его так (вопреки рекомендуемым настройкам):

Настройки WP Super Cache

И также изменил настройки для Permalinks:

Настройки Permalinks

Но кстати после установки nginx я отключил этот плагин, и всплеска потребления ресурсов не произошло. Так что непонятно, есть от него польза или нет.

WP-Memory-Usage

Надо же ведь как-то следить за сервером, когда компьютер с SSH-терминалом вне досягаемости.

Есть вот такой плагин: WP-Memory-Usage - позволяет смотреть потребление памяти прямо из админки (хотя не знаю, насколько значение соответствует действительности).

Потребление памяти WordPress

Судя по-всему, он показывает не общее потребление, а конкретно сколько памяти потребовалось, чтобы всё прогрузить для текущего пользователя, так что пользы не очень много. Выключил.

Установка nginx в качестве прокси-сервера для Apache

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

Схема такая:

  1. Apache переносится с 80 порта на другой, например 8080, причём делается доступным только локально;
  2. Ставится nginx и запускается на 80 порту, то есть теперь запросы принимает он;
  3. Далее nginx настраивается таким образом, чтобы php-файлы передавать в Apache на его порт, а всё остальное обрабатывать самому.

Дальше пойдут инструкции по настройке, которых я нахватался по форумам и блогам, дополненные мной. Ресурсов было настолько много, что я не помню, где что брал. Простите меня все авторы, которых я не указал.

Итак, сначала надо поставить плагин Permalink Fix & Disable Canonical Redirects Pack, который исправляет что-то там с перенаправлениями в Permalinks.

Настройки Apache

Сначала /etc/apache2/apache2.conf:

ServerRoot "/etc/apache2"
Mutex file:${APACHE_LOCK_DIR} default
PidFile ${APACHE_PID_FILE}
Timeout 300
KeepAlive Off
MaxKeepAliveRequests 100
KeepAliveTimeout 10
User ${APACHE_RUN_USER}
Group ${APACHE_RUN_GROUP}
HostnameLookups Off
ErrorLog ${APACHE_LOG_DIR}/error.log
LogLevel crit
IncludeOptional mods-enabled/*.load
IncludeOptional mods-enabled/*.conf
Include ports.conf

<Directory />
    Options FollowSymLinks
    AllowOverride None
    <Limit PUT DELETE CONNECT OPTIONS PATCH PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK>
        deny from all
    </Limit>
</Directory>

<Directory /var/www>
        Options FollowSymLinks
        AllowOverride FileInfo
</Directory>

<Directory /usr/share>
        AllowOverride None
        Require all granted
</Directory>

AccessFileName .htaccess

<FilesMatch "^\.ht">
        Require all denied
</FilesMatch>


AccessFileName .htaccess

<FilesMatch "^\.ht">
  Require all denied
</FilesMatch>

LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined
LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %O" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent

IncludeOptional conf-enabled/*.conf
IncludeOptional sites-enabled/*.conf

<DirectoryMatch ^.*/wp-content/uploads/>
  AllowOverride None
  php_flag engine off
  php_admin_value engine Off
</DirectoryMatch>

<DirectoryMatch ^.*/wp-content/blogs.dir/>
  AllowOverride None
  php_flag engine off
  php_admin_value engine Off
</DirectoryMatch>

#<DirectoryMatch ^.*/wp-admin/>
#  AuthType Basic
#  AuthName "Restricted Area"
#  AuthUserFile /etc/apache2/.htpasswd
#  Require valid-user
#</DirectoryMatch>
<VirtualHost *>
   ServerAdmin admin@example.com
   DocumentRoot /var/www
   Servername example.com
   ServerAlias example.com www.example.com
 </VirtualHost>

 <IfModule mpm_prefork_module>
    StartServers          1
    MinSpareServers       1
    MaxSpareServers       4
    MaxClients            4
    MaxRequestsPerChild   1000
</IfModule>

Теперь /etc/apache2/ports.conf:

Listen 127.0.0.1:8080

<IfModule ssl_module>
  Listen 443
</IfModule>

<IfModule mod_gnutls.c>
  Listen 443
</IfModule>

# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

Перезапускаем сервер:

service apache2 restart

Теперь он работает на порту 8080, и сайт ваш стал недоступен из интернета. Даже если вы попробуете открыть его как example.com:8080.

Настройки nginx

Установка:

apt-get install nginx

Пишем конфиг для сайта - просто файл без расширения - /etc/nginx/sites-enabled/mysite:

server {
    listen 80;
    server_name example.com;
    root /var/www;
    index index.php;

    gzip on;
    gzip_disable "msie6";
    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript;

    location ~ /\. {
            deny all; # запрет для скрытых файлов
    }

    location ~* /(?:uploads|files)/.*\.php$ {
            deny all; # запрет для загруженных скриптов
    }

    location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
            access_log off;
            log_not_found off;
            expires max; # кеширование статики
    }

    location / {
        try_files $uri $uri/ /index.php?q=$uri&$args;
    }

    location ~ \.php$ {
        proxy_set_header X-Real-IP  $remote_addr;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host $host;
        proxy_pass http://127.0.0.1:8080;
    }
}

Теперь конфиг всего сервера /etc/nginx/nginx.conf:

user www-data;
worker_processes 1;
pid /run/nginx.pid;

events {
	worker_connections 768;
    use epoll;
	# multi_accept on;
}

http {

	##
	# Basic Settings
	##

	sendfile on;
	tcp_nopush on;
	tcp_nodelay on;
	keepalive_timeout 5;
	types_hash_max_size 2048;

	include /etc/nginx/mime.types;
	default_type application/octet-stream;

	##
	# Logging Settings
	##

	access_log /var/log/nginx/access.log;
	error_log /var/log/nginx/error.log;

	##
	# Virtual Host Configs
	##

	include /etc/nginx/conf.d/*.conf;
	include /etc/nginx/sites-enabled/*;
}

Запуск сервера и перезагрузка настроек:

service nginx start
nginx -s reload

Всё, nginx работает и раздаёт сайт.

Проверить текущий веб-сервер можно командой:

curl -s -I example.com|awk '$1~/Server:/ {print $2}'

В результате ваще этого всего - не то, чтобы я заметил драматическое снижение потребления оперативки, но падать от нехватки оной база перестала. Я уменьшил дроплет обратно с 1 ГБ до 512 МБ - всё огонь. Конечно, если одновременно подключится несколько десятков пользователей, то в какой-то момент некоторым начнёт выдаваться 502 Bad Gateway, но база не падает, и как только нагрузка снизится, сайт будет отдаваться нормально.

Короче, можно сказать, что для выживания WordPress на 512 МБ оперативной памяти необходим nginx и корректные настройки для него, Apache и MySQL. По желанию также можно попробовать различные припарки вроде swap-файла и плагина WP Super Cache.