Glashkoff.com

Полезные советы, софт для Windows и Android, создание сайтов на WordPress

Создание сайтов

Настройка Arch Linux для хостинга сайтов с оптимизацией PageSpeed

Предисловие

Расскажу, как настроить редкую на серверах операционную систему Arch Linux для хостинга сайта с изоляцией (не стопроцентной!) веб-серверного софта и модулем PageSpeed для Nginx. При этом сайт будет настроен на хорошую отзывчивость и высокий рейтинг тестов PageSpeed Insights и GTmetrix.

Достоинства такого решения:

  1. Картинки, скрипты и стили сайта будут автоматически оптимизированы.
  2. Настройка сервера позволит лучше понять, как работает ОС Linux.
  3. На сервере с Arch Linux будет всегда свежайший софт.
  4. За счёт минимума излишеств требования к производительности сервера невысоки, работает быстро.
  5. Неплохой уровень безопасности (за счёт chroot).

Недостатки:

  1. Долго и нудно настраивается, так как нужно вводить много команд и вдумчиво читать мануалы.
  2. Система менее стабильна по сравнению с Debian, Ubuntu, CentOS из-за недостаточно тщательно протестированных новейших версий софта.
  3. В моей инструкции описывается размещение только одного сайта на CMS WordPress. Хостить несколько сложнее.

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

Я не уверен, что инструкция ниже подойдёт зелёным новичкам, только вступивших на тропу постижения дзена в мире GNU. Если что-то будет непонятно или, наоборот, вы с высоты опыта заметили ошибки и недоработки — комментарии под статьями всегда открыты.

Лучший способ разобраться в чём-то — объяснить это остальным.

Не помню кто

Поэтому не стесняйтесь задавать вопросы.

Что такое Arch Linux

Arch Linux — один из универсальных дистрибутивов GNU/Linux, оптимизированный для процессоров архитектуры x86-64. Для процессоров ARM сборки тоже существуют, поэтому на сервер с ARM от, например, Scaleway, теоретически поставить можно. Я не пробовал.

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

Другая важная черта — Арч распространяется по модели rolling release (нет разделения на значимые релизы). Можно взять диск с установщиком Arch Linux пятилетней давности, поставить систему на ПК и после обновления через интернет весь софт будет свежайшим.

Чем хорош Arch Linux

Archlinux — полнейшая противоположность системам вроде Debian, Ubuntu, CentOS. Здесь нет удобного автоматического инсталлятора, нужно читать документацию и набирать много команд. Судя по отзывам в интернете, у непривычных к чтению настройка Арча вызывает настоящую головную боль. Но действительно ли эта система плоха? Нет!

Нельзя ожидать одинакового подхода от всех ОС, мир GNU/Linux славится разнообразием.

Концепция Arch Linux такова (как я её понимаю): пользователю даётся полный карт-бланш по конструированию системы. Полная свобода действий и никаких излишеств.

Если пользователи Убунты похожи на скульпторов, отсекающих у глыбы всё лишнее, чтобы получить красивую композицию, то юзеры Арча работают подобно строителям, по кирпичику собирая систему, отвечающую их требованиям.

Подход «от минимального — к нужному» позволил Archlinux зарекомендовать себя системой, шустро работающей на старом железе.

Если вам не нравится минимализм системы, нет времени сидеть и разбираться, но хочется хотя бы немного посидеть на чём-то отличном от Debian (и Windows), гляньте дистрибутив Manjaro, он основан на Arch Linux.

Что нужно знать тем, кто привык к Убунтам

1. Изначально ставится минимум софта. Более того, придётся пройти вручную весь процесс установки, вводя команды в терминале. Это сложно, если вы только познакомились с миром *nix, поэтому можно так не заморачиваться, поставив, например, ОС Manjaro Linux, основанную на герое этой заметки. Там установка проще, но и гибкости меньше.

2. Пользователи Арча находятся на острие прогресса, так как в системном репозитории программы почти всегда самых свежих версий. Увы, из-за этого выше риск столкнуться с багами. Ситуации, когда после очередного обновления система становились неработоспособной, остались в прошлом, но какая-нибудь гадость может произойти всё равно. Впрочем, из-за приобретённой способности вдумчиво читать мануалы пользователи Arch Linux, как правило, умеют откатывать компоненты системы до прошлых версий, поэтому сбои — лишь досадная случайность, не более.

3. У Archlinux есть AUR. Вместо десятков и сотен независимых репозиториев, как это принято в мире Debian, в Arch Linux помимо нескольких стандартных хранилищ весь софт публикуется в AUR (Arch User Repository). Это такая большая свалка ПО для *nix. Так как строгого контроля за тем, что туда загружается, нет, есть риск получить вместо безобидной утилиты бэкдор или глюки. Зато у Arch User Repository громадное преимущество перед классическими репозиториями: при установке черег AUR софт, как правило, компилируется из оригинальных исходников или загружается из первоначального источника. Например, пакет mc-git содержит всегда самую актуальную версию файлового менеджера Midnight Commander, ведь инсталляция происходит путём компиляции исходных кодов напрямую из кодового репозитория на Github, который ведёт автор программы. Такой подход автоматизирует установку модульного софта вроде nginx, ведь не нужно ждать, пока автор ПО соберёт нужную сборку.

Если вы обладаете терпением и внимательностью, а также хочется повысить свои навыки работы в GNU/Linux, Arch Linux — отличный выбор. Настраивается дольше, сложнее, но результат получается именно тот, который нужен, в отличии от типовых наборов софта в других ОС.

Чем хорош Arch Linux для серверов

На всяких VPS и отдельных серверах с Arch Linux ситуация проще с установкой, но сложнее с поддерживанием.

Достоинства:

  • Во-первых, установку ОС делает сам провайдер хостинга, разворачивая готовый образ системы (не всегда).
  • Во-вторых, не нужно заморачиваться с установкой драйверов видеокарты, звуковой карты, прочей периферии и графической оболочки. Зачем там рабочий стол, если сидеть придётся через командную строку по SSH?
  • В-третьих, за счет того, что изначально ставится очень мало софта, даже дешёвый одноядерный VPS с 512 Мб RAM будет довольно шустро работать.

Недостаток один, зато огроменный: если после очередного обновления сервер «сломается» (например, откажется загружаться из-за сбоя скрипта обновления ядра Linux), сайт не будет открываться всё то время, пока разбираетесь с проблемой. Если у вас личный блог или сайт-визитка, то, в принципе, ничем страшным это не обернётся. Интернет-магазинам и прочим высоконагруженным сервисам такая нестабильность грозит убытками.

Arch Linux — отличный вариант для серверов со скромными характеристиками: одноядерных, с 512 или гигабайтом ОЗУ, небольшим жёстким диском. На таком можно запустить сайт-визитку или другой не шибко требовательным к ресурсам проект.

Сила Arch Linux в том, что с его помощью можно сделать так, чтобы операционная система занимала как можно меньше места на жёстком диске и в оперативной памяти. Тут Debian и Ubuntu выступают не в лучшем свете — с ними ставится много полезного, но лишнего софта. Кого-то может устроить CentOS, но для меня эта система не является эталоном серверных ОС, весомых преимуществ перед «коллегами» я не вижу.

Ставим Arch Linux легко и просто на любой сервер с помощью «магии»

В процессе написания статьи я три раза переустанавливал Арч. Это был не самый приятный опыт, ибо приходилось производить много нудных действий, которые в других ОС автоматизированы. Настройку сети, например. После третьего круга ада я сказал «с меня хватит!» и поискал способы автоматизировать это дело.

Для быстрой установки Арча нашёл поистине магический способ — скрипт vps2arch. Он умеет быстро, без единого вопроса, конвертировать в Arch Linux уже имеющуюся операционную систему! Поддерживаются:

  • Debian 7, 8 9;
  • Ubuntu 14.10, 15.10, 16.04, 17.04;
  • CentOS 6, 6.5, 7.

Все данные при этом удаляются, в том числе сайты (если они были), но сетевые настройки и пароль от пользователя root остаются. Система получается чистой, но доступен pacman, поэтому требующийся софт можно доустановить быстро и безболезненно.

Всё, что нужно, это:

1. Арендовать VPS (я выбрал Debian 9, но можно и другую ОС, список выше).

2. Выполнить несколько команд от пользователя root:

wget http://tinyurl.com/vps2arch
chmod +x vps2arch
./vps2arch

3. Перезагрузить сервер. Всё, Arch Linux установлен!

Это, пожалуй, самый крутой в мире скрипт, который я когда-либо видел. Он элегантен в своей простоте и наглядно показывает сильную сторону Linux — настраиваемость.

После установки Arch Linux на сервере будет занято примерно 40 мегабайт ОЗУ. Это довольно компактная система.

Скорость WordPress и дешёвые сервера

Чаще всего я пишу о Windows как системе для домашних ПК, Linux как серверных ОС и WordPress как способе создать хороший сайт. Будет логичным рассказать дальнейшие шаги на примере сайта, работающем на CMS WordPress.

Для сайтов на WP, посещаемых более чем 20-30 тысячами людей в сутки, я рекомендую брать VPS с как минимум двумя гигабайтами ОЗУ и двухъядерным процессором. Тогда сайт будет быстро открываться у всех. Но за счёт чего возникают тормоза в более дешёвых конфигурациях серверов?

Арендуя дешёвую VPS-ку рублей эдак за сто в месяц, вы должны помнить о возможных трудностях, мешающих сделать страницы сайта быстро открывающимися. Есть три фактора, влияющих на скорость генерации страничек, со стороны сервера:

  1. Скорость жёсткого диска. В дешёвых используют обычные HDD. В принципе, наличие HDD в серверном оборудовании — не трагедия, диск вашего виртуального сервера может работать очень шустро… но не всегда. Когда «соседи» по серверу начинают одновременно что-то писать или читать, начинаются ощутимые тормоза. Вывод: чтобы сайт открывался быстро, нужно сократить количество файловых операций, держа всё в ОЗУ VPS. С 512 мегабайтами особо не развернёшься, а вот с 1 Гб «на борту» можно что-то сделать.
  2. Работа скриптов PHP или любого другого языка (смотря какой движок у сайта). Для WordPress минимальным количеством ОЗУ, при котором админка работает нормально — 128 Мб. То есть в идеале, если двое человек одновременно вызовут редактор записей, на сервере для генерации страниц их админок в этот момент должно быть доступно 256 Мб ОЗУ. Для троих потребуется 384 Мб и так далее.
  3. На практике не всё так плохо. Можно ограничить память для скриптов в php.ini всего лишь 48 мегабайтами и админка… нет, не начнёт тормозить. Просто иногда «тяжёлые» скрипты будут аварийно завершать работу из-за нехватки памяти, вместо страниц появятся сообщения о 502 или 503 ошибке. Если на сайте только один администратор, способный создавать посты, а остальные могут лишь комментировать записи, проблем вовсе не будет, ибо требования по памяти в этих условиях не настолько велики.
  4. Работа базы данных. Это, пожалуй, самое больное место многих CMS.
    Интернет-магазинам из-за большого количества категорий противопоказаны сервера с малым объёмом ОЗУ. Проблема в том, что для генерации страниц рубрик, списка товаров, постов в блоге выполняются десятки запросов к базе данных. Отдельные запросы выполняются довольно быстро — 20-100 миллисекунд, но в сумме получаются секунды. А к сайтам, чьи страницы открываются больше 500 мс, не так уж хорошо относятся ни поисковые системы (отклик сервера — фактор ранжирования), ни люди (кто захочет ждать долгой загрузки страниц?). Поэтому запросы в базах данных кэшируются в RAM для быстрого переиспользования. Вот только кэш запросов может занимать гигабайты. Чтобы хоть как-то обойти ограничения производительности, можно сделать сайт статичным с помощью плагинов файлового кэширования, чтобы открывались готовые html-странички. Альтернативный подход к решению проблемы медленного открытия сайта — использование файловой CMS — тоже имеет право на жизнь, но там слишком много нюансов: от скорости жёсткого диска до банальных ограничений по возможностям (базы данных не просто так придумали).

