12개 구축

NVMe 위에 Nextcloud, 뒤에 객체 스토리지로 개인 클라우드 구축

Ryzen 인스턴스에서 Nextcloud를 실행하고, 로컬 NVMe에 Postgres와 Redis를, 모든 사용자 파일은 스토리지 노드의 버킷에 저장되며, 비공개 터널로 연결됩니다.

만들 결과물

두 대의 머신. Ryzen 인스턴스는 Nextcloud, PHP-FPM, Postgres, Redis를 실행하며, 모두 로컬 Gen4 NVMe에 있습니다. 지연 시간에 민감한 작업이 여기서 실행됩니다. 옆에 있는 스토리지 노드는 실제 파일 콘텐츠를 오브젝트 버킷에 보관하며, 값싼 테라바이트가 여기 있습니다. 두 노드 사이에 WireGuard 터널이 있으므로, 이 배치에서 어떤 것도 다른 것에 노출되지 않습니다.

이 분할이 중요한 이유는 기본 오브젝트 스토어를 사용하는 Nextcloud는 사용자 데이터를 로컬 디스크에 전혀 쓰지 않기 때문입니다. NVMe에는 데이터베이스와 코드가 들어 있고, 버킷에는 누군가 업로드하는 모든 바이트가 들어 있습니다. 200GB에서 20TB로 늘어나는 것은 마이그레이션이 아니라 스토리지 노드 업그레이드가 됩니다.

시작하기 전에

  • 애플리케이션용 R-8과 버킷용 S-50, 가능하면 같은 도시에 있어 터널 왕복 시간이 1밀리초 미만이어야 합니다.
  • 두 노드 사이의 터널. WireGuard 가이드가 정확히 이 구성을 만듭니다. 앱에는 10.9.0.1, 스토리지 노드에는 10.9.0.2를 사용하세요.
  • 앱 인스턴스를 가리키는 호스트 이름, 아래 cloud.example.com.

1. 스토리지 노드의 오브젝트 스토리지

MinIO는 단일 바이너리이며 다른 것은 필요 없습니다. 가져와서 검증하고 설치합니다:

cd /usr/local/bin
wget https://dl.min.io/server/minio/release/linux-amd64/minio
wget https://dl.min.io/server/minio/release/linux-amd64/minio.sha256sum
sha256sum -c minio.sha256sum
chmod +x minio
useradd -r -s /sbin/nologin minio
install -d -o minio -g minio /srv/objects

스토리지 노드의 벌크 티어는 NVMe와 별도로 마운트됩니다. 버킷을 벌크 티어에 두고 NVMe는 쓰기 캐시용으로 남겨 두세요. 그것이 존재하는 이유입니다. 그런 다음 유닛 파일은 /etc/systemd/system/minio.service에 있습니다:

[Unit]
Description=MinIO
After=network-online.target [email protected]
Wants=network-online.target

[Service]
User=minio
Group=minio
Environment=MINIO_ROOT_USER=nextcloud
Environment=MINIO_ROOT_PASSWORD=<forty random characters>
ExecStart=/usr/local/bin/minio server /srv/objects --address 10.9.0.2:9000
Restart=always
LimitNOFILE=65535
NoNewPrivileges=yes
ProtectSystem=strict
ReadWritePaths=/srv/objects

[Install]
WantedBy=multi-user.target

터널 주소에만 바인딩하고 전체에 바인딩하지 않는 것이 중요한 줄입니다. 오브젝트 스토어는 공용 인터페이스에서 응답할 이유가 없으며, 이렇게 하면 방화벽 실수로 노출되지 않습니다.

systemctl daemon-reload && systemctl enable --now minio
ss -ltn | grep 9000

2. 애플리케이션 호스트: 패키지

Ryzen 인스턴스에서:

apt update && apt install -y nginx postgresql redis-server unzip \
  php8.4-fpm php8.4-pgsql php8.4-gd php8.4-curl php8.4-zip php8.4-xml \
  php8.4-mbstring php8.4-intl php8.4-bcmath php8.4-gmp php8.4-imagick \
  php8.4-redis php8.4-apcu

3. 데이터베이스

sudo -u postgres psql -c "CREATE USER nextcloud WITH PASSWORD '<a long password>';"
sudo -u postgres psql -c "CREATE DATABASE nextcloud OWNER nextcloud TEMPLATE template0 ENCODING UTF8;"

이 규모에서는 기본 Postgres 설정으로 충분합니다. 이 Nextcloud가 수백 명을 대상으로 한다면 Postgres 튜닝 가이드를 참고하세요. 거기 있는 숫자는 시간을 들일 가치가 있습니다.

4. 설치 전에 구성하는 Nextcloud

cd /var/www
wget https://download.nextcloud.com/server/releases/latest.zip
unzip -q latest.zip && rm latest.zip
chown -R www-data:www-data /var/www/nextcloud

이제 설치 프로그램이 실행되기 전에 해야 하는 부분입니다. 기본 오브젝트 스토어는 모든 파일을 수동으로 옮기지 않고는 기존 인스턴스에 추가할 수 없기 때문입니다. /var/www/nextcloud/config/config.php을 생성합니다:

