Как изменить Ansible Playbook, чтобы он выполнялся только на определенных серверах?

Ответ

В инфраструктурных проектах я постоянно работаю с целевым выполнением плейбуков. Вот основные практики, которые использую.

1. Определение целей в инвентари (inventory) — основной метод. Группирую хосты в inventory/production.ini:

[webservers]
web01.example.com ansible_user=deploy
web02.example.com

[databases]
db01.example.com

[all:vars]
ansible_python_interpreter=/usr/bin/python3

Затем в плейбуке указываю группу:

- name: Configure Nginx on web servers
  hosts: webservers  # Выполнится ТОЛЬКО на этой группе
  tasks: ...

2. Динамическое ограничение с помощью --limit: Запускаю плейбук для подмножества хостов из группы, например, для одного сервера из webservers:

ansible-playbook site.yml --limit "web01.example.com"
# Или по шаблону:
ansible-playbook site.yml --limit "*.example.com"

3. Использование тегов (tags): Позволяет выполнять не весь плейбук, а только задачи с определенными тегами на любой группе хостов.

- name: Deploy application
  hosts: all
  tasks:
    - name: Copy configs
      copy: src=app.conf dest=/etc/
      tags: config

    - name: Restart service
      systemd: name=app state=restarted
      tags: deploy

Запуск: ansible-playbook deploy.yml --tags "deploy" --limit "webservers"

4. Динамические группы и переменные извне: Часто в CI/CD нужно передать целевые хосты динамически. Использую переменную hosts:

- name: Run ad-hoc update
  hosts: "{{ target_hosts | default('all') }}"
  tasks: ...

Запуск с передачей хостов через переменную окружения или аргумент:

export TARGET_HOSTS="web01,db01"
ansible-playbook update.yml -e "target_hosts=$TARGET_HOSTS"

Для сложной логики (например, выполнить задачу только если сервер в определенном дата-центре) использую условие when с фактами Ansible: when: ansible_facts['datacenter'] == 'ams1'.

Ответ 18+ 🔞

Ну, слушай, вот работаешь ты с этой инфраструктурой, а потом понимаешь — овердохуища серверов, и на всех сразу плейбук гонять как-то не очень. Ну, ёпта, представь: ты обновляешь конфиг, а он на базе данных тоже запустится и всё к ебеням сломает. Поэтому надо уметь целиться, как снайпер, а не палить из пушки по воробьям.

1. Группировка в инвентаре — это святое, основа основ. Вот смотри, в файлике inventory/production.ini ты просто раскидываешь хосты по полочкам, как в армии: пехота — к пехоте, артиллерия — к артиллерии.

[webservers]
web01.example.com ansible_user=deploy
web02.example.com

[databases]
db01.example.com

[all:vars]
ansible_python_interpreter=/usr/bin/python3

А потом в плейбуке просто говоришь: «Эй, Ansible, работай только с пехотой!». И он послушно выполнит всё только на webservers. Просто, как три копейки, но доверия ебать ноль, что новички это сразу поймут.

2. Динамический лимит через --limit — для точечных ударов. Бывает, обосраться можно от волнения: надо не на всю группу, а на один конкретный хост что-то применить. Например, web01 забаговался. Зачем всех трогать? Берёшь и ограничиваешь:

ansible-playbook site.yml --limit "web01.example.com"
# Или, если хочешь по маске, типа на всех в домене:
ansible-playbook site.yml --limit "*.example.com"

Вот это уже похоже на работу, а не на мартышлюшку с гранатой.

3. Теги (tags) — твои лучшие друзья в бардаке. Представь плейбук, где задач — как говна за баней. А тебе надо только конфиги скопировать и всё. Зачем весь цирк запускать? Вешаешь на задачи бирки.

- name: Deploy application
  hosts: all
  tasks:
    - name: Copy configs
      copy: src=app.conf dest=/etc/
      tags: config  # Бирочка "конфиг"

    - name: Restart service
      systemd: name=app state=restarted
      tags: deploy  # Бирочка "деплой"

И потом запускаешь с точностью хирурга: ansible-playbook deploy.yml --tags "deploy" --limit "webservers". Выполнится только перезапуск сервиса и только на веб-серверах. Красота, ёпта! Удивление пиздец, как же раньше без этого жили.

4. Динамические группы извне — когда всё летит из CI/CD. А вот это, чувак, высший пилотаж. Когда твой CI-пайpline сам решает, на какие хосты деплоить. Ты в плейбуке делаешь хостс переменной:

- name: Run ad-hoc update
  hosts: "{{ target_hosts | default('all') }}"  # Если переменной нет — работаем везде
  tasks: ...

А запускаешь так, передавая хосты хоть из переменной окружения, хоть с Луны:

export TARGET_HOSTS="web01,db01"
ansible-playbook update.yml -e "target_hosts=$TARGET_HOSTS"

Вот тут уже терпения ноль ебать, если переменную не передали и плейбук пошёл на «all» — будет вам хиросима и нигерсраки.

А для совсем хитрожопой логики (типа «обновляй только если сервер в Амстердаме») используешь условие when. Смотришь факты Ansible и фильтруешь: when: ansible_facts['datacenter'] == 'ams1' Вот тогда ты уже не просто кнопкодав, а настоящий инженер, который умом работает, а не тыкается, как слепой кот в жопу.