diff --git a/deploy.sh b/deploy.sh index a6e935e..9340fe3 100644 --- a/deploy.sh +++ b/deploy.sh @@ -1,248 +1,147 @@ #!/bin/bash -# Настройка цветов для вывода +set -euo pipefail + RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color +NC='\033[0m' -# Конфигурация REPO_URL="https://git.tvstart.ru/ychernenko/WFL.git" TARGET_DIR="/root/WFL" SERVICE_NAME="wfl-data.service" TARGET_ENV="/mnt/wfl/.env" -show_help() { - echo "Использование: $0 -t <домашняя команда> -r <релиз> [-l <лига>]" - echo " -t Домашняя команда" - echo " -r Релиз (тег или ветка в git)" - echo " -l Лига (опционально)" - echo " -h Показать эту справку" - echo "" - echo "Пример: $0 -t cska -r main" - echo "Пример: $0 -t zenit -r Barabanov_TEST -l vtb" - echo "Пример: $0 -t avtodor -r main" - echo "" - exit 0 -} - -# Функция для вывода цветных сообщений log_info() { - echo -e "${GREEN}[INFO]${NC} $1" >&2 + echo -e "${GREEN}[INFO]${NC} $1" } log_warn() { - echo -e "${YELLOW}[WARN]${NC} $1" >&2 + echo -e "${YELLOW}[WARN]${NC} $1" } log_error() { - echo -e "${RED}[ERROR]${NC} $1" >&2 + echo -e "${RED}[ERROR]${NC} $1" } -log_debug() { - echo -e "${BLUE}[DEBUG]${NC} $1" >&2 +show_help() { + echo "Использование: $0 [-r <релиз>] [-h]" + echo " -r Релиз (ветка или тег), по умолчанию: main" + echo " -h Показать справку" + echo + echo "Пример:" + echo " $0 -r main" + exit 0 } -# Функция проверки зависимостей системы -check_dependencies() { - local deps=("git" "python3" "pip" "netstat" "systemctl") - local missing=() - - for dep in "${deps[@]}"; do - if ! command -v "$dep" &> /dev/null; then - missing+=("$dep") - fi - done - - if [ ${#missing[@]} -ne 0 ]; then - log_warn "Отсутствуют некоторые команды: ${missing[*]}" - return 1 +check_root() { + if [[ $EUID -ne 0 ]]; then + log_error "Скрипт должен запускаться с правами root" + exit 1 fi - return 0 } -# Функция проверки и установки пакетов install_packages() { log_info "Обновление списка пакетов..." - if ! apt-get update; then - log_error "Ошибка при обновлении списка пакетов" - exit 1 - fi - - log_info "Установка необходимых пакетов..." - if ! apt-get install -y python3 python3-pip python3-venv git net-tools; then - log_error "Ошибка при установке пакетов" - exit 1 - fi - - # Проверка установки - if ! command -v python3 &> /dev/null; then - log_error "Python3 не установлен!" - exit 1 - fi - - if ! command -v pip &> /dev/null; then - log_error "pip не установлен!" - exit 1 - fi - - log_info "Версия Python: $(python3 --version)" - log_info "Версия pip: $(pip --version)" + apt-get update + + log_info "Установка пакетов..." + apt-get install -y python3 python3-pip python3-venv git net-tools } -# Функция загрузки кода download_code() { local release="$1" - local target_dir="$TARGET_DIR" - - log_info "Создание рабочей директории $target_dir..." - mkdir -p "$target_dir" - - if [ -d "$target_dir/.git" ]; then + + log_info "Подготовка директории $TARGET_DIR..." + mkdir -p "$TARGET_DIR" + + if [ -d "$TARGET_DIR/.git" ]; then log_info "Обновление существующего репозитория..." - cd "$target_dir" || exit 1 + cd "$TARGET_DIR" git fetch --all - if ! git checkout "$release"; then - log_error "Не удалось переключиться на релиз $release" - log_info "Доступные ветки:" - git branch -r - exit 1 - fi + git checkout "$release" git pull origin "$release" else - log_info "Клонирование репозитория $REPO_URL..." - if ! git clone "$REPO_URL" "$target_dir"; then - log_error "Ошибка при клонировании репозитория" - exit 1 - fi - cd "$target_dir" || exit 1 - if ! git checkout "$release"; then - log_error "Не удалось переключиться на релиз $release" - log_info "Доступные ветки:" - git branch -r - exit 1 - fi + log_info "Клонирование репозитория..." + git clone "$REPO_URL" "$TARGET_DIR" + cd "$TARGET_DIR" + git checkout "$release" fi - - log_info "Код успешно загружен (релиз: $release)" + + log_info "Код загружен" log_info "Текущая ветка: $(git branch --show-current)" log_info "Последний коммит: $(git log --oneline -1)" } -# Функция настройки виртуального окружения setup_venv() { - local target_dir="$TARGET_DIR" - cd "$target_dir" || exit 1 - - # Удаляем существующее виртуальное окружение для чистоты + cd "$TARGET_DIR" + if [ -d ".venv" ]; then - log_info "Удаление существующего виртуального окружения..." + log_info "Удаление старого виртуального окружения..." rm -rf .venv fi - + log_info "Создание виртуального окружения..." - if ! python3 -m venv .venv; then - log_error "Не удалось создать виртуальное окружение" - exit 1 - fi - - if [ ! -f ".venv/bin/activate" ]; then - log_error "Не удалось создать виртуальное окружение" - exit 1 - fi - - log_info "Активация виртуального окружения и установка зависимостей..." + python3 -m venv .venv + source .venv/bin/activate - - # Обновление pip - if ! pip install --upgrade pip; then - log_warn "Не удалось обновить pip" - fi - - # Проверка наличия requirements.txt + + log_info "Обновление pip..." + pip install --upgrade pip + if [ -f "requirements.txt" ]; then log_info "Установка зависимостей из requirements.txt..." - if pip install -r requirements.txt; then - log_info "Все зависимости успешно установлены" - else - log_error "Ошибка при установке зависимостей из requirements.txt" - exit 1 - fi + pip install -r requirements.txt else - log_warn "Файл requirements.txt не найден, устанавливаем базовые зависимости..." - if ! pip install requests pandas numpy fastapi uvicorn python-telegram-handler python-dotenv; then - log_error "Ошибка при установке базовых зависимостей" - exit 1 - fi - fi - - # Проверка основных пакетов - log_info "Проверка установки основных пакетов..." - if ! python -c " -try: - import requests, pandas, numpy - print('✓ Все основные пакеты успешно импортируются') -except ImportError as e: - print(f'✗ Ошибка импорта: {e}') - exit(1) -"; then - log_error "Ошибка при проверке пакетов" + log_error "Файл requirements.txt не найден" exit 1 fi - - log_info "Установленные пакеты:" - pip list --format=columns -} -# Функция создания systemd сервиса -create_systemd_service() { - log_info "Создание systemd сервиса" - local team="$1" - local league="$2" - # Останавливаем и отключаем старый сервис если он есть + log_info "Проверка uvicorn..." + .venv/bin/python -c "import fastapi, uvicorn; print('FastAPI и uvicorn установлены')" +} + +check_required_files() { + cd "$TARGET_DIR" + + if [ ! -f "app.py" ]; then + log_error "Файл app.py не найден в $TARGET_DIR" + exit 1 + fi + + log_info "Файл app.py найден" +} + +setup_firewall() { + log_info "Открытие порта 8000..." + + if command -v ufw >/dev/null 2>&1 && systemctl is-active --quiet ufw; then + ufw allow 8000/tcp || log_warn "Не удалось открыть порт 8000 в ufw" + fi + + if command -v firewall-cmd >/dev/null 2>&1 && systemctl is-active --quiet firewalld; then + firewall-cmd --permanent --add-port=8000/tcp || true + firewall-cmd --reload || true + fi +} + +create_systemd_service() { + local service_file="/etc/systemd/system/$SERVICE_NAME" + + log_info "Создание systemd unit: $service_file" + if systemctl is-active --quiet "$SERVICE_NAME"; then - log_info "Остановка $SERVICE_NAME..." + log_info "Остановка старого сервиса..." systemctl stop "$SERVICE_NAME" fi if systemctl is-enabled --quiet "$SERVICE_NAME"; then - log_info "Отключение $SERVICE_NAME..." - systemctl disable "$SERVICE_NAME" + systemctl disable "$SERVICE_NAME" || true fi - - log_info "Создание systemd сервиса для команды: $team" - if [ -n "$league" ]; then - log_info "Лига: $league" - fi - - # Останавливаем и отключаем старый сервис если он есть - if systemctl is-active --quiet wfl-data.service; then - log_info "Остановка wfl-data.service..." - systemctl stop wfl-data.service - fi - - if systemctl is-enabled --quiet wfl-data.service; then - log_info "Отключение wfl-data.service..." - systemctl disable wfl-data.service - fi - - # Формируем команду для data сервиса - local data_command="python3 $TARGET_DIR/get_data.py --team \"$team\"" - if [ -n "$league" ]; then - data_command="$data_command --league \"$league\"" - fi - - log_info "Создание файла сервиса: $SERVICE_NAME" - # Формируем команду для data сервиса - local data_service_file="/etc/systemd/system/$SERVICE_NAME" - - log_info "Создание файла сервиса: $data_service_file" - cat > "$data_service_file" << EOF + cat > "$service_file" </dev/null | awk '{print $7; exit}' || - ip a 2>/dev/null | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | - grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1' | head -n1) - else - log_error "Скрипт работает только на Linux" - exit 1 - fi - - log_info "Определен IP адрес: $ip_address" - - # База данных хостов в виде пар IP-команда - declare -A hosts=( - ["10.10.35.21"]="cska" - ["10.10.35.22"]="Lokomotiv Kuban" - ["10.10.35.23"]="uralmash" - ["10.10.35.24"]="betcity parma" - ["10.10.35.25"]="avtodor" - ["10.10.35.26"]="zenit" - ["10.10.35.27"]="samara" - ["10.10.35.28"]="mba-mai" - ["10.10.35.29"]="Pari Nizhny Novgorod" - ["10.10.35.30"]="unics" - ["10.10.35.31"]="enisey" - ) - - # Определение команды по IP - detected_team="${hosts[$ip_address]}" - - # Определение финальной команды (приоритет у аргумента, затем у автоопределения) - if [[ -n "$team_arg" ]]; then - final_team="$team_arg" - log_info "Используется команда из аргумента: $final_team" - elif [[ -n "$detected_team" ]]; then - final_team="$detected_team" - log_info "Используется автоопределенная команда: $final_team" - else - log_error "Не удалось определить команду. Укажите явно через -t" - echo "Доступные команды:" >&2 - for ip in "${!hosts[@]}"; do - echo " $ip: ${hosts[$ip]}" >&2 - done - exit 1 - fi - - # Возвращаем только чистую строку с названием команды (без цветовых кодов) - echo "$final_team" -} - -# Функция настройки firewall -setup_firewall() { - log_info "Настройка firewall (открытие порта 8000)..." - - # Проверяем наличие ufw - if command -v ufw &> /dev/null && systemctl is-active --quiet ufw; then - ufw allow 8000/tcp - log_info "Порт 8000 открыт в ufw" - fi - - # Для firewalld (CentOS/RHEL) - if command -v firewall-cmd &> /dev/null && systemctl is-active --quiet firewalld; then - firewall-cmd --permanent --add-port=8000/tcp - firewall-cmd --reload - log_info "Порт 8000 открыт в firewalld" - fi -} - -# Функция проверки порта -check_port() { - local port=8000 - local occupied=false - - if command -v netstat &> /dev/null && netstat -tuln 2>/dev/null | grep ":$port " > /dev/null; then - occupied=true - elif command -v ss &> /dev/null && ss -tuln 2>/dev/null | grep ":$port " > /dev/null; then - occupied=true - fi - - if [ "$occupied" = true ]; then - log_warn "Порт $port уже занят. Возможно, приложение уже запущено." - return 1 - fi - return 0 -} - -# Функция управления сервисами -manage_services() { - local action="$1" - - case $action in - "start") - log_info "Запуск сервиса $SERVICE_NAME..." - if ! systemctl start "$SERVICE_NAME"; then - log_error "Ошибка при запуске сервиса" - return 1 - fi - ;; - "restart") - log_info "Перезапуск сервиса $SERVICE_NAME..." - if ! systemctl restart "$SERVICE_NAME"; then - log_error "Ошибка при перезапуске сервиса" - return 1 - fi - ;; - "stop") - log_info "Остановка сервиса $SERVICE_NAME..." - if ! systemctl stop "$SERVICE_NAME"; then - log_error "Ошибка при остановке сервиса" - return 1 - fi - ;; - esac - - # Даем время сервису на запуск/остановку +start_service() { + log_info "Запуск сервиса..." + systemctl restart "$SERVICE_NAME" sleep 3 - return 0 } -# Функция проверки статуса сервиса check_service_status() { - log_info "Статус сервиса $SERVICE_NAME:" - if systemctl is-active "$SERVICE_NAME" &>/dev/null; then + if systemctl is-active --quiet "$SERVICE_NAME"; then + log_info "Сервис успешно запущен" systemctl status "$SERVICE_NAME" --no-pager -l - return 0 else - log_warn "Сервис $SERVICE_NAME не запущен" - return 1 + log_error "Сервис не запущен" + journalctl -u "$SERVICE_NAME" -n 50 --no-pager + exit 1 fi } -# Функция проверки системы -check_system() { - log_info "Проверка системы..." - - # Проверка дистрибутива - if [ -f /etc/os-release ]; then - source /etc/os-release - log_info "Дистрибутив: $PRETTY_NAME" - fi - - # Проверка свободного места - local available_space=$(df /root --output=avail | tail -1) - if [ "$available_space" -lt 1048576 ]; then # Меньше 1GB - log_warn "Мало свободного места: $((available_space / 1024)) MB" - else - log_info "Свободное место: $((available_space / 1024 / 1024)) GB" - fi - - # Проверка памяти - local total_mem=$(grep MemTotal /proc/meminfo | awk '{print $2}') - log_info "Оперативная память: $((total_mem / 1024)) MB" -} - -# Функция очистки при прерывании -cleanup() { - log_info "Очистка..." - if [ -f "/etc/systemd/system/$SERVICE_NAME" ]; then - manage_services "stop" - systemctl disable "$SERVICE_NAME" 2>/dev/null || true - rm -f "/etc/systemd/system/$SERVICE_NAME" - systemctl daemon-reload - fi -} - -# Основная функция main() { - local team="" # переменная для команды - local release="main" # значение по умолчанию - local league="" # переменная для лиги - - # Обработка аргументов командной строки - while getopts "t:r:l:h" opt; do + local release="main" + + while getopts "r:h" opt; do case $opt in - t) team="$OPTARG" ;; r) release="$OPTARG" ;; - l) league="$OPTARG" ;; h) show_help ;; *) log_error "Неверный аргумент"; exit 1 ;; esac done - - # Проверка обязательных аргументов - if [ -z "$team" ] && [ -z "$release" ]; then - log_error "Необходимо указать команду (-t) и релиз (-r)" - show_help - fi - - log_info "Начало установки WFL Data Service..." - log_info "Команда: $team, Релиз: $release" - if [ -n "$league" ]; then - log_info "Лига: $league" - else - log_info "Лига: не указана (будет использовано значение по умолчанию)" - fi - - # Проверка прав root - if [[ $EUID -ne 0 ]]; then - log_error "Этот скрипт должен запускаться с правами root" - exit 1 - fi - - # Проверка системы - check_system - - # Проверка зависимостей - check_dependencies - - # Установка пакетов - install_packages - - # Загрузка кода - download_code "$release" - - # Настройка виртуального окружения - setup_venv - - # Определение команды - final_team=$(detect_team "$team") - log_info "Финальная команда: '$final_team'" - - # Проверка что команда не пустая и не содержит спецсимволов - if [[ -z "$final_team" || "$final_team" =~ [^a-zA-Z0-9[:space:]-] ]]; then - log_error "Некорректное название команды: '$final_team'" - exit 1 - fi - - # Настройка firewall - setup_firewall - - # Проверка порта - check_port - - # Создание systemd сервиса - create_systemd_service "$final_team" "$league" - - log_info "Настройка завершена!" - - # Запуск сервиса - if manage_services "start"; then - log_info "Сервис успешно запущен" - else - log_error "Не удалось запустить сервис" - exit 1 - fi - - # Проверка статуса - check_service_status - - # Получаем IP для вывода - ip_address=$(ip route get 1 2>/dev/null | awk '{print $7; exit}' || - ip a 2>/dev/null | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | - grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1' | head -n1) - - log_info "==================================================" - log_info "Установка завершена успешно!" + + check_root + + log_info "Начало деплоя" log_info "Релиз: $release" - log_info "Рабочая директория: $TARGET_DIR" - log_info "Команда: $final_team" - if [ -n "$league" ]; then - log_info "Лига: $league" - fi - log_info "" - log_info "Для просмотра логов:" + + install_packages + download_code "$release" + check_required_files + setup_venv + setup_firewall + create_systemd_service + start_service + check_service_status + + log_info "========================================" + log_info "Готово" + log_info "Сервис: $SERVICE_NAME" + log_info "Директория: $TARGET_DIR" + log_info "Логи:" log_info " journalctl -u $SERVICE_NAME -f" - log_info " journalctl -t WFL -f" - log_info "" - log_info "Управление сервисом:" - log_info " Перезапуск: systemctl restart $SERVICE_NAME" - log_info " Остановка: systemctl stop $SERVICE_NAME" - log_info " Статус: systemctl status $SERVICE_NAME" - log_info " Логи: journalctl -u $SERVICE_NAME" - log_info "" - log_info "Проверка работы:" - log_info " Проверить процессы: ps aux | grep get_data.py" - log_info " Проверить логи: tail -f $TARGET_DIR/logs/*.log 2>/dev/null || echo 'Директория логов не найдена'" - log_info "==================================================" + log_info "Статус:" + log_info " systemctl status $SERVICE_NAME" + log_info "Перезапуск:" + log_info " systemctl restart $SERVICE_NAME" + log_info "========================================" } -# Запуск основной функции main "$@" \ No newline at end of file