V prvním rozhovoru jsme se s Tomášem Smetkou pobavili o jeho dítku, Pobo Page Builderu, a o tom, kam se za rok ve vývoji posunuli. Následně jsme ale nakousli i další téma, a sice technologický background, na němž celý projekt běží. Ti, kteří si chtějí rozjet Nuxt/Next.js aplikaci na vlastním serveru s VPS Centrem jsou nyní na správné adrese.
V následujícím článku si ukážeme nastavení, instalaci a zprovoznění aplikace postavené na Node.js s podporou SSR a routováním za proxy Nginx na VPS či dedikovaných serverech.
Pro naše účely využijeme framework Nuxt 3, jehož základ tvoří Vue.js (pro Next.js a React bude konfigurace totožná). Pro ukázku použijeme doménu www.droparchitect.com, co by velmi jednoduchou osobní prezentaci s podporou více jazyků. Budeme předpokládat následující doménovou strukturu:
- www.droparchitect.com – Prezentace postavená na Nuxt 3 se zapnutým SSR (server side rendering)
- api.droparchitect.com – REST API v PHP
- app.droparchitect.com – SPA aplikace běžící čistě prohlížeči (např. CRM / ERP, které nepotřebuje SSR)
Adresářová struktura na serveru bude následující:
VPS Centrum
Vyzkoušejte zdarma naši aplikaci pro správu serveru a domén. Budete si připadat jako zkušený administrátor.
www/hosting/droparchitect.com -┓
├── api/public
├── app
└── www
Instalace Node.js
Abychom mohli zprovoznit aplikaci na serveru, je potřeba nainstalovat Node.js. Připojíme se na server a zjistíme, zda je Node.js nainstalován:
node -v
V našem případě Node.js nainstalován není, čili provedeme jeho stažení a instalaci. V době psaní tohoto článku byla poslední verze LTS 22.x, kterou stáhneme příkazem:
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
Následně provedeme instalaci:
sudo apt-get install -y nodejs
Na závěr opět ověříme verzi node (node -v), v terminálu bychom měli vidět přibližně:
v22.xx.x
Konfigurace proxy Nginx serveru
Nyní se podíváme konfiguraci Nginx serveru, kterou upravíme tak, aby reflektoval doménovou strukturu v úvodu.
Přihlásíme se VPS Centra a ověříme, zda máme aktivní Nginx, případně přepneme.
Vytvoření konfigurace pro Nginx
Vytvoříme si šablonu konfiguračního souboru pro Nginx s PHP 8.3:
touch /etc/nginx/scripts/nginx-node-template.conf
A vložíme do něj:
Freelo - Nástroj na řízení úkolů a projektů
Přidej se, pozvi svůj tým a klienty, rozděl práci a sleduj, jak se úkoly dají do pohybu.
upstream socket_{{hostname}}_php {
server unix:/run/php8.3-fpm-{{hostname}}.sock;
}
proxy_cache_path /www/hosting/cache-{{hostname}} levels=1:2 keys_zone=cache_{{hostname}}:10m max_size=10g inactive=60m use_temp_path=off;
# Redirect HTTP to HTTPS
server {
listen 80;
server_name app.{{hostname}} api.{{hostname}} www.{{hostname}} {{hostname}};
return 301 https://$host$request_uri;
}
# SPA App - app.{{hostname}}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name app.{{hostname}};
include /etc/nginx/scripts/acme.conf;
include /etc/nginx/scripts/domain-default.conf;
include /etc/nginx/sites-available/domains_conf/{{hostname}}-ssl.conf;
root /www/hosting/{{hostname}}/app;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
}
# API server - api.{{hostname}}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name api.{{hostname}};
include /etc/nginx/scripts/acme.conf;
include /etc/nginx/scripts/php-version.conf;
include /etc/nginx/scripts/domain-default.conf;
access_log /var/log/nginx/{{hostname}}/{{hostname}}-access.log;
error_log /var/log/nginx/{{hostname}}/{{hostname}}-error.log notice;
include /etc/nginx/sites-available/domains_conf/{{hostname}}-ssl.conf;
root /www/hosting/{{hostname}}/api/public;
index index.php;
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_pass socket_{{hostname}}_php;
fastcgi_param PATH_INFO $fastcgi_script_name;
fastcgi_intercept_errors on;
}
location ~ ^/(php_status|php_ping)$ {
allow 127.0.0.1;
allow ::1;
allow {{upstream_ip}};
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_pass socket_{{hostname}}_php;
}
location @socket_{{hostname}}_php {
limit_conn connperip 20;
limit_req zone=flood burst=12 nodelay;
limit_req zone=bot burst=100 nodelay;
fastcgi_pass socket_{{hostname}}_php;
}
location ~ /\.ht {
deny all;
}
}
# Nuxt server - www.{{hostname}}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name www.{{hostname}} {{hostname}};
include /etc/nginx/scripts/acme.conf;
include /etc/nginx/scripts/domain-default.conf;
include /etc/nginx/sites-available/domains_conf/{{hostname}}-ssl.conf;
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_min_length 256;
gzip_types
application/atom+xml
application/geo+json
application/javascript
application/x-javascript
application/json
application/ld+json
application/manifest+json
application/rdf+xml
application/rss+xml
application/xhtml+xml
application/xml
font/eot
font/otf
font/ttf
image/svg+xml
text/css
text/javascript
text/plain
text/xml;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
proxy_buffering on;
proxy_pass http://{{upstream_ip}}:{{upstream_port}};
proxy_read_timeout 90;
proxy_connect_timeout 90;
# Cache
proxy_cache cache_{{hostname}};
proxy_cache_revalidate on;
proxy_cache_min_uses 3;
proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
proxy_cache_lock on;
proxy_cache_background_update on;
proxy_cache_valid 200 1d;
}
}
Dále si vytvoříme konfigurační include soubor pro SSL:
touch /etc/nginx/scripts/nginx-node-ssl.conf
A vložíme do něj:
ssl_certificate /etc/letsencrypt/live/{{hostname}}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{{hostname}}/privkey.pem;
ssl_session_timeout 20m;
ssl_prefer_server_ciphers on;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/ssl/certs/ssl-bundle.pem;
resolver 8.8.4.4 8.8.8.8 valid=300s;
resolver_timeout 10s;
add_header X-Content-Type-Options nosniff;
Nyní máme hotovu šablonu a překopírujeme konfiguraci pro Nginx:
sed -e "s/{{hostname}}/droparchitect.com/g" -e "s/{{upstream_ip}}/95.168.198.18/g" -e "s/{{upstream_port}}/3001/g" /etc/nginx/scripts/nginx-node-template.conf > /etc/nginx/sites-available/droparchitect.com.conf
Věnujte prosím pozornost hodnotám v příkazu:
- Na místo droparchitect.com změníme na tu, kterou pracujeme (bez www)
- IP adresu 95.168.198.18 zvolíme podle toho, jaká nám byla přidělena
- Port 3001 zvolíme dle svého uvážení (pro naše účely záměrně volíme záměrně jinou, než výchozí)
- Název konfiguračního souboru droparchitect.com.conf opět zvolíme podle názvu domény
Nyní překopírujeme konfiguraci pro SSL:
# sed -e "s/{{hostname}}/droparchitect.com/g" /etc/nginx/scripts/nginx-node-ssl.conf > /etc/nginx/sites-available/domains_conf/droparchitect.com-ssl.conf
Opět zvolíme název konfiguračního souboru dle názvu naší domény.
Nyní validujeme konfiguraci Nginx:
nginx -t
měl by vrátit:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successfu
a restartujeme Nginx:
systemctl restart nginx
Všimněte si, že Nuxt poběží na portu „3001“ namísto standardního „3000“. Je to z toho důvodu, abychom si:
- ukázali možnost spouštět více Nodejs aplikací na jednom serveru a nedocházelo ke kolizím portů.
- Nginx máme nakonfigurovaný, nyní si Nuxt aplikaci připravíme pro běh na serveru.
Stažení a build Nuxt aplikace
Na serveru přejdeme do adresáře: /www/hosting/droparchitect.com a stáhneme si Nuxt aplikaci do adresáře „www“:
git clone git@github.com:xxx/xxx.git www
Následně nainstalujeme závislosti a vytvoříme build:
cd www
npm install
npm run build
Aplikaci zatím nebudeme spouštět, to si ukážeme níže. Aby Nuxt běžel na portu 3001, přidáme si do package.json nový skript:
"scripts": {
...
...
"prod": "PORT=3001 node .output/server/index.mjs
}
Nyní máme připravený build aplikace, který poběží na portu 3001.
Vytvoření supervisora pro běh aplikace
Abychom měli jistotu, že aplikace pojede i po restartu serveru, využijeme nástroj Supervisor.
Ověříme, zda je supervisor nainstalován:
supervisorctl
Pokud nainstalován není, nainstalujeme jej:
apt-get install supervisor
A spustíme:
systemctl enable supervisor
systemctl start supervisor
Supervisor máme úspěšně nainstalovaný a spuštěný, nyní můžeme vytvořit konfiguraci pro běh aplikace.
Vytvoříme konfigurační soubor pro Nuxt aplikaci:
touch /etc/supervisor/conf.d/www.droparchitect.com.conf
A upravíme jej následovně:
[program:www.droparchitect.com]
directory=/www/hosting/droparchitect.com/www/ command=npm run prod
user=user
autostart=true
autorestart=true
stopasgroup=true
stopsignal=QUIT stdout_logfile=/www/hosting/droparchitect.com/log/worker-log.log
stderr_logfile=/www/hosting/droparchitect.com/log/worker-error.log
Nyní reloadneme konfiguraci supervisora a spustíme aplikaci:
supervisorctl reread
supervisorctl update
supervisorctl start www.droparchitect.com
Ověříme stav aplikace:
supervisorctl status
Měli bychom vidět něco podobného:
www.droparchitect.com RUNNING pid 1234, uptime 0:00:00
Nyní by měla aplikace běžet v prohlížeči.
Vysvětlení konfigurace:
directory
– adresář, kde se nachází aplikacecommand
– příkaz, který spustí příkaz pro spuštění aplikace (npm run prod v package.json)user
– uživatel, pod kterým bude aplikace spuštěnaautostart
– automaticky spustí aplikaci po restartu serveruautorestart
– automaticky restartuje aplikaci v případě pádustopasgroup
– zastaví procesy v rámci skupinystopsignal
– signál, kterým se zastaví aplikacestdout_logfile
– soubor pro logování výstupustderr_logfile
– soubor pro logování chyb
Tímto máme rozběhnutý frontend – živou ukázku můžeme vidět na www.droparchitect.com.
Na závěr se pustíme do nastavení Github pipeline, který nám zajistí automatický deploy aplikace.
Sestavení Github pipeline
V projektu vytvoříme soubor .github/workflows/deploy.yml a upravíme jej následovně:
name: Deploy website to production (www.droparchitect.com)
on:
push:
branches: ["master"]
jobs:
production:
runs-on: ubuntu-latest
steps:
- name: Deploy to www.droparchitect.com
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USERNAME }}
password: ${{ secrets.SERVER_PASS }}
script: |
cd /www/hosting/droparchitect.com/www
git reset --hard
git pull origin master
npm install
npm run build
supervisorctl restart www.droparchitect.com
Následně přejdeme do nastavení Github repozitáře a v sekci „Secrets“ přidáme:
- SERVER_HOST – název serveru (např. xxx01.vas-server.cz v závislosti na přiděleném serveru)
- SERVER_USERNAME – uživatel, pod kterým se připojíme na server
- SERVER_PASS – heslo uživatele
Nyní se po každém pushnutí do masteru provede deploy aplikace na server, kdy se stáhnou nové změny, nainstalují závislosti, provede build a restartuje se aplikace.
Na závěr necháme ve VPS centrum vygenerovat SSL certifikáty pro domény app.droparchitect.com, www.droparchitect.com a api.droparchitect.com:
Tímto máme kompletně nastavený stack pro běh Nuxt aplikace za Nginx proxy na VPS serveru.
V případě dotazů nebo problémů mě neváhejte kontaktovat na tomas@droparchitect.com.