«Тяжёлые» плагины и переусложнённые шаблоны, замедляющие работу сайта, тоже не следует сбрасывать со счетов. Поэтому не следует считать, что производительность сайта зависит только от железа и конфигурации Nginx, PHP, MySQL и прочих веб-серверных приложений. Иногда плохой плагин способен с сайтом сделать то же, что квадратные колёса с Формулой-1…

Так что же, придётся отдавать свои кровные за более дорогой и быстрый сервер? Ну, нет, можно обойтись и малым. Пока сайт не шибко посещаемый, можно экономить. Чтобы написать эту статью, я арендовал, пожалуй, один из самых недорогих виртуальных серверов, которые только можно найти, скопировал туда свой блог glashkoff.com и всё нормально заработало.

Кстати, существует два типа масштабируемости серверов:
1. Вертикальное — увеличение производительности за счёт прямого добавления «мощностей». Например, повышения частоты процессора, увеличения размера RAM, замены жёсткого диска на быстрый SSD.
2. Горизонтальное — увеличение количества серверов и распределение нагрузки между ними.
Вертикальное масштабирование работает просто и понятно: перешёл на тариф подороже — сервер стал быстрее, странички быстрее открываются. Однако предел у вертикального масштабирования наступает довольно быстро, ведь у ЦП есть ограничение по частоте. Горизонтальное требует большей вдумчивости, но возможностей предоставляет больше. Например, можно разместить сайты на относительно недорогих серверах в разных частях света, чтобы посетители всегда получали быстрый доступ независимо от своего местонахождения.

Что такое PageSpeed

Есть два продукта от Google с похожими названиями.

PageSpeed Insights — онлайн-тест от разработчиков Google, анализирующий качество сайта. Измеряется и выявляется очень многое: отклик сайта, размеры страниц, возможность оптимизации картинок, мешающие быстрой загрузке скрипты и стили и тому подобное.

У онлайн-теста есть проблемка: его оценка, по моему мнению, не адекватна. Чтобы добиться оценки 100/100 в мобильной и десктопных версиях веб-ресурса, нужно очень и очень сильно постараться. И не факт, что такая оптимизация под тест положительно скажется на внешнем виде сайта и удобстве. Например, мобильная версия айтишного сайта Habr.com набрала 59 баллов. А на нём даже картинок нет!

Анализ сайта в PageSpeed Insights

Чтобы вы поняли, что беспокоиться по поводу средней оценки не стоит, вот результат анализа главной страницы Яндекса:

Если популярный IT-ресурс и крупнейшая поисковая система не стремятся набрать 100 баллов, то вам, скорее всего, тоже нет смысла беспокоиться по этому поводу. Однако есть кое-что, что способно действительно улучшить скорость загрузки сайта без особых усилий — PageSpeed Modules.

PageSpeed Modules — дополнение для двух популярных веб-серверов: Apache и Nginx. Работает оптимизирующей прослойкой между браузером пользователя и вебсерверным софтом. Всё, что будет выдавать Nginx или Apache, будет переработано, сжато и сложено в промежуточный кэш. Или не сложено, а будет оптимизироваться на лету при каждом запросе страницы — смотря как настроите. В этой инструкции я предложу свой конфигурационный файл, основанный на найденных в интернете подобных файлах. Вам ничто не помешает доработать его при необходимости.

Где купить дешёвый VPS

Здесь могла бы быть ваша реклама! (Шутка. Хотя…)

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

Как оказалось, поиск дешёвого VPS — тяжёлая задача. Хостеров-однодневок, занимающихся перепродажей облачных услуг, громадьё. Чтобы не отдать деньги нехорошим товарищам, приходится искать ИНН организации, проверять разрешение на оказание услуг, искать владельцев, смотреть наличие судебных дел. Отзывы и обзоры помогают слабо — большая часть фейковые, ибо рынок высококонкурентный.

Сначала остановил свой выбор на SkyHost.ru. Они предлагают тариф VDS-Zero за 84 рубля в месяц, что дёшево не только по меркам России, но и мировым.

Дешёвый VDS

Если платить сразу за год работы сервера, выйдет 80 рублей в месяц, но я не рискнул вот так просто выкладывать свои кровные, ибо не был уверен в стабильности их серверов. И, знаете, не прогадал. SkyHost оказался примером безалаберности и пофигизма. Я продержался два десятка дней, затем, после всех проблем, вспылил, отписал им в техподдержку их недостатки и попрощался, выключив арендованную виртуальную машину.

Вот список замеченных мною проблем у SkyHost.ru:

  1. Неправильно сконфигурированный новогодний промокод на скидку, который не работал без предварительной регистрации (проблему решил, списавшись с техподдержкой).
  2. Излишне сложная форма регистрации, где одних только полей с физическим адресом больше четырёх.
  3. Крайне неторопливая админка управления серверами.
  4. Неработающая кнопка загрузки Rescue режима сервера (по нажатию висит заглушка, сообщающая, что нужно просить об этом техподдержку).
  5. «Молча» зависающий хост-сервер. В админке SkyHost есть раздел «Объявления», где, насколько я понял, должны публиковаться оповещения, если проводится восстановление серверов, но он пуст. Вот и гадай — то ли VPS завис, то ли машина уровня выше.
  6. Невозможно изменить имя хоста (идентификатор сервера) в админ-панели.
  7. Обещанные пять адресов IPv6 в админке не видны. Только IPv4.
  8. Нельзя переустановить ОС. Точнее, создать новый сервер с этой ОС можно, а в списке переустановки Arch Linux нет.
  9. Нельзя при переустановке ОС сохранить IP адрес. Если просить техподдержку переустановить Arch Linux, выяснится пренеприятнейшая особенность: изменится IP сервера. Получается, если с сервером что-то случится, даунтайм будет запредельно долгим, ведь потребуется ждать обновления DNS серверов. За это время на старом IP может работать какой-то посторонний ресурс, если кто-то купит VDS в это же время.

Собственно, последнее смехотворное по своей глупости ограничение — невозможность сохранить IP адрес — и было последней каплей, после чего я «сбежал» от них. Так что этот хостинг могу рекомендовать только в качестве тестовой площадки. За 84 рубля в месяц вы получите «песочницу», в которой можно повысить навыки, борясь с бестолковой программной платформой хостера. Ибо опыт бесценен.

Далее я арендовал виртуальную машину у VDSina.ru. Там ещё дешевле — 60 рублей в месяц, но места на жёстком диске кот наплакал:

Дешёвый VPS

Впрочем, Arch Linux занимает около полутора-двух гигабайт, для одного сайта места хватит. Какое качество услуг предлагает VDSina? Я бы сказал так: нормальное. Претензий к ним нет. Отмечу очень продуманную тарификацию серверных ресурсов, напоминающую таковую у Digital Ocean. А ещё у них видно, на сколько дней работы сервера хватит денег на счету.

Не забудьте купить домен и сделать у него DNS-запись типа А с IP-адресом сервера. Иначе придётся открывать сайт, вводя IP.

Подключение к серверу

Для подключения к серверу по протоколу SSH, если сидите на Windows, рекомендую использовать Kitty. Это лучшее средство для работы с терминалом по протоколу SSH для этой ОС.

Но так как я люблю пробовать новые инструменты, для этой заметки буду использовать утилиту Terminus. Она медленная и глючная, но очень стильная, поэтому для создания скриншотов подходит идеально.

Terminus
Terminus

Настройка сервера на Arch Linux

Итак, при заходе на сервер вас встретит командная строка. Надеюсь, у вас есть опыт работы в ней или хотя бы сильное желание гуглить ответы на возникающие вопросы. В принципе, книг по Linux много для разного уровня знаний читателей, это не проблема.

Ниже я буду приводить команды, которые нужно ввести в терминале. Естественно, перепечатывать ничего не нужно. Их можно просто копировать. Внимательно читайте написанное мною, потому что в некоторых случаях (например, при создании правила iptables) нужно указать собственные значения.

Кое-что нужно выполнить в первую очередь: обновить софт, настроить защиту и упростить себе жизнь в командной строке. После можно думать о том, как хостить сайт.

Создание пользователя

Так как под root ни в одной операционной системе сидеть не стоит, да и часть софта под ним не запустится, требуется создать отдельный пользовательский аккаунт для администрирования сервера. Заодно укажем ему оболочку zsh, которая лучше bash во всём (но требует настройки, об этом позже).

В коде ниже я устанавливаю zsh и создаю пользователя glashkoff (замените имя на другое!) с группой admin, а затем даю ему возможность использовать права суперпользователя командой «sudo». Подробнее про управление пользователями в Arch Linux есть в Wiki.

sudo pacman -S zsh
groupadd admin
useradd -m -g admin -s /bin/zsh glashkoff
echo "%admin ALL=(ALL) ALL" >> /etc/sudoers
passwd glashkoff

После ввода команды «passwd имя_юзера» появится строка для ввода пароля для свежесозданного пользователя. После нужно сидеть через SSH под свежесозданным пользователем, а про юзера root забудьте.

Кстати, если вы увидите такой вопрос:

This is the Z Shell configuration function for new users, zsh-newuser-install.
You are seeing this message because you have no zsh startup files (the files .zshenv, .zprofile, .zshrc, .zlogin in the directory ~). This function can help you with a few settings that should make your use of the shell easier.

Жмите 0. Чуть позже в соответствующей главе я расскажу, как сделать с помощью zsh командную строку удобнее.

Как устанавливать и обновлять софт

Как я уже писал выше, в Arch Linux используется менеджер пакетов pacman. Все его возможности можете узнать в русскоязычной Wiki. Более того, я настоятельно рекомендую там покопаться. Тексты справок написаны хорошо и настолько доступно, насколько это возможно.

Через pacman софт обновляется такой командой:

sudo pacman -Syu

На вопрос «Proceed with installation?» жмите Y и Enter. Так как хостеры нечасто обновляют образы операционных систем, свежих версий ПО будет много, первоначальное обновление займёт время.

Установка нового ПО делается так:

sudo pacman -S имя_пакета1 имя_пакета2 и т.д.

Удалять программы рекомендую сразу со всеми зависимостями, не используемыми другими софтинами:

sudo pacman -Rs имя_пакета

Если нужно не просто удалить софт, а заодно стереть все конфигурационные файлы (pacman делает их бекапы), используйте параметр «n»:

sudo pacman -Rsn имя_пакета

Также не стоит забывать про AUR. Да, там много заброшенного авторами софта, неактуального и даже вредного, но есть и классные штуки. Поэтому нужно подключить AUR, установив какой-нибудь сторонний менеджер пакетов. Например, yay. Это, пожалуй, не лучший, но простой и современный инструмент установки софта. Главный недостаток yay — тянет за собой интерпретатор языка Go, из-за чего на жёстком диске становится на 400 Мб меньше места. Зато установка утилиты действительно проста, в отличии от других менеджеров пакетов для Arch User Repository.

