Linux – E1W


Postupy rozbehania Linuxu pre E1W

Tieto kroky boli odskúšané na Ubuntu 24.04.

Ako vytvoriť SSH kľúč

V PowerShell, CMD alebo v Linux termináli spusti:

ssh-keygen -t ed25519 -C "tvoje.meno@tvojafirma.sk"

Potom sa ťa to opýta:

Enter file in which to save the key (/home/pavol/.ssh/id_ed25519):

Stlač ENTER (uloží sa do default .ssh priečinka), alebo zadaj vlastnú cestu.

Následne:

Enter passphrase (empty for no passphrase):

Odporúčané: nechaj prázdne → ENTER.

Kde sa vytvorený kľúč uloží?

Windows:

C:\Users\<tvoje_meno>\.ssh\
  id_ed25519        ← PRIVÁTNÝ KĽÚČ
  id_ed25519.pub    ← PUBLIC KĽÚČ

Linux/macOS:

~/.ssh/
  id_ed25519
  id_ed25519.pub

Prenesenie PUBLIC KEY na VPS

Privátny kľúč nikdy nahrávať na VPS!

Na VPS sa nahráva iba .pub.

Zobrazíš si public key:

cat ~/.ssh/id_ed25519.pub

Výstup bude napríklad:

ssh-ed25519 AAAAC3Nza.... tvoje.meno@tvojafirma.sk

Pridaj ho na VPS

Pripoj sa na VPS:

ssh root@IP_SERVERA

Vytvor súbor:

mkdir -p ~/.ssh
nano ~/.ssh/authorized_keys

Vlož tam celý obsah .pub kľúča.

Nastav práva:

chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

Hotovo. Od teraz VPS prijíma tvoj SSH public key.

Otestuj prihlásenie pomocou SSH kľúča

Na Windows:

ssh -i C:\Users\<meno>\.ssh\id_ed25519 root@IP_SERVERA

Na Linux/macOS (automaticky nájde kľúč):

ssh root@IP_SERVERA

Ak sa prihlásiš bez hesla → OK.

Výpis private kľúča

cat ~/.ssh/id_ed25519

Použitie SSH Private Key v Azure DevOps – SSH service connection

V Azure DevOps:

  1. Project Settings
  2. Service connections
  3. New service connection
  4. Typ: SSH
  5. Vyplň:
    • Host: IP VPS
    • Username: root
    • Private Key: vlož obsah id_ed25519 (súkromný kľúč)
    • Passphrase: prázdne (ak si nechal prázdne)

Private Key → musí byť v čisto OpenSSH formáte a musí vyzerať takto:

-----BEGIN OPENSSH PRIVATE KEY-----
<base64>
-----END OPENSSH PRIVATE KEY-----

Stane sa, že sa nevie prihlásiť, kvôli validácii host key. Podarilo sa mi to rozbehať tak, že pipeline sa mi spúšťa na agentovi, ktorého mám nainštalované na Windows ako službu, ktorá beží pod NetworkService. Vytvoril som adresár

C:\Windows\ServiceProfiles\NetworkService\.ssh\

a do neho súbor known_hosts. AI radí urobiť ho skriptom

ssh-keyscan -t ed25519 194.146.13.157 >> C:\Users\pavol\.ssh\known_hosts

Mne to na Windows nešlo, musel som pustiť na macos toto:

ssh-keyscan -t ed25519 194.146.13.157

Výstup bol niečo ako:

# XXX.XXX.XXX.XXX:22 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.14
XXX.XXX.XXX.XXX ssh-ed25519 AAAAC3NzaC...

a to som nakopíroval do súboru known_hosts a potom sa to rozbehlo

Nastavenie VPS, Docker, Nginx reverse proxy a 2 kontajnery (e1w + e1w-dev)

VPS bude hostovať 2 Docker kontajnery:

  • e1w → produkcia
  • e1w-dev → development

Proxy (Nginx) obslúži 2 domény:

  • webl1.erpio.app → kontajner e1w
  • web9.erpio.app → kontajner e1w-dev

Použije sa wildcard certifikát (*.erpio.app)

Inštalácia Docker + Compose

apt update && apt upgrade -y
apt install ca-certificates curl gnupg -y
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg

