이 글은 기존 운영했던 WordPress 블로그인 PyxisPub: Development Life (pyxispub.uzuki.live) 에서 가져온 글 입니다. 모든 글을 가져오지는 않으며, 작성 시점과 현재 시점에는 차이가 많이 존재합니다.
작성 시점: 2017-11-05
NextCloud 는 Dropbox 와 비슷하게 파일 호스팅 서비스를 제공하는 솔루션인데, 개인 서버에도 설치할 수 있다.
오늘 퇴근하고 나서 약 5시간 동안 삽질 끝에 설치에 성공했는데, 겸사겸사 정리도 해보려고 한다.
제목대로, 제로부터 NextCloud 구축까지 시행할 것이다.
즉, 아무것도 없는 상태에서 Nginx, PHP-FPM, MariaDB, Let's Encrypt SSL 연결, NextCloud 까지 전부 설정을 마칠 것이다.
작업 예상시간은 1시간 이하이다.
참고로, [ ] 로 감싸진 것의 경우 자기가 원하는 정보로 교체하면 된다.
VPS 는 웹호스팅과 서버호스팅의 중간 형태로, 하나의 물리 서버를 여러 개의 가상 서버로 나누어 사용하면서 각각의 가상 서버를 독립적으로 운영할 수 있게 하는 것이다.
여기서는 Vultr 에 있는 UzukiLive 서버에서 진행한다. 옵션은 아래와 같다.
NextCloud를 어느정도까지 사용할 것에 따라 다르지만, 보통 2 CPU 에 16GB 이상이 150명까지 환경에 적당하다고 한다.
Deployment Recommendation 에 어느정도 나와있으니 자기의 목적에 맞는 VPS 를 찾아보자.
나는 적어도 혼자나 많아봤자 5~6명일거라서, 기존에 $5 옵션 이었던 1 CPU 1GB 에서 1 CPU 2GB 로 증설했다.
초기 서버 설정으로 관리용 계정을 root 이외에 하나 더 만드는 것이다.
마음에 드는 터미널로 접속하자.
ssh root@***.***.***.***
비밀번호는 Vultr 기준 인스턴스 메인에 있다.
adduser [pyxis]
유저를 추가하는 명령어이다.
그러면 비밀번호와 정보를 물어볼텐데, 적당히 채워주고 생성을 마친다.
usermod -aG sudo [pyxis]
그리고 방금 추가한 유저에게 root 권한을 부여한다.
이렇게 해서 루트 유저 추가가 끝났고, 만든 유저로 로그인하자.
ssh pyxis@***.***.***.***
이런 메세지가 표시되면 성공한 것이다.
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 값을 변경하는데, 각각 설명은 다음과 같다.
참고로 nano 의 사용법을 몰라도 이 세개만 기억하면 된다.
변경한 후, Nginx 의 서비스를 재시작해주자.
sudo systemctl restart nginx.service
현재 최신버전은 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
이런 구조로 되면 된다.
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.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
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
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 설정할 때 필요하니 잘 기억해두자.
사실 하나하나 추가해야 되지만, 여기서는 완전한 파일만 기재한다.
/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
이 작업을 위해서는 도메인이 필요하다. 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 [pyxis@uzuki.live] -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
참고로 꽤나 오래 걸리므로 커피 한잔 마시고 오는 것이 좋다.
/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+ 로 통과할 수 있는 설정이나 필요한 조건에 따라 더 추가할 수 있다.
Let's Encrypt 는 무료인 대신 90일 마다 갱신을 해줘야 한다. crontab 로 자동으로 갱신하도록 설정해준다.
crontab -e
42 23 * * 1 /usr/bin/certbot renew >> /var/log/le-renew.log
기존 http 1.1 을 개선한 http의 새로운 프로토콜로, 여러 방법을 사용하여 지연 시간을 감소시킨 프로토콜이다.
위 11번의 설정 파일에서 16 ~ 17줄 부분에 교체만 해주면 된다.
listen 443 ssl http2;
listen [::]:443 ssl http2;
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;
추가
마지막으로 server_name 로 설정한 주소로 들어가보면 설정 마법사가 나온다.
맨 위 두개 필드에는 아이디와 비번을, 그 다음에는 파일을 보관할 장소를 적는다.
마지막 4개 필드에는 7번에서 설정한 데이터베이스 정보를 넣는데, 각각 유저 이름, 비밀번호, 데이터베이스 이름, 주소를 넣는다.
만일 finish setup 에서 넘어가지 않다면 맨 밑 localhost 를 localhost:3306 으로 변경하면 되는 것 같다.
총 15단계에 걸쳐서 설치가 끝났다.
추가적으로 OPCache, redis 를 설치하면 더 속도가 상승되지만 그건 나중에 언급해도 괜찮을 것 같다.