sudo pacman -S --needed base-devel git
#Жмите Enter, Y, чтобы поставить все утилиты для компиляции программ
cd /tmp
git clone https://aur.archlinux.org/yay.git
cd yay
makepkg -si

Итак, после всех манипуляций у вас появится yay. Через него ПО инсталлируется так:

yay имя_пакета

Утилита выведет нумерованный список всех пакетов, где встречается искомое название. Нужно набрать номер на клавиатуре и нажать Enter, затем Y и снова Enter.

Ещё yay можно использовать вместо pacman для обновления системы и всех установленных через yay пакетов:

yay -Syu

Я считаю, что yay удобнее pacman’а, потому что доступ ко всем крупным репозиториям Арча включён в ней по умолчанию.

Настройка ZSH

Русская локаль

Ранее я рекомендовал сменить bash на zsh. Если вы это сделали, то ваша командная строка сейчас, скорее всего, работает странно при вводе кириллицы. Это нормально — zsh нужно настраивать, как и локаль системы. По умолчанию не поддерживается вывод и ввод расширенного диапазона символов, из-за чего нельзя ввести кириллицу, а псевдографика в Midnight Commander отображается не полностью.

Заодно русифицируем Arch Linux. Если вы предпочитаете английский, просто вместо ru_RU пишите en_US. Так или иначе операции с локалью проделать нужно, иначе консоль не сможет выводить спецсимволы.

Сначала нужно открыть список доступных локалей:

sudo nano /etc/locale.gen

Расскоментируйте или добавьте следующую строку:

ru_RU.UTF-8   UTF-8

Для сохранения текста в редакторе nano нажмите Ctrl+X, Y и Enter. Затем запустите locale-gen.

sudo locale-gen

Если вы работаете непосредственно с системой (она установлена на вашем ПК или вы видите экран через VNC), то нужно настроить консоль так, как написано в Wiki. В описываемом мной случае установка упрощённая — нужно всего лишь включить русскую локаль при подключении по SSH. Для этого достаточно отредактировать один файлик.

sudo nano /etc/locale.conf

В locale.conf, скорее всего, будет строка «LANG=en_US.utf8». Измените на:

LANG=ru_RU.utf8

Затем перезагрузите сервер:

sudo reboot

Oh My Zsh

Что касается настройки zsh, там всё не шибко сложнее. Уже есть готовый комплект настройки Oh My Zsh. Это разумный компромисс между функциональностью и скоростью работы. При желании вашу консоль можно превратить в нечто подобное тому, что отображено на скриншоте ниже.

Тема agnoster для Oh My Zsh

В общем, там есть где развернуться. Но я вместо украшательств расскажу, как добавить в zsh:

  • удобную историю введённых команд,
  • автозавершение строк,
  • нормальную поддержку русских букв.

Это минимум, нужный для экономии времени и избавления от головной боли.

Итак, сначала поставьте Oh My Zsh:

sh -c "$(curl -fsSL https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"

Затем загрузите пару плагинов и запустите редактирование файла конфигурации .zshrc того пользователя, под которым сидите:

git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting
git clone https://github.com/zsh-users/zsh-autosuggestions.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions
nano ~/.zshrc

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

Над строкой «export ZSH=…» добавьте строку, улучшающую отображение цветов в терминале:

export TERM="xterm-256color"

Далее в конце документа найдите блок «plugins=(..)» и допишите внутри названия плагинов, включающих полезные функции (можно просто разделять пробелом):

plugins=(
  git  
  zsh-autosuggestions
  zsh-syntax-highlighting
  archlinux 
)

После добавьте в самый конец файла две строчки, которые позволят клавишами Home/End перемещать курсор в начало/конец набранных команд:

bindkey "^[[1~" beginning-of-line
bindkey "^[[4~" end-of-line

Сохраните изменения и переподключитесь к консоли по SSH. Теперь:

  • Благодаря плагину archlinux вместо некоторых длинных команд можно обходиться короткими, список здесь. Работа с pacman и yay существенно облегчится.
  • Консоль хранит историю команд, это избавляет от необходимости помнить дословно, что же там требуется ввести. Можно набрать первые буквы, затем стрелками вверх и вниз перебирать ранее введённые команды.
  • Плагин автозавершения при наборе подсказывает в строке возможную команду. Если он угадал верно, можно нажать стрелку вправо для её ввода.
  • Благодаря zsh не требуется вручную писать полные названия файлов и программ. Кнопка TAB будет выводить список возможных вариантов ниже строки ввода, стрелками выбирается подходящий.

При желании можете пойти дальше и поставить Powerlevel9k, превращающую консоль в нечто космическое. Но делайте это без меня. Я просто скачаю патченый шрифт Cousine и включу стандартный вариант темы agnoster.

Консоль после Oh-My-Zsh. Обратите внимание: команды подсвечены зелёным.

Тюнинг сервера

Базовая защита SSH

Обязательно смените стандартный 22-й порт на любой другой. Для этого отредактируйте файл sshd_config.

sudo nano /etc/ssh/sshd_config

Требуется сделать три правки. Это минимум того, что нужно делать с удалённым входом на сервер по протоколу SSH.

  • Раскомментируйте строчку «#Port 22», убрав символ решётки, заодно укажите новый порт (трёх- или четырёхзначное число, кроме 443).
  • Замените значение «PermitRootLogin» «yes» на «no», чтобы запретить вход под пользователем root (напоминаю — под ним сидеть нельзя!).
  • В конце файла допишите строку «AllowUsers имя_пользователя», перечислив имена (логины) пользователей через пробел, которым разрешено подключаться к серверу. Включение этой опции автоматически запретит вход любым юзерам, кроме перечисленных, поэтому будьте внимательны, не опечатайтесь.

После сохранения конфига остаётся только перезагрузить службу sshd.service:

sudo systemctl restart sshd.service

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

Установка Sshguard

Смена порта для подключения избавит от вечно ломящихся ботов, но порт по-прежнему можно обнаружить. Чтобы усложнить подбор пароля, нужна программа типа Fail2ban или Sshguard. Первый с задачами справляется, но на низкопроизводительных серверах вызывает ощутимую нагрузку на ЦП, поэтому имеет смысл воспользоваться другой утилитой с узкоспециализированной задачей.

Установка Sshguard и создание правил для системного файрвола:

sudo pacman -S sshguard
sudo iptables -N sshguard
sudo iptables -A INPUT -m multiport -p tcp --destination-ports порты -j sshguard
sudo bash -c "iptables-save > /etc/iptables.rules"

Вместо «порты» нужно указать через запятую порты, которые требуется блокировать при попытках брутфорса: 80,443, а также указанный в sshd_config. 80 и 443 желательно указать для усложнения работы злоумышленникам, чтобы с того же IP нельзя было сканировать запущенные сайты.

В итоге файл конфигурации /etc/iptables.rules получится таким:

Разрешите автозапуск Sshguard и перезапустите, чтобы всё заработало как надо.

sudo systemctl enable sshguard.service
sudo systemctl restart sshguard.service

Подробнее о настройке написано в справке.

Включение swap-файла

На данном этапе оперативной памяти сервера, скорее всего, будет занято не более 60 Мб.

Использовано 56 Мб, свободно 412 Мб

Всё изменится в худшую сторону, когда будет запущен сайт. Чтобы программы не закрывались из-за нехватки памяти, нужно подключить swap-файл. Виртуальная память расширится, но, увы, скорости это не прибавит. Поэтому не шикуем и делает на 512 мегабайт.

sudo fallocate -l 512M /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

Чтобы не приходилось при каждой перезагрузке вводить команду swapon, отредактируйте файл /etc/fstab:

sudo nano /etc/fstab

Допишите в конец строчку:

/swapfile none swap defaults 0 0

Теперь софт в условиях нехватки памяти будет тормозить, но работать.

Появился swap-файл

Установка LEMP на Arch Linux

На Arch Linux, к сожалению, невозможно установить Webinoly. Точнее, теоретически это возможно, на практике придётся переписывать часть скриптов. Для лёгкой инсталляции доступна панель Webmin, но зачем нужен этот неповоротливый гигант с излишествами на скромной VPS’ке? Придётся ставить «ручками» так называемый LEMP-stack: веб-сервер nginx, базу данных MariaDB и интерпретатор скриптов PHP. Это не будет архисложной задачей, ибо ниже я указал все необходимые команды.

Установка Nginx и PHP в chroot

Первый компонент, который нужно поставить, в установке наиболее сложен. Напомню, моя цель — использовать самый свежий софт. Да, из репозитория extra можно поставить nginx-mainline со свежими наработками разработчиков веб-сервера, но это не то, что нужно в 2019 году для работы сайта.

А нужен nginx с модулем pagespeed. Он всячески оптимизирует код страниц, скрипты, стили и даже изображения, заодно кэшируя результат. К счастью, в AUR он присутствует.

#Сначала сам nginx
yay -S nginx-mainline-mod-pagespeed
#Затем PHP и модули к нему. Возможно, вам придётся что-то доустановить, если плагины этого потребуют.
yay -S php-fpm php-gd php-imagick

Будьте терпеливы, дождитесь окончания инсталляции.

Почему-то в дефолтной установке PHP отключены многие модули, которые, возможно, понадобятся в будущем, поэтому откройте /etc/php/php.ini:

sudo nano /etc/php/php.ini

Найдите где-то в первой трети файла секцию, обозначенную словами «Dynamic Extensions» и уберите знак «;» у следующих строчек:

extension=bcmath
extension=bz2
extension=curl
extension=exif
extension=gd
extension=iconv
extension=mysqli
zend_extension=opcache
extension=xmlrpc
extension=zip

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

Затем нужно настроить запуск вебсервера от имени пользователя с ограниченными правами (та самая установка nginx в chroot).

Chroot — распространённый способ изолировать приложения и даже целые операционные системы. Например, так можно на телефоне с Android запустить полноценную Ubuntu.
Запущенный под chroot’ом софт не может «достучаться» до файлов родительской операционной системы. Поэтому, если сайт всё-таки взломает какой-нибудь злоумышленник (уязвимости ведь никто не отменял!) , он увидит, что на сервере как-то маловато данных. Видны лишь те каталоги и файлы, что нужны для запуска Nginx и работы скриптов PHP, а также файлы сайта.
К сожалению, chroot не обеспечивает стопроцентной изоляции. Это всего лишь подмена корневого каталога сервера «/» на, например, «/srv/myuser/». То есть программы не изолируются полностью, межпроцессное взаимодействие никуда не исчезает. Программа, запущенная под пользователем root, может получить полный доступ к системе.

Скрипт для быстрой установки nginx в chroot, предлагаемый в справке, с изъяном: нет поддержки модулей nginx. Да и вообще он не устроил меня из-за того, что там совершенно ничего не говорится о HTTPS протоколе, а точнее, как сайтам в chroot назначать бесплатные Let’s Encrypt сертификаты. А про PHP вообще ни слова! Поэтому я сделал несколько скриптов, которые делают то, что нужно мне: устанавливают веб-сервер в ограниченное окружение, туда же докидывают программку для получения сертификата Let’s Encrypt, а также обновляют всё это дело.

  • instnginx — установка nginx в chroot окружение.
  • updnginx — обновление файлов nginx в chroot без замены конфигурационных файлов и не трогая каталог сайта.

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

