🚀
Хостинг

Узилишсиз деплой: симлинк ва релиз папкалари билан сайтни янгилаш

05.11.2026
← Барча мақолалар

Оддий деплой усулларида, хоҳ FTP бўлсин, хоҳ git pull, янгиланиш ишлаб турган сайт папкасининг ичида содир бўлади. Файллар бирин-кетин алмаштирилади, Composer кутубхоналари қайта ёзилади ва шу бир неча сония ёки дақиқа давомида сайт ярим эски, ярим янги ҳолатда туради. Шу пайтда кирган мижоз оқ саҳифа, "Class not found" хатоси ёки бузилган дизайнни кўриши мумкин. Узилишсиз деплой ғояси оддий: янги версия алоҳида папкада тўлиқ тайёрланади, синовдан ўтади ва фақат шундан кейин сайт бир лаҳзада унга йўналтирилади.

Папкалар тузилиши

Бу усулнинг асоси учта папкадан иборат. releases ичида ҳар бир деплой ўз номи билан, одатда сана ва вақт кўринишида сақланади. shared папкасида версиядан версияга ўзгармайдиган нарсалар туради: .env файли, фойдаланувчилар юклаган расмлар, журнал файллари. current эса ҳақиқий папка эмас, балки охирги релизга ишора қилувчи симлинк, яъни рамзий ҳавола. Веб-сервер фақат current ни билади, шунинг учун ҳаволани бошқа папкага буриш бутун сайтни янги версияга ўтказади.

/var/www/example.uz/
├── current -> releases/20261104-1530
├── releases/
│   ├── 20261102-1010/
│   ├── 20261103-1745/
│   └── 20261104-1530/
└── shared/
    ├── .env
    └── storage/

Ушбу тузилма VPS'да энг осон қўлланади, чунки у ерда веб-сервернинг илдиз папкасини ўзингиз белгилайсиз. Shared хостингда сайт папкаси панел томонидан олдиндан белгиланган бўлиши мумкин; уни симлинк билан алмаштириш мумкинми ёки йўқлигини sayt.uz кабинетидан ёки техник ёрдам орқали аниқланг.

Янги релиз тайёрлаш

Ҳар бир деплой янги папка яратишдан бошланади. Код репозиториядан ўша папкага олинади, боғлиқликлар ўрнатилади ва умумий файллар симлинк орқали уланади. Муҳими, бу босқичларнинг бирортаси ҳам ишлаб турган сайтга тегмайди: агар Composer хато берса ёки фронтенд йиғилмаса, скрипт тўхтайди ва мижозлар ҳеч нарсани сезмайди.

#!/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

Маълумотлар базаси миграциялари алоҳида эътибор талаб қилади. Улар current алмаштирилишидан олдин бажарилади, яъни бир неча сония давомида эски код янги жадвал тузилмаси билан ишлайди. Шунинг учун миграцияларни "орқага мос" қилиб ёзиш керак: аввал янги устун қўшилади, код уни ишлата бошлагач, кейинги деплойда эски устун олиб ташланади. Устунни номини ўзгартириш ёки ўчиришни битта деплойда қилиш узилишга олиб келади.

Симлинкни атомар алмаштириш

Кўпчилик ln -sfn буйруғини ишлатади, лекин у аслида эски ҳаволани ўчириб, кейин янгисини яратади ва шу икки амал орасида жуда қисқа вақт ҳавола умуман йўқ бўлади. Ишончли усул вақтинчалик ҳавола яратиб, уни mv -T билан жойига кўчиришдир: Linux'да файл номини алмаштириш битта атомар амал бўлгани учун сўровлар ё эски, ё янги версияга тушади.

ln -s "$REL" $APP/current_tmp
mv -T $APP/current_tmp $APP/current

PHP-FPM, OPcache ва Nginx созламаси

Симлинк алмаштирилгандан кейин ҳам PHP баъзан эски файлларни бажаришда давом этади. Бунинг сабаби OPcache ва PHP'нинг realpath кеши: улар файлларни ҳақиқий йўли бўйича эслаб қолади ва current янги папкага бурилганини дарҳол сезмайди. Nginx'да $document_root ўрнига $realpath_root ишлатилса, PHP'га ҳар доим симлинк очилгандан кейинги ҳақиқий йўл узатилади ва муаммо асосан ҳал бўлади.

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;
    }
}

Қўшимча кафолат учун деплой охирида PHP-FPM'ни reload қилиш мумкин. restart дан фарқли ўлароқ, reload ишлаб турган сўровларни узмайди: эски жараёнлар жорий сўровни тугатади, янгилари эса янги код билан ишга тушади. Сокет йўли ва хизмат номи PHP версиясига боғлиқ, шунинг учун ўз серверингиздаги номни текширинг.

sudo systemctl reload php8.3-fpm

Орқага қайтариш ва эски релизларни тозалаш

Бу усулнинг энг катта фойдаси роллбэкнинг тезлигида. Янги версияда хато чиқса, ҳеч нарсани қайта юклаш шарт эмас: current ни олдинги папкага қайтариш кифоя ва сайт бир сонияда аввалги ҳолатига келади. Диск тўлиб кетмаслиги учун эса охирги бир нечта релиз сақланиб, қолганлари ўчирилади.

# 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

Ёдда тутинг: маълумотлар базаси роллбэк билан бирга қайтмайди. Шунинг учун миграцияларни орқага мос ёзиш қоидаси бу ерда ҳам ҳал қилувчи аҳамиятга эга.

Мисол: Тошкентдаги интернет-дўкон

Тошкентдаги маиший техника сотадиган интернет-дўкон кунига бир неча марта янгиланади ва ҳар бир деплой пайтида бир-икки дақиқа хато саҳифаси чиқиб, саватчадаги буюртмалар йўқоларди. Дастурчи VPS'да юқоридаги тузилмани жорий қилгач, янги версия фонда йиғиладиган бўлди ва алмаштириш кўзга кўринмас даражада қисқарди. Бир куни янги тўлов саҳифасида хато топилганда, у олдинги релизга бир буйруқ билан қайтди, хатони тинчгина тузатди ва кейинги деплойни бир соатдан кейин қилди.

Тайёр воситалар

Бу жараённи қўлда скрипт билан қилиш ўрганиш учун фойдали, лекин реал лойиҳаларда тайёр воситалардан фойдаланиш қулайроқ. PHP дунёсида Deployer, Ruby'да Capistrano худди шу релиз, shared ва current тузилмасини автоматик яратади, қулфлаш ва тозалашни ҳам ўзи бажаради. GitHub Actions ёки GitLab CI билан бирлаштирилса, main тармоғига пуш қилиниши билан деплой ўзи ишга тушади. Қайси йўлни танламанг, асосий тамойил бир хил қолади: янги версия тўлиқ тайёр бўлмагунча мижоз уни кўрмаслиги керак.

Ўхшаш мақолалар

📧 ISPmanager'да корпоратив почта қутиси яратиш ва созлаш 🔀 nvm билан Node.js версияларини бошқариш: ўрнатиш, алмаштириш ва .nvmrc ⚡ Redis'ни хостингда ишлатиш: сессия ва кеш учун амалий қўлланма ⚙️ PM2 билан Node.js иловани бошқариш: ишга тушириш, логлар ва кластер режим
🌐 Тил
🇺🇿 O'zbek 🇺🇿 Ўзбек ✓ 🇷🇺 Русский 🇬🇧 English