Безопасно ли ограничивать Ansible playbooks одной машиной?

227

Я использую Ansible для некоторых простых задач управления пользователями с небольшой группой компьютеров. В настоящее время мои playbooks настроены на, hosts: allи мой файл hosts - это просто одна группа со всеми перечисленными машинами:

# file: hosts
[office]
imac-1.local
imac-2.local
imac-3.local

Мне часто приходилось ориентироваться на одну машину. Команда ansible-playbookможет ограничить игры следующим образом:

ansible-playbook --limit imac-2.local user.yml

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

Есть ли лучшая практика для ограничения прогонов playbook на одной машине? В идеале, книги должны быть безвредными, если не учитывать некоторые важные детали.

joemaller
источник

Ответы:

209

Оказывается, можно ввести имя хоста непосредственно в книгу воспроизведения, поэтому запуск книги с помощью hosts: imac-2.localбудет работать нормально. Но это немного неуклюже.

Лучшим решением может быть определение хостов playbook с помощью переменной, а затем передача определенного адреса хоста через --extra-vars:

# file: user.yml  (playbook)
---
- hosts: '{{ target }}'
  user: ...

Запуск playbook:

ansible-playbook user.yml --extra-vars "target=imac-2.local"

Если {{ target }}не определено, playbook ничего не делает. Группа из файла hosts также может быть пропущена, если это необходимо. В целом, это кажется гораздо более безопасным способом создания потенциально разрушительной книги.

Playbook, ориентированный на одного хоста:

$ ansible-playbook user.yml --extra-vars "target=imac-2.local" --list-hosts

playbook: user.yml

  play #1 (imac-2.local): host count=1
    imac-2.local

Сборник пьес с группой хостов:

$ ansible-playbook user.yml --extra-vars "target=office" --list-hosts

playbook: user.yml

  play #1 (office): host count=3
    imac-1.local
    imac-2.local
    imac-3.local

Забыть определять хостов безопасно!

$ ansible-playbook user.yml --list-hosts

playbook: user.yml

  play #1 ({{target}}): host count=0
joemaller
источник
52
Это разрешимо в 1.5.3 с--limit office[0]
NG.
4
Переменная должна быть заключена в кавычки - то есть: '{{ target }}'- в соответствии с docs.ansible.com/…
Limbo Peng
9
Это «отказоустойчивый» ответ, в отличие от некоторых других - если вы что-то пропустите, это ничего не даст. Работа на «только» одном хосте с использованием Ansible 1.7 run_onceможет быть разрушительной, так что это не очень хорошая идея.
RichVel
4
Если вы хотите более короткую команду, -eэто эквивалент--extra-vars
Уильям Туррелл
1
Если ваша конфигурация требует, чтобы хосты не могли быть пустыми или неопределенными, то работает переменная в сочетании с фильтром jinja, например:hosts: "{{ target | default('no_hosts')}}"
Zach Weg
178

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

ansible-playbook -i "imac1-local," user.yml

Обратите внимание на запятую ( , ) в конце; это говорит о том, что это список, а не файл.

Теперь, это не защитит вас, если вы случайно передадите настоящий файл инвентаря, так что это может быть не лучшим решением этой конкретной проблемы. Но это удобный трюк, чтобы знать!

Tybstar
источник
2
Это восхитительно. Я регулярно использую флаг -l, который работает с etc / ansible / hosts (который заполняется с помощью API обнаружения EC2), но иногда мне просто нужна отдельная машина. Спасибо!
Вик
3
Должен ли этот трюк использовать файл hosts? Я использую хосты как динамический инвентаре для нашей системы AWS EC2 и возвращает: skipping: no hosts matched. Возможно, этот трюк больше не работает, так как --limitработает?
hamx0r
1
Этот трюк не работал для меня. Но это сработало $ ansible-playbook -kK --limit=myhost1 myplaybook.yml. Смотри ответ Марвана.
Донн Ли
2
Следует отметить, что для того, чтобы это работало, хосты должны быть включены allв пьесу (ы) - мне потребовалось некоторое время, чтобы понять ...
Remigius
83

Этот подход завершится, если для проверки переменной play_hosts предусмотрено более одного хоста . Модуль сбоя используется для выхода, если условие одного хоста не выполняется. В приведенных ниже примерах используется файл hosts с двумя хостами alice и bob.

user.yml (playbook)

---
- hosts: all
  tasks:
    - name: Check for single host
      fail: msg="Single host check failed."
      when: "{{ play_hosts|length }} != 1"
    - debug: msg='I got executed!'

Запустите playbook без фильтров хоста