<?php
$CONFIG = array(
  "objectstore" => array(
    "class" => "\\OC\\Files\\ObjectStore\\S3",
    "arguments" => array(
      "bucket" => "nextcloud",
      "autocreate" => true,
      "key" => "nextcloud",
      "secret" => "<the same forty characters>",
      "hostname" => "10.9.0.2",
      "port" => 9000,
      "use_ssl" => false,
      "use_path_style" => true,
      "region" => "local",
    ),
  ),
);

use_ssl은 의도적으로 false입니다. 트래픽은 이미 인증되고 암호화된 WireGuard 터널을 통과하며, 스토리지 노드에서 TLS를 두 번 종료하는 것은 CPU 사이클과 갱신할 인증서만 추가할 뿐입니다. 두 머신이 다른 도시에 있게 된다면 켜세요.

chown www-data:www-data /var/www/nextcloud/config/config.php
chmod 640 /var/www/nextcloud/config/config.php
cd /var/www/nextcloud
sudo -u www-data php occ maintenance:install --database pgsql \
  --database-name nextcloud --database-user nextcloud \
  --database-pass "<a long password>" \
  --admin-user admin --admin-pass "<another long password>"

5. 캐시, 크론 및 마지막 설정

sudo -u www-data php occ config:system:set memcache.local --value "\\OC\\Memcache\\APCu"
sudo -u www-data php occ config:system:set memcache.distributed --value "\\OC\\Memcache\\Redis"
sudo -u www-data php occ config:system:set memcache.locking --value "\\OC\\Memcache\\Redis"
sudo -u www-data php occ config:system:set redis host --value /run/redis/redis-server.sock
sudo -u www-data php occ config:system:set redis port --value 0
sudo -u www-data php occ config:system:set trusted_domains 0 --value cloud.example.com
sudo -u www-data php occ config:system:set overwrite.cli.url --value https://cloud.example.com
sudo -u www-data php occ background:cron

TCP 대신 유닉스 소켓을 통한 Redis는 모든 잠금 작업에서 syscall 왕복을 절약하며, Nextcloud는 잠금을 많이 사용합니다. www-data를 redis 그룹에 추가하여 소켓에 접근할 수 있게 한 다음, 백그라운드 작업을 타이머에 연결합니다:

usermod -aG redis www-data
systemctl restart php8.4-fpm redis-server
echo "*/5 * * * * php -f /var/www/nextcloud/cron.php" | crontab -u www-data -

PHP-FPM 기본값은 공유 머신을 가정합니다. 당신의 머신은 공유되지 않으므로, /etc/php/8.4/fpm/pool.d/www.conf에서 pm = dynamic, pm.max_children = 60, pm.start_servers = 12, pm.min_spare_servers = 8, pm.max_spare_servers = 20를 설정하고, memory_limit1G로 올리며 /etc/php/8.4/fpm/php.ini에서 설정합니다. 업로드는 upload_max_filesizepost_max_size16G이어야 합니다. 비디오가 90%에서 실패한 이유를 설명하는 것을 좋아하지 않는다면요.

확인

이 구성의 요점은 사용자 파일이 애플리케이션 서버에 없다는 것입니다. 세 가지 명령으로 증명하세요.

sudo -u www-data php occ status
du -sh /var/www/nextcloud/data

상태는 설치됨이고 유지 관리 모드가 아니어야 합니다. 데이터 디렉토리는 수 메가바이트여야 하며 영원히 그 상태를 유지해야 합니다. 오브젝트 스토리지가 기본이 되면 로그만 보관합니다.

이제 무언가 업로드하고 어디에 저장되는지 확인하세요:

head -c 50M /dev/urandom > /tmp/test.bin
curl -u admin -T /tmp/test.bin https://cloud.example.com/remote.php/dav/files/admin/test.bin
du -sh /var/www/nextcloud/data

업로드가 성공하고 데이터 디렉토리는 50MB 증가하지 않았습니다. 스토리지 노드에서 오브젝트가 직접 보입니다:

ls -R /srv/objects/nextcloud | tail -5
du -sh /srv/objects/nextcloud

오브젝트 이름은 urn:oid: 뒤에 파일 ID가 붙습니다. 그래서 버킷은 사용자 폴더를 탐색할 수 있는 사본이 아니며, 데이터베이스와 버킷을 함께 백업하거나 전혀 백업하지 않아야 합니다.

이후

Postgres 덤프와 버킷에 대해 오프노드 백업 애드온을 사용하거나 제3의 사이트에 직접 백업을 실행하세요. 데이터베이스가 없는 버킷은 번호가 매겨진 blob 더미이고, 버킷이 없는 데이터베이스는 더 이상 존재하지 않는 파일의 인덱스입니다. 둘 중 하나를 잃으면 둘 다 잃는 것입니다.

준비 완료

도시를 고르고, 크기를 고르고, 코인으로 결제하세요.

신원 확인 양식도, 승인을 기다리는 담당자도, 검증을 위한 전화도 없습니다. 청구서가 결제되면 자격 증명이 받은 편지함에 도착합니다.