05 β Ilovani qo'lda serverga joylash¶
β¬ οΈ Oldingi: 04 β Tarmoq va server xavfsizligi Β· π README Β· Keyingi: 06 β Docker nima: konteynerlar β‘οΈ
Bu bobda: nazariyani bir chetga qo'yib, haqiqiy ishni qilamiz β kichik lekin ishlaydigan Node.js (Express) "vazifalar" (todo) REST API yozamiz (
GET /tasks,POST /tasks, xotirada), uni lokalda ishga tushiramiz, so'ng qo'lda VPS serverga joylaymiz:sshbilan ulanish, Node o'rnatish (nodesource yoki nvm), kodnigit clone(yokiscp/rsync) bilan olib borish,npm install,node app.jsvacurlbilan tekshirish. Hammasi ishlaydi β lekin keyin bu yo'lning oltita og'rig'ini sezasiz: terminal yopilsa ilova o'ladi, "menda ishlaydi" muammosi, ochiq xunuk port, HTTPS yo'qligi, har yangilashda qo'lda downtime, va qayta tiklashning og'irligi. Har og'riq β kitobning keyingi qaysidir bobida hal qilinadi; oxirida "og'riqlar -> yechimlar xaritasi" jadvalini ko'rasiz. Bu bob β kitobning qolgan qismi uchun motivatsiya: nima uchun Docker, CI/CD, Nginx, systemd va Kubernetes kerakligini his qildiradi.
Muammo: ilova lokalda ishlaydi, endi nima?¶
Tasavvur qiling β ilovangizni yozib bo'ldingiz. node app.js deysiz, brauzerda localhost:3000 ochiladi, hammasi zo'r. Lekin bu faqat sizning kompyuteringizda. Dunyodagi boshqa hech kim uni ko'ra olmaydi: kompyuteringiz uxlab qolsa yoki uni o'chirsangiz, ilova ham yo'qoladi.
"Internetga chiqarish" β buni deploy (joylashtirish) deyiladi: ilovani doimo yoqilgan, internetga ulangan serverga ko'chirib, u yerda ishlatib turish. Server haqiqiy bo'lsa, ilova ham haqiqatan ishlaydi. Bu bobda buni eng oddiy, eng "qo'l bilan" usulda qilamiz. Maqsad β ilovani ishlatish, va shu jarayonning qanchalik mashaqqatli ekanini his qilish. Chunki aynan shu mashaqqat keyingi 20+ bobning sababi.
π Bu bob motivatsiya bobi. Biz "to'g'ri" deploy qilmaymiz β ataylab eng sodda yo'ldan boramiz, og'riqlarni sanaymiz, keyin har birini kitobning tegishli bobiga ulagan holatda yo'l xaritasini chizamiz.
Namuna ilova: vazifalar (todo) API¶
Butun kitob bo'ylab bizga deploy qilish uchun bitta namuna ilova kerak. U kichkina bo'lishi kerak (diqqatimiz deployda, ilova mantig'ida emas), lekin haqiqiy bo'lishi shart β port tinglaydigan, so'rovga javob beradigan, internetga chiqarsa ma'no kasb etadigan. Backend dasturchilarning ko'pchiligi tushunadigan narsa β REST API. Shuning uchun kichik "vazifalar" (todo) API yozamiz.
Ilovaning arxitekturasi juda oddiy: klient (brauzer yoki curl) HTTP so'rov yuboradi, Express uni qabul qiladi, marshrut (route) ishlaydi, javob qaytadi. Vazifalar hozircha xotirada (oddiy massivda) saqlanadi β ma'lumotlar bazasi keyinroq.
Ikki fayl kifoya. Avval package.json:
{
"name": "vazifalar-api",
"version": "1.0.0",
"type": "module",
"main": "app.js",
"scripts": {
"start": "node app.js"
},
"dependencies": {
"express": "^5.1.0"
}
}
βΉοΈ
"type": "module"β zamonaviy ESM (import/export) sintaksisini yoqadi."scripts.start"esanpm startdeganda nima ishga tushishini bildiradi β bu odat keyin server va Docker'da qo'l keladi.
Endi app.js β ilovaning o'zi:
import express from "express";
const app = express();
app.use(express.json());
// Vazifalar xotirada saqlanadi (server qayta ishga tushsa yo'qoladi)
let vazifalar = [
{ id: 1, matn: "DevOps kitobini o'qish", bajarildi: false },
];
let keyingiId = 2;
// Barcha vazifalarni qaytarish
app.get("/tasks", (req, res) => {
res.json(vazifalar);
});
// Yangi vazifa qo'shish
app.post("/tasks", (req, res) => {
const matn = req.body?.matn;
if (!matn) {
return res.status(400).json({ xato: "matn maydoni shart" });
}
const vazifa = { id: keyingiId++, matn, bajarildi: false };
vazifalar.push(vazifa);
res.status(201).json(vazifa);
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Vazifalar API http://localhost:${PORT} da ishlamoqda`);
});
Bu yerda muhim bir narsa bor: port process.env.PORT || 3000 orqali olinadi. Ya'ni PORT muhit o'zgaruvchisi (environment variable) berilsa β o'sha, aks holda 3000. Bu β production ilovaning birinchi qoidasi: sozlamalar kodda emas, muhitda. Serverda portni o'zgartirish uchun kodga tegmaysiz.
Lokalda sinab ko'ramiz:
Ikkinchi terminalda curl bilan tekshiramiz:
Yangi vazifa qo'shamiz (POST):
curl -X POST http://localhost:3000/tasks \
-H "Content-Type: application/json" \
-d '{"matn":"Serverga deploy qilish"}'
Ishladi. Endi xuddi shu ilovani boshqa kishilar ham ko'ra oladigan qilishimiz kerak β ya'ni serverga olib chiqamiz.
π‘ Bu kichik ilova β kitobning "tirik qahramoni". Keyin uni Docker konteyneriga joylaymiz, CI/CD pipeline'iga ulaymiz, Nginx ortiga qo'yamiz, HTTPS beramiz va oxirida Kubernetes'da masshtablaymiz. Hozir esa β eng oddiy yo'l.
Bizga server kerak¶
Server β bu shunchaki doimo yoqilgan, internetga doimiy IP bilan ulangan kompyuter. Uni o'zingiz sotib olishingiz shart emas: cloud provayderlardan (DigitalOcean, Hetzner, AWS Lightsail, Vultr va h.k.) oyiga bir necha dollarga VPS (Virtual Private Server β virtual xususiy server) ijaraga olasiz. Yangi hisoblar uchun ko'pincha bepul kredit beriladi.
VPS olganingizda sizga uchta narsa beriladi: IP manzil (masalan 203.0.113.10), foydalanuvchi (odatda root yoki ubuntu) va kirish usuli (parol yoki, to'g'ri yo'l β SSH kalit, 04-bobda ko'rganmiz). Biz Ubuntu 26.04 LTS (yoki hali qo'llab-quvvatlanadigan 24.04 LTS) serveridan foydalanamiz.
β οΈ Bu bobdagi server qadamlari illustrativ β ular haqiqiy VPSda bajariladi va shuning uchun o'z serveringizda ishlab ko'rishingiz kerak. Lokal kompyuterda
apt,ufw,ssh root@...ishlamaydi (yoki ma'no kasb etmaydi). Buyruqlar to'g'ri yozilgan β ularni arzon VPS olib, o'sha yerda sinab ko'ring.
Qadamma-qadam: qo'lda deploy¶
Quyidagi olti qadam β qo'lda deployning klassik yo'li. Har biri qo'l mehnati, har biri keyin avtomatlashtiriladigan narsa.
1-qadam: serverga ssh bilan ulanish¶
Lokal terminalingizdan serverga kiramiz (04-bobda SSH kalitni sozlagansiz):
Endi terminalingiz server ichida ishlaydi. Buyruqlar serverda bajariladi.
2-qadam: Node.js o'rnatish¶
Server toza Ubuntu β unda Node yo'q. Ikki keng tarqalgan usul bor.
Usul A β NodeSource (tizim bo'yicha, server uchun afzal):
curl -fsSL https://deb.nodesource.com/setup_22.x -o nodesource_setup.sh
sudo -E bash nodesource_setup.sh
sudo apt-get install -y nodejs
node -v
Usul B β nvm (Node Version Manager, bir nechta versiyani boshqarish uchun qulay):
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
# yangi qobiq oching yoki: source ~/.bashrc
nvm install 22
nvm use 22
node -v
π‘ Server uchun odatda NodeSource (Usul A) afzal β Node butun tizim uchun bir marta o'rnatiladi. nvm bir foydalanuvchi bir nechta loyihada turli Node versiyalarini ishlatadigan holatda qulay. Aynan shu "qaysi versiya?" savoli β keyinroq bizni Docker'ga olib boradigan og'riqlardan biri.
3-qadam: kodni serverga olib borish¶
Eng toza yo'l β kod GitHub'da bo'lsa, serverda git clone:
sudo apt-get install -y git
git clone https://github.com/foydalanuvchi/vazifalar-api.git
cd vazifalar-api
Agar kod faqat lokalda bo'lsa, lokal terminaldan (server emas) rsync yoki scp bilan yuborasiz:
# Lokal kompyuterda bajariladi:
rsync -av --exclude node_modules ./vazifalar-api/ ubuntu@203.0.113.10:~/vazifalar-api/
π
node_modules'ni hech qachon ko'chirmang β u yuzlab megabayt va platformaga bog'liq bo'lishi mumkin. Serverdanpm installqiling, u kerakli paketlarni o'sha server uchun o'rnatadi. Shuning uchun--exclude node_modules.
4-qadam: bog'liqliklarni o'rnatish¶
Server ichida, loyiha papkasida:
npm package.json dagi dependencies'ni o'qib, Express'ni va uning bog'liqliklarini node_modules/ ga o'rnatadi.
5-qadam: ilovani ishga tushirish va portni ochish¶
Ilova ishlamoqda β lekin server firewall (ufw, 04-bob) 3000-portni tashqaridan bloklayotgan bo'lishi mumkin. Buni boshqa terminaldan (yana bir ssh seansi) ochamiz:
β οΈ 3000-portni to'g'ridan-to'g'ri butun internetga ochish β vaqtinchalik, "ishlasin" yechimi. Production'da ilova porti tashqariga ochilmaydi; oldida Nginx turadi va faqat 80/443 ochiq bo'ladi (16-18-boblar). Hozircha β sezish uchun ochamiz.
6-qadam: tashqaridan tekshirish¶
Lokal kompyuteringizdan (yoki brauzerdan) serverning IP'siga murojaat qilamiz:
Tabriklaymiz β ilovangiz internetda. Dunyoning istalgan nuqtasidan http://203.0.113.10:3000/tasks ochib uni ko'rish mumkin.
Endi... bir nafas oling. Chunki bu quvonch uzoq davom etmaydi.
Va mana β og'riqlar boshlanadi¶
Og'riq 1: terminalni yopsangiz, ilova o'ladi¶
ssh seansidan chiqasiz (yoki shunchaki terminalni yopasiz) β va ilova to'xtaydi. Chunki node app.js sizning terminal seansingizga "bog'langan" jarayon. Seans tugasa β jarayon ham.
Vaqtinchalik yamoq β nohup yoki screen/tmux:
# yoki screen: nomli seans ochib, undan "uzilib" chiqasiz
screen -S vazifalar
node app.js
# Ctrl+A keyin D bosib uziling β ilova ishlab qoladi
Bu ishlaydi, lekin xunuk: server qayta yuklansa ilova o'z-o'zidan ko'tarilmaydi, qulab tushsa qayta ishga tushmaydi, loglar tartibsiz. Asl yechim β operatsion tizimning xizmat menejeri, ya'ni systemd: u ilovani fon xizmati qiladi, qulasa avtomatik qayta ishga tushiradi, server o'chib-yonsa o'zi ko'taradi.
β Yechim: systemd (19-bob).
nohup/screenβ vaqtinchalik plaster; systemd service β to'g'ri yechim.
Og'riq 2: "menda ishlaydi-ku!"¶
Lokalingizda Node 22 bor edi, serverda esa 18 o'rnatib qo'ydingiz β va ilova ishlamaydi. Yoki lokal Linux, server boshqa distributiv; yoki bir paket lokalda bor, serverda yo'q. Klassik gap: "Menda ishlaydi-ku!" β lekin serverda yo'q.
Sabab β muhit farqi: Node versiyasi, OS, tizim kutubxonalari, muhit o'zgaruvchilari. Har bir serverni qo'lda bir xil holatga keltirish β qiyin va xatoga moyil.
β Yechim: Docker (06β11-boblar). Ilovani konteynerga β Node, kod va barcha bog'liqliklar bilan bitta "qutiga" β joylaymiz. Bu quti lokalda ham, serverda ham, hamkasbingizda ham aynan bir xil ishlaydi. "Menda ishlaydi" muammosi yo'qoladi.
Og'riq 3: http://203.0.113.10:3000 β xunuk va xavfli¶
Foydalanuvchiga "saytim 203.0.113.10:3000 da" deb aytib bo'lmaydi. Bizga domen nomi (masalan vazifalar.uz) va odatdagi 80/443 portlar kerak β :3000siz. Bundan tashqari, ilova portini to'g'ridan internetga ochish β xavfsizlik nuqtai nazaridan yomon: ilova bevosita hujum yuzasiga aylanadi.
β Yechim: Nginx reverse proxy (16β17-boblar). Nginx 80/443-portda turadi, so'rovlarni ichkaridagi
localhost:3000ga uzatadi. Foydalanuvchi:3000ni ko'rmaydi, ilova porti tashqariga ochilmaydi, bir nechta ilovaga yuk taqsimlash ham mumkin bo'ladi.
Og'riq 4: HTTPS yo'q¶
URL http:// β https:// emas. Ya'ni trafik shifrlanmagan: parollar, tokenlar ochiq uzatiladi, brauzer "Xavfsiz emas" deb ogohlantiradi. Bugungi internetda HTTPSsiz sayt β qabul qilib bo'lmas holat.
β Yechim: Let's Encrypt + Certbot (18-bob). Bepul, avtomatik yangilanadigan TLS sertifikat.
http://https://ga aylanadi, brauzerda qulf belgisi paydo bo'ladi.
Og'riq 5: har yangilashda β yana qo'lda, yana downtime¶
Kodda bitta satr o'zgartirdingizmi? Yana hamma narsa boshidan: ssh, git pull, npm install, eski jarayonni o'ldirish, qaytadan ishga tushirish. Bu vaqtda sayt bir necha soniya/daqiqa ishlamaydi (downtime). Kuniga o'n marta deploy qilsangiz β bu jahannam. Va bir kun yarim tunda, charchagan holatda, bir qadamni o'tkazib yuborib β production'ni buzasiz.
β Yechim: CI/CD β GitHub Actions (12β15-boblar). Har
git pushda avtomatik: test ishlaydi, image quriladi, serverga deploy bo'ladi β odam aralashuvisiz. Git & GitHub kitobida Actions asoslarini ko'rganmiz; bu yerda uni deploy uchun ishlatamiz.
Og'riq 6: qayta tiklash β butunlay qo'lda¶
Server qulab tushsa yoki yangisini olsangiz, hamma narsa boshidan: Node o'rnatish, firewall, foydalanuvchi, ilova... yodingizda bormi har bir buyruq? Yo'q. "Qaysi paketni o'rnatgan edim?" degan savol qoladi.
β Yechim: Infrastructure as Code β Ansible/Terraform (27-bob). Butun serverni kod bilan tasvirlaysiz; bitta buyruq bilan noldan tiklanadigan, takrorlanadigan infratuzilma. Kubernetes (21β24-boblar) esa ilovani avtomatik tiklash va masshtablashni o'z zimmasiga oladi.
Og'riqlar -> yechimlar xaritasi¶
Yuqoridagi olti og'riqning hammasi β bekorga emas. Har biri kitobning aniq bir bobida hal qilinadi. Mana to'liq xarita:
| # | Qo'lda deploy og'rig'i | Yechim | Bob |
|---|---|---|---|
| 1 | Terminal yopilsa ilova to'xtaydi; qulasa ko'tarilmaydi | systemd xizmat (auto-restart) | 19 |
| 2 | "Menda ishlaydi" β Node/OS/bog'liqlik farqi | Docker konteyner | 06β11 |
| 3 | Ochiq :3000 port β xunuk va xavfli |
Nginx reverse proxy | 16β17 |
| 4 | HTTPS yo'q β trafik shifrlanmagan | Let's Encrypt / Certbot | 18 |
| 5 | Har yangilash qo'lda + downtime | CI/CD (GitHub Actions) | 12β15 |
| 6 | Qayta tiklash butunlay qo'lda | IaC (Ansible/Terraform), K8s | 27, 21β24 |
π Shu jadval β kitobning yo'l xaritasi. Keyingi har bir bobni o'qiyotganda "bu qaysi og'riqni yechyapti?" deb o'zingizdan so'rang. 20-bob esa hammasini birlashtirib, namuna ilovani to'liq productionga chiqaradi.
Qo'lda deploy yomon emas β uni bir marta bilish shart, chunki avtomatlashtirish ostida aynan shu qadamlar bajariladi. Lekin uni har kuni qo'lda takrorlash β DevOps aynan yo'q qiladigan narsa. Keyingi bobdan boshlab biz har bir qadamni avtomatik, takrorlanadigan va ishonchli qilamiz. Birinchi to'xtash β Docker: "menda ishlaydi" muammosining yakuni.
05-bob mashqlari¶
Oson¶
- Namuna
vazifalar-apiilovasini lokalda yarating (app.js+package.json),npm installvanpm startqiling, so'ngcurl http://localhost:3000/tasksbilan boshlang'ich vazifani ko'ring. POST /tasksgacurl -X POST ... -d '{"matn":"yangi vazifa"}'yuboring va201javobini hamda yangiidni ko'ring. So'ngGET /tasksqaytarganda ikki vazifa borligini tasdiqlang.- Ilovani
PORT=4000 npm startbilan boshqa portda ishga tushiring.process.env.PORTqanday ishlayotganini tushuntiring. POST /tasksgamatnmaydonisiz (-d '{}') so'rov yuboring. Qaysi status kod qaytadi va nega?
O'rta¶
- Qo'lda deployning olti qadamini (ssh -> Node -> kod -> npm install -> ishga tushirish -> tekshirish) o'z so'zlaringiz bilan ketma-ketlik sifatida yozing. Har qadam yonida "bu qadam qo'lda bo'lgani uchun qanday xato kelib chiqishi mumkin?" deb bitta xavfni belgilang.
node_modulespapkasini nega serverga ko'chirmaslik kerakligini tushuntiring varsyncbuyrug'ida uni--excludebilan chiqarib tashlang.nohup node app.js > app.log 2>&1 &buyrug'ining har bir qismini (nohup,> app.log,2>&1,&) ajratib tushuntiring. Bu yechim nega vaqtinchalik?- Olti og'riqning har birini mos yechim/bobga ulang (xotiradan jadval tuzing). Qaysi og'riq sizni eng ko'p qiziqtiradi va nega?
Qiyin¶
- Namuna ilovaga uchinchi marshrut qo'shing:
DELETE /tasks/:idβ berilganidli vazifani o'chirsin, topilsa204, topilmasa404qaytarsin. Lokaldacurl -X DELETEbilan sinab ko'ring. - NodeSource (Usul A) va nvm (Usul B) bilan Node o'rnatishni solishtiring: qaysi biri server uchun, qaysi biri ko'p-versiyali ish stoli uchun afzal va nega? Har birining bitta kamchiligini ayting.
- "Menda ishlaydi" muammosini boshdan kechiring (yoki tasavvur qiling): namuna ilovani Node 22 talab qiladigan kodga ataylab o'zgartiring (masalan yangi sintaksis ishlating), so'ng eski Node bo'lgan muhitda ishga tushirilsa nima bo'lishini tushuntiring. Docker buni qanday yo'qotadi?
- Bitta
bashskript (deploy.sh) yozing β qo'lda deployning server tomonidagi qadamlarini birlashtirsin:git pull,npm install, eski jarayonni to'xtatibnohupbilan qayta ishga tushirish. Skript yozgach, nega bu hali ham "to'g'ri" deploy emasligini (downtime, auto-restart yo'q, atomik emas) ayting.
Yechim β 9
app.js ga quyidagi marshrutni qo'shing (boshqa marshrutlar yonida):
// Vazifani o'chirish
app.delete("/tasks/:id", (req, res) => {
const id = Number(req.params.id);
const oldingi = vazifalar.length;
vazifalar = vazifalar.filter((v) => v.id !== id);
if (vazifalar.length === oldingi) {
return res.status(404).json({ xato: "Vazifa topilmadi" });
}
res.status(204).end();
});
Sinash:
curl -i -X DELETE http://localhost:3000/tasks/1 # 204 No Content
curl -i -X DELETE http://localhost:3000/tasks/999 # 404 Not Found
Number(req.params.id) β URL parametri har doim satr bo'lgani uchun id ni songa aylantiramiz. 204 β "muvaffaqiyatli, lekin qaytaradigan tana yo'q". Topilmasa 404.
Yechim β 10
NodeSource (Usul A) β Node'ni tizim paketi sifatida butun serverga o'rnatadi. Server uchun afzal: bitta versiya, barcha foydalanuvchi va xizmatlar uchun bir xil, systemd xizmati uchun ishonchli yo'l. Kamchiligi: bir serverda bir vaqtda bir nechta Node versiyasini saqlash qiyin; yangilash uchun apt bilan ishlash kerak.
nvm (Usul B) β har foydalanuvchi o'z uy papkasiga bir nechta Node versiyasini o'rnatadi va nvm use bilan almashtiradi. Ish stoli / ishlab chiqish muhiti uchun afzal: turli loyihalar turli Node versiyalarini talab qilganda qulay. Kamchiligi: foydalanuvchi qobig'iga bog'liq (source ~/.bashrc), shuning uchun systemd xizmati uchun yo'lni aniq ko'rsatish kerak; har foydalanuvchida alohida o'rnatiladi.
Xulosa: serverda NodeSource, ko'p-loyihali ish stolida nvm. Va aslida "qaysi Node versiyasi?" savolining o'zi β Docker bu og'riqni butunlay yo'q qiladigan sababdir (versiya image ichida qotiriladi).
Yechim β 11
Masalan app.js ga ataylab yangi sintaksis/API qo'shasiz (faqat yangi Node'da bor narsa). Eski Node bo'lgan serverda node app.js qilsangiz β SyntaxError yoki is not a function kabi xato bilan qulaydi, lokalingizda esa muammosiz ishlardi. Mana shu β "menda ishlaydi-ku" muammosining mohiyati: ilova muhitga bog'liq, muhit esa serverdan serverga farq qiladi.
Docker buni qanday yo'qotadi: ilovani FROM node:22 kabi aniq versiyali base image ustiga quramiz. Endi kod, Node versiyasi va barcha bog'liqliklar bitta image ichida qotiriladi. Bu image lokalda ham, serverda ham, har qanday CI'da ham β bitobit bir xil. Serverda "qaysi Node?" degan savol umuman yo'qoladi: konteyner o'z Node'ini olib keladi. (Buni 06β08-boblarda batafsil ko'ramiz.)
Yechim β 12
Sodda deploy.sh (server tomonida, loyiha papkasida bajariladi):
#!/usr/bin/env bash
set -euo pipefail
echo "1/4 Eng yangi kodni olish..."
git pull origin main
echo "2/4 Bog'liqliklarni o'rnatish..."
npm install --omit=dev
echo "3/4 Eski jarayonni to'xtatish (bo'lsa)..."
pkill -f "node app.js" || true
echo "4/4 Ilovani qayta ishga tushirish..."
nohup node app.js > app.log 2>&1 &
echo "Tayyor. Loglar: app.log"
set -euo pipefail β xato bo'lsa skript darhol to'xtaydi (03-bobdagi xavfsiz skript odati). pkill ... || true β jarayon bo'lmasa ham skript yiqilmasin.
Nega bu hali ham "to'g'ri" deploy emas:
- Downtime bor β eski jarayon o'lib, yangisi ko'tarilguncha sayt javob bermaydi.
- Auto-restart yo'q β server qayta yuklansa yoki ilova qulasa, hech kim uni ko'tarmaydi (nohup buni qilmaydi).
- Atomik emas β npm install yarim yo'lda buzilsa, ilova nuqson holatda qoladi, qaytarish (rollback) yo'q.
- Hali ham qo'lda ishga tushiriladi β skriptni kimdir ssh qilib chaqirishi kerak.
To'g'ri yechim: ilovani systemd xizmati qilish (19-bob, auto-restart + reboot'da ko'tarilish) va deployni CI/CD ga ko'chirish (12β15-boblar, git pushda avtomatik), ideal holatda Docker image orqali (atomik, rollback oson).
β¬ οΈ Oldingi: 04 β Tarmoq va server xavfsizligi Β· π README Β· Keyingi: 06 β Docker nima: konteynerlar β‘οΈ