Pridaj repozitár:

echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo $VERSION_CODENAME) stable" \
| tee /etc/apt/sources.list.d/docker.list > /dev/null

Nainštaluj docker:

apt update
apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y

Tu mi to skončilo chybou – … GPG error… AI poradila pustiť tento skript a potom to už išlo spustiť

# 1) Odstráň prípadné staré záznamy, ktoré môžu kolidovať
sudo rm -f /etc/apt/sources.list.d/docker.list
sudo rm -f /etc/apt/keyrings/docker.gpg

# 2) Vytvor adresár na keyring a nastav práva
sudo install -m 0755 -d /etc/apt/keyrings

# 3) Stiahni GPG kľúč a ulož ho v binárnej (dearmored) podobe
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
| sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

# 4) Povolenia, aby k nemu vedel APT pristupovať
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# 5) Zapíš Docker repozitár s korektným 'signed-by=' a správnym codename (noble)
sudo sh -c 'echo "deb [arch=$(dpkg --print-architecture) \
signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo $VERSION_CODENAME) stable" \
> /etc/apt/sources.list.d/docker.list'

# 6) Update balíčkov
sudo apt update

Príprava adresárovej štruktúry pre oba kontajnery

mkdir -p /var/www/e1w
mkdir -p /var/www/e1w-dev
mkdir -p /etc/nginx/ssl

Sem nahráš certifikáty:

/etc/nginx/ssl/fullchain.pem
/etc/nginx/ssl/privkey.pem

Nastavíš oprávnenia:

chmod 600 /etc/nginx/ssl/privkey.pem
chmod 644 /etc/nginx/ssl/fullchain.pem

Tieto pem súbory som si vyrobil z pfx, ktoré som dával na windows mašiny:

openssl pkcs12 -in cert.pfx -clcerts -nokeys -out fullchain.pem
openssl pkcs12 -in cert.pfx -nocerts -nodes -out privkey.pem

Docker Compose súbory

Pre každý kontajner som vytvoril príslušný siteconfig.json, kvôli zobrazovaniu informácie (pri čísle verzie), že ktorý server aktuálne mi zobrazuje webového klienta. V docker-compose.yml je potom tento súbor namapovaný namiesto toho, ktorý je súčasťou buildu.

# DEV
mkdir -p /var/www/e1w-dev/config/siteconfig
printf '{\n  "SiteIdent": "DEVL1"\n}\n' > /var/www/e1w-dev/config/siteconfig/erpiositeconfig.json

# PROD
mkdir -p /var/www/e1w/config/siteconfig
printf '{\n  "SiteIdent": "W1"\n}\n' > /var/www/e1w/config/siteconfig/erpiositeconfig.json

docker-compose.yml pre PRODUKCIU – /var/www/e1w/docker-compose.yml

services:
  e1w:
    image: e1w:latest
    build:
      context: .                  # koreň repozitára
      dockerfile: E1Wserver/Dockerfile
    container_name: e1w
    restart: always
    ports:
      - "5001:8080"
    environment:
      - ASPNETCORE_URLS=http://+:8080
    volumes:      
      - /var/www/e1w/config/siteconfig/erpiositeconfig.json:/app/wwwroot/siteconfig/erpiositeconfig.json:ro

docker-compose.yml pre DEV – /var/www/e1w-dev/docker-compose.yml

services:
  e1w-dev:
    image: e1w-dev:latest
    build:
      context: .                  # koreň repozitára
      dockerfile: E1Wserver/Dockerfile
    container_name: e1w-dev
    restart: always
    ports:
      - "5002:8080"
    environment:
      - ASPNETCORE_URLS=http://+:8080
    volumes:      
      - /var/www/e1w-dev/config/siteconfig/erpiositeconfig.json:/app/wwwroot/siteconfig/erpiositeconfig.json:ro

Nastavenie Nginx reverse proxy

Inštalácia Nginx (Ubuntu 24.04)

apt update
apt install -y nginx

Over, že beží:

systemctl status nginx --no-pager
# prípadne:
systemctl enable --now nginx

Pozn.: Balík nginx štandardne vytvorí štruktúru /etc/nginx/sites-available a /etc/nginx/sites-enabled.

