1. Введение: Выполнение логики развертывания в фоновом режиме

Здравствуйте! В предыдущей части мы настроили среду промежуточного сервера и создали основную структуру вебхука FastAPI для проверки секрета при получении запросов от GitHub Webhook. В этом процессе мы предварительно включили код функций handle_deploy и should_rebuild в файл main.py, что дало возможность заглянуть в основные идеи развертывания.

В этой четвертой части мы еще раз четко рассмотрим, как работает логика обработчика развертывания, которая была кратко обсуждена в третьей части, и сосредоточимся на том, как зарегистрировать этот вебхук FastAPI как службу Systemd, чтобы он автоматически запускался и работал стабильно даже после перезагрузки сервера. Теперь ваша система автоматического развертывания готова стать еще более надежной!

Тем, кто не видел предыдущие статьи, рекомендуется сначала ознакомиться с ними.

① Почему мы реализуем это самостоятельно?

② Общая архитектура и проектирование процесса

③ Настройка среды промежуточного сервера и основная настройка вебхука FastAPI


2. Взгляд на логику обработчика развертывания (handle_deploy)

Функция handle_deploy из файла main.py, которую мы написали в третьей части, отвечает за выполнение реальной работы развертывания в фоновом режиме, когда поступает запрос от вебхука GitHub. Эта функция выполняет несколько ключевых задач.

Рекомендуется просмотреть пример кода третьей части вместе с приведенным ниже руководством.

2.1. Управление несколькими проектами и настройка переменных окружения

Чтобы управлять несколькими GitHub репозиториями (проектами) с одним вебхуком, необходимо указать, где находится каждый репозиторий на сервере. Функция handle_deploy использует для этого словарь repo_paths, и эти значения предназначены для считывания из переменных окружения сервера.

В примере кода появляется переменная окружения, такая как SAMPLE_PROJECT_1_PATH. Вам нужно настроить актуальные пути для каждого проекта в файле .env промежуточного сервера (этот файл должен находиться в той же директории, что и main.py) или в файле службы systemd следующим образом:

Фрагмент кода

# ~/projects/webhook_server/.env

# Секрет GitHub Webhook (настроенный в 3 части)
GITHUB_WEBHOOK_SECRET="your_github_webhook_secret_value"

# Актуальные пути к каждому проекту на сервере
SAMPLE_PROJECT_1_PATH="/var/www/my-project1"
SAMPLE_PROJECT_2_PATH="/home/user/another-project"
SAMPLE_PROJECT_3_PATH="/opt/third-project"

Функция handle_deploy использует имя репозитория, полученное из полезной нагрузки вебхука, чтобы найти путь к проекту в словаре repo_paths. Если сопоставленный путь не найден, остается предупреждение Unknown repository и выполнение завершается.

Кроме того, в логике включено чтение дополнительных значений, таких как DEBUG или COLOR, из .env файла каждого проекта (например, /var/www/my-project1/.env), которые могут использоваться для выбора файла Docker Compose (docker-compose.dev.yml или docker-compose.prod.yml) и настройки имени Docker проекта. Это очень полезно для настройки процесса развертывания в соответствии с особенностями проекта.

2.2. Определение необходимости повторной сборки Docker образа (should_rebuild)

Повторная сборка образа Docker каждый раз при развертывании занимает много времени и является неэффективной. Функция should_rebuild использует команду diff Git, чтобы обнаружить, изменились ли ключевые файлы, такие как Dockerfile, requirements.txt или .env, которые влияют на сборку образа Docker.

Эта функция проверяет изменения с помощью команды git diff --name-only HEAD~1 и, если измененный список файлов включает заранее определенные trigger_files (например, Dockerfile, requirements.txt, .env, REBUILD_TRIGGER и т. д.), возвращает True, чтобы указать на необходимость повторной сборки образа.

В частности, файл REBUILD_TRIGGER является полезным трюком, который принудительно вызывает повторную сборку, даже если файл пуст. Его можно вручную создать на сервере, когда требуется новая сборка порой по причинам, отличным от git pull. Функция should_rebuild автоматически удаляет этот файл, чтобы избежать ненужной повторной сборки при следующем развертывании.

2.3. Выполнение команд Git и Docker Compose