Важно: проверьте наличие каталога «/srv/http». Теоретически он должен быть, т.к. в Wiki Arch Linux пользователь http везде упоминается. Иначе придётся создать самим с указанием домашнего каталога «/srv/http».

Этот каталог станет корневым для программ, которым нужно закрыть доступ в остальную систему. Но так как root везде имеет права суперпользователя, нужно обезопасить себя от ошибок. Вдруг случайно запустим программу под рутом, полагая, что раз она ограничена chroot, то не навредит основной системе?


Внимание: так как «/srv/http» принадлежит пользователю root, без sudo команды mkdir, touch, nano и прочие не сработают. Чтобы не вводить перед каждой командой «sudo», можно временно запустить шелл под суперпользователем командой «sudo zsh» или «sudo bash».

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

touch /tmp/instnginx
touch /tmp/updnginx
nano /tmp/instnginx
#Вставляем текст (см. ниже)
nano /tmp/updnginx
#Вставляем текст второго скрипта

Содержимое instnginx:

#!/bin/bash
export JAIL=/srv/http
groupadd http
useradd http -M -s /bin/nologin -g http
mkdir -p /srv/http
chown -R root:root $JAIL/
mkdir $JAIL/dev
mknod -m 0666 $JAIL/dev/null c 1 3
mknod -m 0666 $JAIL/dev/random c 1 8
mknod -m 0444 $JAIL/dev/urandom c 1 9
mknod -m 0666 $JAIL/dev/zero c 1 5
mkdir -p $JAIL/etc/nginx/logs
mkdir -p $JAIL/etc/nginx/certs
mkdir -p $JAIL/usr/{lib,bin}
mkdir -p $JAIL/usr/local/bin
cp -H /usr/bin/nginx $JAIL/usr/bin/
cp -H /usr/bin/php-fpm $JAIL/usr/bin/
mkdir -p $JAIL/usr/lib/nginx/modules
mkdir -p $JAIL/usr/share/nginx
mkdir -p $JAIL/var/{log,lib}/nginx
mkdir -p $JAIL/var/cache/{opcache,sessions,pagespeed}
mkdir -p $JAIL/www/cgi-bin
mkdir -p $JAIL/www/.acme/.well-known/acme-challenge
mkdir -p $JAIL/{run,tmp}
mkdir -p $JAIL/tmp/cache
mount -t tmpfs none $JAIL/run -o 'noexec,size=1M'
mount -t tmpfs none $JAIL/var/cache/sessions -o 'noexec,size=3M'
mkdir -p $JAIL/etc/php/conf.d
mkdir -p $JAIL/etc/php/php-fpm.d
mkdir -p $JAIL/usr/lib/php/modules
cp -rH /usr/lib/php $JAIL/usr/lib/
touch $JAIL/etc/shells
touch $JAIL/etc/nginx/pagespeed.conf
cd $JAIL; ln -s usr/lib lib
cd $JAIL; ln -s usr/lib lib64
cd $JAIL/usr; ln -s lib lib64
cp -rH /usr/share/nginx $JAIL/usr/share
cp -rH /usr/lib/nginx $JAIL/usr/lib
cp -rH /var/lib/nginx $JAIL/var/lib
cp $(ldd /usr/bin/nginx | grep /usr/lib | sed -sre 's/(.+)(\/usr\/lib\/\S+).+/\2/g') $JAIL/usr/lib
cp $(ldd /usr/lib/nginx/modules/ngx_pagespeed.so | grep /usr/lib | sed -sre 's/(.+)(\/usr\/lib\/\S+).+/\2/g') $JAIL/usr/lib
cp $(ldd /usr/bin/php-fpm | grep /usr/lib | sed -sre 's/(.+)(\/usr\/lib\/\S+).+/\2/g') $JAIL/usr/lib
cp $(ldd /usr/lib/php/modules/*.so | grep /usr/lib | sed -sre 's/(.+)(\/usr\/lib\/\S+).+/\2/g') $JAIL/usr/lib
cp -H /lib64/ld-linux-x86-64.so.2 $JAIL/lib
cp -H /lib/libnss_* $JAIL/lib
cp -rfvL /etc/{group,passwd,shadow,gshadow,services,localtime,nsswitch.conf,nscd.conf,protocols,hosts,ld.so.cache,ld.so.conf,resolv.conf,host.conf,nginx,php} $JAIL/etc
curl -o $JAIL/usr/local/bin/wp https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
chmod +x $JAIL/usr/local/bin/wp
openssl dhparam -out $JAIL/etc/nginx/certs/dhparam 4096
chown -R http:http $JAIL/www
chown -R http:http $JAIL/etc/nginx
chown -R http:http $JAIL/run
chown -R http:http $JAIL/var
chown -R http:http $JAIL/tmp/cache
find $JAIL/ -gid 0 -uid 0 -type d -print | xargs sudo chmod -rw
find $JAIL/ -gid 0 -uid 0 -type d -print | xargs sudo chmod +x
find $JAIL/etc -gid 0 -uid 0 -type f -print | xargs sudo chmod -x
find $JAIL/usr/bin -type f -print | xargs sudo chmod ug+rx
find $JAIL/ -group http -user http -print | xargs sudo chmod o-rwx
chmod +r /srv/http/etc/php/php-fpm.d
chmod +rw $JAIL/tmp
chmod +rw $JAIL/run
setcap 'cap_net_bind_service=+ep' $JAIL/usr/bin/nginx

Содержимое updnginx:

#!/bin/bash
export JAIL=/srv/http
systemctl stop nginx.service
systemctl stop php-fpm.service
cp /usr/bin/nginx $JAIL/usr/bin/
cp /usr/bin/php-fpm $JAIL/usr/bin/
cp -r /usr/lib/php $JAIL/usr/lib/
cp -r /usr/lib/nginx $JAIL/usr/lib/
cp -r /var/lib/nginx $JAIL/var/lib/nginx
cp $(ldd /usr/bin/nginx | grep /usr/lib | sed -sre 's/(.+)(\/usr\/lib\/\S+).+/\2/g') $JAIL/usr/lib
cp $(ldd /usr/lib/nginx/modules/ngx_pagespeed.so | grep /usr/lib | sed -sre 's/(.+)(\/usr\/lib\/\S+).+/\2/g') $JAIL/usr/lib
cp $(ldd /usr/bin/php-fpm | grep /usr/lib | sed -sre 's/(.+)(\/usr\/lib\/\S+).+/\2/g') $JAIL/usr/lib
cp $(ldd /usr/lib/php/modules/*.so | grep /usr/lib | sed -sre 's/(.+)(\/usr\/lib\/\S+).+/\2/g') $JAIL/usr/lib
cp /lib64/ld-linux-x86-64.so.2 $JAIL/lib
cp /usr/lib/libnss_* $JAIL/usr/lib
curl -o $JAIL/usr/local/bin/wp https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
chmod +x $JAIL/usr/local/bin/wp
chown -R http:http $JAIL/www
chown -R http:http $JAIL/etc/nginx
chown -R http:http $JAIL/run
chown -R http:http $JAIL/var
chown -R http:http $JAIL/tmp/cache
find $JAIL/ -gid 0 -uid 0 -type d -print | xargs sudo chmod -rw
find $JAIL/ -gid 0 -uid 0 -type d -print | xargs sudo chmod +x
find $JAIL/etc -gid 0 -uid 0 -type f -print | xargs sudo chmod -x
find $JAIL/usr/bin -type f -print | xargs sudo chmod ug+rx
find $JAIL/ -group http -user http -print | xargs sudo chmod o-rwx
chmod +r /srv/http/etc/php/php-fpm.d
chmod +rw $JAIL/tmp
chmod +rw $JAIL/run
systemctl start nginx.service
systemctl start php-fpm.service

Затем установите скрипт обновления, ведь он понадобится в будущем:

sudo cp /tmp/updnginx /usr/local/bin
sudo chown root:root /usr/local/bin/updnginx
sudo chmod +x-w /usr/local/bin/updnginx

Теперь можно запустить instnginx:

sudo bash /tmp/instnginx

Внимание! Из-за команды «openssl dhparam -out $JAIL/etc/nginx/certs/dhparam 4096» установка может затянуться минут на 20-30. Проявите терпение.

В будущем обновление софта в chroot надо делать так:
1. Обновите софт в системе.
2. Копируйте новые версии файлов командой «sudo updnginx«.
Есть и другой способ — сделать ссылки

Когда скрипт отработает, поправьте fstab:

sudo nano /etc/fstab

Добавьте в /etc/fstab две строки, чтобы создать в ОЗУ парочку небольших виртуальных дисков:

tmpfs /srv/http/run tmpfs rw,noexec,relatime,size=1024k 0 0
tmpfs /srv/http/var/cache/sessions tmpfs rw,noexec,relatime,size=3096k 0 0

В Wiki упоминается перенос каталога /srv/http/tmp в оперативную память, чтобы злоумышленник не мог забить жёсткий диск временными файлами. К сожалению, выделить даже 100 Мб на слабых VPS, где каждый мегабайт RAM на счету, невозможно. Да и, как показала практика, ста мегабайт бывает недостаточно. Так что этот аспект в настройке я пропущу, ограничившись выделением мегабайта памяти для srv/http/run и трёх мегабайт под хранение сессий PHP (ускоряет открытие сайта). Просто сделайте мысленную пометку: если места на жёстком диске становится мало, нужно глянуть каталог /srv/http/tmp.

Затем нужно сделать так, чтобы nginx и php-fpm запускались в chroot.

#Копируем сервис nginx в системный каталог, чтобы изменения не стёрлись при обновлении:
sudo cp /usr/lib/systemd/system/nginx.service /etc/systemd/system/nginx.service
#Редактируем сервис nginx:
sudo nano /etc/systemd/system/nginx.service
#затем копируем сервис php-fpm:
sudo cp /usr/lib/systemd/system/php-fpm.service /etc/systemd/system/php-fpm.service
#Редактируем сервис php-fpm:
sudo nano /etc/systemd/system/php-fpm.service

Содержимое nginx.service должно быть таким:

[Unit]
Description=A high performance web server and a reverse proxy server
After=network.target network-online.target nss-lookup.target

[Service]
Type=simple
PIDFile=/srv/http/run/nginx.pid
ReadOnlyDirectories=/proc /etc
InaccessibleDirectories=/sys
PrivateTmp=true
PrivateDevices=true
ProtectHome=true
ProtectSystem=full
SyslogLevel=notice
User=root
Group=root
ExecStartPre=setcap 'cap_net_bind_service=+ep' /srv/http/usr/bin/nginx
ExecStartPre=/usr/bin/chroot --userspec=http:http /srv/http /usr/bin/nginx -t -q -g 'pid /run/nginx.pid; daemon on; master_process on;'
ExecStart=/usr/bin/chroot --userspec=http:http /srv/http /usr/bin/nginx -g 'pid /run/nginx.pid; daemon on; master_process on;'
ExecStartPost=/bin/sleep 0.1
ExecReload=/usr/bin/chroot --userspec=http:http /srv/http /usr/bin/nginx -g 'pid /run/nginx.pid; daemon on; master_process on;' -s reload
ExecStop=/usr/bin/chroot --userspec=http:http /srv/http /usr/bin/nginx -g 'pid /run/nginx.pid;' -s quit
KillSignal=SIGQUIT
KillMode=mixed

[Install]
WantedBy=multi-user.target

Содержимое php-fpm.service:

[Unit]
Description=The PHP FastCGI Process Manager
After=network.target

[Service]
Type=simple
PIDFile=/srv/http/run/php-fpm.pid
ReadOnlyDirectories=/proc
PrivateTmp=true
PrivateDevices=true
ProtectHome=true
ProtectSystem=full
SyslogLevel=err
User=root
Group=root
TimeoutSec=300
ExecStart=/usr/bin/chroot --userspec=http:http /srv/http /usr/bin/php-fpm --fpm-config /etc/php/php-fpm.conf
ExecReload=/bin/kill -USR2 $MAINPID

[Install]
WantedBy=multi-user.target

Отредактируйте /srv/etc/php/php-fpm.conf (команда «cp /dev/null имя файла» очищает содержимое файла):

sudo sudo cp /dev/null /srv/http/etc/php/php-fpm.conf
sudo nano /srv/http/etc/php/php-fpm.conf

Для краткости выкинул из конфига все комментарии. Маловероятно, что вы будете в дальнейшем что-то менять в этом файле, потому что все важные настройки в файле www.conf. Весь текст php-fpm.conf уместился в пять строчек:

[global]
pid = /run/php-fpm.pid
error_log = /var/log/php-fpm.log
log_level = warning
include=/etc/php/php-fpm.d/*.conf

Далее требуется отредактировать www.conf по адресу «/srv/http/etc/php/php-fpm.d/www.conf». Если не привести его в нормальный вид, php-fpm при запуске будет ругаться ошибкой «ERROR: Unable to globalize ‘/etc/php/php-fpm.d/*.conf’ (ret=2) from /etc/php/php-fpm.conf at line 143.»

sudo sudo cp /dev/null /srv/http/etc/php/php-fpm.d/www.conf
sudo nano /srv/http/etc/php/php-fpm.d/www.conf

Очистите содержимое и вставьте (вместо mysite можете указать любое удобное название):

[mysite]
listen = /run/php-fpm.sock
listen.owner = http
listen.group = http
listen.allowed_clients = 127.0.0.1
pm = dynamic
pm.max_children = 4
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
pm.max_requests = 200
request_terminate_timeout = 600s
security.limit_extensions = .php .php3 .php4 .php5 .php7
php_admin_value[mysql.default_socket] = /run/mysqld/mysqld.sock
;Две строчки ниже можно удалить - они для выявления медленных скриптов сайта
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm-slowlog.log

Отредактируйте ещё и /srv/http/etc/php/php.ini. Да, я в курсе, что выше писал про редактирование этого же файла в основной системе. Смысл доработки в php.ini в chroot в том, чтобы включить оптимизации, которые вне изоляции не нужны.

sudo nano /srv/http/etc/php/php.ini

Найдите следующие строки и поправьте. При необходимости раскомментируйте, убрав «;» в начале строки.

open_basedir = /www/cgi-bin
disable_functions=exec,passthru,shell_exec,system,proc_open,popen,curl_multi_exec,parse_ini_file,show_source
zend.enable_gc = On
max_execution_time = 60
memory_limit = 64M
upload_max_filesize = 8M
session.save_handler = files
session.save_path = "/var/cache/sessions"
session.use_strict_mode = 1
session.gc_probability = 1
opcache.enable=1
opcache.memory_consumption=12
opcache.validate_timestamps=1
opcache.file_cache=/var/cache/opcache

Думаете, на этом всё? Почти! Теперь нужно включить службы и запустить. Желательно перед этим перезагрузить сервер, чтобы подключились RAM-диски.

Небольшое отступление: на этом этапе я застрял, потому что nginx сыпал ошибками вида «Unable to create SHM segment [1]globalstatistics, mmap failed with errno=19.», «Unable to create SHM segment [1]/tmp/cache flush /named_locks, mmap failed with errno=19.», «Problem during shared memory setup; statistics functionality unavailable.», «Unable to create memory segment for locks.», «Unable to find SHM segment [1]globalstatistics to attach to.» Это было странно и непонятно. Про запуск nginx с модулем pagespeed в режиме chroot в интернете написано совсем ничего. Я уж было подумал, что ошибки возникают из-за слишком ограниченных прав, что ngx_pagespeed.so не работает в chroot, что придётся переписывать всю статью и рассказать про SELinux, AppArmor либо systemd-nspawn или даже Docker, но потом мой усталый мозг вспомнил про волшебную утилиту strace, благодаря которой удалось отследить все источники ошибок и скопировать отсутствующие файлы в /srv/http/. Так что, если у вас в Linux программа падает с ошибкой, используйте команду «strace исполняемый_файл». Выводимая утилитой информация весьма… информативна.

sudo systemctl daemon-reload
sudo systemctl enable nginx.service
sudo systemctl enable php-fpm.service 
sudo systemctl start nginx.service
sudo systemctl start php-fpm.service 

Если всё сделано правильно (а я больше десятка раз прогнал все шаги, чтобы убедиться, что мой метод работает), при открытии зарегистрированного и настроенного на цифровой адрес сервера домена появится приветственная страница Nginx:

Вебсервер уже работает

Полдела сделано, но… Во-первых, всё будет работать только по незащищённому протоколу HTTP, во-вторых, без сервера MySQL-совместимой базы данных нельзя запустить сайт на WordPress.

Настройка Nginx на получение сертификата

Прежде чем включать SSL на сайте, нужно правильно настроить веб-сервер. Помните, я ставил nginx с модулем pagespeed? Вот их и нужно «подружить», а заодно включить возможность получения сертификата.

Внимание! Подразумевается, что домен уже куплен, его A запись содержит IP сервера. Иначе переходите к главе про установку MySQL в chroot, а nginx и получение SSL-сертификата настроите потом.

Мой конфиг однозначно не идеален, но работает. За основу взял тот, что генерирует панель управления сайтами Webinoly, сведя к меньшему количеству файлов.

sudo sudo cp /dev/null /srv/http/etc/nginx/nginx.conf
sudo nano /srv/http/etc/nginx/nginx.conf

Сначала вставляем конфиг для открытия сайта только по HTTP и только для получения первого сертификата SSL от Let’s Encrypt. Если он у вас уже есть, то ничего страшного — за повторный запрос ключа выдающие ключи сервера не банят, если это происходит не часто. Так как вы уже могли на этом этапе загрузить какие-то файлы сайта, для предотвращения их кражи в конфиге полностью заблокирован доступ (сайт будет отдавать 403 ошибку).

worker_processes auto;
worker_rlimit_nofile 397395;

events {

  worker_connections 8192;
  multi_accept on;
}

http {

  sendfile on;
  tcp_nopush on;
  tcp_nodelay on;
  server_tokens off;
  reset_timedout_connection on;
  send_timeout 35;
  keepalive_timeout 35;
  client_max_body_size 4M;
  client_header_buffer_size 1k;
  client_body_buffer_size 32k;
  large_client_header_buffers 4 8k;
  client_body_timeout 35;
  client_header_timeout 35;
  open_file_cache_valid 200s;
  open_file_cache_min_uses 3;
  open_file_cache max=133124 inactive=20s;
  include /etc/nginx/mime.types;
  default_type application/octet-stream;
  access_log off;
  error_log /var/log/nginx/nginx-error.log;
  log_format we_log '$remote_addr $upstream_response_time $upstream_cache_status [$time_local] '
  '$http_host "$request" $status $body_bytes_sent '
  '"$http_referer" "$http_user_agent"';

  upstream php {

    server unix:/run/php-fpm.sock;
  }

  server {

    listen 80;
    listen [::]:80;

    server_name ДОМЕН_САЙТА;
    root /www/cgi-bin;
    index index.html index.htm;


    location /.well-known/ {

      allow all;
      alias /www/.acme/.well-known/;
      try_files $uri $uri/ =404;
    }

    location / {

      deny all;

    }
  }
}

Чтобы вы не писали в комментах возмущения вида «можно было сделать иначе!», заранее предупреждаю: да, я знаю, что можно иначе. Зато мой вариант предельно прост: не нужно заморачиваться с получением самоподписанного сертификата, который всё равно удалится после получения доверенного. Сайт с этим конфигом на момент настройки будет недоступен, но это допущение в угоду простоте. Как только будет время продумать решение с одним конфигом, я перепишу эту главу.

Ниже будет другой конфиг, нацеленный на полноценную работу сайта на WordPress, заточенный под работу PageSpeed. Алгоритм таков: сначала ставим конфиг выше, перезапускаем nginx, получаем сертификат, меняем конфиг на новый с PageSpeed, перезапускаем nginx снова. Сайт начнёт работать по HTTPS с автоматической оптимизацией изображений.

Скрипт acme.sh для работы SSL

Скрипт для получения сертификата acme.sh будет работать вне chroot, его изоляция будет излишней тратой времени. Слишком много бинарных файлов и связанных библиотек придётся переносить в «/srv/http», это снизит безопасность. Пускай лучше периодически запускается под root’ом.

#Ставим cronie и acme.sh для автоматизации обновления сертификата SSL
sudo -u root pacman -S cronie acme.sh

В домашнем каталоге пользователя, под которым у вас открыт терминал, появится скрытый каталог «.acme.sh». Не удаляйте его, иначе через три месяца, когда истечёт сертификат, сайт превратится в тыкву.

Сертификат получается вот такой длинной командой (если у сайта есть зеркало на www, можно добавить параметр «—domain www.ДОМЕН):

#На всякий случай перезапускаем вебсервер
sudo systemctl restart nginx.service
#Получаем сертификат
sudo -u root /root/.acme.sh/acme.sh --issue --domain ДОМЕН --webroot /srv/http/www/.acme --keylength 4096 --cert-file /srv/http/etc/nginx/certs/cert --key-file /srv/http/etc/nginx/certs/key --fullchain-file /srv/http/etc/nginx/certs/fullchain --reloadcmd "chown -R http:http /srv/http/etc/nginx/certs; systemctl reload nginx.service" --log /tmp/acme.log

На всякий случай знайте: чтобы обновить вручную, замените «—issue» на «—renew «.

При появлении ошибок смотрите лог в /tmp/acme.log. Если ругается на отсутствие файла acme.sh, читайте дальше — нужно сначала переместить скрипт в /root и запустить получение сертификата Let’S Encrypt повторно

Важный момент: сертификат-то получили, но его нужно периодически обновлять! По умолчанию скрипт acme.sh записывает требуемую команду в cron, но в моём случае этого не произошло. Проверить это можно командой «sudo crontab -l»:

Нет заданий в cron

Насколько я понял, в Arch Linux скрипт инсталлируется некорректно. Он должен находиться в домашнем каталоге пользователя, вместо этого попадает в «/usr/share/acme.sh». Поэтому необходимо скопировать файлы куда следует (лучше в /root) и пересоздать задачу в cron:

sudo mkdir /root/.acme.sh
sudo cp -R /usr/share/acme.sh/* /root/.acme.sh/
#Здесь, если в прошлый раз не получилось, снова запускайте получение сертификата.
sudo acme.sh --installcronjob --home "/root/.acme.sh"
sudo cat /var/spool/cron/root
Задача для пользователя root успешно добавлена

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

Настройка Nginx для работы WordPress с модулем PageSpeed

Теперь можно спокойно переключиться на файл конфигурации, при котором:

  • сайт будет открываться по HTTPS;
  • будет работать оптимизация с помощью модуля PageSpeed.

Новое содержимое /srv/http/etc/nginx/nginx.conf (обратите внимание на фразу «ТУТ_ВАШ_ДОМЕН»!):

worker_processes auto;
worker_rlimit_nofile 397395;
load_module /usr/lib/nginx/modules/ngx_pagespeed.so;

events {

  worker_connections 8192;
  multi_accept on;
}

http {

  sendfile on;
  tcp_nopush on;
  tcp_nodelay on;
  server_tokens off;
  reset_timedout_connection on;
  send_timeout 35;
  keepalive_timeout 35;
  client_max_body_size 4M;
  client_header_buffer_size 1k;
  client_body_buffer_size 32k;
  large_client_header_buffers 4 8k;
  client_body_timeout 35;
  client_header_timeout 35;
  open_file_cache_valid 200s;
  open_file_cache_min_uses 3;
  open_file_cache max=133124 inactive=20s;
  types_hash_max_size 2048;
  limit_req_status 403;
  limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
  limit_req_zone $binary_remote_addr zone=wp:10m rate=3r/s;
  include /etc/nginx/mime.types;
  add_header X-Cache-Status $upstream_cache_status;
  add_header X-Frame-Options SAMEORIGIN;
  add_header X-Content-Type-Options nosniff;
  add_header X-XSS-Protection "1; mode=block";
  default_type application/octet-stream;
  ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
  ssl_session_timeout 10m;
  ssl_session_cache shared:SSL:20m;
  ssl_dhparam /etc/nginx/certs/dhparam;
  ssl_ecdh_curve prime256v1:secp384r1:secp521r1;
  ssl_prefer_server_ciphers on;
  ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT';
  gzip on;
  gzip_disable "msie6";
  gzip_vary on;
  gzip_proxied any;
  gzip_comp_level 6;
  gzip_buffers 16 8k;
  gzip_http_version 1.1;
  gzip_types application/atom+xml application/javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/svg+xml image/x-icon text/css text/plain text/x-component text/xml text/javascript;
  access_log off;
  error_log /var/log/nginx/nginx-error.log;
  log_format we_log '$remote_addr $upstream_response_time $upstream_cache_status [$time_local] '
  '$http_host "$request" $status $body_bytes_sent '
  '"$http_referer" "$http_user_agent"';

  include pagespeed.conf;

  upstream php {

    server unix:/run/php-fpm.sock;
  }

  server {

    listen 80;
    listen [::]:80;

    server_name ТУТ_ВАШ_ДОМЕН;

    pagespeed off;

    return 301 https://$host$request_uri;
  }

  server {

    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name ТУТ_ВАШ_ДОМЕН;

    ssl_certificate /etc/nginx/certs/fullchain;
    ssl_certificate_key /etc/nginx/certs/key;
    ssl_trusted_certificate /etc/nginx/certs/cert;
    ssl_stapling on;
    ssl_stapling_verify on;

    pagespeed AddResourceHeader "Access-Control-Allow-Origin" "https://ТУТ_ВАШ_ДОМЕН";
    pagespeed SslCertDirectory "/etc/ssl/certs";
    pagespeed SslCertFile "/etc/ssl/certs/DST_Root_CA_X3.pem";
    pagespeed CacheFragment mysite;
    pagespeed Domain "https://ТУТ_ВАШ_ДОМЕН";
    pagespeed RespectXForwardedProto on;
    pagespeed LoadFromFileMatch "^https?://ТУТ_ВАШ_ДОМЕН" "/www/cgi-bin";
    pagespeed LoadFromFileRuleMatch disallow \.*;
    pagespeed LoadFromFileRuleMatch allow \.(js|css|png|jpg|jpeg|gif)(\?ver=[0-9.]+)?$ps_dollar;

    location /pagespeed_admin {

      allow 127.0.0.1; deny all;
    }
    location /pagespeed_console {

      allow 127.0.0.1; deny all;
    }
    location /ngx_pagespeed_message {

      allow 127.0.0.1; deny all;
    }
    location /pagespeed_global_admin {

      allow 127.0.0.1; deny all;
    }
    location /ngx_pagespeed_statistics {

      allow 127.0.0.1; deny all;
    }
    location /ngx_pagespeed_global_statistics {

      allow 127.0.0.1; deny all;
    }


    root /www/cgi-bin;

    index index.php index.html index.htm;

    location /.well-known/ {

      pagespeed off;
      allow all;
      alias /www/.acme/.well-known/;
      try_files $uri $uri/ =404;
    }

    set $nopgsp 0;

    location /wp-admin {

      set $nopgsp 1;
      pagespeed off;
      location ~ /wp-admin/admin-ajax.php$ {

        include fastcgi_params;
        fastcgi_pass php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
      }
      location ~* /wp-admin/.*\.php$ {

        limit_req zone=wp burst=6 nodelay;
        include fastcgi_params;
        fastcgi_pass php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
      }
    }
    location = /wp-login.php {

      set $nopgsp 1;
      pagespeed off;
      limit_req zone=one burst=1 nodelay;
      include fastcgi_params;
      fastcgi_pass php;
      fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
    location /wp-content/uploads/ {

      location ~ \.php$ {

        deny all;
      }
    }
    location ~ ([^/]*)sitemap(.*)\.x(m|s)l$ {

      pagespeed off;
      rewrite ^(.*)/sitemap\.xml$ $1/sitemap_index.xml permanent;
      rewrite ^.*/([a-z]+)?-?sitemap\.xsl$ /index.php?xsl=$1 last;
      # Rules for yoast sitemap with wp|wpsubdir|wpsubdomain
      rewrite ^.*/sitemap_index\.xml$ /index.php?sitemap=1 last;
      rewrite ^.*/([^/]+?)-sitemap([0-9]+)?\.xml$ /index.php?sitemap=$1&sitemap_n=$2 last;
      # Following lines are optional. Needed for Yoast Premium.
      rewrite ^.*/news_sitemap\.xml$ /index.php?sitemap=wpseo_news last;
      rewrite ^.*/locations\.kml$ /index.php?sitemap=wpseo_local_kml last;
      rewrite ^.*/geo_sitemap\.xml$ /index.php?sitemap=wpseo_local last;
      rewrite ^.*/video-sitemap\.xsl$ /index.php?xsl=video last;
      access_log off;
    }

    location = /favicon.ico {

      access_log off;
      log_not_found off;
      expires max;
    }
    location = /robots.txt {

      try_files $uri $uri/ /index.php?$args;
      log_not_found off;
    }

    # Cache static files
    location ~* \.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|css|rss|atom|js|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf|swf)$ {

      add_header X-Cache-Status $upstream_cache_status;
      add_header X-Frame-Options SAMEORIGIN;
      add_header X-Content-Type-Options nosniff;
      add_header X-XSS-Protection "1; mode=block";
      add_header Strict-Transport-Security "max-age=31536000";
      add_header "Access-Control-Allow-Origin" "*";
      log_not_found off;
      if ($nopgsp = '0') {

        expires max;
      }
    }

    location ~ /\.well-known {

      allow all;
    }
    location ~ /\. {

      deny all;
      log_not_found off;
    }

    location ~* ^.+\.(bak|log|old|orig|original|php#|php~|php_bak|save|swo|swp|sql)$ {
      deny all;
      log_not_found off;
    }

    if ($uri ~* "^.+(wp-config|readme|license|example)\.(txt|html)$") {

      return 404;
    }

    location ~ \.(conf)$ {

      access_log off;
      return 404;
    }

    location / {

      if ($nopgsp) {

        pagespeed off;
      }
      try_files $uri $uri/ /index.php?$args;
    }
    location ~ \.php$ {

      include fastcgi_params;
      if ($nopgsp) {

        pagespeed off;
      }

      if ( -f $request_filename ) {

        fastcgi_pass php;
      }

      fastcgi_index index.php;
      fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

    }

    add_header X-Cache-Status $upstream_cache_status;
    add_header X-Frame-Options SAMEORIGIN;
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";
    add_header Cache-Control "public, no-cache";
    add_header Referrer-Policy "unsafe-url";
    add_header Strict-Transport-Security "max-age=31536000";
  }

}

