Alpine Linux на VPS
linux PHP Caddy rclone fail2ban FTP VDS/VPS Alpine Linux веб-сервер
Крайне минималистичная система, что может пригодиться на слабых конфигурациях. Подходит в случае KVM-виртуализации и при наличии возможности подгружать/подключать ISO.
Решил воспользоваться «виртуальным» вариантом x86_64 (внизу на странице). Для установки в консоли сервера (где-то в ЛК хостинга) загружаемся с подключенного образа. В принципе далее по инструкции – предлагаю воспользоваться «более медленным» вариантом.
Установка
Входим под root (без пароля), setup-alpine
.
- Keyboard layout:
us
- Variant:
us
- Hostname:
vm876632
(номер виртуальной машины) - Сетевой интерфейс – по умолчанию (eth0)
- IP-адрес – в моем случае DHCP нет, настраиваем вручную по данным из ЛК (ниже пример с локальным адресом):
- 192.168.56.101
- Маска 255.255.255.0 (/24)
- Шлюз: 192.168.56.1
- IPv6 не будем настраивать*
- Не хотим настраивать сеть вручную (n)
- Домен DNS – пусто
- DNS nameserver – например Яндекс:
77.88.8.8 77.88.8.1
- Пароль root: предлагаю сразу ввести сложный (или по крайней мере длинный), чтобы хоть как-то защитить SSH
- Временная зона:
Europe/Moscow
(у меня) - Proxy – по умолчанию (none)
- Зеркало APK – сначала включить «общественный» репозиторий c, потом f (найти самое быстрое)
- Не хотим настраивать пользователя – no (по умолчанию)
- SSH-сервер – по умолчанию (openssh)
- Разрешить вход root по ssh –
yes
- Ключ SSH мы пока не можем ввести – значение по умолчанию (none) (хотя если вдруг можете – обычно на этом этапе буфер обмена не работает – то стоит ввести, тогда на предыдущем надо было бы оставить prohibit-password)
- Диск:
vda
(скорректировать по факту) - Как хотим использовать –
sys
- Диск будет стерт –
y
Установка завершена. Теоретически можно переделать установку, вызвав setup-disk с параметрами, например поменять размер раздела подкачки. Если посмотреть fdisk -l
, то увидим, что в моем случае объем раздела подкачки составил 940 мегабайт (2x объем оперативки).
* Примечание. На моем хостинге IPv6 как бы есть, но работает он почему-то только с предустанавливаемыми Дебианами/Убунтами и RHEL'ами. Что не так с альтернативными системами, я, к сожалению, не разобрался, так что рассматривать настройку IPv6 не будем.
Перезагружаемся – reboot
. На всякий случай стоит принудительно выбрать жесткий диск при загрузке – может вновь загрузиться образ. Проверим подключение по SSH – должно работать.
Обновим систему:
apk update
apk upgrade
Я также устанавливаю Midnight Commander:
apk add mc
Хотя именно в Alpine Linux он как-то не очень хорошо себя проявляет, в частности засоряет историю команд.
Посмотрим свободную память и место на диске:
free -m
total used free shared buff/cache available
Mem: 470 38 376 0 57 419
Swap: 940 0 940
df -H
Filesystem Size Used Available Use% Mounted on
devtmpfs 10.0M 0 10.0M 0% /dev
shm 235.0M 0 235.0M 0% /dev/shm
/dev/vda3 4.6G 68.7M 4.3G 2% /
tmpfs 94.0M 104.0K 93.9M 0% /run
/dev/vda1 271.1M 23.9M 228.2M 9% /boot
tmpfs 235.0M 0 235.0M 0% /tmp
Как видим, занято всего ничего: 24 мегабайта в разделе загрузки и 69 – в основном разделе. Не то, что эти ваши Rocky с Debian, которым нужно 2-3 гига.
Выключаем – poweroff
– размонтируем/отключаем/… ISO в ЛК и включаем машину обратно.
Fail2ban
Все по инструкции. Установка:
apk add fail2ban
Совсем не по инструкции у меня посыпались ошибки package mentioned in index not found (try 'apk update'). Так что да, apk update
и потом еще раз apk add fail2ban
. Со второй попытки вроде удачно.
Добавляем в «автозагрузку»:
rc-update add fail2ban
На всякий случай проверить, что сервис добавился:
rc-status
Запуск:
rc-service fail2ban start
Должна создаться конфигурация по умолчанию /etc/fail2ban/jail.d/alpine-ssh.conf. На мой взгляд, в ней многовато количество попыток (maxretry) – поставлю 3, да и bantime по умолчанию маловат – установлю скажем 12 часов:
[sshd]
enabled = true
filter = alpine-sshd
port = ssh
logpath = /var/log/messages
maxretry = 3
bantime = 12h
[sshd-ddos]
enabled = true
filter = alpine-sshd-ddos
port = ssh
logpath = /var/log/messages
maxretry = 3
bantime = 12h
Перезапуск сервиса:
rc-service fail2ban restart
Или можно вместо этого fail2ban-client reload
.
Посмотреть статистику:
fail2ban-client stats
Посмотреть, что там с iptables:
iptables -S
iptables -L -v -n | more
PHP
В apk на данный момент есть PHP 8.2 и 8.3. Опять же, на данный момент, это устраивает. Если нет – можно пойти по стопам авторов официальных образов Docker и собрать PHP из исходников.
Установим PHP-FPM 8.3 и некий минимальный набор расширений:
apk add php83 php83-fpm php83-curl php83-gd php83-intl php83-mbstring php83-opcache php83-xml
Выдано предупреждение:
If you need ICU with non-English locales and legacy charset support, install package icu-data-full.
Определенно это нам нужно, так что устанавливаем:
apk add icu-data-full
Вообще что касается расширений, скорее всего их понадобится довольно много доустанавливать – в аналогичных ситуациях тот же remi (в RHEL'ах) как будто больше расширений включает в основу. Например (в смысле «понадобится доустановить») – допустим, сайт использует БД SQLite (помимо прочего):
apk add php83-dom php83-fileinfo php83-pcntl php83-pdo_sqlite php83-session
Проверка, что PHP как-то установился:
php -i
Вывод длинный, но зато вся конфигурация.
Корректируем при необходимости основной /etc/php83/php.ini, дополнительные в /etc/php83/conf.d/ (например 00_opcache.ini может потребоваться), а также настройки PHP-FPM в /etc/php83/php-fpm.d/www.conf. В частности, PHP-FPM по умолчанию слушает порт (а не сокет), а пользователь и группа nobody
. Давайте-ка наверное сразу сделаем более «канонично» с www-data, да и я как-то больше сокет предпочитаю.
На всякий случай проверим наличие пользователя:
cat /etc/passwd | grep x:82
cat /etc/passwd | grep www-data
Обе команды не должны что-то показать. Создаем прямо как авторы образа Docker (в свою очередь, они ссылаются на установку веб-серверов из пакетов):
adduser -u 82 -D -S -G www-data www-data
В Alpine Linux (точнее в Busybox) нестандартная команда создания пользователя.
-u UID
id пользователя-D
не устанавливать пароль-S
создать системного пользователя-G GRP
группа
Правим /etc/php83/php-fpm.d/www.conf (фрагменты):
user = www-data
group = www-data
listen = /run/php-fpm83/www.sock
Добавляем в «автозапуск»:
rc-update add php-fpm83
На всякий случай reboot
, после чего ищем php-fpm например в top
.
PID PPID USER STAT VSZ %VSZ CPU %CPU COMMAND
2365 2334 www-data S 202m 43% 0 0% {php-fpm83} php-fpm: pool www
2366 2334 www-data S 173m 37% 0 0% {php-fpm83} php-fpm: pool www
2334 1 root S 173m 37% 0 0% {php-fpm83} php-fpm: master proces
Вроде есть контакт. Убедимся, что мы не открыли лишние порты:
netstat -tulpn | grep LISTEN
В выводе должны быть только 22-е порты (SSH).
Caddy
https://wiki.alpinelinux.org/wiki/Caddy
Внезапно тоже есть в apk.
apk update
apk add caddy caddy-openrc
Или, как авторы образа Docker, скачать бинарник с GitHub. Правда в этом случае видимо надо будет «вручную» делать сервис, а это довольно здоровый скрипт.
Пакет поставляется с пустым /etc/caddy/Caddyfile, так что придется все делать самим. Создадим файл /var/www/html/index.php:
<?php phpinfo(); ?>
Поменяем владельца каталога:
chown -R www-data:www-data /var/www/html
Предварительно сконфигурируем сервер (добавим в /etc/caddy/Caddyfile):
http:// {
root * /var/www/html
encode zstd gzip
file_server
php_fastcgi unix//run/php-fpm83/www.sock
}
Директива (file_server
) пока фактически не нужна (поскольку иных файлов и нет), но на перспективу пусть будет.
Веб-сервер запускается под пользователем caddy. Необходимо разрешить этому пользователю взаимодействовать с сокетом PHP-FPM. Правим /etc/php83/php-fpm.d/www.conf:
listen.acl_users = www-data,caddy
Перезапускаем php-fpm и запускаем Caddy:
rc-service php-fpm83 restart
rc-service caddy start
Заходим на сервер «по IP» – должны увидеть страницу сведений о PHP.
По желанию можно убрать заголовки Server и X-Powered-By. Возвращаемся обратно в Alpine и в /etc/caddy/Caddyfile добавляем директиву:
header -Server
А в /etc/php83/php.ini исправляем:
expose_php = Off
Перезапускаем сервисы php-fpm83 и caddy. «Лишние» заголовки должны исчезнуть.
Добавляем веб-сервер в «автозапуск»:
rc-update add caddy
Git и composer
В принципе и то, и другое есть в apk. Так что на всякий случай обновляем и затем устанавливаем:
apk update
apk add git composer
Основное неудобство скорее всего будет заключаться в пользователе – переключиться в www-data нельзя, так что git может ругаться:
fatal: detected dubious ownership in repository at '/var/www/html'
To add an exception for this directory, call:
git config --global --add safe.directory /var/www/html
Вероятно так и стоит поступить. Либо заводить пользователя и менять на него php-fpm83 и Caddy.
vsftpd
Возможно, по FTP заливать «исходники» будет удобнее. В Wiki для этого предлагают воспользоваться vsftpd.
Установка
Как всегда, из пакетов:
apk add vsftpd vsftpd-openrc
В /etc/vsftpd/vsftpd.conf правим/раскомментируем:
anonymous_enable=NO
local_enable=YES
write_enable=YES
local_umask=022
По желанию – включить текстовый режим передачи (управление переводами строк):
ascii_upload_enable=YES
ascii_download_enable=YES
Убрать ошибку 500 OOPS: child died или т.п. – добавить:
seccomp_sandbox=NO
Разрешение входа только www-data (пользователям, перечисленным в списке) – добавить:
userlist_enable=YES
userlist_deny=NO
userlist_file=/etc/vsftpd/user_list
Соответственно список – файл /etc/vsftpd/user_list:
www-data
Установить пароль пользователю www-data:
passwd www-data
Запуск и добавление в «автозагрузку»:
rc-service vsftpd start
rc-update add vsftpd
Ограничение корневой директории
Небольшая проблема такой конфигурации в том, что по FTP открывается вся файловая система, доступная пользователю www-data. Можно или не обращать на это внимание, или поменять домашнюю директорию системному пользователю и добавить (раскомментировать) настройку в /etc/vsftpd/vsftpd.conf chroot_local_user=YES
, или разбираться с виртуальными пользователями, что делается, по видимому, через PAM и наверное не особо-то и надо.
Чтобы поменять домашнюю директорию, наверное, не стоит непосредственно редактировать /etc/passwd. Установим пакет shadow, чтобы воспользоваться более-менее привычным usermod:
apk add shadow
Однако прежде чем менять настройки пользователя, придется остановить сервисы, его использующие. В частности PHP:
rc-service php-fpm83 stop
И только затем выполнить:
usermod --home /var/www www-data
Соответственно запускаем PHP обратно и перезапускаем vsftpd для применения настроек.
Fail2ban
Фильтр для vsftpd входит в комплект поставки, поэтому есть смысл добавить ловушку и для FTP. Сначала убедимся, что логирование vsftpd включено (в /etc/vsftpd/vsftpd.conf):
xferlog_enable=YES
По всей видимости, отдельно именно входы-выходы не логируются, т.е. либо только вместе с фиксацией передачи файлов, либо вообще никак. Файл логов по умолчанию: /var/log/vsftpd.log
Соответственно создаем «клетку» /etc/fail2ban/jail.d/vsftpd.conf:
[ftp]
enabled = true
filter = vsftpd
port = ftp
logpath = /var/log/vsftpd.log
maxretry = 3
bantime = 12h
Перезагружаем конфигурацию fail2ban:
fail2ban-client reload
«Неправильно» входим по FTP 3 раза (получаем ошибку 530 Login incorrect), а на 4-й уже «Соединение прервано после 20 секунд неактивности». Также по желанию можно перепроверить вывод iptables -S | grep f2b-ftp
– должна быть запись с нашим IP и правилом недоступности порта. Кстати по умолчанию vsftpd работает по IPv4.
Облачная синхронизация
В этом смысле я предпочитаю rclone, который опять же есть в apk.
apk add rclone
Настраиваем подключение:
rclone config
Я как-то традиционно пользуюсь mega.nz. «Из коробки» поддерживается еще довольно много облаков, а также универсальные протоколы FTP, sFTP и WebDAV. Для определенности создаю подключение к Меге под названием meganz
.
Проверка (именно с двоеточием в конце):
rclone lsd meganz:
Должно отобразиться содержимое корневой директории.
Теперь можно использовать синхронизацию в скриптах и/или crontab. Например, забегая вперед, в последнем случае:
0 4 * * * /usr/bin/rclone sync /var/www/html/public/images meganz:/backups/images
Тем самым в 4 часа утра синхронизируем с облаком каталог изображений.
Заключение
Собственно, осталось вместо заглушки разместить настоящий сайт и отполировать настройки Caddy, PHP и rclone. С чем, я надеюсь, вы справитесь и без меня.
В целом, все необходимое в Alpine Linux есть, что делает ее достаточно перспективной для использования в качестве ОС для веб-сервера, особенно какого-нибудь "статического", особенно на начальных тарифах VDS/VPS. Хотя вот разработчики FrankenPHP утверждают, что с musl PHP якобы медленно работает. Имейте в виду, если что.
Категория: Программирование, веб | Опубликовано 11.12.2024 | Редакция от 03.08.2025
Похожие материалы
FreeBSD на VPS
Продолжаю устанавливать что-нибудь этакое на VPS. На сей раз решил, так сказать, вернуться к истокам - ведь когда-то многие веб-сервера были на фряхе, а также посмотреть, насколько она компактна сама по себе и в плане ресурсоемкости.
Веб-сервер на FreeBSD с использованием клеток
Здесь вам не Докер, а клетки (jails) - будем говорить, это контейнеры FreeBSD, когда это еще не было мейнстримом (на минуточку, они появились еще во FreeBSD 4.x - 2000 год). Практический смысл в моем случае - неким образом изолированно использовать разные версии PHP, ну и чуть ближе познакомиться с технологией, с которой я уже сталкивался при обзоре TrueNAS. Основано, как говорится, на реальных событиях - я переносил сайты на Drupal 7.x и Yii с сервера на Linux.