Vytvor config pre produkciu:

nano /etc/nginx/sites-available/e1w

Vlož:

server {
    listen 80;
    server_name webl1.erpio.app;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name webl1.erpio.app;

    ssl_certificate /etc/nginx/ssl/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/privkey.pem;

    location / {
        proxy_pass http://localhost:5001;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

Vytvor config pre DEV

nano /etc/nginx/sites-available/e1w-dev

Vlož:

server {
    listen 80;
    server_name web9.erpio.app;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name web9.erpio.app;

    ssl_certificate /etc/nginx/ssl/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/privkey.pem;

    location / {
        proxy_pass http://localhost:5002;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

Aktivácia:

ln -s /etc/nginx/sites-available/e1w /etc/nginx/sites-enabled/
ln -s /etc/nginx/sites-available/e1w-dev /etc/nginx/sites-enabled/
nginx -t
systemctl restart nginx

V tomto kroku som kontajnery nespúšťal, nechal som to až po rozbehnutí DevOps pipelines, keď ich buildli a následne spustili.

Azure DevOps – CI/CD pipeline pre e1w a e1w-dev
  • e1w-dev pipeline → automatický deploy po každom commite
  • e1w pipeline → ručný deploy (prod release)
  • Obe používajú to isté Git repo
  • Deployment = upload súborov z repo na VPS + docker compose build -> up -d
  • Predpokladá sa vytvorená SSH service connection podľ iného bodu

Pipeline pre DEV (automatická)

V repo vytvor azure-pipelines-e1w-dev.yml, deploy po každom push do master

trigger:
- master # auto-trigger je push do master

pool: WinNEOKrabica

steps:
- checkout: self

- task: CopyFilesOverSSH@0
  displayName: 'Copy repo → VPS (/var/www/e1w-dev)'
  inputs:
    sshEndpoint: 'Contabo docker web client'
    sourceFolder: '$(System.DefaultWorkingDirectory)'
    targetFolder: '/var/www/e1w-dev'
    cleanTargetFolder: false

- task: SSH@0
  displayName: 'Docker compose build & up (e1w-dev)'
  inputs:
    sshEndpoint: 'Contabo docker web client'
    runOptions: 'commands'
    failOnStdErr: false
    commands: |
      set -e
      cd /var/www/e1w-dev && docker compose -f ./docker-compose.yml config >/dev/null 2>&1 && docker compose -f ./docker-compose.yml build --progress=plain 2>&1 && docker compose -f ./docker-compose.yml up -d 2>&1

Pipeline pre PRODUKCIU (ručne spúšťaná) azure-pipelines-e1w.yml:

trigger: none   # žiadny auto-trigger, spúšťaš manuálne

pool: WinNEOKrabica

steps:
- checkout: self

- task: CopyFilesOverSSH@0
  displayName: 'Copy repo → VPS (/var/www/e1w)'
  inputs:
    sshEndpoint: 'Contabo docker web client'
    sourceFolder: '$(System.DefaultWorkingDirectory)'
    targetFolder: '/var/www/e1w'
    cleanTargetFolder: false

- task: SSH@0
  displayName: 'Docker compose build & up (e1w)'
  inputs:
    sshEndpoint: 'Contabo docker web client'
    runOptions: 'commands'
    failOnStdErr: false
    commands: |
      set -e
      cd /var/www/e1w && docker compose -f ./docker-compose.yml config >/dev/null 2>&1 && docker compose -f ./docker-compose.yml build --progress=plain 2>&1 && docker compose -f ./docker-compose.yml up -d 2>&1

Následne dvakrát pre dev a prod:

  • Pipelines
  • New pipeline
  • Azure repos Git (YAML)
  • Vybrať repository
  • Existing Azure Pipelines YAML file
  • vybrať branch a súbor yml podľa návodu vyššie

Skúsiť spusťiť pipeline

“Load balancer” HAProxy

HAProxy bude:

  • terminovať HTTPS cez wildcard cert
  • rozdeľovať traffic medzi web1/web2/web3
  • používať cookie sticky session (ako ARRAffinity)
  • podporovať WebSockets (SignalR)
  • mať admin štatistiky na porte 8404

DNS – treba upraviť A záznam pre web.erpio.app a nasmerovať ho na IP kde je HAProxy

Nainštalovanie HAProxy na VPS:

sudo apt update
sudo apt install -y haproxy

Overenie či beží:

systemctl status haproxy

Pripravenie wildcard certifikátu pre HAProxy:

openssl pkcs12 -in ErpioApp2026.pfx -out wildcard.erpio.app.pem -nodes

Táto varianta niekedy končí chybou SEC_ERROR_UNKNOWN_ISSUER. Vyzerá to, že lepšia varianta je zobrať pem, ktorý prišiel od autority a doplniť tam private key. Vraj by pem mal obsahovať toto:

-----BEGIN CERTIFICATE-----
(tvoj wildcard cert)
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
(RapidSSL intermediate)
-----END CERTIFICATE-----
-----BEGIN PRIVATE KEY-----
(tvoj key)
-----END PRIVATE KEY-----

Overenie sa dá urobiť na adrese: https://www.ssllabs.com/ssltest/analyze.html?d=web.erpio.app

Nakopírovať ho do cieľa a nastavenie práv:

sudo mkdir -p /etc/haproxy/certs
sudo mv wildcard.erpio.app.pem /etc/haproxy/certs/wildcard.erpio.app.pem
sudo chmod 600 /etc/haproxy/certs/wildcard.erpio.app.pem

Kompletná konfigurácia HAProxy (HTTPS → 3 backendy s sticky cookie)

Uprav súbor:

nano /etc/haproxy/haproxy.cfg

a dať tam tento obsah:

global
log /dev/log local0
log /dev/log local1 notice
maxconn 20000
user haproxy
group haproxy
daemon
defaults
mode http
log global
option httplog
option dontlognull
timeout connect 5s
timeout client 2h
timeout server 2h
timeout http-request 10s
timeout http-keep-alive 1m
option forwardfor

# ADMIN ŠTATISTIKY
listen stats
bind *:8404
mode http
stats enable
stats uri /haproxy?stats
stats refresh 5s
stats auth admin:NejakeSilneHeslo12345
# FRONTEND – HTTPS
frontend fe_https
bind *:443 ssl crt /etc/haproxy/certs/wildcard.erpio.app.pem
http-request set-header X-Forwarded-Proto https
http-request set-header X-Forwarded-Host %[req.hdr(Host)]
default_backend be_app
# BACKEND – 3 IIS servery
backend be_app
balance roundrobin

# sticky sessions pre Blazor/SignalR
cookie SRV_ID insert indirect nocache secure httponly

# DOČASNE: SSL/TCP health-check (bez HTTP GET /health)
# - kontroluje sa, že server prijme TLS
# - žiadny problém s Host header / SNI pre health-check
default-server check check-ssl inter 3s fall 3 rise 2 verify none

# per-server SNI pre istotu (väčšinou netreba, ale neuškodí)
server web1 web1.erpio.app:443 ssl sni str(web1.erpio.app) cookie w1
server web2 web2.erpio.app:443 ssl sni str(web2.erpio.app) cookie w2
server web3 web3.erpio.app:443 ssl sni str(web3.erpio.app) cookie w3

Syntax check:

sudo haproxy -c -f /etc/haproxy/haproxy.cfg

ak ok:

sudo systemctl restart haproxy

Otestovanie balancera – otvoriť stránku web.erpio.app, ak sa zobrazí, je to nádejné. Test:

  • vypni jeden node (dočasne stop IIS) → HAProxy ho vyradí
  • otvor v Chrome Incognito 3× za sebou → zobrazia sa requesty na rovnaký server (sticky cookie)
  • skús reload → cookie drží server (affinity)

Admin štatistiky

http://IP_TVOJEHO_VPS:8404/haproxy?stats

naše sa dajú nájsť na http://erpio.link/balancerstats

Tu uvidíš:

  • online/offline nody
  • počet spojení
  • aktuálna záťaž
  • health-check status

TO DO : verzia s dvomi HAProxy (high-availability)

  • automatický failover do 2 sekúnd
  • 2× HAProxy
  • keepalived + VRRP → virtuálna IP

Doriešiť s AI