Содержимое /srv/http/etc/nginx/pagespeed.conf, выстраданное мною путём многочасового тестирования параметров настроек модуля PageSpeed через тест PageSpeed Insights:

pagespeed on;
#pagespeed ForceCaching on;

pagespeed AdminPath                            /pagespeed_admin;
pagespeed ConsolePath                        /pagespeed_console;
pagespeed MessagesPath                   /ngx_pagespeed_message;
pagespeed GlobalAdminPath               /pagespeed_global_admin;
pagespeed StatisticsPath              /ngx_pagespeed_statistics;
pagespeed GlobalStatisticsPath /ngx_pagespeed_global_statistics;

# save on bandwidth and load resources directly from file system
resolver  8.8.8.8;
pagespeed UseNativeFetcher                                  off;
pagespeed NoTransformOptimizedImages                         on;
pagespeed InPlaceResourceOptimization                        on;
pagespeed ProcessScriptVariables                            all;
pagespeed PreserveUrlRelativity                              on;
pagespeed EnableCachePurge                                   on;
pagespeed PurgeMethod                                     PURGE;
pagespeed Statistics                                         on;
pagespeed StatisticsLogging                                  on;
#pagespeed RateLimitBackgroundFetches                        on;
pagespeed RespectVary                                        on;
#pagespeed CriticalImagesBeaconEnabled                     true;

