Привіт! Я Kernelka і сьогодні ми зробимо ваш Nginx справжньою ракетою 🚀: безперервні релізи, перезавантаження без обриву з’єднань і надійний план відкату. Ми поєднаємо перевірені техніки graceful reload, atomic-підміну конфігів та systemd-магію. Результат — zero‑downtime на бойовому сервер Linux.
План гри: що дає zero‑downtime
Безпростаєве розгортання — це коли ви оновлюєте конфіги/сертифікати/бекенди, а користувачі навіть не помічають змін. Ключові елементи:
- Попередня перевірка конфігурації (
nginx -t) і лише потім graceful reload. - Atomic-підміна директорії з конфігами через символічне посилання (symlink swap).
reuseportдля одночасної роботи старих і нових воркерів на одному порту.- systemd для організації залежностей і автоматизації задач (так, це саме ті cron та systemd timers).
Готуємо Nginx до безперервних релізів
1) Включаємо reuseport для справді м’яких перезапусків
SO_REUSEPORT дозволяє старим і новим процесам Nginx ділити той самий порт, поки йде перезавантаження. Створіть сніпет і підключіть його у ваших server {} блоках.
sudo mkdir -p /etc/nginx/snippets
sudo tee /etc/nginx/snippets/reuseport.conf >/dev/null <<'EOF'
# Додайте відповідні рядки для ваших віртуальних хостів
listen 80 reuseport;
# Якщо є TLS
# listen 443 ssl http2 reuseport;
EOF
# Приклад підключення у вашому vhost
# у файлі сервера додайте: include /etc/nginx/snippets/reuseport.conf;
sudo nginx -t && sudo systemctl reload nginx
2) Atomic-підміна конфігів через "live"-посилання
Структура: усі релізи у /etc/nginx/conf.d/releases/<build-id>, а робоча збірка — це посилання /etc/nginx/conf.d/live. Основний конфіг Nginx підтягує лише live/*.conf.
# 2.1. Завантажувач конфігу
sudo tee /etc/nginx/conf.d/000-loader.conf >/dev/null <<'EOF'
# Підхоплюємо лише активний реліз
include /etc/nginx/conf.d/live/*.conf;
EOF
# 2.2. Створимо перший реліз і активуємо його
REL=init-$(date +%Y%m%d-%H%M%S)
sudo mkdir -p /etc/nginx/conf.d/releases/$REL
# Покладіть сюди ваші *.conf, наприклад app.conf
sudo tee /etc/nginx/conf.d/releases/$REL/app.conf >/dev/null <<'EOF'
server {
include /etc/nginx/snippets/reuseport.conf;
server_name _;
location / { return 200 'OK'; }
}
EOF
# Активуємо атомарно (ln -sfn не рве існуючі з’єднання)
sudo ln -sfn /etc/nginx/conf.d/releases/$REL /etc/nginx/conf.d/live
sudo nginx -t && sudo systemctl reload nginx
3) Скрипт деплою з перевіркою і миттєвим відкатом
Нижче — простий деплой-скрипт. Він копіює нові конфіги у нову директорію релізу, перевіряє їх, атомарно перемикає live і робить graceful reload. У разі помилки — відкат.
sudo tee /usr/local/bin/nginx-zero-downtime-deploy >/dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
RELEASE="$(date +%Y%m%d-%H%M%S)"
SRC_DIR="/srv/nginx-config" # тут ваш git-клон з *.conf
REL_DIR="/etc/nginx/conf.d/releases/$RELEASE"
LIVE_LINK="/etc/nginx/conf.d/live"
# 1) Оновлюємо джерело (можна замінити на rsync з CI)
if [[ -d "$SRC_DIR/.git" ]]; then
git -C "$SRC_DIR" pull --ff-only
fi
# 2) Готуємо новий реліз
sudo mkdir -p "$REL_DIR"
sudo cp -a "$SRC_DIR"/*.conf "$REL_DIR"/
# 3) Тимчасово перемикаємо live на новий реліз і перевіряємо
PREV="$(readlink -f "$LIVE_LINK" || true)"
sudo ln -sfn "$REL_DIR" "$LIVE_LINK"
if ! sudo nginx -t; then
echo "Config test failed. Rolling back..." >&2
[[ -n "$PREV" ]] && sudo ln -sfn "$PREV" "$LIVE_LINK"
exit 1
fi
# 4) Graceful reload
sudo systemctl reload nginx
echo "Deployed release: $RELEASE"
EOF
sudo chmod +x /usr/local/bin/nginx-zero-downtime-deploy
systemd socket activation: де вона дійсно корисна
Чесно: сам Nginx у мейнстримі не підтримує прийом дескрипторів від systemd для HTTP-портів. Зате socket activation ідеальна для бекендів — наприклад, php-fpm чи uvicorn на UNIX-сокетах. Це прибирає 502-помилки під час перезапусків бекенда.
# Приклад: вмикаємо php-fpm через сокет
sudo tee /etc/systemd/system/php-fpm.socket >/dev/null <<'EOF'
[Unit]
Description=PHP-FPM Socket (on-demand)
[Socket]
ListenStream=/run/php-fpm.sock
SocketMode=0660
SocketUser=www-data
SocketGroup=www-data
[Install]
WantedBy=sockets.target
EOF
sudo tee /etc/systemd/system/php-fpm.service >/dev/null <<'EOF'
[Unit]
Description=PHP-FPM Service
Requires=php-fpm.socket
After=network.target
[Service]
Type=simple
ExecStart=/usr/sbin/php-fpm --nodaemonize --fpm-config /etc/php-fpm.conf
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now php-fpm.socket
Тепер у nginx налаштування використовуйте upstream на unix:/run/php-fpm.sock — systemd сам підніме php-fpm при першому зверненні.
Автоматизація через systemd timers (замість cron)
Щоб деплой ішов сам (CI/CD, nightly або з гіту) — запустіть таймер. Це і є приємна автоматизація задач на нормальному сервер Linux.
sudo tee /etc/systemd/system/deploy-nginx.service >/dev/null <<'EOF'
[Unit]
Description=Deploy Nginx configs atomically
[Service]
Type=oneshot
ExecStart=/usr/local/bin/nginx-zero-downtime-deploy
EOF
sudo tee /etc/systemd/system/deploy-nginx.timer >/dev/null <<'EOF'
[Unit]
Description=Run Nginx deploy every 5 minutes
[Timer]
OnCalendar=*:0/5
Persistent=true
[Install]
WantedBy=timers.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now deploy-nginx.timer
Логи запусків дивіться так:
journalctl -u deploy-nginx.service -n 100 --no-pager
Альтернативні способи
- Blue-Green: тримайте дві групи
server {}і перемикайте трафік черезmapабо окремий L4-проксі. Добре для великих релізів. - Containerized: тримайте Nginx у контейнері, а конфіги — як том; перемикайте тег/том атомарно й робіть graceful reload всередині контейнера.
- Path-активація: замість таймера можна зробити
systemd.path, який реагує на зміни у/srv/nginx-config.
GUI-спосіб через Cockpit
Якщо вам зручніше через веб-інтерфейс, встановіть Cockpit і керуйте службами/таймерами:
sudo apt install -y cockpit || sudo dnf install -y cockpit
sudo systemctl enable --now cockpit.socket
Далі в браузері відкрийте https://<IP>:9090 → Services → знайдіть nginx і ваш deploy-nginx.timer: вмикайте/вимикайте, викликайте Reload, дивіться журнали. Зручно й безпечно 🛡️
FAQ
Q: Після reload бачу короткі 502. Чому?
A: Зазвичай падає бекенд. Увімкніть для нього systemd socket activation або додайте proxy_next_upstream з кількома upstream’ами. Переконайтеся, що Nginx говорить з бекендом по unix-сокету або по TCP з health-check.
Q: “address already in use” під час запуску?
A: Перевірте, що ви не намагаєтеся одночасно тримати порт у systemd .socket і у Nginx. Для zero‑downtime використовуйте reuseport, а не socket activation на 80/443.
Q: Як швидко відкочуватися?
A: Знайдіть попередній реліз у /etc/nginx/conf.d/releases/ і перемкніть посилання назад:
PREV="/etc/nginx/conf.d/releases/<prev-id>"
sudo ln -sfn "$PREV" /etc/nginx/conf.d/live
sudo nginx -t && sudo systemctl reload nginx
Q: Чому таймер не запускається?
A: Перевірте systemctl list-timers, час сервера і що таймер увімкнено (enabled). Дивіться логи сервісу деплою, а не таймера.
Q: Чи достатньо nginx -s reload?
A: Так, якщо конфіги валідні і ввімкнено reuseport. Для великих змін інколи краще staged-підміна й поетапний reload.
Порада від Kernelka
Додайте у nginx налаштування worker_shutdown_timeout 10s; — старі воркери коректно догризуть активні з’єднання перед виходом. І завжди тримайте напоготові сценарій відкату в тому ж репозиторії, що й деплой.
Підсумок
- Перевіряйте конфіги і робіть graceful reload замість рестарту.
- Використовуйте
reuseportі atomic-підміну черезlive-посилання. - Вмикайте systemd socket activation для бекендів, не для 80/443 у Nginx.
- Заведіть таймери cron та systemd timers для стабільного CI/CD.
- Майте швидкий rollback і журнали на долоні.
Готово! Ваш Nginx тепер розгортається без простою й чемно обслуговує трафік навіть під час змін.

Прокоментувати
На сайті відображається лише твоє ім'я та коментар. Електронна пошта зберігається виключно для зв'язку з тобою за потреби та в жодному разі не передається стороннім особам.