Oddiy deploy usullarida, xoh FTP bo'lsin, xoh git pull, yangilanish ishlab turgan sayt papkasining ichida sodir bo'ladi. Fayllar birin-ketin almashtiriladi, Composer kutubxonalari qayta yoziladi va shu bir necha soniya yoki daqiqa davomida sayt yarim eski, yarim yangi holatda turadi. Shu paytda kirgan mijoz oq sahifa, "Class not found" xatosi yoki buzilgan dizaynni ko'rishi mumkin. Uzilishsiz deploy g'oyasi oddiy: yangi versiya alohida papkada to'liq tayyorlanadi, sinovdan o'tadi va faqat shundan keyin sayt bir lahzada unga yo'naltiriladi.
Papkalar tuzilishi
Bu usulning asosi uchta papkadan iborat. releases ichida har bir deploy o'z nomi bilan, odatda sana va vaqt ko'rinishida saqlanadi. shared papkasida versiyadan versiyaga o'zgarmaydigan narsalar turadi: .env fayli, foydalanuvchilar yuklagan rasmlar, jurnal fayllari. current esa haqiqiy papka emas, balki oxirgi release'ga ishora qiluvchi symlink, ya'ni ramziy havola. Veb-server faqat current ni biladi, shuning uchun havolani boshqa papkaga burish butun saytni yangi versiyaga o'tkazadi.
/var/www/example.uz/
├── current -> releases/20261104-1530
├── releases/
│ ├── 20261102-1010/
│ ├── 20261103-1745/
│ └── 20261104-1530/
└── shared/
├── .env
└── storage/
Ushbu tuzilma VPS'da eng oson qo'llanadi, chunki u yerda veb-serverning ildiz papkasini o'zingiz belgilaysiz. Shared hostingda sayt papkasi panel tomonidan oldindan belgilangan bo'lishi mumkin; uni symlink bilan almashtirish mumkinmi yoki yo'qligini sayt.uz kabinetidan yoki texnik yordam orqali aniqlang.
Yangi release tayyorlash
Har bir deploy yangi papka yaratishdan boshlanadi. Kod repozitoriyadan o'sha papkaga olinadi, bog'liqliklar o'rnatiladi va umumiy fayllar symlink orqali ulanadi. Muhimi, bu bosqichlarning birortasi ham ishlab turgan saytga tegmaydi: agar Composer xato bersa yoki frontend yig'ilmasa, skript to'xtaydi va mijozlar hech narsani sezmaydi.
#!/bin/bash
set -e
APP=/var/www/example.uz
REL=$APP/releases/$(date +%Y%m%d-%H%M%S)
git clone --depth 1 --branch main git@github.com:misol/example-sayt.git "$REL"
cd "$REL"
ln -s $APP/shared/.env .env
rm -rf storage && ln -s $APP/shared/storage storage
composer install --no-dev --optimize-autoloader
php artisan config:cache
php artisan migrate --force
Ma'lumotlar bazasi migratsiyalari alohida e'tibor talab qiladi. Ular current almashtirilishidan oldin bajariladi, ya'ni bir necha soniya davomida eski kod yangi jadval tuzilmasi bilan ishlaydi. Shuning uchun migratsiyalarni "orqaga mos" qilib yozish kerak: avval yangi ustun qo'shiladi, kod uni ishlata boshlagach, keyingi deploy'da eski ustun olib tashlanadi. Ustunni nomini o'zgartirish yoki o'chirishni bitta deploy'da qilish uzilishga olib keladi.
Symlink'ni atomar almashtirish
Ko'pchilik ln -sfn buyrug'ini ishlatadi, lekin u aslida eski havolani o'chirib, keyin yangisini yaratadi va shu ikki amal orasida juda qisqa vaqt havola umuman yo'q bo'ladi. Ishonchli usul vaqtinchalik havola yaratib, uni mv -T bilan joyiga ko'chirishdir: Linux'da fayl nomini almashtirish bitta atomar amal bo'lgani uchun so'rovlar yo eski, yo yangi versiyaga tushadi.
ln -s "$REL" $APP/current_tmp
mv -T $APP/current_tmp $APP/current
PHP-FPM, OPcache va Nginx sozlamasi
Symlink almashtirilgandan keyin ham PHP ba'zan eski fayllarni bajarishda davom etadi. Buning sababi OPcache va PHP'ning realpath keshi: ular fayllarni haqiqiy yo'li bo'yicha eslab qoladi va current yangi papkaga burilganini darhol sezmaydi. Nginx'da $document_root o'rniga $realpath_root ishlatilsa, PHP'ga har doim symlink ochilgandan keyingi haqiqiy yo'l uzatiladi va muammo asosan hal bo'ladi.
server {
server_name example.uz;
root /var/www/example.uz/current/public;
location ~ \.php$ {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
}
Qo'shimcha kafolat uchun deploy oxirida PHP-FPM'ni reload qilish mumkin. restart dan farqli o'laroq, reload ishlab turgan so'rovlarni uzmaydi: eski jarayonlar joriy so'rovni tugatadi, yangilari esa yangi kod bilan ishga tushadi. Socket yo'li va xizmat nomi PHP versiyasiga bog'liq, shuning uchun o'z serveringizdagi nomni tekshiring.
sudo systemctl reload php8.3-fpm
Orqaga qaytarish va eski release'larni tozalash
Bu usulning eng katta foydasi rollback'ning tezligida. Yangi versiyada xato chiqsa, hech narsani qayta yuklash shart emas: current ni oldingi papkaga qaytarish kifoya va sayt bir soniyada avvalgi holatiga keladi. Disk to'lib ketmasligi uchun esa oxirgi bir nechta release saqlanib, qolganlari o'chiriladi.
# oldingi release'ga qaytish
PREV=$(ls -1dt $APP/releases/* | sed -n 2p)
ln -s "$PREV" $APP/current_tmp && mv -T $APP/current_tmp $APP/current
# faqat oxirgi 5 ta release'ni qoldirish
ls -1dt $APP/releases/* | tail -n +6 | xargs -r rm -rf
Yodda tuting: ma'lumotlar bazasi rollback bilan birga qaytmaydi. Shuning uchun migratsiyalarni orqaga mos yozish qoidasi bu yerda ham hal qiluvchi ahamiyatga ega.
Misol: Toshkentdagi internet-do'kon
Toshkentdagi maishiy texnika sotadigan internet-do'kon kuniga bir necha marta yangilanadi va har bir deploy paytida bir-ikki daqiqa xato sahifasi chiqib, savatchadagi buyurtmalar yo'qolardi. Dasturchi VPS'da yuqoridagi tuzilmani joriy qilgach, yangi versiya fonda yig'iladigan bo'ldi va almashtirish ko'zga ko'rinmas darajada qisqardi. Bir kuni yangi to'lov sahifasida xato topilganda, u oldingi release'ga bir buyruq bilan qaytdi, xatoni tinchgina tuzatdi va keyingi deploy'ni bir soatdan keyin qildi.
Tayyor vositalar
Bu jarayonni qo'lda skript bilan qilish o'rganish uchun foydali, lekin real loyihalarda tayyor vositalardan foydalanish qulayroq. PHP dunyosida Deployer, Ruby'da Capistrano xuddi shu release, shared va current tuzilmasini avtomatik yaratadi, qulflash va tozalashni ham o'zi bajaradi. GitHub Actions yoki GitLab CI bilan birlashtirilsa, main tarmog'iga push qilinishi bilan deploy o'zi ishga tushadi. Qaysi yo'lni tanlamang, asosiy tamoyil bir xil qoladi: yangi versiya to'liq tayyor bo'lmaguncha mijoz uni ko'rmasligi kerak.