pagespeed FetchWithGzip                                     off;
pagespeed InPlaceWaitForOptimized                           off;
pagespeed InPlaceRewriteDeadlineMs                       100000;
pagespeed CacheFlushPollIntervalSec                           0;
pagespeed HttpCacheCompressionLevel                           6;

pagespeed LogDir                                     "/var/log";

pagespeed FileCachePath                 "/var/cache/pagespeed/";
pagespeed CreateSharedMemoryMetadataCache "/var/cache/pagespeed/" 50000;
pagespeed FileCacheSizeKb                             	 400000;
pagespeed FileCacheCleanIntervalMs                      3600000;
pagespeed FileCacheInodeLimit                             50000;

pagespeed LRUCacheKbPerProcess                             8192;
pagespeed LRUCacheByteLimit                               16384;
pagespeed DefaultSharedMemoryCacheKB                      50000;

pagespeed MessageBufferSize                              200000;
pagespeed StatisticsLoggingIntervalMs                     60000;
pagespeed StatisticsLoggingMaxFileSizeKb                    512;

pagespeed MaxSegmentLength                                  250;
pagespeed MaxCombinedJsBytes                             376480;
pagespeed MaxCombinedCssBytes                            376480;
pagespeed CssInlineMaxBytes                               10000;
pagespeed CssFlattenMaxBytes                             376480;

# additional settings 
pagespeed FetchHttps enable,allow_unknown_certificate_authority;

pagespeed FetcherTimeoutMs                                 4000;
pagespeed ImageMaxRewritesAtOnce                              4;
pagespeed RewriteDeadlinePerFlushMs                        1500;

pagespeed NumRewriteThreads                                   2;
pagespeed NumExpensiveRewriteThreads                          8;
pagespeed ImplicitCacheTtlMs                     30274560000000;
pagespeed LoadFromFileCacheTtlMs                 30274560000000;
pagespeed FileCacheCleanIntervalMs                    345600000;


# optimization filters
pagespeed RewriteLevel CoreFilters;
pagespeed EnableFilters extend_cache;
pagespeed EnableFilters add_instrumentation;
pagespeed EnableFilters in_place_optimize_for_browser;

