Skip to main content

Минимальный Playbook для обновления серверов (Ansible, Ubuntu 24.04+)

image.png

Короткая «заметка» для BookStack: готовые playbook’и и команды. Работает как с ansible-core из APT, так и с полным ansible из pipx/PPA. Предполагается, что у тебя уже есть проект с ansible.cfg и инвентори inventories/inventory.ini (из прошлой заметки).


Предпосылки

    В проекте уже есть ansible.cfg и inventories/inventory.ini.

    На целевых хостах установлен Python 3.

    Для Arch нужен модуль community.general.pacman:

    ansible-galaxy collection install community.general
    

    Файл инвентори используйте реальный (частая ошибка — неверный путь).

    Быстрая проверка окружения

    ansible --version
    ansible all -i inventories/inventory.ini --list-hosts
    

    Самый1) простойПростой playbookплейбук (Debian/Ubuntu)

    Файл: playbooks/simple-update.yml

    ---
    - name: Simple update for Debian/Ubuntu
      hosts: all
      become: yes
      gather_facts: yes
    
      tasks:
        - name: Update cache
          ansible.builtin.apt:
            update_cache: yes
            cache_valid_time: 3600
          when: ansible_os_family == "Debian"
    
        - name: Upgrade packages
          ansible.builtin.apt:
            upgrade: dist
            autoremove: yes
            autoclean: yes
          when: ansible_os_family == "Debian"
    
        - name: Check if reboot is required
          ansible.builtin.stat:
            path: /var/run/reboot-required
          register: reboot_required
          when: ansible_os_family == "Debian"
    
        - name: Reboot if required
          ansible.builtin.reboot:
            msg: "Rebooting due to system updates"
            reboot_timeout: 600
            pre_reboot_delay: 1060   # меньше 60 на Linux ≈ 0
          when: ansible_os_family == "Debian" and reboot_required.stat.exists
    
        - name: Wait for host after reboot (optional)
          ansible.builtin.wait_for_connection:
            timeout: 600
            delay: 20
          when: ansible_os_family == "Debian" and reboot_required.stat.exists
    

    Запуск:

    ansible-playbook -i inventories/inventory.ini playbooks/simple-update.yml
    

    2) Универсальный playbookплейбук (Debian/Ubuntu, RHEL/CentOS/Alma/Rocky/Fedora, Arch)

    Файл: playbooks/update-all-systems.yml

    ---
    - name: Update all Linux servers (multi-distro)
      hosts: all
      become: yes
      gather_facts: yes
      serial: 1  # побезопасно одномудля хосту за раз (безопаснее на проде)прод-сред
    
      tasks:
        # --- Debian/Ubuntu ---
        - name: Update cache (Debian/Ubuntu)
          ansible.builtin.apt:
            update_cache: yes
            cache_valid_time: 3600
          when: ansible_os_family == "Debian"
    
        - name: Upgrade packages (Debian/Ubuntu)
          ansible.builtin.apt:
            upgrade: dist
            autoremove: yes
            autoclean: yes
          when: ansible_os_family == "Debian"
    
        - name: Check if reboot required (Debian/Ubuntu)
          ansible.builtin.stat:
            path: /var/run/reboot-required
          register: debian_reboot_required
          when: ansible_os_family == "Debian"
    
        # --- RHEL/CentOS/Fedora ---
        - name: Ensure needs-restarting is available (RHEL-like)
          ansible.builtin.package:
            name:
              - dnf-yum-utils          -# yum-utilsRHEL 7+/CentOS
              - dnf-plugins-core   # Fedora/новые DNF
            state: present
          when: ansible_os_family == "RedHat"
          ignore_errors: yes
    
        - name: Update cache and upgrade (RHEL-like)
          ansible.builtin.yum:
            name: "*"
            state: latest
            update_cache: yes
          when: ansible_os_family == "RedHat"
    
        - name: Check if reboot required (RHEL-like)
          ansible.builtin.command: needs-restarting -r
          register: rhel_reboot_required
          failed_when: false
          changed_when: false
          when: ansible_os_family == "RedHat"
    
        # --- Arch Linux ---
        - name: Update packages (Arch)
          community.general.pacman:
            update_cache: yes
            upgrade: yes
          when: ansible_os_family == "Archlinux"
    
        # --- Unified reboot logic ---
        - name: Reboot if required (Debian/Ubuntu)
          ansible.builtin.reboot:
            msg: "Rebooting due to system updates"
            reboot_timeout: 900
            pre_reboot_delay: 1060
          when: ansible_os_family == "Debian" and debian_reboot_required.stat.exists
    
        - name: Reboot if required (RHEL-like)
          ansible.builtin.reboot:
            msg: "Rebooting due to system updates"
            reboot_timeout: 900
            pre_reboot_delay: 1060
          when: ansible_os_family == "RedHat" and rhel_reboot_required.rc == 1
    
        - name: Wait for host after reboot (when reboot happened)
          ansible.builtin.wait_for_connection:
            timeout: 900
            delay: 20
          when: >
            (ansible_os_family == "Debian" and debian_reboot_required.stat.exists) or
            (ansible_os_family == "RedHat" and rhel_reboot_required.rc == 1)
    

    Запуск:

    ansible-playbook -i inventories/inventory.ini playbooks/update-all-systems.yml
    

    3) «Безопасное» обновление с базовымипред-чеками проверкамии «бэкапом» списка пакетов

    Файл: playbooks/secure-update.yml

    ---
    - name: Secure system update with basic safety pre-checks
      hosts: all
      become: yes
      gather_facts: yes
    
      serial: 1
    
      vars:
        max_load_1min: 2.4.0
        min_free_space_mb: 1000512
        backup_before_update: true
        backup_dir: "/backup/pre-update-var/backups/preupdate-{{ ansible_date_time.epochdate }}"
    
      tasks:
        - name: CheckFail 1-minuteif loadavg1min load too high
          ansible.builtin.assert:
            that:
              - (ansible_loadavg['1'] | float) <= max_load_1min
            fail_msg: "System load too high: {{ ansible_loadavg['1'] }}"
    
        # ansible_loadavg приходит из gather_facts
    
        - name: Fail if any mount has too little free space
          ansible.builtin.fail:
            msg: "Low space on {{ item.mount }}: {{ (item.size_available // 1024 // 1024) }} MB"
          loop: "{{ ansible_mounts }}"
          loop_control:
            label: "{{ item.mount }}"
          when: (item.size_available // 1024 // 1024) < min_free_space_mb
    
        - name: Create backup directory
          ansible.builtin.file:
            path: "{{ backup_dir }}"
            state: directory
            mode: '0755'
          when: backup_before_update
    
        - name: Save package list (Debian/Ubuntu)
          ansible.builtin.shell: dpkg --get-selections > "{{ backup_dir }}/packages.txt"
          args: { warn: false }
          when: ansible_os_family == "Debian" and backup_before_update
    
        - name: Save package list (RHEL-like)
          ansible.builtin.shell: rpm -qa > "{{ backup_dir }}/packages.txt"
          args: { warn: false }
          when: ansible_os_family == "RedHat" and backup_before_update
    
        - name: Update cache / packages (Debian/Ubuntu)
          block:
            - ansible.builtin.apt:
                update_cache: yes
                cache_valid_time: 3600
            - ansible.builtin.apt:
                upgrade: dist
                autoremove: yes
                autoclean: yes
          when: ansible_os_family == "Debian"
    
        - name: Update cache / packages (RHEL-like)
          ansible.builtin.yum:
            name: "*"
            state: latest
            update_cache: yes
          when: ansible_os_family == "RedHat"
    
        - name: Check if reboot required (Debian/Ubuntu)
          ansible.builtin.stat:
            path: /var/run/reboot-required
          register: reboot_required
          when: ansible_os_family == "Debian"
    
        - name: Check if reboot required (RHEL-like)
          ansible.builtin.command: needs-restarting -r
          register: rhel_reboot_required
          failed_when: false
          changed_when: false
          when: ansible_os_family == "RedHat"
    
        - name: Reboot if required
          ansible.builtin.reboot:
            msg: "Rebooting due to system updates"
            reboot_timeout: 900
            pre_reboot_delay: 1560
          when: >
            (ansible_os_family == "Debian" and reboot_required.stat.exists) or
            (ansible_os_family == "RedHat" and rhel_reboot_required.rc == 1)
    
        - name: Wait for host after reboot
          ansible.builtin.wait_for_connection:
            timeout: 900
            delay: 20
          when: >
            (ansible_os_family == "Debian" and reboot_required.stat.exists) or
            (ansible_os_family == "RedHat" and rhel_reboot_required.rc == 1)
    

    Запуск (пример без «бэкапа»):

    ansible-playbook -i inventories/inventory.ini playbooks/secure-update.yml -e "backup_before_update=false"
    

    Логирование4) иОбновление уведомленияс (опционально)простым логированием

    Минимальный лог в файл на целевом хосте. Уведомления почтой работают при наличии локального MTA на контрол-ноде (используется delegate_to: localhost). Если MTA нет — проще интегрировать Telegram/SMTP через коллекции (community.general).

    Файл: playbooks/update-with-logging.yml

    ---
    - name: Update with simple logging (cross-distro)
      hosts: all
      become: yes
      gather_facts: yes
    
      vars:
        log_file: "/var/log/ansible-updates.log"
    
      tasks:
        - name: Start log
          ansible.builtin.lineinfile:
            path: "{{ log_file }}"
            line: "{{ ansible_date_time.iso8601 }} - Start update on {{ inventory_hostname }}"
            create: yes
    
        -# name: Update cache (generic)
          package:
            update_cache: yes
          become: yesDebian/Ubuntu
        - name: Update all& packagesupgrade package:(Debian/Ubuntu)
          block:
            - ansible.builtin.apt:
                update_cache: yes
                cache_valid_time: 3600
            - ansible.builtin.apt:
                upgrade: dist
                autoremove: yes
                autoclean: yes
          when: ansible_os_family == "Debian"
    
        # RHEL/CentOS/Fedora
        - name: Update & upgrade (RHEL-like)
          ansible.builtin.yum:
            name: "*"
            state: latest
            register:update_cache: update_resultyes
          when: ansible_os_family == "RedHat"
    
        # Arch
        - name: Update & upgrade (Arch)
          community.general.pacman:
            update_cache: yes
            upgrade: yes
          when: ansible_os_family == "Archlinux"
    
        - name: Log completion
          ansible.builtin.lineinfile:
            path: "{{ log_file }}"
            line: "{{ ansible_date_time.iso8601 }} - Update completed on {{ inventory_hostname }}"
    
        when: update_result is succeeded
    
        - name: Check reboot flag (Debian/Ubuntu)
          ansible.builtin.stat:
            path: /var/run/reboot-required
          register: reboot_requireddebian_reboot_required
          when: ansible_os_family == "Debian"
    
        - name: Check reboot flag (RHEL-like)
          ansible.builtin.command: needs-restarting -r
          register: rhel_reboot_required
          failed_when: false
          changed_when: false
          when: ansible_os_family == "RedHat"
    
        - name: Log reboot requirement
          ansible.builtin.lineinfile:
            path: "{{ log_file }}"
            line: "{{ ansible_date_time.iso8601 }} - Reboot required on {{ inventory_hostname }}"
          when: >
            (ansible_os_family == "Debian" and reboot_required.debian_reboot_required.stat.existsexists) or
            (ansible_os_family == "RedHat" and rhel_reboot_required.rc == 1)
    

    Для e-mail/Telegram: подключи коллекцию и соответствующий модуль (например, community.general.mail с параметрами SMTP, community.general.telegram), затем добавь задачу delegate_to: localhost.


    Полезные варианты запуска

    # Простой запуск
    ansible-playbook -i inventories/inventory.ini playbooks/update-all-systems.yml
    
    # Dry-run (ничего не меняет, показывает план)
    ansible-playbook -i inventories/inventory.ini playbooks/update-all-systems.yml --check --diff
    
    # Только определённая группа или хост
    ansible-playbook -i inventories/inventory.ini playbooks/update-all-systems.yml --limit webservers
    ansible-playbook -i inventories/inventory.ini playbooks/update-all-systems.yml --limit n-1-dsw
    
    # Подробный вывод
    ansible-playbook -i inventories/inventory.ini playbooks/update-all-systems.yml -vvv
    

    Ручной «план Б» (без Ansible)

    Debian/Ubuntu

    sudo apt-get update
    sudo apt-get dist-upgrade -y
    sudo apt-get autoremove -y
    sudo apt-get autoclean -y
    test -f /run/reboot-required && sudo reboot
    

    RHEL/CentOS/Alma/Rocky/Fedora

    sudo dnf -y install dnf-plugins-core || sudo yum -y install yum-utils
    sudo dnf -y upgrade || sudo yum -y update
    sudo needs-restarting -r; [ "$?" -eq 1 ] && sudo reboot
    

    Arch

    sudo pacman -Syu
    # Передачаребут переменных ansible-playbookпо -iвашей inventories/inventory.iniполитике secure-update.yml(например, -eпосле "backup_before_update=false"обновления ядра)
    

    Частые нюансы и быстрые решения

      Reboot на RHEL-подобных: команда needs-restarting -r может отсутствовать. В playbook’е выше мы ставим dnf-utils/yum-utils/dnf-plugins-core и игнорируем ошибку — этого достаточно, чтобы на поддерживаемых системах появился инструмент.

      Порядок обновлений: для прод-групп уместно serial: 1 или serial: 20% и max_fail_percentage: 0.

      Почта: модуль mail требует локальный sendmail на контрол-ноде. Для SMTP используй community.general.mail (нужна коллекция).

      Arch: перезагрузка не определяется автоматически — решай по политике (например, всегда перезагружать по расписанию или проверять обновление ядра).


      Мини-шпаргалка по структуре проекта

      ~/ansible-projects/
      ├─ ansible.cfg
      ├─ inventories/
      │  └─ inventory.ini
      ├─ playbooks/
      │  ├─ simple-update.yml
      │  ├─ update-all-systems.yml
      │  ├─ secure-update.yml
      │  └─ update-with-logging.yml
      └─ roles/
      (по мере роста)
      

      Готово.

      Готово

      Эти четыре playbook’аплейбука покрываютзакрывают быстрые обновления,апдейты, мульти-дистрибутив, «безопасный» режим и базовоелогирование; логирование.остальное Дальше(уведомления, расписание) можно вынести логику в роли, добавить уведомленияотдельно.

      (Telegram/SMTP) и интегрировать в расписание (cron/systemd timers).