제로부터 시작하는 NextCloud 설치하기 on VPS

NextCloudDropbox 와 비슷하게 파일 호스팅 서비스를 제공하는 솔루션인데, 개인 서버에도 설치할 수 있다.

오늘 퇴근하고 나서 약 5시간 동안 삽질 끝에 설치에 성공했는데, 겸사겸사 정리도 해보려고 한다.

목적

제목대로, 제로부터 NextCloud 구축까지 시행할 것이다.

즉, 아무것도 없는 상태에서 Nginx, PHP-FPM, MariaDB, Let’s Encrypt SSL 연결, NextCloud 까지 전부 설정을 마칠 것이다.

작업 예상시간은 1시간 이하이다.

참고로, [ ] 로 감싸진 것의 경우 자기가 원하는 정보로 교체하면 된다.

0. VPS 구매하기

VPS 는 웹호스팅과 서버호스팅의 중간 형태로, 하나의 물리 서버를 여러 개의 가상 서버로 나누어 사용하면서 각각의 가상 서버를 독립적으로 운영할 수 있게 하는 것이다.

여기서는 Vultr 에 있는 UzukiLive 서버에서 진행한다. 옵션은 아래와 같다.

NextCloud를 어느정도까지 사용할 것에 따라 다르지만, 보통 2 CPU 에 16GB 이상이 150명까지 환경에 적당하다고 한다.

Deployment Recommendation 에 어느정도 나와있으니 자기의 목적에 맞는 VPS 를 찾아보자.

나는 적어도 혼자나 많아봤자 5~6명일거라서, 기존에 $5 옵션 이었던 1 CPU 1GB 에서 1 CPU 2GB 로 증설했다.

1. 루트 유저 추가하기

초기 서버 설정으로 관리용 계정을 root 이외에 하나 더 만드는 것이다.

마음에 드는 터미널로 접속하자.

ssh [email protected]***.***.***.***

비밀번호는 Vultr 기준 인스턴스 메인에 있다.

adduser [pyxis]

유저를 추가하는 명령어이다.

그러면 비밀번호와 정보를 물어볼텐데, 적당히 채워주고 생성을 마친다.

usermod -aG sudo [pyxis]

그리고 방금 추가한 유저에게 root 권한을 부여한다.

이렇게 해서 루트 유저 추가가 끝났고, 만든 유저로 로그인하자.

ssh [email protected]***.***.***.***

이런 메세지가 표시되면 성공한 것이다.

2. Nginx

Nginx Web Server 로, Apache 를 쓸 수도 있었지만 개인적으로는 Nginx 가 좀 더 친숙했으므로 Nginx 를 사용하려고 한다.

sudo apt-get -y install nginx

위 명령어로 Nginx 를 설치한다.

한 가지 팁으로, sudo 를 일일히 치기 귀찮다면 sudo -i로 root 모드로 들어간 상태에서 진행할 수 있다.

설치를 다 한다음, sudo nano /etc/nginx/nginx.conf를 입력해서 에디터를 연다.

user www-data;
worker_processes 8;
pid /run/nginx.pid;
 
events {
    worker_connections 768;
    # multi_accept on;                                                                                                                                                                                                                   
}
 
