В данной статье я расскажу о том, как я заставил свой блог на WordPress летать за счёт грамотного кэширования, сжатия и другой оптимизации серверной и клиентской сторон. На момент написания статьи характеристики VDS следующие:
CPU: 1 x 2GHz
HDD: 10Gb
RAM: 512Mb
OS: Debian 8 x64

Схема работы системы выглядит следующим образом:

image

Описание работы схемы


Для посетителей сайта происходит перенаправление на HTTPS, где nginx работает в качестве прокси для Varnish, при этом на выходе nginx помимо реализации HTTPS-соединения происходит gzip-сжатие данных, передаваемых пользователю. Следующим элементом в данной системе является HTTP-акселератор Varnish, ожидающий соединения на 6081 порту. Получая запрос от клиента он выполняет поиск запрашиваемого URL в кэше, и в случае его обнаружения мгновенно отдаёт его фронтенду. Таким образом, при наличии запрашиваемого файла в кэше скорость запроса к страницам сокращается до скорости запроса к статическим данным. Если же запрашиваемого файла в кэше не обнаруживается, Varnish передаёт запрос бэкенду. Так же в Varnish реализована оптимизация клиентской стороны — здесь статическим данным устанавливаются заголовки Cache-Control и Expires, указывающие браузеру на необходимость кэширования этих данных на стороне клиента. Таким образом сокращается время загрузки сайта и уменьшается нагрузка на сервер.

В роли бэкенда выступает опять же nginx, ожидающий соединений на 127.0.0.1:81. Интерпретация PHP реализована с помощью FPM. Версия PHP — 5.6 с включенным по умолчанию акселератором OPcache. В качестве СУБД — MariaDB 10, являющаяся одной из лучших по производительности и кушающих в меру оперативную память СУБД среди форков MySQL. В качестве движка таблиц — MyISAM, так как запись производится редко, в основном чтение, для которого данный движок больше оптимизирован. За счёт отключения движка InnoDB реализуется экономия оперативной памяти. Наконец, в качестве CMS функционирует WordPress с установленным плагином Varnish HTTP Purge, отправляющий PURGE-запросы на адреса страниц, на которых были произведены изменения, что приводит к очистке кэша Varnish для данных страниц. Таким образом, пользователь получает всегда актуальную версию сайта. Далее я детально расскажу об установке и настройке данных компонентов, а так же о проблемах, с которыми я столкнулся.

Установка и настройка nginx


Устанавливаем:

apt-get install nginx

Содержимое основного конфига /etc/nginx/nginx.conf:

# Пользователь и группа, от имени которых будет запущен процесс
user                    www-data www-data;

# Число воркеров в новых версиях рекомендовано устанавливать в auto
worker_processes        auto;

error_log               /var/log/nginx/error.log;
pid                     /var/run/nginx.pid;

events {
    # Максимальное количество соединений одного воркера
    worker_connections              1024;

    # Метод выбора соединений (для FreeBSD будет kqueue)
    use                             epoll;

    # Принимать максимально возможное количество соединений
    multi_accept                    on;
}

http {
    # Указываем файл с mime-типами и указываем тип данных по-умолчанию
    include                         /etc/nginx/mime.types;
    default_type                    application/octet-stream;

    # Отключить вывод версии nginx в ответе
    server_tokens off;

    # Метод отправки данных sendfile эффективнее чем read+write
    sendfile                        on;

    # Ограничивает объём данных, который может передан за один вызов sendfile(). Нужно для исключения ситуации когда одно соединение может целиком захватить воркер
    sendfile_max_chunk  128k;

    # Отправлять заголовки и и начало файла в одном пакете
    tcp_nopush                      on;
    tcp_nodelay                     on;

    # Сбрасывать соединение если клиент перестал читать ответ
    reset_timedout_connection       on;
    # Разрывать соединение по истечению таймаута при получении заголовка и тела запроса
    client_header_timeout           3;
    client_body_timeout             5;
    # Разрывать соединение, если клиент не отвечает в течение 3 секунд
    send_timeout                    3;

    # Задание буфера для заголовка и тела запроса
    client_header_buffer_size       2k;
    client_body_buffer_size         256k;
    # Ограничение на размер тела запроса
    client_max_body_size            12m;

    # Отключаем лог доступа
    access_log                      off;

    # Подключаем дополнительные конфиги
    include                         /etc/nginx/conf.d/*.conf;
}

Создадим файл настроек бэкенда /etc/nginx/conf.d/backend.conf:

server {
    # Ожидать локального соединения на 81 порту
    listen 127.0.0.1:81;

    # Корневая директория и индексовый файл
    root /var/www/site.ru/public_html;
    index index.php;

# Включить gzip-сжатие на выходе бэкенда. В кэш пойдут уже сжатые версии файлов. Здесь происходит сжатие на 9 уровне компрессии. Обратите внимание, среди типов отсутствует text/plain, его мы сжимаем во фронтенде на 1 уровне компрессии, чтобы избежать высокой степени загрузки CPU при отдаче динамических данных
    gzip                on;
    gzip_comp_level     9;
    gzip_min_length     512;
    gzip_buffers        8 64k;
    gzip_types text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript image/svg+xml;
    gzip_proxied        any;

    # Имя хоста
    server_name site.ru www.site.ru;

    # Запрет на доступ к скрытым файлам
    location ~ /\. {
        deny all;
    }

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

    # Поиск запрашиваемого URI по трем путям
    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    # Добавление слэша в конце для запросов */wp-admin
    rewrite /wp-admin$ $scheme://$host$uri/ permanent;

    location ~ \.php$ {
        # При ошибке 404 выдавать страницу, сформированную WordPress
        try_files $uri =404;

        # При обращении к php передавать его на интерпретацию FPM
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
    }
}

