Обновление проекта
Цикл разработки
Компьютер: код → тесты → git commit → git push
│
Сервер: git pull → pip install → migrate → collectstatic → restart gunicorn
Никогда не правьте код прямо на продакшен-сервере: изменения потеряются при следующем git pull и не попадут в историю.
Ручное обновление
ssh myblog
cd ~/blog
git pull
source venv/bin/activate
set -a; source .env; set +a
pip install -r requirements.txt
python manage.py migrate
python manage.py collectstatic --noinput
sudo systemctl restart gunicorn
Каждый шаг важен:
| Шаг | Зачем |
|---|---|
git pull |
Новый код |
pip install -r requirements.txt |
Новые или обновлённые пакеты |
migrate |
Изменения структуры базы |
collectstatic |
Новые или изменённые CSS/JS/картинки |
restart gunicorn |
Python-код и шаблоны загружаются в память процессов один раз — без перезапуска изменения не применятся |
Скрипт деплоя
Чтобы не забыть шаг, автоматизируем. ~/blog/deploy.sh:
#!/usr/bin/env bash
set -euo pipefail
cd ~/blog
echo "→ Резервная копия базы"
pg_dump -U blog -h localhost blog | gzip > ~/backups/before-deploy-$(date +%F-%H%M).sql.gz
echo "→ Код"
git pull --ff-only
echo "→ Зависимости"
source venv/bin/activate
set -a; source .env; set +a
pip install -r requirements.txt --quiet
echo "→ Миграции и статика"
python manage.py migrate --noinput
python manage.py collectstatic --noinput
python manage.py check --deploy
echo "→ Перезапуск"
sudo systemctl restart gunicorn
echo "✓ Готово"
chmod +x deploy.sh
./deploy.sh
set -euo pipefail — остановиться при первой ошибке, при обращении к неопределённой переменной и при ошибке в любой части конвейера.
Чтобы sudo systemctl restart gunicorn не спрашивал пароль, разрешите эту команду пользователю deploy (sudo visudo -f /etc/sudoers.d/deploy):
deploy ALL=NOPASSWD: /bin/systemctl restart gunicorn
Запуск с компьютера одной командой:
ssh myblog "~/blog/deploy.sh"
Плавный перезапуск
restart на долю секунды прерывает обслуживание. Gunicorn умеет перезапускать воркеры плавно по сигналу HUP. Добавьте в gunicorn.service:
ExecReload=/bin/kill -s HUP $MAINPID
и используйте sudo systemctl reload gunicorn.
Если что-то пошло не так
sudo journalctl -u gunicorn -n 100 # последние логи
git log --oneline -5 # что выкатили
git checkout <предыдущий-коммит> # откат кода
sudo systemctl restart gunicorn
Откат миграций: python manage.py migrate blog 0007 (к нужной миграции) — поэтому и нужна копия базы перед деплоем.
Куда двигаться дальше
- CI/CD: GitHub Actions запускает тесты на каждый push и выкатывает проект на сервер по SSH автоматически.
- Docker: проект и все зависимости в контейнерах, одинаковая среда на компьютере и сервере.
- Мониторинг: Sentry для ошибок, UptimeRobot для проверки доступности.
Итог
- Обновление:
git pull→pip install→migrate→collectstatic→ перезапуск Gunicorn. - Скрипт
deploy.shсset -euo pipefailи копией базы делает выкладку надёжной. - Плавный перезапуск —
reloadс сигналом HUP. - Логи и git помогают быстро откатиться.
Практика решено 0 из 5
Расставьте шаги обновления проекта на сервере.
-
cd ~/blog && git pull -
python manage.py migrate -
source venv/bin/activate -
pip install -r requirements.txt -
sudo systemctl restart gunicorn -
python manage.py collectstatic --noinput
Вы поменяли только шаблон. Что обязательно сделать на сервере после git pull, чтобы изменения появились?
Напишите скрипт deploy.sh: остановка при любой ошибке (set -e), переход в ~/blog, git pull,
активация окружения, установка зависимостей, миграции, сборка статики без вопросов и перезапуск gunicorn.
#!/usr/bin/env bash
set -e
cd ~/blog
git pull
source venv/bin/activate
set -a; source .env; set +a
pip install -r requirements.txt
python manage.py migrate --noinput
python manage.py collectstatic --noinput
sudo systemctl restart gunicorn
echo "Готово"
Какие практики делают обновления безопаснее?
Что делает sudo systemctl reload gunicorn в отличие от restart (для сервиса с ExecReload=/bin/kill -s HUP $MAINPID)?
Войдите, чтобы отмечать пройденные уроки.