http {
 
    ##                                                                                                                                                                                                                                   
    # Basic Settings                                                                                                                                                                                                                     
    ##                                                                                                                                                                                                                                   
 
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    server_tokens off;                                                                                                                                                                                                                 
 
    # server_names_hash_bucket_size 64;                                                                                                                                                                                                  
    # server_name_in_redirect off;                                                                                                                                                                                                       
 
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

worker_processes 값과 server_tokens 값을 변경하는데, 각각 설명은 다음과 같다.

  • worker_processes: 하나의 worker 가 동시에 실행할 수 있는 Thread 수이다.
  • server_tokens: 서버 버전 정보 표시 금지

참고로 nano 의 사용법을 몰라도 이 세개만 기억하면 된다.

  • 방향키: 이동, 백스페이스: 지우기
  • control + K : 라인 지우기
  • control + X : 저장

변경한 후, Nginx 의 서비스를 재시작해주자.

sudo systemctl restart nginx.service

3. NextCloud 다운로드

현재 최신버전은 12.0.3 이다.

cd /var/www
sudo wget https://download.nextcloud.com/server/releases/nextcloud-12.0.3.zip
unzip nextcloud-12.0.3.zip
rm -rf nextcloud-12.0.3.zip

이런 구조로 되면 된다.

4. NextCloud 폴더에 권한 설정

sudo adduser nextcloud
sudo chown -R nextcloud:www-data /var/www/nextcloud
sudo chmod -R o-rwx /var/www/nextcloud

nextcloud 유저를 만든 다음, 해당 유저 및 www-data 그룹을 방금 압축을 푼 /var/www/nextcloud 에 권한을 설정한다.

5. PHP-FPM 설정

5.6 에 비해 7.0에서 좀 더 php의 속도가 증가했으므로 7.0을 설치하기를 추천한다.

sudo apt-get -y install php-cli php-json php-curl php-imap php-gd php-mysql php-xml php-zip php-intl php-mcrypt php-imagick php-mbstring
sudo apt-get install -y php-fpm

설치가 다 되면 /etc/php/7.0/fpm/pool.d 폴더에 nextcloud 전용 설정 파일을 만든다.

[nextcloud]
listen = /var/run/nextcloud.sock
 
listen.owner = nextcloud
listen.group = www-data
 
user = nextcloud
group = www-data
 
pm = ondemand
pm.max_children = 30
pm.process_idle_timeout = 60s
pm.max_requests = 500
 
env[HOSTNAME] = $HOSTNAME
env[PATH] = /usr/local/bin:/usr/bin:/bin
env[TMP] = /tmp
env[TMPDIR] = /tmp
env[TEMP] = /tmp

다 만들었으면 php 서비스를 재시작하자.

sudo systemctl restart php7.0-fpm.service

6. MariaDB 설치

sudo apt-get install -y mariadb-server mariadb-client

MySQL 를 사용해도 상관없다.

$ sudo mysql_secure_installation
 
Set root password? [Y/n] Y
Remove anonymous users? [Y/n] Y
Disallow root login remotely? [Y/n] Y
Remove test database and access to it? [Y/n] Y
Reload privilege tables now? [Y/n] Y

7. Nextcloud 데이터베이스 설정

sudo mysql -u root -p
MariaDB> CREATE DATABASE nextcloud;
Query OK, 1 row affected (0.00 sec)

MariaDB> CREATE USER "nextcloud"@"localhost";
Query OK, 0 rows affected (0.00 sec)
 
MariaDB> SET password FOR "nextcloud"@"localhost" = password('[PASSWORD]');
Query OK, 0 rows affected (0.00 sec)
 
MariaDB> GRANT ALL PRIVILEGES ON nextcloud.* TO "nextcloud"@"localhost" IDENTIFIED BY "[PASSWORD]";
Query OK, 0 rows affected (0.00 sec)
 
MariaDB> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)
 
MariaDB> EXIT
Bye

PASSWORD 는 마지막 NextCloud 설정할 때 필요하니 잘 기억해두자.

8. Nginx 설정 파일 만들기

사실 하나하나 추가해야 되지만, 여기서는 완전한 파일만 기재한다.

/etc/nginx/sites-available 폴더에 nextcloud 란 이름으로 파일을 만든다.

upstream php-handler {

    server unix:/var/run/nextcloud.sock;
}