На тему детального описания настройки HTTPS в nginx рекомендую к прочтению данную статью: habrahabr.ru/post/252821
Создаём файл настроек фронтэнда /etc/nginx/conf.d/frontend.conf:

server {
    # Редирект на HTTPS
    listen      REAL_IP:80;
    server_name site.ru www.site.ru;
    return 301 https://$server_name$request_uri;
}

server {
    listen      93.170.105.102:443 ssl;
    server_name site.ru www.site.ru;

    # Устанавливать Keep-Alive соединения с посетителями
    keepalive_timeout               60 60;

    # Сжатие данных перед отправкой клиенту. Обратите внимание, из типов здесь присутствует только text/plain, для других данных применяется сжатие на бэкенде на более высоком уровне компрессии, после чего эти данные отправляются в кэш. Сделано для того, чтобы избежать нагрузок на CPU при сжатии динамических документов.
    gzip                on;
    gzip_comp_level     1;
    gzip_min_length     512;
    gzip_buffers        8 64k;
    gzip_types text/plain;
    gzip_proxied        any;

    # Отдавать предпочтение шифрам, заданным на сервере
    ssl_prefer_server_ciphers on;
    # Установка длительности TLS сессии в 2 минуты
    ssl_session_cache shared:TLS:2m;
    ssl_session_timeout 2m;

    # Задание файла, содержащего сертификат сайта и сертификат УЦ
    ssl_certificate      /etc/ssl/combined.crt;
    # Указание закрытого ключа
    ssl_certificate_key  /etc/ssl/3_site.ru.key;

    # Файл с параметрами Диффи-Хеллмана
    ssl_dhparam /etc/ssl/dh2048.pem;

    # Поддерживаемые протоколы
    ssl_protocols TLSv1.2 TLSv1.1 TLSv1;

    # Наборы шифров, данный набор включает forward secrecy
    ssl_ciphers EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA512:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:ECDH+AESGCM:ECDH+AES256:DH+AESGCM:DH+AES256:RSA+AESGCM:!aNULL:!eNULL:!LOW:!RC4:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS;

    # Передача Strict-Transport-Secutiry заголовка
    add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains';

    location / {
        # Проксирование на Varnish
        proxy_pass      http://127.0.0.1:6081/;

        proxy_set_header    Host              $host;
        proxy_set_header    X-Real-IP         $remote_addr;
        proxy_set_header    X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header    X-Forwarded-Proto https;
        proxy_set_header    X-Forwarded-Port  443;
    }
}

Перечитаем конфиги nginx:

service nginx reload

Теперь при попытке зайти на сайт увидим ошибку 502. Это нормально, так как Varnish пока не запущен.

Установка и настройка Varnish


Устанавливаем Varnish:

apt-get install varnish

Файл параметров запуска располагается здесь — /etc/default/varnish. В DAEMON_OPTS задаём следующие параметры:

DAEMON_OPTS="-a :6081              -T 127.0.0.1:6082              -f /etc/varnish/default.vcl              -S /etc/varnish/secret              -s malloc,128m"

-a — задаёт порт, на котором Varnish будет принимать соединения, в нашем случае от фронтенда — nginx;
-T — здесь крутится админка, подробнее в описании к флагу -S;
-f — файл с конфигурацией VCL — специальном языке, предназначенном для определения правил обработки запросов и кэширования в Varnish;
-S — Varnish имеет панель администрирования. Для входа необходимо выполнить команду varnishadm, при этом пользователь должен иметь права на чтение файла /etc/varnish/secret для прохождения аутентификации;
-s указание места хранения кэша и его размер, в данном случае 128Mб в оперативной памяти.

Как вы уже, наверное, поняли, самое интересное нас ждёт в файле с правилами обработки запросов. Во время старта процесса Varnish’а данный файл компилируется. В VCL используется несколько подразделов-функций, в которых описываются эти правила. Кратко расскажу о них, полное описание рекомендую прочитать на официальном сайте.