Функция handle_deploy использует модуль subprocess Python для выполнения команд git и docker compose на сервере.

  • subprocess.run(["git", "-C", repo_path, "pull"], check=True): Опция check=True вызывает subprocess.CalledProcessError, если возникает ошибка при выполнении команды Git, что позволяет вашей программе на Python зафиксировать это и соответствующим образом зарегистрировать ошибку. Опция -C позволяет выполнить команду Git в указанной директории.

  • subprocess.run(["docker", "compose", "-p", project_name, "-f", compose_file, "up", "-d", "--build"], check=True): В зависимости от результата функции should_rebuild, команда будет выполнена с up -d или с добавлением опции --build для повторной сборки образа. Опция -p указывает имя проекта Docker Compose, чтобы избежать конфликтов между несколькими проектами.

Эта логика позволяет эффективно обновлять услуги, получая последний код проекта с помощью одного запроса вебхука и повторно собирая образы только в случае необходимости.


3. Запуск вебхука FastAPI как службы Systemd

До сих пор мы вручную запускали сервер вебхука командой uvicorn main:app --reload. Однако при перезагрузке сервера этот процесс исчезает, и служба останавливается даже при разрыве сеанса терминала. Чтобы избежать этого и добиться стабильной работы, необходимо зарегистрировать вебхук FastAPI как службу Systemd.

3.1. Почему нужно использовать Systemd?

  • Автозапуск: Сервис вебхука автоматически запускается при перезагрузке сервера.

  • Постоянное выполнение: Работает в фоновом режиме и не зависит от сеанса терминала.

  • Удобное управление: С помощью команды systemctl можно легко управлять запуском, остановкой, перезапуском, проверкой состояния и просмотром логов службы.

  • Эффективное использование ресурсов: Как было описано во второй части, сам вебхук сервер является легким приложением на Python и запускается через Systemd, в то время как такие тяжелые инструменты, как Git или Docker, работают непосредственно с установленными на системе версиями, что помогает избежать ненужных настроек Docker в Docker и увеличения размера контейнеров.

3.2. Написание файла службы Systemd (*.service)

Служба Systemd определяется с помощью файла .service. Этот файл должен находиться в каталоге /etc/systemd/system/. Мы создадим файл с именем github-webhook-deployer.service. Я выбрал длинное название для простоты объяснения, но вы можете выбрать более короткое и понятное имя.

# Создание файла (требуется право sudo)
sudo nano /etc/systemd/system/github-webhook-deployer.service

Содержимое файла:

# /etc/systemd/system/github-webhook-deployer.service

[Unit]
Description=Служба развертывания GitHub Webhook
After=network.target # Этот сервис запускается после активации сети.

[Service]
User=your_username # Учетная запись пользователя, под которой выполняется этот сервис (например: ubuntu, your_user)
Group=your_username # Группа, под которой выполняется этот сервис (например: ubuntu, your_user)
WorkingDirectory=/home/your_username/projects/webhook_server # Директория с приложением FastAPI

# Настройка переменных окружения:
# 1. Поскольку приложение FastAPI загружает файл .env, переменные, используемые внутри приложения, такие как GITHUB_WEBHOOK_SECRET, не обязательно нужно загружать через EnvironmentFile. Но если вы хотите управлять всеми окружениями согласованно на уровне systemd, можете оставить путь EnvironmentFile.
# EnvironmentFile=/home/your_username/projects/webhook_server/.env

# 2. Чтобы вызвать системные команды git, docker, docker compose с помощью subprocess.run(), необходимо явно добавить путь, где находятся эти команды, в переменную окружения PATH.
# Примерный путь PATH включает обычные пути Linux и путь к bin Python виртуальной среды. Вам следует отредактировать 'your_username' и 'venv' в соответствии с тем, что соответствует вашей среде.
# Точный PATH можно проверить в файле .bashrc сервера или с помощью команды 'echo $PATH'.
Environment="PATH=/home/your_username/projects/webhook_server/venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

ExecStart=/home/your_username/projects/webhook_server/venv/bin/uvicorn main:app --host 0.0.0.0 --port 8000 # Путь к файлу uvicorn в виртуальной среде
Restart=always # Сервис всегда перезапускается после его завершения.
StandardOutput=journal # Стандартный вывод отправляется в журнал Systemd.
StandardError=journal # Стандартные ошибки отправляются в журнал Systemd.

[Install]
WantedBy=multi-user.target # Этот сервис запускается в многопользовательском режиме (обычное состояние запуска сервера).