server {

    listen 80;
    listen [::]:80;
    server_name [cloud.uzuki.live];

    root /var/www/nextcloud/;

    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Robots-Tag none;
    add_header X-Download-Options noopen;
    add_header X-Permitted-Cross-Domain-Policies none;
    add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains;';

    location = /robots.txt {

        allow all;
        log_not_found off;
        access_log off;
    }

    location = /.well-known/carddav {

        return 301 $scheme://$host/remote.php/dav;
    }

    location = /.well-known/caldav {

        return 301 $scheme://$host/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$uri;
    }

    location ~ ^/.well-known/acme-challenge/* {

        allow all;
    }

    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/.+|ocs-provider/.+)\.php(?:$|/) {

        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        fastcgi_param modHeadersAvailable true;
        fastcgi_param front_controller_active true;
        fastcgi_pass php-handler;
        fastcgi_intercept_errors on;
        fastcgi_request_buffering off;
    }

    location ~ ^/(?:updater|ocs-provider)(?:$|/) {

        try_files $uri/ =404;
        index index.php;
    }

    location ~* \.(?:css|js|woff|svg|gif)$ {

        try_files $uri /index.php$uri$is_args$args;
        add_header Cache-Control "public, max-age=7200";
        add_header X-Content-Type-Options nosniff;
        add_header X-XSS-Protection "1; mode=block";
        add_header X-Robots-Tag none;
        add_header X-Download-Options noopen;
        add_header X-Permitted-Cross-Domain-Policies none;

        access_log off;
    }

    location ~* \.(?:png|html|ttf|ico|jpg|jpeg)$ {

        try_files $uri /index.php$uri$is_args$args;
        access_log off;
    }
}
sudo ln -s /etc/nginx/sites-available/nextcloud /etc/nginx/sites-enabled/nextcloud
nginx -t

심볼릭 링크를 생성하고 설정 파일이 문법에 맞는지 확인한다.

이와 같이 나온다면 재시작을 해주자.

sudo systemctl restart nginx.service
sudo systemctl restart php7.0-fpm.service

9. Let’s Encrypt로 SSL 인증받기

이 작업을 위해서는 도메인이 필요하다. NextCloud 는 SSL 이하 사용을 권장하므로 Self-Signing 라도 해야되지만 SSL 인증서를 구매하거나 적어도 Let’s Encrypt 로 인증을 거쳐야 한다.

sudo apt-get install -y software-properties-common
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install -y certbot
sudo certbot certonly --webroot -w /var/www/nextcloud --agree-tos --email [[email protected]] -d [cloud.uzuki.live] --rsa-key-size 4096

인증이 완료되었다고 나오면 dhparams 키도 추가로 생성해주자.

sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096
sudo chmod 600 /etc/ssl/certs/dhparam.pem

참고로 꽤나 오래 걸리므로 커피 한잔 마시고 오는 것이 좋다.

10. SSL 설정 반영하기

/etc/nginx/sites-available/nextcloud 를 수정한다.

Let’s Encrypt 로 받은 pem 키와 위에서 생성한 dhparam 키를 각각 넣어준다.

upstream php-handler {

    server unix:/var/run/nextcloud.sock;
}

server {

    listen 80;
    listen [::]:80;
    server_name [cloud.uzuki.live];
    return 301 https://$server_name$request_uri;
}

server {

    listen 443 ssl;
    listen [::]:443 ssl;
    server_name [cloud.uzuki.live];

    root /var/www/nextcloud/;

    ssl on;
    ssl_certificate /etc/letsencrypt/live/[cloud.uzuki.live]/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/[cloud.uzuki.live]/privkey.pem;
    ssl_dhparam /etc/ssl/certs/dhparam.pem;

    ssl_session_cache shared:SSL:1m;
    ssl_session_timeout 1440m;
    ssl_buffer_size 8k;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

    ssl_ciphers 'kEECDH+ECDSA+AES128 kEECDH+ECDSA+AES256 kEECDH+AES128 kEECDH+AES256 kEDH+AES128 kEDH+AES256 DES-CBC3-SHA +SHA !aNULL !eNULL !LOW !kECDH !DSS !MD5 !EXP !PSK !SRP !CAMELLIA !SEED';
    ssl_prefer_server_ciphers on;

    ssl_trusted_certificate /etc/letsencrypt/live/cloud.uzuki.live/chain.pem;
    ssl_stapling on;
    ssl_stapling_verify on;

    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Robots-Tag none;
    add_header X-Download-Options noopen;
    add_header X-Permitted-Cross-Domain-Policies none;
    add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains;';

    location = /robots.txt {

        allow all;
        log_not_found off;
        access_log off;
    }

    location = /.well-known/carddav {

        return 301 $scheme://$host/remote.php/dav;
    }

    location = /.well-known/caldav {

        return 301 $scheme://$host/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$uri;
    }

    location ~ ^/.well-known/acme-challenge/* {

        allow all;
    }

    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/.+|ocs-provider/.+)\.php(?:$|/) {

        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        fastcgi_param HTTPS on;
        #Avoid sending the security headers twice
        fastcgi_param modHeadersAvailable true;
        fastcgi_param front_controller_active true;
        fastcgi_pass php-handler;
        fastcgi_intercept_errors on;
        fastcgi_request_buffering off;
    }

    location ~ ^/(?:updater|ocs-provider)(?:$|/) {

        try_files $uri/ =404;
        index index.php;
    }

    location ~* \.(?:css|js|woff|svg|gif)$ {

        try_files $uri /index.php$uri$is_args$args;
        add_header Cache-Control "public, max-age=7200";
        add_header X-Content-Type-Options nosniff;
        add_header X-XSS-Protection "1; mode=block";
        add_header X-Robots-Tag none;
        add_header X-Download-Options noopen;
        add_header X-Permitted-Cross-Domain-Policies none;
        # Optional: Don't log access to assets
        access_log off;
    }

    location ~* \.(?:png|html|ttf|ico|jpg|jpeg)$ {

        try_files $uri /index.php$uri$is_args$args;
        access_log off;
    }
}
sudo systemctl reload nginx.service

위 설정들은 SSL Test를 A+ 로 통과할 수 있는 설정이나 필요한 조건에 따라 더 추가할 수 있다.

11. 인증서 자동 갱신

Let’s Encrypt 는 무료인 대신 90일 마다 갱신을 해줘야 한다. crontab 로 자동으로 갱신하도록 설정해준다.

crontab -e
42 23 * * 1 /usr/bin/certbot renew >> /var/log/le-renew.log

12. http2

기존 http 1.1 을 개선한 http의 새로운 프로토콜로, 여러 방법을 사용하여 지연 시간을 감소시킨 프로토콜이다.

위 11번의 설정 파일에서 16 ~ 17줄 부분에 교체만 해주면 된다.

listen 443 ssl http2;
listen [::]:443 ssl http2;

13. 타임아웃 설정

1. /etc/php/7.0/fpm/pool.d/nextcloud.conf 맨 밑 request_terminate_timeout = 300 추가

2. /etc/nginx/sites-available/nextcloud 의 105번째 줄 밑에 fastcgi_read_timeout 300; 추가

14. 접속

마지막으로 server_name 로 설정한 주소로 들어가보면 설정 마법사가 나온다.

이미지 출처: https://www.pcextreme.nl/community/d/153-set-up-your-own-cloud-storage-within-minutes-using-nextcloud-and-aurora-s3

맨 위 두개 필드에는 아이디와 비번을, 그 다음에는 파일을 보관할 장소를 적는다.

마지막 4개 필드에는 7번에서 설정한 데이터베이스 정보를 넣는데, 각각 유저 이름, 비밀번호, 데이터베이스 이름, 주소를 넣는다.

만일 finish setup 에서 넘어가지 않다면 맨 밑 localhost 를 localhost:3306 으로 변경하면 되는 것 같다.

15. 끝

총 15단계에 걸쳐서 설치가 끝났다.

추가적으로 OPCache, redis 를 설치하면 더 속도가 상승되지만 그건 나중에 언급해도 괜찮을 것 같다.