Задача сообщает changed при каждом запуске, хотя должна быть идемпотентной — диагностируйте и исправьте.
Playbook задуман идемпотентным, но каждый запуск ansible-playbook сообщает те же задачи как changed, хотя хост уже настроен. Изучите play и повторяющийся recap, затем объясните причину и исправьте её.
- name: Configure app host
hosts: web
become: true
tasks:
- name: Ensure release directory exists
shell: "mkdir -p /opt/app/releases"
- name: Fetch the release archive
shell: "curl -sSL https://cdn.example.com/app-1.4.2.tgz -o /opt/app/app.tgz"
- name: Install nginx
command: "apt-get install -y nginx"
$ ansible-playbook site.yml
web01 : ok=4 changed=3 unreachable=0 failed=0
$ ansible-playbook site.yml # на хосте между запусками ничего не менялось
web01 : ok=4 changed=3 unreachable=0 failed=0
Почему он сообщает changed=3 каждый раз и как сделать его идемпотентным?
Каждая задача — shell/command, который просто выполняется и безусловно сообщает changed — Ansible не видит состояние за сырой командой, сходимости нет. Исправьте нативными идемпотентными модулями: file: state=directory, get_url и apt: name=nginx state=present. Где команда неизбежна, оградите её через creates: или changed_when:.
- ✗Винить кэш фактов или
become, а не сыройshell/command - ✗Думать, что один
registerделаетcommandидемпотентным - ✗Не заменять команды модулями состояния
file/get_url/apt
- →Что делает
changed_when: falseдля по сути read-only команды? - →Как повёл бы себя режим
--checkна модульной версии этого play?
Сначала прочитайте recap: changed=3 на каждом запуске, хотя на хосте ничего не менялось, — это подпись неидемпотентной задачи. Все три задачи используют shell/command, а такой модуль просто исполняет строку и сообщает changed всегда: Ansible не может заглянуть за сырую команду и понять, что делать уже нечего. Это императивный скрипт, а не объявленное состояние.
Фикс — объявить желаемое состояние нативными модулями, которые сами проверяют текущее состояние хоста и меняют его, лишь если оно расходится:
- name: Configure app host
hosts: web
become: true
tasks:
- name: Ensure release directory exists
ansible.builtin.file:
path: /opt/app/releases
state: directory
- name: Fetch the release archive
ansible.builtin.get_url:
url: "https://cdn.example.com/app-1.4.2.tgz"
dest: /opt/app/app.tgz
- name: Install nginx
ansible.builtin.apt:
name: nginx
state: present
Теперь второй запуск даёт changed=0: file видит, что каталог уже есть; get_url пропускает уже скачанный файл; apt видит установленный nginx. Если модуля для действия нет и сырая команда неизбежна, верните ей идемпотентность ограждением: creates: /path/marker (пропустить, если файл есть), when: (условие) или changed_when: false для команд только для чтения. Диагностику удобно ловить в --check (dry-run) — модульная версия там честно покажет changed=0.