sub vcl_recv — данная функция используется когда приходит запрос от клиента;
sub vcl_pass — выполняется, когда запрос клиента необходимо передать напрямую бэкенду, не кэшировать и не искать соответствия в кэше;
sub vcl_hash — определяет правила кэширования, можно использовать несколько хранилищ для одного и того же документа, в зависимости от разных условий, например, поддержки сжатия клиентом, или каких-либо других особенностей клиента. В нашем случае не будет использоваться, так как клиент у нас для Varnish’а один — nginx на фронтенде;
sub vcl_backend_response — данная функция используется когда приходит запрос от бэкенда (nginx);
sub vcl_deliver — используется непосредственно перед отправкой данных клиенту, например, для добавления/изменения заголовков.

Схема работы компонентов VCL может быть представлена следующим образом:

image

Если обращение к бэкенду происходит при этом из функции vcl_miss ответ бэкенда отправляется и в кэш. Сам язык очень похож на C. Приступим к настройке. Открываем файл /etc/varnish/default.vcl и начинаем кодить:

# Сообщаем компилятору о том, что используется новая версия VCL 4
vcl 4.0;

# Настройки бэкенда
backend default {
    .host = "127.0.0.1";
    .port = "81";
}

# Диапазон IP/Хостов, которым разрешено выполнять PURGE-запросы для очистки кэша
acl purge {
    "localhost";
    "127.0.0.1";
}

# Получение запроса от клиента
sub vcl_recv {
        # Разрешить очистку кэша вышеописанному диапазону
        if (req.method == "PURGE") {
                # Если запрос не из списка, то разворачивать
                if (!client.ip ~ purge) {
                        return(synth(405, "This IP is not allowed to send PURGE requests."));
                }
                return (purge);
        }

        # POST-запросы а также страницы с Basic-авторизацией пропускать
        if (req.http.Authorization || req.method == "POST") {
                return (pass);
        }

        # Пропускать админку и страницу входа
        if (req.url ~ "wp-(login|admin)" || req.url ~ "preview=true") {
                return (pass);
        }

        # Пропускать sitemap и файл robots, у меня sitemap генерируется плагином Google XML Sitemaps
        if (req.url ~ "sitemap" || req.url ~ "robots") {
                return (pass);
        }

        # Удаляем cookies, содержащие "has_js" и "__*", добавляемые CloudFlare и Google Analytics, так как Varnish не будет кэшировать запросы, для которых установлены cookies.
        set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(_[_a-z]+|has_js)=[^;]*", "");

        # Удаление префикса ";" в cookies, если вдруг будет обнаружен
        set req.http.Cookie = regsub(req.http.Cookie, "^;\s*", "");

        # Удаляем Quant Capital cookies (добавляются некоторыми плагинами)
        set req.http.Cookie = regsuball(req.http.Cookie, "__qc.=[^;]+(; )?", "");
        # Удаляем wp-settings-1 cookie
        set req.http.Cookie = regsuball(req.http.Cookie, "wp-settings-1=[^;]+(; )?", "");

        # Удаляем wp-settings-time-1 cookie
        set req.http.Cookie = regsuball(req.http.Cookie, "wp-settings-time-1=[^;]+(; )?", "");

        # Удаляем wp test cookie
        set req.http.Cookie = regsuball(req.http.Cookie, "wordpress_test_cookie=[^;]+(; )?", "");

        # Удаляем cookie, состоящие только из пробелов (или вообще пустые)
        if (req.http.cookie ~ "^ *$") {
                    unset req.http.cookie;
        }

        # Для статических документов удаляем все cookies, пусть себе кэшируются 
        if (req.url ~ "\.(css|js|png|gif|jp(e)?g|swf|ico|woff|svg|htm|html)") {
                unset req.http.cookie;
        }

        # Если установлены cookies "wordpress_" или "comment_" пропускаем напряиую к бэкенду
        if (req.http.Cookie ~ "wordpress_" || req.http.Cookie ~ "comment_") {
                return (pass);
        }

        # Если cookie не найдено, удаляем данный параметр из пришедшего запроса как таковой
        if (!req.http.cookie) {
                unset req.http.cookie;
        }

        # Не кэшировать запросы с установленными cookies, это уже не касается WordPress
        if (req.http.Authorization || req.http.Cookie) {
                # Not cacheable by default
                return (pass);
        }

        # Кэшировать всё остальное
        return (hash);
}

sub vcl_pass {
        return (fetch);
}

sub vcl_hash {
        hash_data(req.url);

        return (lookup);
}

