Розгортання React і Vite сайту на Nginx із версіями релізів
Still Brewing UAОпубліковано: Оновлено:
Статичному сайту не потрібен Node.js на сервері. Потрібна тека з файлами, правильні права та спосіб змінити версію так, щоб не було моменту, коли сайт оновлений наполовину. Ось процес, яким я користуюся, і причини кожного кроку.
Збирайте локально, публікуйте результат
Vite перетворює проєкт на теку зі звичайними файлами: HTML, бандли JavaScript і CSS з хешами в назвах та статичні ресурси. Ця тека і є весь сайт. Збирання на своїй машині лишає сервер без інструментів, які працювали б лише кілька секунд на день.
VITE_SITE_URL=https://example.com npm run buildАдреса сайту — значення часу збірки. Vite вбудовує її в бандл і в кожен абсолютний URL на сторінках: canonical, Open Graph, sitemap. Передача через командний рядок повністю тримає production-значення поза репозиторієм: нічого не треба редагувати перед збіркою й нічого зайвого не потрапляє в коміт.
Завантажуйте лише результат збірки
На сервер потрапляє тільки вміст теки збірки. Вихідний код, історія Git, тести, lock-файли та залежності лишаються на вашій машині. Менше файлів на сервері — менше що захищати, менше ризику віддати зайве й помітно швидше завантаження.
Саме тому не потрібне жодне середовище виконання. Nginx читає файли з диска й віддає їх. Немає процесу, який треба тримати живим, немає порту для резервування й нічого перезапускати, коли змінюється вміст.
Окремий каталог для кожного релізу
Кожне розгортання йде у власний каталог, названий коротким хешем коміту, з якого його зібрано. Така назва дає відповідь на питання «що зараз працює?» однією командою, а повернення назад зводиться до вказівки на каталог, який уже існує.
/var/www/example/
├── releases/
│ ├── <previous-release-id>/
│ └── <release-id>/
└── current -> releases/<release-id>Спершу завантажуйте у тимчасовий каталог у домашній теці, перевіряйте там і лише потім копіюйте на місце. Якщо передавання перерветься, неповна копія ніколи не потрапить у каталог, який читає вебсервер.
ssh -i /path/to/key user@203.0.113.10 'mkdir -m 700 ~/upload-<release-id>'
scp -r -i /path/to/key ./dist/. user@203.0.113.10:~/upload-<release-id>/Перевірте завантаження до публікації
Кількість файлів і розміри можуть збігатися, а вміст — відрізнятися. Порівняння контрольної суми кожного файлу з обох боків швидке й повністю знімає сумніви.
# локально
(cd dist && find . -type f | sort | xargs sha256sum) > local.txt
# на сервері
ssh -i /path/to/key user@203.0.113.10 \
'cd ~/upload-<release-id> && find . -type f | sort | xargs sha256sum' > remote.txt
diff local.txt remote.txt && echo "однакові"Якщо списки відрізняються лише роздільником між хешем і назвою файлу — це різниця у форматуванні між системами, а не у вмісті. Перш ніж вважати, що щось пішло не так, порівняйте самі хеші.
Власник і права
Файли належать root і недоступні вебсерверу для запису. Nginx потрібно лише читати їх. Каталогам потрібен біт виконання, щоб у них можна було заходити; звичайним файлам — ні.
sudo chown -R root:root /var/www/example/releases/<release-id>
sudo find /var/www/example/releases/<release-id> -type d -exec chmod 755 {} +
sudo find /var/www/example/releases/<release-id> -type f -exec chmod 644 {} +Нічого не має бути доступним для запису всім. Швидка перевірка після кожного розгортання ловить випадковий режим доступу, доки він не став звичкою.
sudo find /var/www/example/releases/<release-id> -perm /o=wПеремикайте версії атомарно
Корінь у Nginx указує на symlink, а не на каталог релізу. Розгортання — це переміщення цього symlink. Якщо робити необережно (видалити, потім створити), виникає коротка мить, коли кореня не існує й відвідувачі отримують помилку.
Створення нового посилання під тимчасовою назвою та перейменування його поверх старого прибирає цей проміжок. Перейменування в межах однієї файлової системи атомарне: кожен запит бачить або старий реліз, або новий, але ніколи — порожнечу.
sudo ln -sfn /var/www/example/releases/<release-id> /var/www/example/current.new
sudo mv -T /var/www/example/current.new /var/www/example/currentОскільки конфігурація не змінилася, Nginx перезавантажувати не потрібно. Він розв’язує symlink на кожен запит і одразу бачить нову ціль. Reload потрібен лише тоді, коли ви справді редагували файл конфігурації.
Зберігайте попередній реліз
Старий каталог лишається точно там, де був. Він коштує кілька мегабайтів і є всім планом відкату. Видалення старих релізів — окреме свідоме прибирання, а не частина розгортання.
Перевіряйте те, що справді віддається
Перевіряйте живі адреси через HTTPS, а не файли на диску. Переконайтеся, що потрібні сторінки повертають 200, редиректи ведуть куди слід, а неіснуюча адреса повертає справжній 404, а не головну сторінку.
curl -I https://example.com/en/
curl -I https://example.com/uk/
curl -I https://example.com/sitemap.xml
curl -I https://example.com/does-not-exist # очікуємо 404Справжній 404 важливіший, ніж здається. Якщо невідомі шляхи тихо віддають головну сторінку, пошукові системи індексують нескінченні її дублікати, а биті посилання лишаються для вас невидимими.
Одна пастка, про яку варто знати: коли HTTP уже перенаправляє на HTTPS, запит на порт 80 повертає короткий текст редиректу, а не вашу сторінку. Перевірка через заголовок Host на localhost і порт 80 виглядатиме як невдале розгортання, хоча воно вдалося. Перевіряйте через HTTPS.
Відкат
Відкат — це та сама команда розгортання з ідентифікатором попереднього релізу. Той самий механізм, та сама атомарність, без зміни конфігурації та без reload.
sudo ln -sfn /var/www/example/releases/<previous-release-id> /var/www/example/current.new
sudo mv -T /var/www/example/current.new /var/www/example/currentЗапишіть точну команду до розгортання, а не після того, як щось зламалося. Відкат, який доводиться відновлювати з пам’яті під тиском, — це не відкат.
Схожі нотатки
- Двомовне SEO для сайту на React і ViteЯк зробити на React і Vite дві повноцінні мовні версії з пререндереним HTML, окремими метаданими, canonical і взаємними hreflang.Читати нотатку
- Безпечний чекліст розгортання статичного сайту на спільному серверіПрактичний чекліст, як додати статичний сайт на сервер, де вже працюють інші проєкти, не зачепивши те, що вже працювало.Читати нотатку
Потрібно зробити щось подібне?
Ці нотатки — з роботи, яку я вже запустив. Якщо потрібне те саме й зроблене як слід, розкажіть, що ви задумали.