← Zpět na všechny články blogu

Instalace, provoz a deploy Nuxt/Next.js aplikací na Vas-Hosting.cz

Tomáš Smetka
Tomáš Smetka Aktualizováno 27. 9. 2024 – 10 min. čtení
Blog

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:

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:

  1. Na místo droparchitect.com změníme na tu, kterou pracujeme (bez www)
  2. IP adresu 95.168.198.18 zvolíme podle toho, jaká nám byla přidělena
  3. 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í)
  4. 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í aplikace
  • command –⁠⁠⁠⁠⁠⁠ 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ěna
  • autostart –⁠⁠⁠⁠⁠⁠ automaticky spustí aplikaci po restartu serveru
  • autorestart –⁠⁠⁠⁠⁠⁠ automaticky restartuje aplikaci v případě pádu
  • stopasgroup –⁠⁠⁠⁠⁠⁠ zastaví procesy v rámci skupiny
  • stopsignal –⁠⁠⁠⁠⁠⁠ signál, kterým se zastaví aplikace
  • stdout_logfile –⁠⁠⁠⁠⁠⁠ soubor pro logování výstupu
  • stderr_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.

Zůstaňte s námi v kontaktu

Jednou za měsíc posíláme souhrn novinek. Nemusíte se bát, spamovat vás nebudeme a odhlásit se můžete kdykoliv...

Karel Dytrych
Tým Váš Hosting
Vyzkoušejte náš trial na týden zdarma

Garance 14denní záruky vrácení peněz

Vyzkoušejte server na týden zdarma

Vyzkoušet server