# code related optimization
pagespeed EnableFilters remove_comments;
pagespeed EnableFilters collapse_whitespace;
pagespeed EnableFilters trim_urls;

# DNS related optimization
pagespeed EnableFilters insert_dns_prefetch;

# image related optimization
pagespeed EnableFilters resize_images;
pagespeed EnableFilters rewrite_images;
pagespeed EnableFilters lazyload_images;
pagespeed LazyloadImagesAfterOnload off;
pagespeed EnableFilters jpeg_subsampling;
pagespeed EnableFilters responsive_images;
pagespeed EnableFilters convert_gif_to_png;
pagespeed EnableFilters strip_image_meta_data;
pagespeed EnableFilters strip_image_color_profile;
#pagespeed EnableFilters convert_jpeg_to_progressive;
pagespeed EnableFilters dedup_inlined_images;
pagespeed EnableFilters inline_preview_images;
pagespeed EnableFilters resize_mobile_images;
pagespeed EnableFilters inline_images;
pagespeed EnableFilters recompress_jpeg;
pagespeed EnableFilters recompress_png;
pagespeed EnableFilters recompress_webp;
pagespeed EnableFilters resize_rendered_image_dimensions;
pagespeed EnableFilters convert_jpeg_to_webp;
pagespeed EnableFilters convert_to_webp_lossless;
pagespeed EnableFilters convert_to_webp_animated;
#pagespeed EnableFilters sprite_images;
pagespeed WebpRecompressionQuality 75;
pagespeed WebpRecompressionQualityForSmallScreens 70;

# javascript related optimization
pagespeed UseExperimentalJsMinifier on;
pagespeed EnableFilters rewrite_javascript;
pagespeed EnableFilters inline_javascript;
#pagespeed EnableFilters combine_javascript;
#pagespeed EnableFilters canonicalize_javascript_libraries;
#pagespeed EnableFilters defer_javascript;

# css style sheets related optimization
pagespeed EnableFilters inline_css;
pagespeed EnableFilters rewrite_css;
#pagespeed EnableFilters combine_css;
#pagespeed EnableFilters outline_css;
pagespeed EnableFilters flatten_css_imports;
#pagespeed EnableFilters prioritize_critical_css;
pagespeed EnableFilters inline_import_to_link;
#pagespeed EnableFilters inline_google_font_css;
#pagespeed GoogleFontCssInlineMaxBytes -1;
pagespeed EnableFilters move_css_above_scripts;
#pagespeed EnableFilters move_css_to_head;
pagespeed EnableFilters fallback_rewrite_css_urls;
#pagespeed EnableFilters rewrite_style_attributes_with_url;

# redis storage backend
#pagespeed RedisServer "127.0.0.1:6379";
#pagespeed RedisTimeoutUs 1000;

Первоисточник этого конфига, который кто-то столь аккуратно выровнял пробелами, я найти не смог. На авторство не претендую, я лишь сделал некоторые правки. Часть строчек закомментирована, потому что эти настройки могут оказаться полезными — загуглите их назначение.

Перезапустите nginx, чтобы новый конфиг заработал:

sudo systemctl restart nginx.service

Установка MariaDB в chroot

MySQL Server, Percona и MariaDB, — три наиболее популярных системы управления реляционными базами данных. Percona и MariaDB — форки оригинальной MySQL Server, появившиеся из-за медленного развития оной. Сейчас такого разрыва в нововведениях, как лет восемь назад, нет, поэтому для WordPress можно ставить любую СУБД из перечисленных.
Долгое время я предпочитал Percona Server из-за высокой производительности, но позже перешёл на MariaDB. Это субъективный опыт, имеющий отношение к плагинам для MariaDB, поэтому не могу утверждать, что та или иная СУБД — лучшая. Просто, раз теперь работаю с MDB, то и ставить буду её.

Сервер баз данных — уязвимое место. Нужно вынести MariaDB в другое chroot окружение и запустить под другим пользователем. В тот же каталог, где «живут» nginx и php-fpm, пускать софт для БД нельзя, поэтому сделаем отдельный chroot.

По-хорошему nginx также нужно отделить от php-fpm, но тогда сложность настройки сервера возрастёт в разы, ведь обе софтины обращаются к одному и тому же каталогу с сайтом.

А нужно ли ставить MySQL в chroot? В интернете на этот вопрос чаще отвечают «нет», чем «да». Вот объяснение моего знакомого Алексея, публикую с его разрешения:
«Обновлять такую конфигурацию сложнее: если с новой версией прилетят обновления системных таблиц, они могут не пропатчить базу и возможны проблемы вплоть до незапуска. Скопировать бинарные файлы в chroot легко и просто, но пропатчить таблицы вручную сложно. И засовывание MySQL в chroot не изолирует процессы полностью.»
Тем не менее, один из моих сайтов запущен как раз в такой конфигурации и за год его работы ни разу не столкнулся с описанной проблемой. Поэтому всё же опишу способ установки MariaDB в chroot, вдруг кому-то нужен именно такой вариант. Например, в учебных целях: если студент сможет «выбраться» из chroot через базу данных, это будет показателем находчивости.

Наиболее свежая на данный момент версия СУБД — mariadb-10.3, но, увы, на момент написания этой статьи она находится в репозитории extra, поэтому я ставил через yay:

yay -S mariadb

Всё так же, как и для nginx:

touch /tmp/instmariadb
touch /tmp/updmariadb
nano /tmp/instmariadb
#Вставляем текст скрипта инсталляции
nano /tmp/updmariadb
#Вставляем текст скрипта обновления

Содержимое instmariadb:

