Бесшумный NAS для дома и собственное файловое облако на Linux. Часть 2
6 июня 2022 г. server linux self-hosted
Вот уже три года я использую в своём цифровом быту QNAP в качестве файлохранилки. Про выбор железки и первоначальную установку я писал в первой части этой статьи. За это время в моем сетапе произошли изменения, и здесь поделюсь своим опытом за прошедшее время. Теперь из названия статьи можно убрать слово “файловое”.
Состояние дисков
За три года ни одной ошибки в SMART-е. Диски в зеркальном RAID-е вот такие — WDC WDS100T2B0A-00SM50
Kodi, но без HDMI
Сама коробка QNAP у меня переехала в кладовку, и кабель HDMI до туда не дотянуть. Поэтому на телевизор с AndroidTV был поставлен Kodi, и в качестве источника данных указываю свой сервер, на котором тоже стоит Kodi. Телевизор подключается по Wi-Fi и тянет данные оттуда. 5 гигагерцового канала хватает, чтобы пробить пару стен и стримить FullHD видео без задержек.
Nextcloud vs Seafile
Прожив с Seafile три года, я понял, что мне стало мало того функционала, который он дает. А дает он очень мало возможностей, по сути только одну — хранить файлы. И никаких тебе плагинов и расширений. Превьювить файлы в браузере умеет очень ограниченно, поиск тоже хромает.
Кроме этого, у Seafile очень бедное приложение для iOS: ни тебе галереи нормальной, ни фоновой загрузки фотографий сделанных на телефон (тут iphone правда сам виноват, потому что режет фоновую работу приложений). Под Android проблемы с фоновой загрузкой нет, но в остальном то же самое.
А после 24 февраля, когда в России начали отключать разные облачные сервисы, стало понятно, что лучше искать self-hosted замену, а с Seafile построить какую-то собственную “экосистему” довольно сложно. Тогда я решил попробовать Nextcloud.
На тот момент в Seafile у меня было было примерно 25к файлов и 150ГБ.
Установка Nextcloud
Я запустил Nextcloud с помощью docker-compose. Потребовалось некоторое время, чтобы подобрать параметры запуска, но в итоге финальная версия моей конфигурации выглядит так:
version: '2'
services:
db:
image: mariadb
command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
restart: always
volumes:
- /opt/nextcloud-mysql:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=<rootpassword>
- MYSQL_PASSWORD=<dbpassword>
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=nextcloud
logging:
options:
max-file: "3"
max-size: "50m"
app:
image: nextcloud:23.0.4-fpm
links:
- db
ports:
- 127.0.0.1:10000:9000
environment:
- NEXTCLOUD_IPADDRESS=<ipaddress>
- NEXTCLOUD_FQDN=<hostname>
- MYSQL_DATABASE=nextcloud
- MYSQL_USER=nextcloud
- MYSQL_PASSWORD=<dbpassword>
- MYSQL_HOST=db
# - NEXTCLOUD_ADMIN_USER=admin
# - NEXTCLOUD_ADMIN_PASSWORD=<adminpassword>
volumes:
- /var/www/html:/var/www/html
restart: always
logging:
options:
max-file: "3"
max-size: "50m"
cron:
image: nextcloud:23.0.4-fpm
restart: always
links:
- db
volumes:
- /var/www/html:/var/www/html
entrypoint: /cron.sh
logging:
options:
max-file: "3"
max-size: "50m"
В app контейнере работает само приложение, в cron - регулярные таски которые запускаются в фоне (разметка фотографий на карте, image recognition и проч.)
В качестве входной точки на сервере я использую nginx. Вот так прокидываю запросы внутрь docker-контейнра, и использую Let’sEncrypt для HTTPS:
upstream php-handler {
server localhost:10000;
}
server {
server_name <hostname>;
listen [::]:443 ssl ipv6only=on; # managed by Certbot
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/<hostname>/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/<hostname>/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
add_header Strict-Transport-Security max-age=15768000;
root /var/www/html;
client_max_body_size 1024m;
# Enable gzip but do not remove ETag headers
gzip on;
gzip_vary on;
gzip_comp_level 4;
gzip_min_length 256;
gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;
add_header Referrer-Policy "no-referrer" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Download-Options "noopen" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Permitted-Cross-Domain-Policies "none" always;
add_header X-Robots-Tag "none" always;
add_header X-XSS-Protection "1; mode=block" always;
# Remove X-Powered-By, which is an information leak
fastcgi_hide_header X-Powered-By;
index index.php index.html /index.php$request_uri;
location = / {
if ( $http_user_agent ~ ^DavClnt ) {
return 302 /remote.php/webdav/$is_args$args;
}
}
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
location ^~ /.well-known {
# The rules in this block are an adaptation of the rules
# in `.htaccess` that concern `/.well-known`.
location = /.well-known/carddav { return 301 /remote.php/dav/; }
location = /.well-known/caldav { return 301 /remote.php/dav/; }
location /.well-known/acme-challenge { try_files $uri $uri/ =404; }
location /.well-known/pki-validation { try_files $uri $uri/ =404; }
# Let Nextcloud's API for `/.well-known` URIs handle all other
# requests by passing them to the front-end controller.
return 301 /index.php$request_uri;
}
location ~ /(ocm-provider|ocs-provider)/ {
return 301 $scheme://$host/$1/;
}
# Rules borrowed from `.htaccess` to hide certain paths from clients
location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/) { return 404; }
location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) { return 404; }
location ~ \.php(?:$|/) {
# Required for legacy support
rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+|.+\/richdocumentscode\/proxy) /index.php$request_uri;
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
set $path_info $fastcgi_path_info;
try_files $fastcgi_script_name =404;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $path_info;
#fastcgi_param HTTPS on;
fastcgi_param modHeadersAvailable true; # Avoid sending the security headers twice
fastcgi_param front_controller_active true; # Enable pretty urls
fastcgi_pass php-handler;
fastcgi_intercept_errors on;
fastcgi_request_buffering off;
}
# Rule borrowed from `.htaccess`
location /remote {
return 301 /remote.php$request_uri;
}
location / {
try_files $uri $uri/ /index.php$request_uri;
}
}
Дальше я поставил у себя на десктопе клиент Nextcloud и попробовал загрузить свои 25к файлов. Клиент кряхтел и пыхтил, но таки за ночь это осилил. А теперь, когда вся масса файлов загружена, добавлять по несколько десятков новых никакой проблемы не составляет.
После установки
Конечно, отзывчивость веб-интерфейса Nextcloud после Seafile бросается в глаза, но в целом, скорость работы стала приемлемой. Три года назад было намного хуже. Nextcloud написан на PHP, так что задумчивость у него в крови 😄 . Обратной стороной этого является легкость написания плагинов.
Сразу после установки я пошел смотреть плагины, которые тут есть. И вот что я для себя выбрал:
Calendar
Ну тут все понятно. CalDAV календарь, который отлично подключается в любой android/iphone календарь. Заменил им iCloud календарь.
Contacts
CardDAV записаная книжка. Перенес контакты из iCloud практически без боли. Один-в-один не переносится, ломаются метки номеров “сотовый”, “домашний”, но это не критично.
Preview Generator
Плагин, который генерирует превью для галереи. Крайне полезная штука, с ней плитки картинок в галерее загружаются значительно быстрее. Для того чтобы появлялись превьюшки для видео, надо поставить ffmpeg внутрь контейнера Nextcloud (или подключить с хоста)
Bookmarks
Облачное хранилище закладок из браузера. Я пользуюсь Firefox, и для него есть плагин floccus, который позволяет загружать загладки в Nextcloud. Сценарий такой: у меня есть ноутбук и ПК, добавил я закладку на ноубуке, потом запустил ПК, а она уже и там есть. Удобно. А для мобилки есть отдельное приложение.
GpxEdit / GpxMotion / GpxPod
После ухода из России Strava, для беговых/вело тернировок я начал писать gpx-трек в OpenGPXTracker, и складывать в отдельную папку в Nextcloud-е. Конечно, полноценной замены Strava не получается — нет самой главной фишки — социальной сети и общения, но для ведения дневника тренировок вполне подходит. И еще нельзя в один клик создать фоточку с треком для инстаграма 😄 (идея для плагина!). Btw, из Strav-ы можно выгрузить все свои треки в gpx-формате (см. Bulk Export, но теперь только через VPN открывается)
Maps
Плагин для работы с картами. Рисует на карте всю инфу до которой дотянется. Самое полезное — рисует фотки на карте по координатам в EXIF.
Tasks
Замена для приложения Напоминания в iPhone
Recognize
Это image recognition в Nextcloud, чтобы в поиске по фото написать “собака”, а он тебе покажет все фотографии на которых есть собака. Долго мучался с этим расширением. Пытался заставить его в фоне просмотреть все мои 20к фоток, ибо жрет много памяти и падает в OOM на моем хиленьком QNAP-е. С такой-то матерью удалось, но результат не оправдал ожидания — смотрит плохо, метки только на английском, часто ошибается. По итогу выглядит примерно так:
На мой взгляд неюзабельно в реальной жизни. Отказался
Self-hosted экосистема
После установки этих плагинов, очень быстро ощутил разницу с Seafile. Там у меня была простая файловая хранилка, а тут настоящая “экосистема” с кучей сервисов. И в iOS-приложении nextcloud смогли починить проблему фоновой загрузки фото. Для этого надо приложению дать права на геолокацию, тогда оно в фоне сможет загружать фото. У приложения Seafile это почему-то работает через раз.
В общем, выжать полезности из Nextcloud-а можно гораздо больше, чем из Seafile. Поэтому остаюсь пока на нём.