$ ansible-playbook user.yml
PLAY [all] ****************************************************************
TASK: [Check for single host] *********************************************
failed: [alice] => {"failed": true}
msg: Single host check failed.
failed: [bob] => {"failed": true}
msg: Single host check failed.
FATAL: all hosts have already failed -- aborting

Запустите playbook на одном хосте

$ ansible-playbook user.yml --limit=alice

PLAY [all] ****************************************************************

TASK: [Check for single host] *********************************************
skipping: [alice]

TASK: [debug msg='I got executed!'] ***************************************
ok: [alice] => {
    "msg": "I got executed!"
}
Марван Алсаббах
источник
1
Определенно лучший, --limitэто путь
Берто
7
play_hostsустарела в Ansible 2.2 и заменена на ansible_play_hosts. Для запуска на одном хосте без необходимости --limitвы можете использовать when: inventory_hostname == ansible_play_hosts[0].
Тревор Робинсон
[WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: {{ play_hosts|length }} == ''на Ansible 2.8.4.
Томас
32

Там ИМХО более удобный способ. Вы действительно можете в интерактивном режиме запросить у пользователя машину (-ы), к которой он хочет применить игровую книгу, благодаря vars_prompt:

---

- hosts: "{{ setupHosts }}"
  vars_prompt:
    - name: "setupHosts"
      prompt: "Which hosts would you like to setup?"
      private: no
  tasks:
    […]
Buzut
источник
2
Очень круто. Это также имеет то преимущество, что книга воспроизведения не относится к файлу инвентаря.
Эрфан
2
Спасибо за редактирование! Мне действительно было интересно, почему ввод по умолчанию рассматривается как «стиль пароля». Я пропустил это в документах :)
Buzut
Могут ли переменные хостов быть установлены из командной строки, чтобы исключить подсказку с этим playbook?
андиг
1
@andig with --extra-varsи обычная переменная в твоей пьесе…
Buzut
На самом деле, я не мог заставить это работать - кажется {{ hosts }}, оценивается перед вводом значения - или есть специальный трюк?
Ремигиус
18

Чтобы расширить ответ joemailer, если вы хотите иметь возможность сопоставления с образцом для сопоставления с любым подмножеством удаленных машин (так же, как это ansibleделает команда), но все же хотите сделать очень трудным случайный запуск playbook на всех машинах, это что я придумал:

Тот же playbook, что и в другом ответе:

# file: user.yml  (playbook)
---
- hosts: '{{ target }}'
  user: ...

Давайте иметь следующие хосты:

imac-10.local
imac-11.local
imac-22.local

Теперь, чтобы запустить команду на всех устройствах, вам нужно явно установить для целевой переменной значение «all».

ansible-playbook user.yml --extra-vars "target=all"

И чтобы ограничить его конкретным шаблоном, вы можете установить target=pattern_here

или, альтернативно, вы можете оставить target=allи добавить --limitаргумент, например:

--limit imac-1*

то есть. ansible-playbook user.yml --extra-vars "target=all" --limit imac-1* --list-hosts

что приводит к:

playbook: user.yml

  play #1 (office): host count=2
    imac-10.local
    imac-11.local
deadbeef404
источник
Это образец, которому я следовал в ansible-django-postgres-nginx
Ajoy
13

Я действительно не понимаю, как все ответы настолько сложны, способ сделать это просто:

ansible-playbook user.yml -i hosts/hosts --limit imac-2.local --check

checkРежим позволяет работать в режиме сухого хода, без внесения каких - либо изменений.

knocte
источник
7
Вероятно, потому что, задаваясь вопросом об ответах, вы пропустили вопрос, в котором был задан способ предотвратить запуск, когда параметры по ошибке опущены. Вы предложили добавить больше параметров, которые противоречат требованию.
Techraf
2
ах, конечно, но если люди оповестят меня, это может быть потому, что они являются новичками Ansible (как, например, я, когда писал свой ответ), которые даже не знают о флаге --check, так что я думаю, что это все еще полезно с точки зрения документации, так как этот вопрос может быть очень googlable
knocte
6

Пользователи AWS, использующие сценарий внешней инвентаризации EC2, могут просто фильтровать по идентификатору экземпляра:

ansible-playbook sample-playbook.yml --limit i-c98d5a71 --list-hosts

Это работает, потому что скрипт инвентаризации создает группы по умолчанию .

Фрэнк
источник
4
Опция --limit не ограничена EC2 и может быть использована для размещения / групп имен вашего инвентаря. Спасибо.
martinezdelariva
5

У нас есть несколько общих сборников, которые могут использовать многие команды. У нас также есть файлы инвентаризации, зависящие от среды, которые содержат несколько групповых объявлений.

