Введение
Надёжному планированию нужно больше подтверждений, чем успешный итог play. Вы развернёте защищённую программу отчётов для обычного пользователя, создадите стабильную именованную запись cron, увидите естественные выполнения, обновите её фазу и удалите отдельную временную запись. Итоговая проверка увидит новый запуск планировщика при сохранении постороннего существующего задания.
Разверните отчёт обычного пользователя и именованное расписание
На этом шаге вы развернёте предоставленную программу отчётов и запланируете её для пользователя automation на обоих управляемых хостах.
Cron установлен, и его демон уже работает как предварительное условие. Узлы не запускают systemd. В каждом crontab automation также есть посторонняя запись keep-existing-report; сохраните её. Модуль cron управляет стабильной именованной записью вместо замены всего crontab пользователя.
Перейдите в проект и изучите предоставленную программу управляющего узла:
cd /home/labex/project
cat files/host-report.sh
Программа добавляет временную метку UTC epoch, настоящее имя хоста и аргумент фазы в защищённый управляемый журнал. Umask 027 даёт новым файлам журнала права 0640. Оставьте программу неизменной и позвольте cron запускать её естественным образом; ручной вызов не демонстрировал бы расписание.
Определите фазу как многократно используемые входные данные:
cat > group_vars/all.yml <<'YAML'
report_phase: initial
YAML
Создайте плейбук. У задания cron есть поля минуты, часа, дня месяца, месяца и дня недели; звёздочка в каждом поле означает запуск раз в минуту. Имя создаёт маркер Ansible для последующих обновлений, а user automation выбирает crontab этого пользователя. Become позволяет управлять расписанием указанного пользователя и явным владельцем файлов; он не заставляет запланированную программу работать от root.
cat > reporting.yml <<'YAML'
- name: Manage a scheduled ordinary-user host report
hosts: all
gather_facts: false
become: true
tasks:
- name: Prepare the ordinary user's reporting directory
ansible.builtin.file:
path: /home/automation/reporting
state: directory
owner: automation
group: automation
mode: "0750"
- name: Deploy the supplied report program
ansible.builtin.copy:
src: /home/labex/project/files/host-report.sh
dest: /home/automation/reporting/host-report.sh
owner: automation
group: automation
mode: "0750"
- name: Keep the named report schedule present
ansible.builtin.cron:
name: ansible-host-report
user: automation
minute: "*"
hour: "*"
day: "*"
month: "*"
weekday: "*"
job: "/home/automation/reporting/host-report.sh {{ report_phase }}"
YAML
Проверьте и примените:
ansible-playbook reporting.yml --syntax-check
ansible-playbook reporting.yml
Изучите фактические crontab automation. Команда выполняется с become, чтобы явно указать запрашиваемую учётную запись:
ansible all -b -m ansible.builtin.command -a 'crontab -u automation -l'
Каждый должен содержать один маркер ansible-host-report и команду ежеминутного запуска плюс неизменную запись keep-existing-report. Одна строка crontab не доказывает выполнение.
Дождитесь первого естественного срабатывания планировщика. timeout ограничивает ожидание 90 секундами; цикл until в bash проверяет непустой журнал каждые две секунды. Команда только читает состояние, пока cron создаёт журнал:
ansible all -m ansible.builtin.command -a "timeout 90 bash -c 'until test -s /home/automation/reporting/host-report.log; do sleep 2; done'"
ansible all -m ansible.builtin.command -a 'cat /home/automation/reporting/host-report.log'
В каждом должна быть запись вроде time= host=web1 phase=initial с соответствующим настоящим именем хоста на web2. Временные метки различаются и должны согласовываться с естественным поминутным расписанием. Если ограниченное ожидание неуспешно, изучите расписание и работающий демон вместо самостоятельной записи успешного журнала.
Обновите одну именованную запись и увидьте новую фазу
На этом шаге вы измените входное значение задания, сохраняя его стабильный идентификатор и постороннее расписание.
Обновите сохранённое значение фазы. Задача cron сформирует новую команду, сохраняя то же имя:
cat > group_vars/all.yml <<'YAML'
report_phase: verified
YAML
ansible-playbook reporting.yml
Снова изучите оба crontab:
ansible all -b -m ansible.builtin.command -a 'crontab -u automation -l'
На каждом хосте по-прежнему должна быть ровно одна запись ansible-host-report, теперь заканчивающаяся на verified. Повторное использование имени обновляет существующую запись вместо накопления второго расписания. Keep-existing-report должен остаться неизменным.
Дождитесь нового естественного запуска с обновлённой фазой. grep -q проверяет совпадающую запись журнала без её вывода; timeout и цикл until ограничивают ожидание:
ansible all -m ansible.builtin.command -a "timeout 90 bash -c 'until grep -q phase=verified /home/automation/reporting/host-report.log; do sleep 2; done'"
ansible all -m ansible.builtin.command -a 'cat /home/automation/reporting/host-report.log'
Теперь оба должны содержать новую запись phase=verified от настоящего планировщика. Старые записи initial — полезная история, их следует сохранить. Обновление команды crontab не меняет старые строки журнала задним числом.
Удалите только именованное временное задание и сохраните стабильность отчётов
На этом шаге вы удалите временное расписание по стабильному имени и проверите, что нужный отчёт обычного пользователя продолжает выполняться.
Создайте безвредную именованную временную запись задачей cron ad hoc. Она планирует /usr/bin/true на полночь и предоставляет настоящую запись для удаления:
ansible all -b -m ansible.builtin.cron -a 'name=ansible-temporary-report user=automation minute=0 hour=0 job=/usr/bin/true'
ansible all -b -m ansible.builtin.command -a 'crontab -u automation -l'
Напишите небольшой плейбук очистки. State absent и совпадающие name/user удаляют только эту управляемую запись. Не очищайте и не заменяйте весь crontab:
cat > cleanup.yml <<'YAML'
- name: Remove only the obsolete named schedule
hosts: all
gather_facts: false
become: true
tasks:
- name: Remove the obsolete report entry by its stable name
ansible.builtin.cron:
name: ansible-temporary-report
user: automation
state: absent
YAML
ansible-playbook cleanup.yml
ansible all -b -m ansible.builtin.command -a 'crontab -u automation -l'
Временный маркер и команда должны исчезнуть. И ansible-host-report с фазой verified, и keep-existing-report должны остаться.
Повторите оба объявления:
ansible-playbook reporting.yml
ansible-playbook cleanup.yml
Оба плейбука должны сообщать changed=0, unreachable=0 и failed=0 для обоих хостов. Cron может независимо продолжать добавлять записи журнала; эти естественные записи времени выполнения не делают декларативное развёртывание неидемпотентным.
Итоговая проверка изучает именованные задания обычного пользователя, точно развёрнутую программу и защищённые журналы, затем ожидает ещё одного естественного срабатывания планировщика на обоих хостах. Это может занять около минуты. Оставьте расписание отчёта и демон работающими, чтобы это наблюдение подтверждало продолжающееся выполнение, а не вручную подготовленный файл успеха.
Резюме
Вы управляли стабильными именованными записями cron для нужного обычного пользователя, увидели настоящее выполнение по расписанию, обновили входное значение команды без дублирования задания и удалили только временную запись. Явные права программы/журнала и сохранённые посторонние расписания оставили область действия ясной. Развёртывание повторилось без изменений, пока отчёт продолжал выполняться естественным образом.



