Домашний сервер (свое облако)

Я уже писал про домашний сервер. Настало время разобраться с облаком. Мне хотелось что-то на подобии «Яндекс диска» или «Google Drive». К счастью такая возможность есть и это Nextcloud. Так же немного познакомимся с Docker и настроим службу в Linux. Приступим.

Подготовка

Для начала необходимо подготовить площадку. Так как у меня по сути 2 сервера, то я буду разворачивать приложение, так сказать, на сервер приложений. Чтобы удобно было это делать я буду всю структуру устанавливать в контейнерах. Я про это уже писал в общих чертах, а теперь буду использовать.

И так, нам понадобится система контейнеризации. Для этого буду использовать Docker. Как его устанавливать, настраивать, использовать написано предостаточно. Все будет происходить стандартным способом. Идем на сайт, читаем, изучаем, выбираем свой дистрибутив и устанавливаем по инструкции. Так как у меня Armbian на основе Debian, то я выбираю его и просто все делаю по инструкции.

После этого нам понадобится Docker Compose. Есть так же инструкция по его установке. Но не все так просто! У меня он не заработал. Это все потому, что по умолчанию его бинарная версия собрана для систем x86_64, а на одноплатнике arm64. Это совершенно разные архитектуры. Но отчаиваться не будем. Заходим в консоль и устанавливаем из репозиториев:


apt install -y docker-compose

Версия не последней свежести, но рабочая. Нам этого будет достаточно.

Установка

Теперь нужно создать директории, которые будут использоваться. Для этого выполняем:


mkdir -p /mnt/nfs/cloud/cloud
mkdir -p /mnt/nfs/cloud/db
mkdir -p /opt/nextcloud

Так как у меня подключен сетевой диск по nfs, то создаю я директории именно на нем. Третья команда создает директорию в /opt. В ней я буду располагать файлы работы сервисов.

Далее выполняю nano /opt/nextcloud/docker-compose.yml и наполняю его:


version: "2.4"

services:
  db:
    image: mariadb:10.7.1-focal
    command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW --innodb-file-per-table=1 --skip-innodb-read-only-compressed
    restart: always
    environment:
      MARIADB_DATABASE: "имя БД"
      MARIADB_USER: "имя пользователя для БД"
      MARIADB_PASSWORD: "пароль БД"
      MARIADB_RANDOM_ROOT_PASSWORD: 1
    volumes:
      - type: bind
        source: /mnt/nfs/cloud/db
        target: /var/lib/mysql
    mem_limit: 256m
    mem_reservation: 64m

  nextcloud:
    image: nextcloud:22.2.3-fpm-alpine
    restart: always
    volumes:
      - type: bind
        source: /mnt/nfs/cloud/cloud
        target: /var/www/html
    mem_limit: 256m
    mem_reservation: 64m
    links:
      - db
    depends_on:
      - db

  nginx:
    image: nginx:1.20.1-alpine
    restart: always
    volumes:
      - type: bind
        source: /mnt/nfs/cloud/cloud
        target: /var/www/html
      - type: bind
        source: ./nginx.conf
        target: /etc/nginx/nginx.conf
    ports:
      - "8082:80"
    links:
      - nextcloud
    depends_on:
      - nextcloud
    mem_limit: 128m
    mem_reservation: 32m

Этот файл описывает какие образы контейнеров необходимо использовать, как их запускать, как объединять их для взаимодействия по сети и как установить ограничение ресурсов. Все необходимы контейнеры можно найти по адресу hub.docker.com. В данном случае мне нужно 3 контейнера: nextcloud, mariadb и nginx.

Теперь выполняем nano /opt/nextcloud/nginx.conf:


worker_processes auto;
pid /run/nginx.pid;

events {
    worker_connections 1024;
    multi_accept on;
}

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 15;
    types_hash_max_size 2048;
    server_tokens off;
    
    include /etc/nginx/mime.types;
    default_type text/javascript;

    access_log off;
    error_log /var/log/nginx/error.log;
    
    gzip on;
    gzip_min_length 100;
    gzip_http_version 1.1;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
    
    client_max_body_size 8M;
    
upstream php-handler {
    server nextcloud:9000;
}

server {
    listen 80;
    listen [::]:80;

    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;

    fastcgi_hide_header X-Powered-By;

    root /var/www/html;

    location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
    }

    location = /.well-known/carddav {
      return 301 $scheme://$host:$server_port/remote.php/dav;
    }
    location = /.well-known/caldav {
      return 301 $scheme://$host:$server_port/remote.php/dav;
    }

    client_max_body_size 512M;
    fastcgi_buffers 64 4K;

    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;


    location / {
        rewrite ^ /index.php;
    }

    location ~ ^\/(?:build|tests|config|lib|3rdparty|templates|data)\/ {
        deny all;
    }
    location ~ ^\/(?:\.|autotest|occ|issue|indie|db_|console) {
        deny all;
    }

    location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+|.+\/richdocumentscode\/proxy)\.php(?:$|\/) {
        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;
        fastcgi_param front_controller_active true;
        fastcgi_pass php-handler;
        fastcgi_intercept_errors on;
        fastcgi_request_buffering off;
        fastcgi_read_timeout 300;
    }

    location ~ ^\/(?:updater|oc[ms]-provider)(?:$|\/) {
        try_files $uri/ =404;
        index index.php;
    }

   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;

    location / {
        rewrite ^ /index.php;
    }

    location ~ ^\/(?:build|tests|config|lib|3rdparty|templates|data)\/ {
        deny all;
    }
    location ~ ^\/(?:\.|autotest|occ|issue|indie|db_|console) {
        deny all;
    }

    location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+|.+\/richdocumentscode\/proxy)\.php(?:$|\/) {
        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;
        fastcgi_param front_controller_active true;
        fastcgi_pass php-handler;
        fastcgi_intercept_errors on;
        fastcgi_request_buffering off;
        fastcgi_read_timeout 300;
    }

    location ~ ^\/(?:updater|oc[ms]-provider)(?:$|\/) {
        try_files $uri/ =404;
        index index.php;
    }
}

}