Чтобы заставить кого-то, вызывающего playbook, указать группу, с которой нужно работать, мы вводим пустую запись в верхней части playbook:

[ansible-dummy-group]
dummy-server

Затем мы включаем следующую проверку в качестве первого шага в общую книгу воспроизведения:

- hosts: all
  gather_facts: False
  run_once: true
  tasks:
  - fail:
      msg: "Please specify a group to run this playbook against"
    when: '"dummy-server" in ansible_play_batch'

Если фиктивный сервер отображается в списке хостов, с которыми запланировано выполнение этой книги воспроизведения (ansible_play_batch), то вызывающая сторона не указала группу, и выполнение книги воспроизведения завершится неудачно.

mcdowellstl
источник
ansible_play_batchперечисляет только текущую партию, поэтому при использовании дозирования это все еще небезопасно. Лучше использовать ansible_play_hostsвместо этого.
Томас
Кроме того, этот трюк кажется самым простым и самым близким к тому, о чем спрашивали; Я принимаю это!
Томас
4

Начиная с версии 1.7, ansible имеет опцию run_once . Раздел также содержит некоторые обсуждения различных других методов.

Беренд де Бур
источник
4

Это показывает, как запустить playbooks на целевом сервере.

Это немного сложнее, если вы хотите использовать локальное соединение. Но это должно быть нормально, если вы используете переменную для параметра hosts и в файле hosts создайте специальную запись для localhost.

Во всех плейбуках линия hosts: установлена ​​в:

- hosts: "{{ target | default('no_hosts')}}"

В файле инвентаризации хостов добавьте запись для localhost, которая устанавливает соединение как локальное:

[localhost]
127.0.0.1  ansible_connection=local

Затем в командной строке выполните команды, явно задающие цель - например:

$ ansible-playbook --extra-vars "target=localhost" test.yml

Это также будет работать при использовании ansible-pull:

$ ansible-pull -U <git-repo-here> -d ~/ansible --extra-vars "target=localhost" test.yml

Если вы забудете установить переменную в командной строке, команда безопасно выдаст ошибку (если вы не создали группу хостов с именем 'no_hosts'!) С предупреждением:

skipping: no hosts matched

И, как упоминалось выше, вы можете настроить таргетинг на одну машину (если она есть в вашем файле hosts) с помощью:

$ ansible-playbook --extra-vars "target=server.domain" test.yml

или группа с чем-то вроде:

$ ansible-playbook --extra-vars "target=web-servers" test.yml
bailey86
источник
0

У меня есть скрипт-обертка, называемая Provision, заставляет вас выбирать цель, поэтому мне не нужно обрабатывать ее где-либо еще.

Для любопытных я использую переменные ENV для опций, которые использует мой vagrantfile (добавляя соответствующий аргумент arible для облачных систем), и позволяю пройти остальным аргументам. Там, где я создаю и инициализирую более 10 серверов одновременно, я включаю автоматическую повторную попытку на неисправных серверах (при условии, что достигнут прогресс - я обнаружил, что при создании примерно 100 серверов одновременно часто некоторые из них отказывают в первый раз) ).

echo 'Usage: [VAR=value] bin/provision [options] dev|all|TARGET|vagrant'
echo '  bootstrap - Bootstrap servers ssh port and initial security provisioning'
echo '  dev - Provision localhost for development and control'
echo '  TARGET - specify specific host or group of hosts'
echo '  all - provision all servers'
echo '  vagrant - Provision local vagrant machine (environment vars only)'
echo
echo 'Environment VARS'
echo '  BOOTSTRAP - use cloud providers default user settings if set'
echo '  TAGS - if TAGS env variable is set, then only tasks with these tags are run'
echo '  SKIP_TAGS - only run plays and tasks whose tags do not match these values'
echo '  START_AT_TASK - start the playbook at the task matching this name'
echo
ansible-playbook --help | sed -e '1d
    s#=/etc/ansible/hosts# set by bin/provision argument#
    /-k/s/$/ (use for fresh systems)/
    /--tags/s/$/ (use TAGS var instead)/
    /--skip-tags/s/$/ (use SKIP_TAGS var instead)/
    /--start-at-task/s/$/ (use START_AT_TASK var instead)/
'
iheggie
источник
0

Немного другое решение состоит в том, чтобы использовать специальную переменную, ansible_limitкоторая является содержимым --limitопции CLI для текущего выполнения Ansible.

- hosts: "{{ ansible_limit | default(omit) }}"

Нет необходимости определять дополнительную переменную здесь, просто запустите playbook с --limitфлагом.

ansible-playbook --limit imac-2.local user.yml
Маноло
источник