# Приём ответа от бэкенда
sub vcl_backend_response {
        # Удаляем ненужные заголовки
        unset beresp.http.Server;
        unset beresp.http.X-Powered-By;

        # Не хранить в кэше robots и sitemap
        if (bereq.url ~ "sitemap" || bereq.url ~ "robots") {
                set beresp.uncacheable = true;
                set beresp.ttl = 30s;
                return (deliver);
        }

        # Для статических файлов, которые отдаёт бэкенд...
        if (bereq.url ~ "\.(css|js|png|gif|jp(e?)g)|swf|ico|woff|svg|htm|html") {
                # Удаляем все куки 
                unset beresp.http.cookie;
                # Устанавливаем срок хранения в кэше - неделю
                set beresp.ttl = 7d;
                # Устанавливаем заголовки Cache-Control и Expires, сообщая браузеру о том, что эти файлы стоит сохранить в кэше клиента и не нагружать лишниий раз наш сервер
                unset beresp.http.Cache-Control;
                set beresp.http.Cache-Control = "public, max-age=604800";
                set beresp.http.Expires = now + beresp.ttl;
        }

        # Не кэшировать админку и страницу логина
        if (bereq.url ~ "wp-(login|admin)" || bereq.url ~ "preview=true") {
                set beresp.uncacheable = true;
                set beresp.ttl = 30s;
                return (deliver);
        }

        # Разрешить устанавливать куки только при обращении к этим путям, всё остальное будет резаться
                if (!(bereq.url ~ "(wp-login|wp-admin|preview=true)")) {
                unset beresp.http.set-cookie;
        }

        # Не кэшировать результат ответа на POST-запрос или Basic авторизации
        if ( bereq.method == "POST" || bereq.http.Authorization ) {
                set beresp.uncacheable = true;
                set beresp.ttl = 120s;
                return (deliver);
        }

        # Не кэшировать результаты поиска
        if ( bereq.url ~ "\?s=" ){
                set beresp.uncacheable = true;
                set beresp.ttl = 120s;
                return (deliver);
        }

        # Не кэшировать страницы ошибок, только нужные вещи в кэше!
        if ( beresp.status != 200 ) {
                set beresp.uncacheable = true;
                set beresp.ttl = 120s;
                return (deliver);
        }


        # Хранить в кэше всё прочее на протяжении одного дня
        set beresp.ttl = 1d;
        # Срок жизни кэша после истечения его TTL
        set beresp.grace = 30s;

        return (deliver);
}

# Действия перед отдачей результата пользователю
sub vcl_deliver {
        # Удаляем ненужные заголовки
        unset resp.http.X-Powered-By;
        unset resp.http.Server;
        unset resp.http.Via;
        unset resp.http.X-Varnish;

        return (deliver);
}

После чего выполняем команду:

service varnish restart

Перейдя теперь в браузере на наш сайт, мы увидим index.php, который нужно предварительно создать.

Проблема Varnish и Debian 8

А что если вы захотите изменить порт, на котором Varnish будет принимать входящие соединения или изменить объём кэша. Судя по официальной документации нужно изменить файл с параметрами запуска Varnish, располагающийся по пути: /etc/default/varnish и перезапустить сервис. Но нет! Ничего не изменится, и если мы зайдём в top и нажмем на клавишу ‘c’, то увидим, что сервис запущен с прежними настройками. А всё дело в том, что в новой версии Debian используется systemd вместо init.d в качестве системы инициализации, и поэтому нужно зайти в файл /lib/systemd/system/varnish.service и прописать там в директиве ExecStart те же параметры запуска:

[Unit]
Description=Varnish HTTP accelerator

[Service]
Type=forking
LimitNOFILE=131072
LimitMEMLOCK=82000
ExecStartPre=/usr/sbin/varnishd -C -f /etc/varnish/default.vcl
ExecStart=/usr/sbin/varnishd -a :6081 -T 127.0.0.1:6082 -f /etc/varnish/default.vcl -S /etc/varnish/secret -s malloc,128m
ExecReload=/usr/share/varnish/reload-vcl

[Install]
WantedBy=multi-user.target

После сохранения выполнить следующие команды для вступления изменений в силу:

systemctl daemon-reload
service varnish restart

В данный момент данная проблема отписана разработчикам, когда и как они её решат — неизвестно, поэтому на всякий случай производите одинаковые изменения в обоих файлах, чтобы однажды после апдейта всё не упало.

Установка и настройка PHP-FPM


Устанавливаем FPM и библиотеку PHP для работы с СУБД:

apt-get install php5-fpm php5-mysqlnd

Заходим в файл конфигурации /etc/php5/fpm/pool.d/www.conf и меняем директиву:

listen = 127.0.0.1:9000

На следующее:

listen = /var/run/php5-fpm.sock

В этом же файле задаём настройки воркеров:

; Динамическое изменение количества воркеров
pm = dynamic
; Максимальное число воркеров, создаются под нагрузкой, не может быть меньше pm.max_spare_servers.
pm.max_children = 10 
; Сколько воркеров запускать при старте FPM
pm.start_servers = 1
; Минимальное количество запасных воркеров (остаются в памяти при отсутствии нагрузки)
pm.min_spare_servers = 1
; Максимальное количество запасных воркеров (при простое, остальные неиспользуемые будут завершаться)
pm.max_spare_servers = 3
; Максимальное количество запросов, которые выполняет один воркер, прежде чем перезапуститься
pm.max_requests = 500

