--- - name: Полная миграция ботов на новый сервер hosts: new_server become: yes vars: # Основная директория проекта project_root: "/home/prod" # Пользователь и группа deploy_user: "deploy" uid: "1001" gid: "1001" # Старый сервер для копирования данных old_server: "root@77.223.98.129" # Опция: пересоздавать папку /home/prod (по умолчанию — нет) recreate_project: false # Grafana настройки grafana_admin_user: "{{ lookup('env', 'GRAFANA_ADMIN_USER') | default('admin') }}" grafana_admin_password: "{{ lookup('env', 'GRAFANA_ADMIN_PASSWORD') | default('admin') }}" tasks: - name: Обновить SSH host key для избежания ошибок при переустановке known_hosts: path: ~/.ssh/known_hosts name: "{{ ansible_host }}" key: "{{ lookup('pipe', 'ssh-keyscan -t rsa,ecdsa,ed25519 ' + ansible_host) }}" state: present delegate_to: localhost run_once: true ignore_errors: yes - name: Обновить кэш пакетов apt: update_cache: yes - name: Установить необходимые пакеты apt: name: - docker.io - docker-compose - make - git - python3-pip - curl - sshpass - rsync - vim - zsh - ufw - htop - iotop - traceroute - ncdu - prometheus-node-exporter state: present - name: Проверить существование swap-файла stat: path: /swapfile register: swap_file_stat - name: Создать swap-файл (2GB) command: fallocate -l 2G /swapfile when: not swap_file_stat.stat.exists - name: Установить правильные права на swap-файл file: path: /swapfile mode: '0600' owner: root group: root - name: Настроить swap-файл command: mkswap /swapfile when: not swap_file_stat.stat.exists - name: Включить swap-файл command: swapon /swapfile when: not swap_file_stat.stat.exists - name: Настроить swappiness = 10 (временно) sysctl: name: vm.swappiness value: '10' state: present reload: yes - name: Настроить swappiness = 10 (постоянно) lineinfile: path: /etc/sysctl.conf regexp: '^vm\.swappiness\s*=' line: 'vm.swappiness = 10' state: present - name: Добавить swap-файл в /etc/fstab для автоматического монтирования lineinfile: path: /etc/fstab line: '/swapfile none swap sw 0 0' state: present create: yes - name: Проверить статус swap command: swapon --show register: swap_status changed_when: false - name: Показать информацию о swap debug: var: swap_status.stdout_lines - name: Включить и запустить prometheus-node-exporter systemd: name: prometheus-node-exporter enabled: yes state: started - name: Проверить статус prometheus-node-exporter command: systemctl status prometheus-node-exporter register: node_exporter_status changed_when: false - name: Показать статус prometheus-node-exporter debug: var: node_exporter_status.stdout_lines - name: Проверить, что node_exporter слушает на порту 9100 command: netstat -tulpn | grep 9100 register: node_exporter_port changed_when: false - name: Показать информацию о порте 9100 debug: var: node_exporter_port.stdout_lines - name: Обновить Docker Compose до последней версии get_url: url: "https://github.com/docker/compose/releases/latest/download/docker-compose-{{ ansible_system }}-{{ ansible_architecture }}" dest: /usr/local/bin/docker-compose mode: '0755' become: yes - name: Включить и запустить Docker systemd: name: docker enabled: yes state: started - name: Разрешить SSH (порт 22) перед включением UFW ufw: rule: allow port: "22" proto: tcp - name: Настроить политику UFW по умолчанию ufw: policy: deny direction: incoming - name: Включить UFW (файрвол) ufw: state: enabled - name: Открыть порты для сервисов ufw: rule: allow port: "{{ item }}" proto: tcp loop: - "8080" # Telegram Bot - "8081" # AnonBot - "9090" # Prometheus - "3000" # Grafana - "9100" # Node Exporter - name: Проверить существование пользователя deploy getent: database: passwd key: "{{ deploy_user }}" register: user_exists failed_when: false - name: Создать группу deploy с GID 1001 group: name: "{{ deploy_user }}" gid: "{{ gid }}" when: user_exists.ansible_facts.getent_passwd is not defined - name: Создать пользователя deploy с UID 1001 (если не существует) user: name: "{{ deploy_user }}" uid: "{{ uid }}" group: "{{ gid }}" shell: /bin/zsh create_home: yes system: no groups: docker append: yes when: user_exists.ansible_facts.getent_passwd is not defined - name: Установить zsh как оболочку по умолчанию для существующего пользователя deploy user: name: "{{ deploy_user }}" shell: /bin/zsh when: user_exists.ansible_facts.getent_passwd is defined - name: Скопировать SSH ключ с локальной машины для пользователя deploy authorized_key: user: "{{ deploy_user }}" key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}" state: present - name: Настроить безопасный SSH lineinfile: path: /etc/ssh/sshd_config regexp: "^{{ item.regexp }}" line: "{{ item.line }}" backup: yes loop: - { regexp: "PermitRootLogin", line: "PermitRootLogin no" } - { regexp: "PasswordAuthentication", line: "PasswordAuthentication no" } - { regexp: "PubkeyAuthentication", line: "PubkeyAuthentication yes" } notify: reload ssh - name: Удалить /home/prod, если требуется (чистое развертывание) file: path: "{{ project_root }}" state: absent when: recreate_project | bool - name: Создать директорию проекта /home/prod file: path: "{{ project_root }}" state: directory owner: "{{ deploy_user }}" group: "{{ deploy_user }}" mode: '0755' - name: Настроить SSH ключи для GitHub authorized_key: user: "{{ deploy_user }}" key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}" state: present - name: Скопировать приватный SSH ключ для Git copy: src: "~/.ssh/id_rsa" dest: "/home/{{ deploy_user }}/.ssh/id_rsa" owner: "{{ deploy_user }}" group: "{{ deploy_user }}" mode: '0600' remote_src: no - name: Настроить SSH config для GitHub lineinfile: path: "/home/{{ deploy_user }}/.ssh/config" line: "Host github.com\n StrictHostKeyChecking no\n UserKnownHostsFile /dev/null" create: yes owner: "{{ deploy_user }}" group: "{{ deploy_user }}" mode: '0600' - name: Переключиться на пользователя deploy meta: reset_connection - name: Клонировать основной репозиторий prod git: repo: git@github.com:KerradKerridi/prod.git dest: "{{ project_root }}" clone: yes update: yes become: yes become_user: "{{ deploy_user }}" - name: Клонировать AnonBot git: repo: git@github.com:KerradKerridi/AnonBot.git dest: "{{ project_root }}/bots/AnonBot" clone: yes update: yes become: yes become_user: "{{ deploy_user }}" - name: Клонировать telegram-helper-bot git: repo: git@github.com:KerradKerridi/telegram-helper-bot.git dest: "{{ project_root }}/bots/telegram-helper-bot" clone: yes update: yes become: yes become_user: "{{ deploy_user }}" - name: Установить правильные права на дашборд Node Exporter Full file: path: "{{ project_root }}/infra/grafana/provisioning/dashboards/node-exporter-full-dashboard.json" owner: "{{ deploy_user }}" group: "{{ deploy_user }}" mode: '0644' become: yes become_user: "{{ deploy_user }}" - name: Скопировать SSH ключ на старый сервер для копирования файлов authorized_key: user: root key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}" state: present delegate_to: "{{ old_server }}" - name: Копировать .env для telegram-helper-bot со старого сервера fetch: src: "/home/prod/bots/telegram-helper-bot/.env" dest: "/tmp/telegram-helper-bot.env" flat: yes delegate_to: "{{ old_server }}" - name: Переместить .env для telegram-helper-bot на новое место copy: src: "/tmp/telegram-helper-bot.env" dest: "{{ project_root }}/bots/telegram-helper-bot/.env" owner: "{{ deploy_user }}" group: "{{ deploy_user }}" mode: '0644' become: yes become_user: "{{ deploy_user }}" - name: Проверить размер БД для telegram-helper-bot stat: path: "/home/prod/bots/telegram-helper-bot/database/tg-bot-database.db" delegate_to: "{{ old_server }}" register: db_size - name: Показать размер БД для telegram-helper-bot debug: msg: "Размер БД: {{ (db_size.stat.size / 1024 / 1024) | round(2) }} MB" - name: Копировать БД для telegram-helper-bot fetch: src: "/home/prod/bots/telegram-helper-bot/database/tg-bot-database.db" dest: "/tmp/tg-bot-database.db" flat: yes delegate_to: "{{ old_server }}" - name: Переместить БД для telegram-helper-bot на новое место copy: src: "/tmp/tg-bot-database.db" dest: "{{ project_root }}/bots/telegram-helper-bot/database/tg-bot-database.db" owner: "{{ deploy_user }}" group: "{{ deploy_user }}" mode: '0644' become: yes become_user: "{{ deploy_user }}" - name: Создать папку voice_users на новом сервере file: path: "{{ project_root }}/bots/telegram-helper-bot/voice_users" state: directory owner: "{{ deploy_user }}" group: "{{ deploy_user }}" mode: '0755' become: yes become_user: "{{ deploy_user }}" - name: Создать временную папку для voice_users на локальной машине file: path: "/tmp/voice_users_migration" state: directory mode: '0755' delegate_to: localhost become: no - name: Копировать voice_users со старого сервера на локальную машину command: > rsync -avz --progress --stats --partial --verbose root@77.223.98.129:/home/prod/bots/telegram-helper-bot/voice_users/ /tmp/voice_users_migration/ delegate_to: localhost become: no - name: Копировать voice_users с локальной машины на новый сервер synchronize: src: "/tmp/voice_users_migration/" dest: "{{ project_root }}/bots/telegram-helper-bot/voice_users/" mode: push rsync_opts: "--progress --stats --partial --verbose" become: yes become_user: "{{ deploy_user }}" - name: Очистить временную папку на локальной машине file: path: "/tmp/voice_users_migration" state: absent delegate_to: localhost become: no - name: Копировать корневой .env файл fetch: src: "/home/prod/.env" dest: "/tmp/root.env" flat: yes delegate_to: "{{ old_server }}" - name: Переместить корневой .env файл на новое место copy: src: "/tmp/root.env" dest: "{{ project_root }}/.env" owner: "{{ deploy_user }}" group: "{{ deploy_user }}" mode: '0644' become: yes become_user: "{{ deploy_user }}" - name: Копировать .env для AnonBot fetch: src: "/home/prod/bots/AnonBot/.env" dest: "/tmp/anonbot.env" flat: yes delegate_to: "{{ old_server }}" - name: Переместить .env для AnonBot на новое место copy: src: "/tmp/anonbot.env" dest: "{{ project_root }}/bots/AnonBot/.env" owner: "{{ deploy_user }}" group: "{{ deploy_user }}" mode: '0644' become: yes become_user: "{{ deploy_user }}" - name: Проверить размер БД для AnonBot stat: path: "/home/prod/bots/AnonBot/database/anon_qna.db" delegate_to: "{{ old_server }}" register: anon_db_size - name: Показать размер БД для AnonBot debug: msg: "Размер БД AnonBot: {{ (anon_db_size.stat.size / 1024 / 1024) | round(2) }} MB" - name: Копировать БД для AnonBot fetch: src: "/home/prod/bots/AnonBot/database/anon_qna.db" dest: "/tmp/anon_qna.db" flat: yes delegate_to: "{{ old_server }}" - name: Переместить БД для AnonBot на новое место copy: src: "/tmp/anon_qna.db" dest: "{{ project_root }}/bots/AnonBot/database/anon_qna.db" owner: "{{ deploy_user }}" group: "{{ deploy_user }}" mode: '0644' become: yes become_user: "{{ deploy_user }}" - name: Установить права на скопированные файлы file: path: "{{ item }}" owner: "{{ deploy_user }}" group: "{{ deploy_user }}" mode: '0644' loop: - "{{ project_root }}/bots/telegram-helper-bot/.env" - "{{ project_root }}/bots/telegram-helper-bot/database/tg-bot-database.db" - "{{ project_root }}/bots/AnonBot/.env" - "{{ project_root }}/bots/AnonBot/database/anon_qna.db" become: yes - name: Исправить права доступа для voice_users (рекурсивно) file: path: "{{ project_root }}/bots/telegram-helper-bot/voice_users" owner: "{{ deploy_user }}" group: "{{ deploy_user }}" mode: '0755' recurse: yes become: yes - name: Запустить ботов через make up command: make up args: chdir: "{{ project_root }}" become: yes become_user: "{{ deploy_user }}" tags: ["start_bots"] # --- НОВОЕ: Проверка портов --- - name: Пауза на 30 секунд — дать контейнерам запуститься pause: seconds: 30 - name: Проверить, что порт 8080 (Telegram Bot) открыт wait_for: port: 8080 host: "{{ ansible_host }}" timeout: 30 state: started - name: Проверить, что порт 8081 (AnonBot) открыт wait_for: port: 8081 host: "{{ ansible_host }}" timeout: 30 state: started - name: Проверить, что порт 9090 (Prometheus) открыт wait_for: port: 9090 host: "{{ ansible_host }}" timeout: 30 state: started - name: Проверить, что порт 3000 (Grafana) открыт wait_for: port: 3000 host: "{{ ansible_host }}" timeout: 30 state: started - name: Проверить, что порт 9100 (Node Exporter) открыт wait_for: port: 9100 host: "{{ ansible_host }}" timeout: 30 state: started - name: Проверить доступность Grafana API uri: url: "http://{{ ansible_host }}:3000/api/health" method: GET status_code: 200 register: grafana_health retries: 5 delay: 10 - name: Проверить, что дашборд Node Exporter загружен в Grafana uri: url: "http://{{ ansible_host }}:3000/api/search?query=Node%20Exporter%20Full" method: GET user: "{{ grafana_admin_user | default('admin') }}" password: "{{ grafana_admin_password | default('admin') }}" status_code: 200 register: dashboard_check retries: 3 delay: 5 - name: Показать информацию о найденных дашбордах debug: var: dashboard_check.json - name: Проверка запуска ботов завершена — всё работает 🟢 debug: msg: "Все сервисы запущены и слушают нужные порты." # handlers для перезагрузки сервисов handlers: - name: reload ssh systemd: name: ssh state: reloaded - name: restart ufw ufw: state: reloaded