Это конфигурационный файл для nginx.

Дело в том что сам Nextcloud написан на PHP и основной web-сервер, накотором он работает — это Apache. Но так же может работать в связка php-fpm + nginx. Ну мне так хочется и для этого есть определенный ряд причин, который приходит с опытом. А раз есть образ Nextcloud с php-fpm, то почему бы им не воспользоваться?..

Далее переходим в нужную директорию (если еще не перешли) с помощью cd /opt/nextcloud и все это добро запускаем:


docker-compose -f docker-compose.yml up

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

После того как все загрузиться и запустится открываем у себя на компьютере локально браузер и вводим адрес http://<ваш IP-адрес вашего сервера>:8082. После все делаем поэтапно: отвечаем на вопросы и наблюдаем за действиями системы. Важно также понимать, что выбираем СУБД MySQL, а адрес сервера указываем db. По факту мы указываем не IP-адрес, а доменное имя. Явно мы его нигде не прописывали, но когда мы перечисляем сервисы в файле, Docker Compose присваивает эти имена адресам и внутри сети Docker все они прекрасно определяются. Так устроена эта контейнеризация.

Как только все будет готово и откроется уже админский аккаунт, браузер закрываем и в консоле на удаленном сервер нажимаем комбинацию Ctrl+C. Ждем пока все контейнеры остановятся.

Демонизация

В таком состоянии облако будет работать пока будет открыта консоль сервера. Но ведь хочется, чтобы он работал постоянно. И эту задачу решить достаточно просто.

Для начала создаем файл командой nano /etc/systemd/system/nextcloud.service и записываем в него следующее:


[Unit]
Description=Nextcloud docker-compose
Requires=docker.service
After=docker.service

[Service]
Restart=always

WorkingDirectory=/opt/nextcloud/

# Compose up
ExecStart=/usr/bin/docker-compose -f docker-compose.yml up

# Compose down, remove containers
ExecStop=/usr/bin/docker-compose -f docker-compose.yml down

[Install]
WantedBy=multi-user.target

Описание сервиса готово. Далее нужно сказать демону демонов (ну так получается), чтобы он обновил свой список демонов:


systemctl daemon-reload

После включаем и запускаем:


systemctl enable nextcloud
systemctl start nextcloud

Посмотреть что демон запущен:


systemctl status nextcloud

Немного ждем и все готово. Можно пользоваться.

Обслуживание системы = хорошая система

В комплекте Nextcloud есть специальный файл, который нужно периодически запускать. Этот файл содержит в себе программный код обслуживания: удаление старых записей, очистка кэша, освобождения места и т.д. Этот файл может работать в 3-х режимах: при каждом запросе на сервер, периодический запрос к этому файлу удаленно, добавление его в планировщик. Я сначала пользовался вторым вариантом, но потом решил перенастроить его именно на локальный планировщик. Суть дела не меняет, но локальный запуск, я считаю, будет более правильным.

Для начала зайдем под паролем администратора в само облако и перейдем в Настройки->Основные параметры. У меня стояло Webcron, так как у меня было настроено так. Теперь я поставил Cron.

После этой манипуляции нужно настроить сам планировщик. Для начала нужно проверить, что все работает правильно. В консоле выполняем:


docker ps

Находим в списке наш запущенный контейнер с сервером. В столбце в моем случае есть строчка nextcloud_nextcloud_1. Такое имя формирует Docker Compose. Если перезапустить службу, то будут созданы новые контейнеры, но Docker Compose присвоит эти же имена. Можно присвоить свои имена. Об этом ищите в документации к Docker и Docker Compose, а меня этот вариант более чем устраивает. По этому имени можно обращаться к запущенному контейнеру. Что нам и нужно.

И Попробуем выполнить:


docker exec -it -u www-data nextcloud_nextcloud_1 php cron.php

Если все прошло успешно, то нужно добавить все это чудо в планировщик. Выполняем crontab -e и добавляем строчку:


5 3 * * * docker exec -u www-data nextcloud_nextcloud_1 php cron.php

Здесь мы говорим планировщику, что запуска задание в 3 часа ночи 5 минут, каждый день.

Закрываем и сохраняем. Готово!

Работа контейнеров

Что получилось?

В админском пользователе можно установить дополнения по вкусу. Я НАСТОЯТЕЛЬНО РЕКОМЕНДУЮ создать обычного пользователя и работать под ним, а админский пользователь останется для настройки и обслуживания.

Ко всему прочему можно «делиться» файлами и папками между облаками назначая различные права доступа. Так же можно зайти на сайт https://scan.nextcloud.com и проверить безопасность, но это можно будет сделать, если у вас есть внешний адрес, доменное имя и облако настроено через него по протоколу HTTPS. А вот об этом я расскажу немного позже. Дело не сложное, но очень полезное.

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

Оставить комментарий

Вы должны быть авторизованы, чтобы разместить комментарий.