Меняем несколько директив в /etc/php5/fpm/php.ini
upload_max_filesize = 10M
post_max_size = 12M
allow_url_fopen = Off

post_max_size задаём чуть больше, чем upload_max_filesize, так как помимо файла в запросе идут другие данные.
Здесь же директивой allow_url_fopen запрещаем выполнять скрипты, расположенные удаленно (убирая возможность эксплуатации уязвимости удалённого инклуда).

И говорим FPM перечитать конфиг:

service php5-fpm reload

Теперь создайте файлик, выводящий phpinfo() и обратитесь к нему в браузере, всё должно работать. Не забывайте, что он уже закэшировался в Varnish и если вы будете изменять конфигурацию PHP, то она не будет обновляться в вашем браузере. Можете написать правило на пропуск данного файла в Varnish, либо же на время тестов проксировать не Varnish, а напрямую бэкенд на 81 порту.

Установка и настройка MariaDB


Эту СУБД я выбрал по причине её лучшей производительности и способности выдерживать большие нагрузки, при этом затрачивая меньшее количество оперативной памяти по сравнению с MySQL, а так же её полной совместимостью с WordPress. Установка очень проста, будет запрошен пароль для пользователя root.

apt-get install mariadb-server

В качестве движка для таблиц я использую MyISAM, по причине того, что запись в таблицу выполняется редко, а на чтении MyISAM показывает лучшие характеристики. Я полностью отключил поддержку InnoDB для освобождения оперативной памяти. Настройки хранятся в файле /etc/mysql/my.cnf. Опишу только те директивы, которые я изменил:

# Кэш для работы с ключами и индексами
key_buffer = 64M

# Кэш запросов
query_cache_size = 32M

# Установка MyISAM в качестве стандартного движка
default-storage-engine=MyISAM

# Отключение движка InnoDB
skip-innodb

После сохранения изменений перезапускаем сервис:

service mysql restart

Настройка WordPress — плагин «Varnish HTTP Purge»


Устанавливаем в панели администрирования WP плагин «Varnish HTTP Purge». Теперь при обновлении данных на измененные страницы будет отправлен PURGE-запрос, очищающий кэш в Varnish, и для посетителей данные всегда будут обновлёнными.

Дополнительная оптимизация


Для оптимизации клиентской стороны с помощью Varnish мы указываем браузеру на необходимость хранения статических данных в локальном кэше клиента. Но если вы жаждете ещё большей оптимизации, перейдите на страничку developers.google.com/speed/pagespeed/insights и введите URL вашего сайта или даже конкретной страницы. Вам предоставится список рекомендаций, а так же предложат архив со сжатыми версиями ваших css и js стилей. Замените их на своём сайте и получите ещё большую скорость загрузки за счёт уменьшенного объема передаваемых данных, так же уменьшится нагрузка на сервер и место, занимаемое данными файлами в кэше.

Как поступить с документами, запрашиваемыми со сторонних серверов, например, шрифтами или библиотекой jquery? Можно перенести их к себе, и тут за счёт установки соединения только с одним сервером возрастёт скорость загрузки страниц, однако, в то же время, возрастёт список обращений и общая нагрузка. Какой вариант выбрать — решайте сами, в зависимости от загруженности вашего сервера и вашей лени.

Итог


По большей части наибольший эффект дали сжатие gzip и кэширование в Varnish. В комментариях уже написали много дополнительных методов оптимизации, которые я непременно изучу и по мере необходимости внедрю. Пока же результаты оптимизации следующие:
До
image
После
image
Полноценные стресс-тесты проведу чуть позже.