#!/bin/bash
export JAIL=/srv/httpdb
mkdir $JAIL
chown -R root:root $JAIL/
groupadd httpdb
useradd httpdb -M -s /bin/nologin -g httpdb
mkdir -p $JAIL/dev
mknod -m 0666 $JAIL/dev/null c 1 3
mknod -m 0666 $JAIL/dev/random c 1 8
mknod -m 0444 $JAIL/dev/urandom c 1 9
mknod -m 0666 $JAIL/dev/zero c 1 5
mkdir -p $JAIL/etc/
mkdir -p $JAIL/usr/{lib,bin}
cp -H /usr/bin/mysql* $JAIL/usr/bin/
cp -H /usr/bin/expr $JAIL/usr/bin/
cp -H /usr/bin/dirname $JAIL/usr/bin/
cp -H /usr/bin/nohup $JAIL/usr/bin/
mkdir -p $JAIL/usr/lib/mysql/plugin
mkdir -p $JAIL/usr/share/mysql
mkdir -p $JAIL/var/{log,lib}/mysql
mkdir -p $JAIL/{run,tmp}
mkdir -p $JAIL/tmp/cache
mount -t tmpfs none $JAIL/run -o 'noexec,size=1M'
mkdir -p $JAIL/run/mysqld
mkdir -p $JAIL/etc/mysql
mkdir -p $JAIL/usr/lib/mysql/plugin
cp -rH /usr/lib/php $JAIL/usr/lib/
touch $JAIL/run/mysqld/mysqld.pid
touch $JAIL/etc/shells
cd $JAIL; ln -s usr/lib lib
cd $JAIL; ln -s usr/lib lib64
cd $JAIL/usr; ln -s lib lib64
cp -rH /usr/share/mysql/* $JAIL/usr/share/mysql
cp -rH /usr/lib/mysql $JAIL/usr/lib
cp -rH /var/lib/mysql $JAIL/var/lib
cp $(ldd /usr/bin/mysql* | grep /usr/lib | sed -sre 's/(.+)(\/usr\/lib\/\S+).+/\2/g') $JAIL/usr/lib
cp $(ldd /usr/bin/expr | grep /usr/lib | sed -sre 's/(.+)(\/usr\/lib\/\S+).+/\2/g') $JAIL/usr/lib
cp $(ldd /usr/bin/dirname | grep /usr/lib | sed -sre 's/(.+)(\/usr\/lib\/\S+).+/\2/g') $JAIL/usr/lib
cp $(ldd /usr/bin/nohup | grep /usr/lib | sed -sre 's/(.+)(\/usr\/lib\/\S+).+/\2/g') $JAIL/usr/lib
cp $(ldd /usr/lib/mysql/plugin/*.so | grep /usr/lib | sed -sre 's/(.+)(\/usr\/lib\/\S+).+/\2/g') $JAIL/usr/lib
cp $(ldd /usr/lib/libjemalloc.so | grep /usr/lib | sed -sre 's/(.+)(\/usr\/lib\/\S+).+/\2/g') $JAIL/usr/lib
cp -H /lib64/ld-linux-x86-64.so* $JAIL/lib
cp -H /usr/lib/libjemalloc.so* $JAIL/lib
cp -H /lib/libnss_* $JAIL/lib
cp -rfvL /etc/{group,passwd,shadow,gshadow,services,localtime,nsswitch.conf,nscd.conf,protocols,hosts,ld.so.cache,ld.so.conf,resolv.conf,host.conf,mysql} $JAIL/etc
sed -i -e '/nobody/b' -e '/httpdb/b' -e d /srv/httpdb/etc/group
sed -i -e '/nobody/b' -e '/httpdb/b' -e d /srv/httpdb/etc/passwd
sed -i -e '/nobody/b' -e '/httpdb/b' -e d /srv/httpdb/etc/shadow
sed -i -e '/nobody/b' -e '/httpdb/b' -e d /srv/httpdb/etc/gshadow
chown -R httpdb:httpdb $JAIL/etc/mysql
chown -R httpdb:httpdb $JAIL/run
chown -R httpdb:httpdb $JAIL/var
chown -R httpdb:httpdb $JAIL/tmp
chown -R httpdb:httpdb $JAIL/usr/share/mysql
chown httpdb:httpdb $JAIL/run/mysqld/mysqld.sock
find $JAIL/ -gid 0 -uid 0 -type d -print | xargs sudo chmod -rw
find $JAIL/ -gid 0 -uid 0 -type d -print | xargs sudo chmod +x
find $JAIL/etc -gid 0 -uid 0 -type f -print | xargs sudo chmod -x
find $JAIL/usr/bin -type f -print | xargs sudo chmod ug+rx
find $JAIL/ -group httpdb -user httpdb -print | xargs sudo chmod o-rwx
chmod +r /srv/httpdb/etc/mysql/*
chmod +rw $JAIL/tmp
chmod +rw $JAIL/run

Содержимое updmariadb:

#!/bin/bash
export JAIL=/srv/httpdb
systemctl stop mariadb.service
cp -H /usr/bin/mysql* $JAIL/usr/bin/
cp -H /usr/bin/expr $JAIL/usr/bin/
cp -H /usr/bin/dirname $JAIL/usr/bin/
cp -H /usr/bin/nohup $JAIL/usr/bin/
cp -rH /usr/lib/php $JAIL/usr/lib/
cd $JAIL; ln -s usr/lib lib
cd $JAIL; ln -s usr/lib lib64
cd $JAIL/usr; ln -s lib lib64
cp -rH /usr/share/mysql/* $JAIL/usr/share/mysql
cp -rH /usr/lib/mysql $JAIL/usr/lib
cp $(ldd /usr/bin/mysql* | grep /usr/lib | sed -sre 's/(.+)(\/usr\/lib\/\S+).+/\2/g') $JAIL/usr/lib
cp $(ldd /usr/bin/expr | grep /usr/lib | sed -sre 's/(.+)(\/usr\/lib\/\S+).+/\2/g') $JAIL/usr/lib
cp $(ldd /usr/bin/dirname | grep /usr/lib | sed -sre 's/(.+)(\/usr\/lib\/\S+).+/\2/g') $JAIL/usr/lib
cp $(ldd /usr/bin/nohup | grep /usr/lib | sed -sre 's/(.+)(\/usr\/lib\/\S+).+/\2/g') $JAIL/usr/lib
cp $(ldd /usr/lib/mysql/plugin/*.so | grep /usr/lib | sed -sre 's/(.+)(\/usr\/lib\/\S+).+/\2/g') $JAIL/usr/lib
cp $(ldd /usr/lib/libjemalloc.so | grep /usr/lib | sed -sre 's/(.+)(\/usr\/lib\/\S+).+/\2/g') $JAIL/usr/lib
cp -H /lib64/ld-linux-x86-64.so* $JAIL/lib
cp -H /usr/lib/libjemalloc.so* $JAIL/lib
cp -H /lib/libnss_* $JAIL/lib
find $JAIL/ -gid 0 -uid 0 -type d -print | xargs sudo chmod -rw
find $JAIL/ -gid 0 -uid 0 -type d -print | xargs sudo chmod +x
find $JAIL/etc -gid 0 -uid 0 -type f -print | xargs sudo chmod -x
find $JAIL/usr/bin -type f -print | xargs sudo chmod ug+rx
find $JAIL/ -group httpdb -user httpdb -print | xargs sudo chmod o-rwx
chmod +r /srv/httpdb/etc/mysql/*
chmod +rw $JAIL/tmp
chmod +rw $JAIL/run
systemctl start mariadb.service

Затем установите скрипт обновления, ведь он понадобится в будущем:

sudo cp /tmp/updmariadn /usr/local/bin
sudo chown root:root /usr/local/bin/updmariadb
sudo chmod +x-w /usr/local/bin/updmariadb

Далее создайте файл /etc/mysql/my.cnf.d/myserver.cnf (да, в основной системе).

sudo touch /etc/mysql/my.cnf.d/myserver.cnf
sudo nano /etc/mysql/my.cnf.d/myserver.cnf

Вот его содержимое:

[myslqd]
default-storage-engine = innodb
innodb_data_home_dir = /var/lib/mysql
innodb_data_file_path = ibdata1:10M:autoextend
innodb_log_group_home_dir = /var/lib/mysql
innodb_buffer_pool_size = 16M
innodb_additional_mem_pool_size = 2M
innodb_log_file_size = 5M
innodb_log_buffer_size = 8M
innodb_flush_log_at_trx_commit = 1
innodb_lock_wait_timeout = 50
init_connect='SET collation_connection = utf8_unicode_ci'
character-set-server = utf8
collation-server = utf8_unicode_ci

Они сообщат серверу БД использовать по умолчанию движок таблиц InnoDB вместо MyISAM. Не буду утверждать, что приведённые выше опции оптимальны, да и InnoDB более требователен к количеству ОЗУ. Однако, если все таблицы базы будут на одном движке, в будущем параметры можно будет без проблем подрегулировать.

Прежде, чем файлы и каталоги скопированы, СУБД нужно инициализировать — создать служебные таблицы, базу данных для сайта, пользователя для доступа, удалить небезопасные настройки. В chroot скрипты инициализации тянуть накладно, поэтому команду ниже нужно запустить до копирования сервера баз данных в директорию chroot. Поэтому нужно вручную запустить сервер БД и скрипт базовой настройки безопасности:

mkdir /run/mysqld
chown -R mysql:mysql /run/mysqld
sudo mysql_install_db --user=mysql --basedir=/usr/ --ldata=/var/lib/mysql/
sudo -u mysql mysqld_safe --pid-file=/run/mysqld/mysqld.pid --socket=/run/mysqld/mysqld.sock --port=3306 --log-error=/tmp/mysql.log --basedir=/usr
#При этом консоль заблокируется, следующую команду нужно вводить в другом сеансе или использовать утилиту screen
mysql_secure_installation
#Далее завершение mysql:
sudo killall mysqld

Скрипт mysql_secure_installation задаст несколько вопросов:

  1. Enter current password for root (Введите текущий пароль пользователя root). Так как БД чисты и пароль при установке не создавали, просто жмите Enter
  2. Set root password? (Установить пароль пользователя root?) Жмите Y, Enter, вводите новый пароль пользователя root, снова Enter, повторно вводите пароль, Enter.
  3. Remove anonymous users? (Удалить пользователей с анонимным доступом?) Соглашаемся, нажимая Y, Enter.
  4. Disallow root login remotely? (Запретить удалённый доступ под учётной записью root?) Если доступ к БД извне не нужен, жмите Y и Enter.
  5. Remove test database and access to it? (Удалить тестовую БД и доступ к ней?) Конечно, эта база уже не нужна, Y и Enter.
  6. Reload privilege tables now? (Перезагрузить таблицы привелегий?) На всякий случай соглашаемся: Y и Enter.

И только после этого можно копировать файлы mariadb в chroot.

sudo bash /tmp/instmariadb

Затем по аналогии с nginx и php-fpm нужно создать службу для MariaDB:

sudo touch /etc/systemd/system/mariadb.service
sudo nano /etc/systemd/system/mariadb.service

Содержимое mariadb.service:

[Unit]
Description=MariaDB database server
Documentation=man:mysqld(8)
Documentation=https://mariadb.com/kb/en/library/systemd/
After=network.target

[Install]
WantedBy=multi-user.target
Alias=mysql.service
Alias=mysqld.service

[Service]
Type=simple
User=root
Group=root
ExecStart=/usr/bin/chroot --userspec=httpdb:httpdb /srv/httpdb /usr/bin/mysqld --basedir=/usr --pid-file=/run/mysqld/mysqld.pid --socket=/run/mysqld/mysqld.sock --port=3306
ExecStartPost=/bin/sleep 0.1
ExecReload=/bin/kill -USR2 $MAINPID
ReadOnlyDirectories=/proc /etc
InaccessibleDirectories=/sys
PrivateTmp=false
PrivateDevices=true
ProtectHome=true
ProtectSystem=full
SyslogLevel=notice
PrivateNetwork=false
KillSignal=SIGTERM
SendSIGKILL=no
Restart=on-abort
RestartSec=5s
UMask=007
Environment="LD_PRELOAD=/usr/lib/libjemalloc.so"
LimitNOFILE=16364

И скрипт, обеспечивающий связь между php-fpm и MySQL.

sudo touch /usr/local/bin/mariadbchroot
sudo nano /usr/local/bin/mariadbchroot

Содержимое /usr/local/bin/mariadbchroot:

#!/bin/bash
mkdir -p /srv/httpdb/run/mysqld
mkdir -p /srv/http/run/mysqld
mkdir -p /run/mysqld
chown httpdb:httpdb -R /srv/httpdb/run/mysqld
chown httpdb:httpdb -R /srv/http/run/mysqld
chown httpdb:httpdb -R /var/run/mysqld
mount --bind /srv/httpdb/run/mysqld /run/mysqld
mount --bind /srv/httpdb/run/mysqld /srv/http/run/mysqld 

Второй сервис, который этот скрипт будет запускать при включении сервера после запуска БД (в её службе скрипт не сработает, т.к. systemd там настроен на ограничение прав).

sudo touch /etc/systemd/system/dbdirs.service
sudo nano /etc/systemd/system/dbdirs.service

Содержимое /etc/systemd/system/dbdirs.service:

[Unit]
Description=Mount MariaDB directories
After=mariadb.service

[Install]
WantedBy=multi-user.target

[Service]
Type=simple
User=root
Group=root
ExecStart=/usr/local/bin/mariadbchroot

Нужен ещё один виртуальный диск для директории /run внутри chroot для MariaDB, поэтому добавьте в /etc/fstab строку:

tmpfs /srv/httpdb/run tmpfs rw,noexec,relatime,size=1024k 0 0

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

sudo systemctl enable mariadb.service
sudo systemctl start mariadb.service

На всякий случай рекомендую перезагрузить сервер командой sudo reboot, но это не обязательно.

Установка WordPress

После всех настроек, если я всё учёл и вы всё сделали правильно, на сервере будет свободно где-то 300-350 мегабайт свободной ОЗУ.

Очень быстро чистую установку WordPress можно сделать с помощью wp-cli. Но сначала создайте БД для сайта.

#Подключитесь к БД
sudo mysql -u root -p
#Введите пароль от root, который указывали при настройке MariaDB.
#Откроется консоль MySQL. Вместо mysite, wpuser, password придумайте свои значения (латинские буквы и цифры без пробела).
CREATE DATABASE mysite;
CREATE USER 'wpuser' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON mysite.* TO 'wpuser';
FLUSH PRIVILEGES;
quit

База готова, ставим WordPress несколькими командами:

#Сначала ставим wp-cli
sudo curl -o /usr/local/bin/wp https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
sudo chmod +x /usr/local/bin/wp
#Затем скачиваем файлы CMS в каталог с сайтом
sudo -u http wp core download --locale=ru_RU --path=/srv/http/www/cgi-bin/
#Создаём файл wp-config.php. Значения mysite, wpuser и password те же, что и при создании базы для сайта.
sudo -u http wp core config --dbname='mysite' --dbuser='wpuser' --dbpass='password' --dbhost='localhost' --dbprefix='wp_' --path=/srv/http/www/cgi-bin/
#Финальная установка. Укажите доменное имя, название блога, логин админа и его пароль с электронной почтой.
sudo -u http wp core install --url='http://mysite.ru' --title='Название блога' --admin_user='adminuser' --admin_password='password' --admin_email='email@domain.ru' --path=/srv/http/www/cgi-bin/
#Установка плагина для очистки кэша PageSpeed Module (кнопка очистки в его настройках).
sudo -u http wp plugin install kagg-pagespeed-module --path=/srv/http/www/cgi-bin/ --activate
#Плагин WP для корректного переименования всех ссылок на "https".
sudo -u http wp plugin install ssl-insecure-content-fixer --path=/srv/http/www/cgi-bin/ --activate

После этих манипуляций сайт на WP откроется и будет работать, как любой другой сайт. Чтобы всё совсем стало хорошо, укажите в настройках адреса сайта протокол https:

Про то, как его настроить, какие плагины поставить, на моём сайте есть рубрика WordPress.

Насколько это жизнеспособно

Работает нормально. Ну, то есть с блогом на дешёвой VPS-ке всё будет в порядке. А вот для магазина с тысячами товаров и сотнями рубрик нужен сервер помощнее. Зато автоматическая оптимизация скриптов никогда не помешает. У меня на тестовом блоге в тесте PageSpeed Insights получилось набрать 100/100:

На сайте была, конечно, стандартная тема и минимум картинок. Более сложные сайты получат меньше очков, однако без модуля ngx_pagespeed оценка получилась бы ещё меньше. Так что экспериментируйте, берите конфиги из этой заметки и делайте сайты лучшими.

Похожие записи:

Написать комментарий

Правила:
  • 1. Уважайте стороннее мнение. Токсичные комментарии удаляются.
  • 2. Комментарии со ссылками видны после проверки модератором.
  • 3. Обсуждение нелицензионного софта и других незаконных тем запрещено.

Тема Rowling от Anders Norén. Копирование материалов сайта разрешается только с указанием автора и активной ссылкой на источник.