Table of Contents
1 Предисловие
Расскажу, как настроить редкую на серверах операционную систему Arch Linux для хостинга сайта с изоляцией (не стопроцентной!) веб-серверного софта и модулем PageSpeed для Nginx. При этом сайт будет настроен на хорошую отзывчивость и высокий рейтинг тестов PageSpeed Insights и GTmetrix.
Внимание!
Статья устарела. Возможно, Pagespeed в настоящее время настраивается иначе.
Достоинства такого решения:
- Картинки, скрипты и стили сайта будут автоматически оптимизированы.
- Настройка сервера позволит лучше понять, как работает ОС Linux.
- На сервере с Arch Linux будет всегда свежайший софт.
- За счёт минимума излишеств требования к производительности сервера невысоки, работает быстро.
- Неплохой уровень безопасности (за счёт chroot).
Недостатки:
- Долго и нудно настраивается, так как нужно вводить много команд и вдумчиво читать мануалы.
- Не подходит для продакшена. Система заведомо менее стабильна по сравнению с Debian, Ubuntu, CentOS из-за недостаточно тщательно протестированных новейших версий софта.
- В моей инструкции описывается размещение только одного сайта на CMS WordPress. Хостить больше несколько сложнее.
Полученный при настройке опыт пригодится при работе в любой системе *nix, ибо наборы программ часто одинаковые, их файлы конфигураций совпадают. Различны лишь способы установки — apt в Ubuntu, yum в CentOS и так далее.
Я не уверен, что инструкция ниже подойдёт зелёным новичкам, только вступивших на тропу постижения дзена в мире GNU. Если что-то будет непонятно или, наоборот, вы с высоты опыта заметили ошибки и недоработки — комментарии под статьями всегда открыты.
Лучший способ разобраться в чём-то — объяснить это остальным.
Не помню кто
Поэтому не стесняйтесь задавать вопросы.
2 Что такое Arch Linux
Arch Linux — один из универсальных дистрибутивов GNU/Linux, оптимизированный для процессоров архитектуры x86-64. Для процессоров ARM сборки тоже существуют, поэтому на сервер с ARM от, например, Scaleway, теоретически поставить можно. Я не пробовал.
Система отличается минимализмом: пользователю предоставляется базовый набор программ, он вправе ставить всё, что ему потребуется.
Другая важная черта — Арч распространяется по модели rolling release (нет разделения на значимые релизы). Можно взять диск с установщиком Arch Linux пятилетней давности, поставить систему на ПК и после обновления через интернет весь софт будет свежайшим.
3 Чем хорош Arch Linux
Archlinux — полнейшая противоположность системам вроде Debian, Ubuntu, CentOS. Здесь нет удобного автоматического инсталлятора, нужно читать документацию и набирать много команд. Судя по отзывам в интернете, у непривычных к чтению настройка Арча вызывает настоящую головную боль. Но действительно ли эта система плоха? Нет!
Нельзя ожидать одинакового подхода от всех ОС, мир GNU/Linux славится разнообразием.
Концепция Arch Linux такова (как я её понимаю): пользователю даётся полный карт-бланш по конструированию системы. Полная свобода действий и никаких излишеств.
Если пользователи Убунты похожи на скульпторов, отсекающих у глыбы всё лишнее, чтобы получить красивую композицию, то юзеры Арча работают подобно строителям, по кирпичику собирая систему, отвечающую их требованиям.
Подход «от минимального — к нужному» позволил Archlinux зарекомендовать себя системой, шустро работающей на старом железе.
Если вам не нравится минимализм системы, нет времени сидеть и разбираться, но хочется хотя бы немного посидеть на чём-то отличном от Debian (и Windows), гляньте дистрибутивы EndevourOS и Manjaro, они основаны на Arch Linux.
4 Что нужно знать тем, кто привык к Убунтам
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 — отличный выбор. Настраивается дольше, сложнее, но результат получается именно тот, который нужен, в отличии от типовых наборов софта в других ОС.
5 Чем хорош Arch Linux для серверов
На всяких VPS и отдельных серверах с Arch Linux ситуация проще с установкой, чем для десктопов, но сложнее с поддерживанием.
Достоинства:
- Во-первых, установку ОС делает сам провайдер хостинга, разворачивая готовый образ системы (не всегда).
- Во-вторых, не нужно заморачиваться с установкой драйверов видеокарты, звуковой карты, прочей периферии и графической оболочки. Зачем там рабочий стол, если сидеть придётся через командную строку по SSH?
- В-третьих, за счет того, что изначально ставится очень мало софта, даже дешёвый одноядерный VPS с 512 Мб RAM будет довольно шустро работать.
Недостаток один, зато огроменный: система не стабильна. Если после очередного обновления сервер «сломается» (например, откажется загружаться из-за сбоя скрипта обновления ядра Linux), сайт не будет открываться всё то время, пока разбираетесь с проблемой. Если у вас личный блог или сайт-визитка, то, в принципе, ничем страшным это не обернётся. Интернет-магазинам и прочим высоконагруженным сервисам такая нестабильность грозит убытками.
Arch Linux — отличный вариант для серверов со скромными характеристиками: одноядерных, с 512 или гигабайтом ОЗУ, небольшим жёстким диском. На таком можно запустить интерактивный сайт-визитку или другой не шибко требовательным к ресурсам проект.
Сила Arch Linux в том, что с его помощью можно сделать так, чтобы операционная система занимала как можно меньше места на жёстком диске и в оперативной памяти. Тут Debian и Ubuntu выступают не в лучшем свете — с ними ставится много полезного, но лишнего софта. Кого-то может устроить CentOS, но для меня эта система не является эталоном серверных ОС, весомых преимуществ перед «коллегами» я не вижу.
6 Ставим 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 мегабайт ОЗУ. Это довольно компактная система.
7 Скорость WordPress и дешёвые сервера
Чаще всего я пишу о Windows как системе для домашних ПК, Linux как серверных ОС и WordPress как способе создать хороший сайт. Будет логичным рассказать дальнейшие шаги на примере сайта, работающем на CMS WordPress.
Для сайтов на WP, посещаемых более чем 20-30 тысячами людей в сутки, я рекомендую брать VPS с как минимум двумя гигабайтами ОЗУ и двухъядерным процессором. Тогда сайт будет быстро открываться у всех. Но за счёт чего возникают тормоза в более дешёвых конфигурациях серверов?
Арендуя дешёвую VPS-ку рублей эдак за сто в месяц, вы должны помнить о возможных трудностях, мешающих сделать страницы сайта быстро открывающимися. Есть три фактора, влияющих на скорость генерации страничек, со стороны сервера:
- Скорость жёсткого диска. В дешёвых используют обычные HDD. В принципе, наличие HDD в серверном оборудовании — не трагедия, диск вашего виртуального сервера может работать очень шустро… но не всегда. Когда «соседи» по серверу начинают одновременно что-то писать или читать, начинаются ощутимые тормоза. Вывод: чтобы сайт открывался быстро, нужно сократить количество файловых операций, держа всё в ОЗУ VPS. С 512 мегабайтами особо не развернёшься, а вот с 1 Гб «на борту» можно что-то сделать.
- Работа скриптов PHP или любого другого языка (смотря какой движок у сайта). Для WordPress минимальным количеством ОЗУ, при котором админка работает нормально — 128 Мб. То есть в идеале, если двое человек одновременно вызовут редактор записей, на сервере для генерации страниц их админок в этот момент должно быть доступно 256 Мб ОЗУ. Для троих потребуется 384 Мб и так далее.
- На практике не всё так плохо. Можно ограничить память для скриптов в php.ini всего лишь 48 мегабайтами и админка… нет, не начнёт тормозить. Просто иногда «тяжёлые» скрипты будут аварийно завершать работу из-за нехватки памяти, вместо страниц появятся сообщения о 502 или 503 ошибке. Если на сайте только один администратор, способный создавать посты, а остальные могут лишь комментировать записи, проблем вовсе не будет, ибо требования по памяти в этих условиях не настолько велики.
- Работа базы данных. Это, пожалуй, самое больное место многих CMS.
Интернет-магазинам из-за большого количества категорий противопоказаны сервера с малым объёмом ОЗУ. Проблема в том, что для генерации страниц рубрик, списка товаров, постов в блоге выполняются десятки запросов к базе данных. Отдельные запросы выполняются довольно быстро — 20-100 миллисекунд, но в сумме получаются секунды. А к сайтам, чьи страницы открываются больше 500 мс, не так уж хорошо относятся ни поисковые системы (отклик сервера — фактор ранжирования), ни люди (кто захочет ждать долгой загрузки страниц?). Поэтому запросы в базах данных кэшируются в RAM для быстрого переиспользования. Вот только кэш запросов может занимать гигабайты. Чтобы хоть как-то обойти ограничения производительности, можно сделать сайт статичным с помощью плагинов файлового кэширования, чтобы открывались готовые html-странички. Альтернативный подход к решению проблемы медленного открытия сайта — использование файловой CMS — тоже имеет право на жизнь, но там слишком много нюансов: от скорости жёсткого диска до банальных ограничений по возможностям (базы данных не просто так придумали).
«Тяжёлые» плагины и переусложнённые шаблоны, замедляющие работу сайта, тоже не следует сбрасывать со счетов. Поэтому не следует считать, что производительность сайта зависит только от железа и конфигурации Nginx, PHP, MySQL и прочих веб-серверных приложений. Иногда плохой плагин способен с сайтом сделать то же, что квадратные колёса с Формулой-1…
Так что же, придётся отдавать свои кровные за более дорогой и быстрый сервер? Ну, нет, можно обойтись и малым. Пока сайт не шибко посещаемый, можно экономить. Чтобы написать эту статью, я арендовал, пожалуй, один из самых недорогих виртуальных серверов, которые только можно найти, скопировал туда свой блог glashkoff.com и всё нормально заработало.
Кстати.
Существует два типа масштабируемости серверов:
1. Вертикальное масштабирование — увеличение производительности за счёт прямого добавления «мощностей». Например, повышения частоты процессора, увеличения размера RAM, замены жёсткого диска на быстрый SSD.
2. Горизонтальное — увеличение количества серверов и распределение нагрузки между ними.
Вертикальное работает просто и понятно: перешёл на тариф подороже — сервер стал быстрее, странички быстрее открываются. Однако предел у вертикального масштабирования наступает довольно быстро, ведь у ЦП есть ограничение по частоте. Горизонтальное требует большей вдумчивости, но возможностей предоставляет больше. Например, можно разместить сайты на относительно недорогих серверах в разных частях света, чтобы посетители всегда получали быстрый доступ независимо от своего местонахождения.
8 Что такое PageSpeed
Есть два продукта от Google с похожими названиями.
PageSpeed Insights — онлайн-тест от разработчиков Google, анализирующий качество сайта. Измеряется и выявляется очень многое: отклик сайта, размеры страниц, возможность оптимизации картинок, мешающие быстрой загрузке скрипты и стили и тому подобное.
У онлайн-теста есть проблемка: его оценка, по моему мнению, не адекватна. Чтобы добиться оценки 100/100 в мобильной и десктопных версиях веб-ресурса, нужно очень и очень сильно постараться. И не факт, что такая оптимизация под тест положительно скажется на внешнем виде сайта и удобстве. Например, мобильная версия айтишного сайта Habr.com набрала 59 баллов. А на нём даже картинок нет!
Чтобы вы поняли, что беспокоиться по поводу средней оценки не стоит, вот результат анализа главной страницы Яндекса:
Если популярный IT-ресурс и крупнейшая поисковая система не стремятся набрать 100 баллов, то вам, скорее всего, тоже нет смысла беспокоиться по этому поводу. Однако есть кое-что, что способно действительно улучшить скорость загрузки сайта без особых усилий — PageSpeed Modules.
PageSpeed Modules — дополнение для двух популярных веб-серверов: Apache и Nginx. Работает оптимизирующей прослойкой между браузером пользователя и вебсерверным софтом. Всё, что будет выдавать Nginx или Apache, будет переработано, сжато и сложено в промежуточный кэш. Или не сложено, а будет оптимизироваться на лету при каждом запросе страницы — смотря как настроите. В этой инструкции я предложу свой конфигурационный файл, основанный на найденных в интернете подобных файлах. Вам ничто не помешает доработать его при необходимости.
9 Где купить дешёвый VPS
Здесь могла бы быть ваша реклама! (Шутка. Хотя…)
Для написания статьи я не хотел арендовать дорогой высокопроизводительный сервер. В условиях ограниченных ресурсов проще придумать конфиг, гарантированно работающий на любых серверах. Поэтому чем хуже сервер — тем лучше для этой инструкции.
Как оказалось, поиск дешёвого VPS — тяжёлая задача. Хостеров-однодневок, занимающихся перепродажей облачных услуг, громадьё. Чтобы не отдать деньги нехорошим товарищам, приходится искать ИНН организации, проверять разрешение на оказание услуг, искать владельцев, смотреть наличие судебных дел. Отзывы и обзоры помогают слабо — большая часть фейковые, ибо рынок высококонкурентный.
Сначала остановил свой выбор на SkyHost.ru. Они предлагают тариф VDS-Zero за 84 рубля в месяц, что дёшево не только по меркам России, но и мировым.
Если платить сразу за год работы сервера, выйдет 80 рублей в месяц, но я не рискнул вот так просто выкладывать свои кровные, ибо не был уверен в стабильности их серверов. И, знаете, не прогадал. SkyHost оказался примером безалаберности и пофигизма. Я продержался два десятка дней, затем, после всех проблем, вспылил, отписал им в техподдержку их недостатки и попрощался, выключив арендованную виртуальную машину.
Вот список замеченных мною проблем у SkyHost.ru:
- Неправильно сконфигурированный новогодний промокод на скидку, который не работал без предварительной регистрации (проблему решил, списавшись с техподдержкой).
- Излишне сложная форма регистрации, где одних только полей с физическим адресом больше четырёх.
- Крайне неторопливая админка управления серверами.
- Неработающая кнопка загрузки Rescue режима сервера (по нажатию висит заглушка, сообщающая, что нужно просить об этом техподдержку).
- «Молча» зависающий хост-сервер. В админке SkyHost есть раздел «Объявления», где, насколько я понял, должны публиковаться оповещения, если проводится восстановление серверов, но он пуст. Вот и гадай — то ли VPS завис, то ли машина уровня выше.
- Невозможно изменить имя хоста (идентификатор сервера) в админ-панели.
- Обещанные пять адресов IPv6 в админке не видны. Только IPv4.
- Нельзя переустановить ОС. Точнее, создать новый сервер с этой ОС можно, а в списке переустановки Arch Linux нет.
- Нельзя при переустановке ОС сохранить IP адрес. Если просить техподдержку переустановить Arch Linux, выяснится пренеприятнейшая особенность: изменится IP сервера. Получается, если с сервером что-то случится, даунтайм будет запредельно долгим, ведь потребуется ждать обновления DNS серверов. За это время на старом IP может работать какой-то посторонний ресурс, если кто-то купит VDS в это же время.
Собственно, последнее смехотворное по своей глупости ограничение — невозможность сохранить IP адрес — и было последней каплей, после чего я «сбежал» от них. Так что этот хостинг могу рекомендовать только в качестве тестовой площадки. За 84 рубля в месяц вы получите «песочницу», в которой можно повысить навыки, борясь с бестолковой программной платформой хостера. Ибо опыт бесценен.
Далее я арендовал виртуальную машину у VDSina.ru. Там ещё дешевле — 60 рублей в месяц, но места на жёстком диске кот наплакал:
Впрочем, Arch Linux занимает около полутора-двух гигабайт, для одного сайта места хватит. Какое качество услуг предлагает VDSina? Я бы сказал так: нормальное. Претензий к ним нет. Отмечу очень продуманную тарификацию серверных ресурсов, напоминающую таковую у Digital Ocean. А ещё у них видно, на сколько дней работы сервера хватит денег на счету.
Не забудьте купить домен и сделать у него DNS-запись типа А с IP-адресом сервера. Иначе придётся открывать сайт, вводя IP.
10 Подключение к серверу
Для подключения к серверу по протоколу SSH, если сидите на Windows, рекомендую использовать Kitty. Это лучшее средство для работы с терминалом по протоколу SSH для этой ОС.
Но так как я люблю пробовать новые инструменты, для этой заметки буду использовать утилиту Terminus. Она медленная и глючная, но очень стильная, поэтому для создания скриншотов подходит идеально.
11 Настройка сервера на Arch Linux
Итак, при заходе на сервер вас встретит командная строка. Надеюсь, у вас есть опыт работы в ней или хотя бы сильное желание гуглить ответы на возникающие вопросы. В принципе, книг по Linux много для разного уровня знаний читателей, это не проблема.
Ниже я буду приводить команды, которые нужно ввести в терминале. Естественно, перепечатывать ничего не нужно. Их можно просто копировать. Внимательно читайте написанное мною, потому что в некоторых случаях (например, при создании правила iptables) нужно указать собственные значения.
Кое-что нужно выполнить в первую очередь: обновить софт, настроить защиту и упростить себе жизнь в командной строке. После можно думать о том, как хостить сайт.
12 Создание пользователя
Так как под 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 командную строку удобнее.
13 Как устанавливать и обновлять софт
Как я уже писал выше, в 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’а, потому что доступ ко всем крупным репозиториям Арча включён в ней по умолчанию.
14 Настройка 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. Это разумный компромисс между функциональностью и скоростью работы. При желании вашу консоль можно превратить в нечто подобное тому, что отображено на скриншоте ниже.
В общем, там есть где развернуться. Но я вместо украшательств расскажу, как добавить в 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.
15 Тюнинг сервера
Базовая защита 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 Мб.
Всё изменится в худшую сторону, когда будет запущен сайт. Чтобы программы не закрывались из-за нехватки памяти, нужно подключить 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
Теперь софт в условиях нехватки памяти будет тормозить, но работать.
16 Установка 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»:
Насколько я понял, в 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
Сертификат будет работать, но сайт пока недоступен для посетителей. Переходим к следующему шагу.
Настройка 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/updmariadb /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 задаст несколько вопросов:
- Enter current password for root (Введите текущий пароль пользователя root). Так как БД чисты и пароль при установке не создавали, просто жмите Enter
- Set root password? (Установить пароль пользователя root?) Жмите Y, Enter, вводите новый пароль пользователя root, снова Enter, повторно вводите пароль, Enter.
- Remove anonymous users? (Удалить пользователей с анонимным доступом?) Соглашаемся, нажимая Y, Enter.
- Disallow root login remotely? (Запретить удалённый доступ под учётной записью root?) Если доступ к БД извне не нужен, жмите Y и Enter.
- Remove test database and access to it? (Удалить тестовую БД и доступ к ней?) Конечно, эта база уже не нужна, Y и Enter.
- 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, но это не обязательно.
17 Установка 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 protected]' --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.
18 Насколько это жизнеспособно
Работает нормально. Ну, то есть с блогом на дешёвой VPS-ке всё будет в порядке. А вот для магазина с тысячами товаров и сотнями рубрик нужен сервер помощнее. Зато автоматическая оптимизация скриптов никогда не помешает. У меня на тестовом блоге в тесте PageSpeed Insights получилось набрать 100/100:
На сайте была, конечно, стандартная тема и минимум картинок. Более сложные сайты получат меньше очков, однако без модуля ngx_pagespeed оценка получилась бы ещё меньше. Так что экспериментируйте, берите конфиги из этой заметки и делайте сайты лучшими.