Комментарии (45)


  1. evilbot
    29.02.2016 13:05
    +1

    Прикрутите PHP 7.


    1. ivashkevitch
      29.02.2016 13:09
      +1

      В ближайших планах.


  1. ibKpoxa
    29.02.2016 13:10
    +3

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

    Почему не используется кеширование в nginx?

    Используется ли memcache в WordPress? Он умеет, если попросить и плагин поставить.

    Какой смысл использовать MariaDB при использовании MyISAM?


    1. ivashkevitch
      29.02.2016 13:52
      -2

      В первую очередь было интересно изучить возможности Varnish, а ещё Varnish быстрее по результатам тестирования, например — http://www.uptimemadeeasy.com/cloud/nginx-or-varnish-which-is-faster/

      memcache не используется, по тому как я до него пока не добрался. Есть рекомендации, с чего начать?

      При выборе СУБД руководствовался данным результатом https://habrahabr.ru/post/242337/


      1. ibKpoxa
        29.02.2016 14:01
        +4

        В любом проекте должна быть еще и надежность и рациональность, чем больше инструментов мы используем — тем больше точек отказа.
        Обработка запросов в цепочке nginx-varnish-nginx-php будет в любом случае медленнее, чем nginx-php, даже если varnish в разы быстрее, чем nginx, хотя и судя по вашей ссылке, nginx не всегда медленнее чем varnish.

        для мемкеша есть модуль у wordpress, он легко ищется.


        1. ivashkevitch
          29.02.2016 14:09

          Спасибо за пояснения.

          Я имел в виду, может посоветуете статей на тему грамотной настройки memcache с подробными комментариями?


          1. ibKpoxa
            29.02.2016 15:44

            основная настройка мемкеша — размер занимаемой памяти, конечно есть еще число тредов, фактор размеров таблиц и т.п., но число требов запросто можно сделать равным числу тредов в пыхе + 1, а фактор размеров таблиц оставить по умолчанию и эффект будет достаточным, в процессе работы надо следить на числов эвикшенов, т.е. за удалением элементов из кеша при записи новых элементов, и на соотношение hit/miss, если соотношение нормальное, например 90% нужной информации находится в мемкеше, то все ок, если информации мало, и много эвикшенов, то не хватает памяти, а если число эвикшенов при этом не велико, то просто запросы такие, что они плохо кешируются.


          1. maxxannik
            01.03.2016 04:13

            Вот тут есть подборка с кейсом http://systemo.biz/kesh-wordpress-varianty-keshirovaniya-i-uskoreniya-sajta/


  1. alekciy
    29.02.2016 13:19
    +1

    # Отключаем лог доступа

    Специальное значение off отменяет все директивы access_log для текущего уровня

    Это именно отмена, а не отключение файла лога. Лог будет писаться в файл off. Что бы действительно отключить запись лога в файл нужно писать access_log /dev/null

    include fastcgi_params;

    include в контексте PHP должен быть вначале.


    1. ivashkevitch
      29.02.2016 14:03
      +1

      Спасибо, исправил.


    1. 1it
      29.02.2016 14:37
      +4

      Это именно отмена, а не отключение файла лога. Лог будет писаться в файл off. Что бы действительно отключить запись лога в файл нужно писать access_log /dev/null

      Бред.

      The special value off cancels all access_log directives on the current level.
      http://nginx.org/en/docs/http/ngx_http_log_module.html

      access_log off; это директива отключающая лог (в конкретном блоке server или location), про "файл off" в документации нет ни слова (и на практике такой файл не возникает).


      1. ivashkevitch
        29.02.2016 14:48

        Спасибо.


      1. nikitasius
        29.02.2016 15:44
        +2

        Возникает в случае error лога.


    1. ibKpoxa
      29.02.2016 15:36
      +5

      >> Лог будет писаться в файл off.
      это для error_log, а у ТС access_log


  1. coh
    29.02.2016 13:19

    На заметку, в MariaDB нет MyISAM, storage engine называется Aria. Выбор движка не очевиден, чем вам не понравился XtraDB?
    Вы не сравнивали производительность WP + Varnish vs WP + memcached (например w3 total cache) vs WP +Varnish + Memcached ?


    1. coh
      29.02.2016 13:23

      Тут обсжудали чем плох MyISAM https://habrahabr.ru/post/269463/


    1. coh
      29.02.2016 14:06

      Поправка: MyISAM все-таки есть, но считается legacy


    1. zapimir
      29.02.2016 14:29

      С чего это в MariaDB нет MyISAM? Там есть и MyISAM и Aria.


  1. alekciy
    29.02.2016 13:40
    +6

    Я возможно что-то пропустил в статье, но где цифры "было-стало"?


    1. ivashkevitch
      29.02.2016 14:57
      +1

      Отключил gzip и убрал из цепочки Varnish — основные компоненты, обеспечивающие повышение производительности. Примерно улучшения следующие (повторное посещение странички):

      image
      image


      1. nikitasius
        29.02.2016 15:56
        +2

        gzip_comp_level 9;

        неверно. Вот замечательная статья, "единички" хватит за глаза.

        Для статики используйте активно ngx_http_gzip_static_module:

        for i in `find ./* -type f -name '*.js'`; do echo $i; gzip -c -9 $i > $i.gz; done;

        for i in `find ./* -type f -name '*.css'`; do echo $i; gzip -c -9 $i > $i.gz; done;

        И не забыть chown'ом юзера вернуть, ибо файлы будут под учеткой, с которой запускали скрипт.

        Отключите server_tokens, чтобы был просто nginx в ответах.

        Использую send_file, используйте и чанки.


        1. ivashkevitch
          29.02.2016 17:55
          +1

          Познавательно, спасибо.


        1. ivashkevitch
          29.02.2016 18:51

          Перенёс сжатие на выход бэкенда, теперь в кэше будут храниться уже сжатые варианты. То есть для статики теперь компрессия будет происходить раз в сутки (время хранения в кэше Varnish). Как думаете, оправданно ли теперь оставить уровень сжатия на 9?


          1. nikitasius
            29.02.2016 19:20

            Если у вас посетилелей мало — 9 на одном ядре много от самого ядра не съест. Но если будет большой наплыв, то nginx без кеша будет сжимать в девятку каждый раз ответ динамики через ваш варниш (=ответ кеша, как я понимаю), что скажется на использовании CPU (которое как минимум делят система, бд, варнишь и nginx).

            На картинке у вас страница весит 8кб, подозреваю что сжатая. Значит на единичке она будет 9-11кб. Выиграшь копеечный, при таком расходе цпу.

            Вы вообще можете отказаться от варниша и обыграть тонкую настройку через location, как делают на проектах, где nginx управляет и кешем и сжатием. Будет менее красиво (в визуальном плане листинга), чем варнишь.

            *отщепятки


            1. ivashkevitch
              29.02.2016 19:29

              будет сжимать в девятку каждый раз ответ динамики через ваш варниш

              Прошу прощения, Вы имели в виду сжатие динамики на выходе бэкенда каждый раз, перед отдачей Varnish?


              1. nikitasius
                29.02.2016 19:42

                Почти..

                Пересмотрел ваш конфиг, у вас там php -> nginx -> varnish -> nginx.

                Мой комментарий был бы к месту, если бы у вас там не было промежуточного nginx. В вашем случае 9тка будет в кеше варниша, так что все ОК :)


                1. ivashkevitch
                  29.02.2016 19:51

                  Да, но Вы до этого всё же были правы. Актуально это будет только для статики, а для динамики будет происходить сжатие каждый раз. Так что нужно переделать. Два правила, например, в зависимости от типа файлов: статику сжимать на выходе из бэкенда на 9 уровне и пусть хранится в кэше Varnish'a, а динамику сжимать на выходе фронтенда на 1 уровне компрессии.

                  И ещё, сейчас стоит сжатие только на бэкенде, динамический контент при этом при отдаче в браузер отдаётся без сжатия. Кажется, Varnish что-то делает не так, как задумывалось, впрочем, при вышеописанной схеме данной проблемы не будет.


  1. laviro
    29.02.2016 14:46

    Может кто то на опыте сказать, реально ли есть смысл к nginx еще varnish добавлять?
    А то как то кеш всегда на nginx делал, а смотрю часто люди в связку еще varnish добавляют.

    А тестировали нагрузку?
    И как тестировали?


    1. ivashkevitch
      29.02.2016 14:53

      На http://www.host-tracker.com/ в 10 вкладках по разным адресам запускал, выжило, даже не тупило. Взялся было запустить синтетические тесты нагрузочные, но они требуют поддержки SSLv3. Пока не до этого, на следующих выходных думаю потестировать.


    1. nikitasius
      29.02.2016 16:37

      У nginx замечательный кеш, в том числе и для fastcgi:

      fastcgi_cache_lock on;
      fastcgi_cache_lock_timeout 6s;

      В чем плюс такой блокировки:
      на страничку блога (если ключ только включает путь, без cookies) привалило 1000 человек, nginx проверил наличие в кеше, если их там нету, то послал всего 1 запрос на бекенд, записал в кешт и затем отдал первому юзеру и остальным 999. И только через 6 секунд запрос будет передан на бекенд для обновления кеша и выдачи нового результата, если толпа не утихает.

      Принципиальное отличие — пришло много народа на один ключ, запросили 1н результат, затем раздали всем, залочив на 6 секунд.


      1. nikitasius
        29.02.2016 16:39

        Но я с варнишом никогда не работал, и, судя по статье, там очень гибко и удобно (=точечно) настраивается кеш.


    1. fear86
      01.03.2016 03:09

      Есть смысл ради https://www.varnish-cache.org/docs/3.0/tutorial/esi.html но тут должна быть поддержка на стороне приложения.


  1. MechanisM
    29.02.2016 16:28

    Похожую связку использовал. Только у меня вместо php-fpm был uwsgi с php плагином.


  1. ennau
    29.02.2016 17:52

    Для дополнения по оптимизации, рекомендую все имиджи оптимизировать, напрм. jpegoptim и optipng утилитами.
    Лучше gzip_level снизить до 5.
    В gzip_types можно добавить jpg и png майм-типы.


    1. nikitasius
      29.02.2016 18:04

      Зачем вам тратить ресурсы на ужимку картинок? Там не многократный выигрыш, как в сравнении с текстами.
      И величина 5 — это чересчур.


      1. ennau
        29.02.2016 21:45

        Эти утилиты не сжимают картинки, а удаляют мета информацию и всякий "мусор" в них, тем самым уменьшая размер картинок. И ресурсы не тратятся, т.к. это единаразовая "акция":-)
        Об уровнях сжатия тут отлично расписано: https://habrahabr.ru/post/99256/


        1. nikitasius
          01.03.2016 01:36

          Так я эту статью выше линковал.
          А удалять теги… вас за это SEO покарают. Сейчас наоборот надо в картинках иметь мета инфу и сайте и страницах. Да и не много там ее будет, в сравнении с общим объемом.


  1. veter
    29.02.2016 18:06

    И что получилось? Какая нагрузка была раньше, а какая выдерживается теперь?


    1. ivashkevitch
      29.02.2016 18:29

      Добавил информацию по поводу производительности в конец статьи. Стресс-тест проведу чуть позже.


  1. farcaller
    29.02.2016 23:51
    +2

    нужно зайти в файл /lib/systemd/system/varnish.service и прописать там в директиве ExecStart те же параметры запуска

    Не нужно, нужно оверрайд сделать в /etc/systemd/system/varnish.service.d/port.conf:

    [Service]
    ExecStart=
    ExecStart=/usr/sbin/varnishd -a :6081 -T 127.0.0.1:6082 -f /etc/varnish/default.vcl -S /etc/varnish/secret -s malloc,128m


  1. antonwork
    01.03.2016 00:46
    +2

    Все тоже самое делал только силами одного nginx, без Varnish. Вот типовая конфигурация nginx: (многое упростил)

    fastcgi_cache_path /tmp/nginx_fcgi  levels=1   keys_zone=zone1:512m;
    ...
    location ~ /help/([047][0-9]+)\.html {
            fastcgi_pass  unix:/var/run/php-cgi.sock;
            ...
            include /etc/nginx/fastcgi_params;
            fastcgi_cache zone1;
            fastcgi_cache_valid 10m;
            fastcgi_cache_key "$host|$document_uri|$args";
    }

    В результате nginx получает банальный хеш md5 от строки, которую вы сами формируете в качестве ключа. Потом он, соблюдая иерархию вложения (задается конфигом), сохраняет ответ бэкенда в определенную директорию. Все что вам нужно — просто удалить этот файл, это и есть purge cahce. Можно все файлы удалить, можно выборочно. Из скрипта, мы знаем url страницы, которую нужно обновить. Просто вызываем функцию, удаляем файл и все.

    function task_dropfrom_nginx ($hash) {
    
            if (file_exists ($path = sprintf ("/tmp/nginx_fcgi/%s/%s", substr($hash, -1), $hash))) {
                    @unlink ($path);
            }
    }
    
    function cache_urldrop ($url, $args = '') {
    
            $hosts = array('ru.example.com', 'www.example.com', 'example.com');
    
            foreach ($hosts as $host) {
                    task_dropfrom_nginx ($hash = md5(sprintf ("%s|%s|%s", $host, $url, $args)));
            }
    }

    У меня была еще следующая надстройка: на фронтендах крутился скрипт на пхп в 15 строчек, который слушал определенный udp порт (в моем случае обращение к нему было закрыто всем кроме внутренней подсети). С бэкэнда, обновившего страницу, просто делаем по адресам фронтендов (их было несколько) рассылку, один udp пакет — один сервер. И через пару миллисекунд кеш отчищен. Гипотетический пакет мог потеряться. Конкретно в моем случае это было не страшно, если кеш не сбросился бы. Если для вас это важно — используйте способы, гарантирующие доставку — очереди.

    Плюс можно еще использовать ssi для этой связки. В результате можно главную страницу отдавать со скоростью статики (из кеша), разбавляя ее разными ssi инклудами, которые либо будут вытащены из кеша (мгновенно), либо будут сгенерированы на бэкенде. Еще модуль memcached творит чудеса, все тоже самое, но без обращений к диску.


    1. nikitasius
      01.03.2016 01:56

      SSI очень мощная смесь на nginx для работы с бекендом.
      А еще интереснее nginx (SSI), который формирует контент на страницу (картинки или тексты), который в свою очередь запрашивается с некоего подобия API за cloudflare (напрямую для медия или через JS).
      Я все самописные вещи стараюсь делать через SSI и API за CF (тут CDN и как допкеш и для скорости).

      Очень хочу вписать какой-нибудь сайт прямо в конфиг nginx (чтобы генерация была на стороне nginx "на лету" с его логикой).


      1. ivashkevitch
        09.03.2016 21:17

        В Varnish есть подобная вещь — называется ESI. Если интересно, написал статью по использованию — https://habrahabr.ru/post/278899/


  1. sfedosimov
    01.03.2016 11:08
    +1

    Таким образом снижается скорость загрузки сайта и нагрузка на веб-сервер.

    Может быть так:

    Таким образом повышается скорость загрузки сайта и снижается нагрузка на веб-сервер.


    1. ivashkevitch
      01.03.2016 11:27

      Спасибо, исправил.