Объяснение:

  • [Unit] секция: Определяет общую информацию о сервисе (описание, зависимости). After=network.target указывает на то, что этот сервис должен запускаться после того, как активируются сетевые службы.

  • [Service] секция: Определяет, как будет выполняться сервис.

    • User, Group: Пользователь и группа, под которыми будет выполняться сервис. Обязательно должен быть установлен пользователь с правами Docker (например, пользователь, заданный командой sudo usermod -aG docker your_username).

    • WorkingDirectory: Директория, в которой находится файл main.py приложения FastAPI.

    • Environment="PATH=...": Этот аспект является ключевым. Когда вы вызываете git, docker, docker compose и другие внешние команды через subprocess.run(), переменная окружения PATH может отличаться от той, что установлена в .bashrc и т.п. Поэтому необходимо явно указать путь, включая путь к uvicorn в виртуальной среде (/home/your_username/projects/webhook_server/venv/bin) и стандартные пути для исполняемых файлов в системе (/usr/local/bin, /usr/bin и т.д.).

    • EnvironmentFile (опционально): Приложение FastAPI загружает файл .env с помощью python-dotenv, так что переменные окружения, используемые внутри приложения (например, GITHUB_WEBHOOK_SECRET, SAMPLE_PROJECT_N_PATH и т.д.), не обязательно должны загружаться через эту настройку. Но если вы хотите, чтобы systemd явно загружал эти переменные перед запуском сервиса (для обеспечения согласованной среды для процесса ExecStart и его дочерних процессов), использование EnvironmentFile может быть полезным. Вы можете комментировать или оставлять это в зависимости от предпочтения.

    • ExecStart: Реальная команда, запускающая сервис. Должен быть точно указан путь к исполняемому файлу uvicorn в виртуальной среде. Опции --host 0.0.0.0 --port 8000 позволяют принимать запросы на 8000 порту с любых IP.

    • Restart=always: Если сервис по какой-либо причине завершится, Systemd автоматически попытается его перезапустить.

    • StandardOutput=journal, StandardError=journal: Все выходные данные и ошибки сервиса отправляются в интегрированную систему логов Systemd — journalctl.

  • [Install] секция: Определяет, под управлением какого таргета сервис будет активирован. WantedBy=multi-user.target означает, что сервис будет автоматически запущен при обычной многопользовательской загрузке сервера.

3.3. Регистрация и запуск службы Systemd

После создания файла службы вам нужно сообщить Systemd о нем и запустить службу.

# Уведомляем Systemd о новом файле службы.
sudo systemctl daemon-reload

# Настраиваем автоматический запуск службы при загрузке.
sudo systemctl enable github-webhook-deployer.service

# Запускаем службу.
sudo systemctl start github-webhook-deployer.service

# Проверяем состояние службы. Должно отображаться 'active (running)'.
sudo systemctl status github-webhook-deployer.service

Теперь ваш вебхук FastAPI будет автоматически запускаться даже после перезагрузки сервера и надежно ждать запросов вебхука в фоновом режиме.

Пример состояния службы Systemd


4. Мониторинг и отладка вебхука FastAPI

Использование службы Systemd значительно упрощает проверку логов и диагностику проблем.

  • Проверка состояния службы:
sudo systemctl status github-webhook-deployer.service

Эта команда показывает текущее состояние службы, последнее время выполнения и идентификатор процесса.

  • Просмотр логов в реальном времени:
sudo journalctl -u github-webhook-deployer.service -f

Опция -u указывает на логи определенной службы, а опция -f позволяет постоянно выводить новые логи в реальном времени. Каждый раз, когда поступает запрос вебхука, сообщения журналов, установленные в main.py, будут отображаться здесь.

Если служба не запускается или не работает должным образом, первым делом стоит проверить логи journalctl на наличие сообщений об ошибках.


5. Заключение: Анонс следующей части

В этой четвертой части мы еще раз четко обсудили, как работает логика обработчика развертывания, которая была реализована в третьей части, и узнали о самом важном этапе — регистрации вебхука FastAPI как службы Systemd для надежного выполнения. Теперь ваш вебхук не будет беспокоиться даже после перезагрузки сервера.

В следующей пятой части мы завершим сборку системы, объединив последние кусочки пазла. Мы сосредоточимся на том, как настроить Nginx в качестве обратного прокси, безопасно выставить вебхук на внешний мир, внедрить HTTPS для повышения безопасности и, в конечном счете, протестировать фактическое автоматическое развертывание, связав вебхук с репозиторием GitHub. Ожидайте с нетерпением!