Yakuniy loyiha — ilovani noldan serverga chiqarish

~60 daq · Linux · Server

Muammodan boshlaylik

Siz 43 dars davomida alohida ko'nikmalarni o'rgandingiz. Haqiqiy ishda ular birga ishlatiladi. Bu loyiha — hammasini bitta oqimga yig'ish.

Vazifa: bo'sh Ubuntu VPS ga Node.js API va statik frontend dan iborat "Dokon" ilovasini chiqarish:

  • HTTPS bilan, o'z domenida
  • Xavfsiz sozlangan server
  • systemd xizmati sifatida, qulasa — o'zi tiklanadi
  • Bir buyruq bilan deploy
  • Har kecha zaxira nusxa
  • Muammo bo'lsa — Telegram ga xabar

Har bir bosqichda qaysi darslarga tayanish kerakligi ko'rsatilgan. Avval o'zingiz bajaring, keyin yechimni oching.

[!INFO] VPS yoki domen bo'lmasa: virtual mashina (2-dars) yoki WSL da ham 1–5-bosqichlarni bajarish mumkin. HTTPS uchun domen kerak — usiz HTTP bilan cheklaning.


Arxitektura

                 Internet
                    │
          ┌─────────▼──────────┐   ufw: 22, 80, 443
          │  Nginx  :80 / :443 │   Let's Encrypt
          └───┬────────────┬───┘
     /api/*   │            │   /*
   ┌──────────▼───┐  ┌─────▼──────────────────┐
   │ Node.js API  │  │ /var/www/dokon/public  │
   │ 127.0.0.1:3000│  │ (statik fayllar)       │
   │ systemd      │  └────────────────────────┘
   └──────────────┘

/opt/dokon/
├── releases/2026-11-06_1500/   ← har deploy — yangi papka
├── current -> releases/...     ← symlink (10-dars)
├── shared/.env                 ← sirlar
└── shared/uploads/             ← foydalanuvchi fayllari

1-bosqich: Serverni tayyorlash

Darslar: 18, 19, 29, 34, 36, 42

  1. Tizimni yangilang, vaqt mintaqasini o'rnating
  2. Shaxsiy foydalanuvchi + sudo + SSH kalit
  3. deploy foydalanuvchisi (deploy uchun; faqat dokon xizmatini qayta ishga tushira oladi)
  4. SSH: root va parol o'chirilgan
  5. ufw, fail2ban, unattended-upgrades
Yechim
# root sifatida
apt update && apt upgrade -y
apt install -y curl git ufw fail2ban unattended-upgrades nginx
timedatectl set-timezone Asia/Tashkent

adduser otabek && usermod -aG sudo otabek
adduser --disabled-password --gecos "" deploy

Lokal kompyuterda:

ssh-copy-id otabek@SERVER_IP
ssh-copy-id deploy@SERVER_IP     # yoki kalitni qo'lda deploy ning authorized_keys iga

Serverda (otabek sifatida, yangi terminalda tekshirgandan keyin):

sudo tee /etc/ssh/sshd_config.d/99-xavfsiz.conf > /dev/null << 'EOF'
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
EOF
sudo sshd -t && sudo systemctl restart ssh

sudo ufw default deny incoming
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable

sudo systemctl enable --now fail2ban
sudo dpkg-reconfigure -plow unattended-upgrades

deploy uchun cheklangan sudo (19-dars):

sudo visudo -f /etc/sudoers.d/deploy
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl restart dokon, /usr/bin/systemctl reload nginx

2-bosqich: Node.js va papkalar tuzilishi

Darslar: 5, 7, 18, 20, 21, 29

  1. Node.js 22 ni rasmiy repozitoriyadan o'rnating
  2. dokon tizim foydalanuvchisi (ilova shu nomidan ishlaydi)
  3. /opt/dokon/{releases,shared/uploads} tuzilmasi
  4. Egalik: deploy fayllarni yozadi, dokon o'qiydi; uploads ga dokon yozadi
  5. shared/.env — 600, faqat dokon o'qiy oladi
Yechim
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo bash -
sudo apt install -y nodejs
node -v

sudo useradd --system --no-create-home --shell /usr/sbin/nologin dokon
sudo usermod -aG dokon deploy               # deploy dokon guruhida

sudo mkdir -p /opt/dokon/{releases,shared/uploads}
sudo chown -R deploy:dokon /opt/dokon
sudo chmod -R 750 /opt/dokon
sudo chown dokon:dokon /opt/dokon/shared/uploads
sudo chmod 770 /opt/dokon/shared/uploads

sudo tee /opt/dokon/shared/.env > /dev/null << 'EOF'
PORT=3000
NODE_ENV=production
JWT_SECRET=bu-yerga-uzun-tasodifiy-qator
EOF
sudo chown dokon:dokon /opt/dokon/shared/.env
sudo chmod 600 /opt/dokon/shared/.env

Tasodifiy sir yaratish: openssl rand -hex 32.

.env ni systemd o'zi (root sifatida) o'qiydi va ilovaga uzatadi — shuning uchun 600 yetarli.


3-bosqich: systemd xizmati

Darslar: 23, 25, 28

/etc/systemd/system/dokon.service:

  • dokon foydalanuvchisi, WorkingDirectory=/opt/dokon/current
  • .env — EnvironmentFile
  • Qulasa — qayta ishga tushsin; xotira chegarasi 512 MB
  • Izolyatsiya: faqat uploads ga yozish mumkin
Yechim
[Unit]
Description=Dokon API
After=network.target

[Service]
Type=simple
User=dokon
Group=dokon
WorkingDirectory=/opt/dokon/current
EnvironmentFile=/opt/dokon/shared/.env
ExecStart=/usr/bin/node /opt/dokon/current/server.js
Restart=on-failure
RestartSec=5
MemoryMax=512M

NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=/opt/dokon/shared/uploads

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable dokon        # hozircha start emas — kod hali yo'q

Ilova current symlink orqali ishga tushadi — deploy da faqat symlink almashadi, xizmat fayli o'zgarmaydi.


4-bosqich: Deploy skripti

Darslar: 10, 35, 37, 38, 39

Lokal kompyuterda deploy.sh:

  1. set -euo pipefail, log funksiyasi
  2. Frontend ni build qiladi (npm run build → dist/)
  3. Serverda yangi reliz papkasi: releases/YYYY-MM-DD_HHMMSS
  4. rsync bilan API kodini (node_modules siz) va frontend ni yuklaydi
  5. Serverda: npm ci --omit=dev, shared/.env va uploads ga symlink
  6. current ni yangi relizga almashtiradi, xizmatni qayta ishga tushiradi
  7. /api/health ni tekshiradi; javob bo'lmasa — oldingi relizga qaytaradi
  8. Eng so'nggi 5 ta relizdan tashqari hammasini o'chiradi
Yechim
#!/usr/bin/env bash
# deploy.sh — Dokon ni serverga chiqarish
set -euo pipefail

readonly SERVER="deploy@dokon"           # ~/.ssh/config dagi nom
readonly ILOVA="/opt/dokon"
readonly RELIZ="$(date +%F_%H%M%S)"
readonly YOL="$ILOVA/releases/$RELIZ"

log() { echo -e "\033[0;32m[$(date +%T)]\033[0m $*"; }

log "Frontend build..."
(cd frontend && npm ci && npm run build)

log "Yuklanmoqda: $RELIZ"
ssh "$SERVER" "mkdir -p '$YOL/public'"
rsync -az --delete --exclude node_modules --exclude .git --exclude .env \
  api/ "$SERVER:$YOL/"
rsync -az --delete frontend/dist/ "$SERVER:$YOL/public/"

log "Serverda o'rnatilmoqda..."
ssh "$SERVER" bash -s << EOF
set -euo pipefail
cd '$YOL'
npm ci --omit=dev --silent
ln -sfn '$ILOVA/shared/.env' .env
ln -sfn '$ILOVA/shared/uploads' uploads

OLDINGI=\$(readlink -f '$ILOVA/current' || true)
ln -sfn '$YOL' '$ILOVA/current'
sudo /usr/bin/systemctl restart dokon

for i in \$(seq 1 15); do
  if curl -sf localhost:3000/api/health > /dev/null; then
    echo "✓ Ilova ishlayapti"
    ls -1dt '$ILOVA'/releases/* | tail -n +6 | xargs -r rm -rf
    exit 0
  fi
  sleep 1
done

echo "✗ Health check muvaffaqiyatsiz — orqaga qaytarilmoqda"
if [[ -n "\$OLDINGI" ]]; then
  ln -sfn "\$OLDINGI" '$ILOVA/current'
  sudo /usr/bin/systemctl restart dokon
fi
exit 1
EOF

log "Deploy tugadi: https://dokon.uz"

Nozik joylar:

  • ssh ... bash -s << EOF — butun skriptni serverda bajarish. $YOL — lokal o'zgaruvchi (heredoc da almashtiriladi); \$OLDINGI — serverdagi o'zgaruvchi (\$ — almashtirilmasin)
  • readlink -f current — oldingi reliz yo'li (rollback uchun)
  • ls -1dt ... | tail -n +6 — eng yangi 5 tadan keyingilari; xargs -r — bo'sh bo'lsa ishlamaslik
  • deploy sudoers da faqat shu buyruqqa ruxsat — shuning uchun to'liq yo'l /usr/bin/systemctl
chmod +x deploy.sh
./deploy.sh

5-bosqich: Nginx va HTTPS

Darslar: 32, 43

  1. Statik fayllar — /opt/dokon/current/public dan, SPA rejimida
  2. /api/ — 127.0.0.1:3000 ga, to'g'ri sarlavhalar bilan
  3. Fayl yuklash chegarasi 20 MB, gzip, yashirin fayllarni bloklash
  4. Let's Encrypt sertifikati
Yechim
sudo usermod -aG dokon www-data      # Nginx public/ ni o'qiy olsin

/etc/nginx/sites-available/dokon:

server {
    listen 80;
    server_name dokon.uz www.dokon.uz;

    root /opt/dokon/current/public;
    index index.html;

    server_tokens off;
    client_max_body_size 20M;

    gzip on;
    gzip_types text/css application/javascript application/json image/svg+xml;

    location / {
        try_files $uri $uri/ /index.html;
    }

    location /assets/ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    location /api/ {
        proxy_pass http://127.0.0.1:3000;
        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;
    }

    location ~ /\.(?!well-known) {
        deny all;
    }
}
sudo ln -sfn /etc/nginx/sites-available/dokon /etc/nginx/sites-enabled/dokon
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx

dig dokon.uz +short                  # server IP siga ko'rsatishi kerak
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d dokon.uz -d www.dokon.uz

www-data ni dokon guruhiga qo'shish — 750 ruxsatli papkalarni o'qishi uchun. Qo'shgandan keyin sudo systemctl restart nginx (guruh o'zgarishi jarayon qayta ishga tushganda kuchga kiradi).


6-bosqich: Zaxira va monitoring

Darslar: 31, 40, 41

  1. Har kecha 03:00 da shared/uploads va shared/.env ning zaxirasi, 14 kunlik rotatsiya — systemd timer bilan
  2. Har 5 daqiqada sog'liq tekshiruvi (sayt, disk, xotira) + Telegram
  3. Zaxirani boshqa joyga ham ko'chirish (lokal kompyuterga rsync)
Yechim

41-darsdagi zaxira.sh va tekshir.sh ni /opt/scripts/ ga joylang.

/etc/systemd/system/dokon-zaxira.service:

[Unit]
Description=Dokon zaxira

[Service]
Type=oneshot
ExecStart=/opt/scripts/zaxira.sh /opt/dokon/shared 14

/etc/systemd/system/dokon-zaxira.timer:

[Unit]
Description=Har kecha Dokon zaxirasi

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true

[Install]
WantedBy=timers.target

/etc/systemd/system/dokon-tekshir.service:

[Unit]
Description=Dokon sog'liq tekshiruvi

[Service]
Type=oneshot
EnvironmentFile=/etc/dokon-tekshir.env
ExecStart=/opt/scripts/tekshir.sh https://dokon.uz/api/health

/etc/systemd/system/dokon-tekshir.timer:

[Timer]
OnBootSec=2min
OnUnitActiveSec=5min

[Install]
WantedBy=timers.target
sudo chmod 600 /etc/dokon-tekshir.env       # TG_TOKEN, TG_CHAT
sudo systemctl daemon-reload
sudo systemctl enable --now dokon-zaxira.timer dokon-tekshir.timer
systemctl list-timers | grep dokon
sudo systemctl start dokon-zaxira.service && journalctl -u dokon-zaxira -n 5

Lokal kompyuterda (yoki boshqa serverda) — zaxirani tortib olish:

rsync -avz dokon:/var/backups/dokon/ ~/dokon-zaxiralar/

41-darsdagi zaxira.sh da MANZIL va LOG ni server yo'llariga o'zgartiring: MANZIL="/var/backups/dokon", LOG="/var/log/dokon-zaxira.log". /var/backups/dokon ni otabek o'qiy olishi uchun: sudo chown root:otabek /var/backups/dokon && sudo chmod 750 /var/backups/dokon.


Yakuniy tekshiruv

Server
[ ] ssh root@... → Permission denied
[ ] Parol bilan kirish rad etiladi
[ ] sudo ufw status — faqat 22, 80, 443
[ ] sudo ss -tlnp — 3000 faqat 127.0.0.1 da
[ ] fail2ban-client status sshd — ishlayapti

Ilova
[ ] https://dokon.uz — yashil qulf; http → https
[ ] https://dokon.uz/mahsulot/42 ni yangilash — 404 emas (SPA)
[ ] https://dokon.uz/.env — 403
[ ] ps aux | grep server.js — dokon foydalanuvchisi
[ ] sudo systemctl kill -s KILL dokon → 5 soniyada o'zi tiklanadi
[ ] sudo reboot → hammasi o'zi ko'tariladi

Deploy
[ ] ./deploy.sh — bir buyruq, yangi reliz
[ ] Ataylab buzilgan kod bilan deploy → avtomatik rollback
[ ] releases/ da 5 tadan ko'p emas

Operatsiyalar
[ ] systemctl list-timers — zaxira va tekshiruv
[ ] Zaxirani boshqa papkaga ochib, tiklashni sinab ko'rish
[ ] sudo systemctl stop dokon → 5 daqiqada Telegram ga xabar

Nima uchun shunday?

Nima uchun bu loyihada shuncha qatlam — git pull && node server.js ham ishlardi-ku?

Ishlardi — birinchi kun. Lekin server — bu birinchi kun emas, keyingi 1000 kun: yuzlab deploy, kutilmagan qayta yuklanishlar, xotira oqishlari, hujumlar, disk to'lishi, va bir kuni — buzilgan deploy yoki o'chirilgan fayl. Bu loyihadagi har bir qatlam ana shu kunlardan biriga javob: alohida foydalanuvchilar — "hujumchi qancha narsaga yetadi", systemd — "kechasi kim qayta ishga tushiradi", relizlar va symlink — "qanday qilib bir soniyada orqaga qaytamiz", health check — "buzilgan versiya production ga chiqmasin", zaxira — "ma'lumotlar qayerda", monitoring — "nega mijozdan bildik". Va bularning hammasi — matnli fayllar va skriptlar: takrorlanadigan, Git da saqlanadigan, ertaga ikkinchi serverga bir soatda ko'chiriladigan.


Xulosa

Siz Linux kursini tugatdingiz! Endi siz:

  • Terminalda erkin ishlaysiz: fayllar, qidirish, havolalar
  • grep, pipe, awk bilan har qanday logni tahlil qilasiz
  • Foydalanuvchilar, ruxsatlar va sudo ni to'g'ri sozlaysiz
  • Jarayonlar, signallar, systemd xizmatlari va loglarni boshqarasiz
  • SSH kalitlari, rsync va firewall bilan serverlar bilan ishlaysiz
  • Ishonchli bash skriptlar yozib, ularni cron va timer bilan avtomatlashtirasiz
  • Bo'sh VPS ni xavfsiz sozlab, unda HTTPS li ilovani ishga tushirasiz

Keyingi qadam

  • Shu loyihani haqiqiy VPS da bajaring — eng yaxshi o'rganish usuli
  • Docker kursi — shu ilovani konteynerlarga o'tkazish: bu darslardagi har bir tushuncha (foydalanuvchilar, signallar, portlar, volume lar, loglar) u yerda yana uchraydi
  • Ansible — yuzlab serverni bir xil sozlash
  • man bash, man systemd.service — chuqurroq o'rganish uchun

Omad! 🐧