Bezpiecznie ograniczasz Ansible Playbooks do jednego komputera?

227

Korzystam z Ansible do prostych zadań zarządzania użytkownikami na małej grupie komputerów. Obecnie mam ustawione playbooki, hosts: alla plik hostów to tylko jedna grupa z wymienionymi wszystkimi komputerami:

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

Często musiałem atakować jedną maszynę. ansible-playbookGranica polecenie może odgrywa tak:

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

Ale wydaje się to dość kruche, zwłaszcza w przypadku potencjalnie destrukcyjnego podręcznika. Pozostawienie limitflagi oznacza, że ​​podręcznik będzie można uruchomić wszędzie. Ponieważ narzędzia te są używane tylko od czasu do czasu, warto podjąć kroki w celu niezawodnego odtwarzania, aby przypadkowo nie nękać za kilka miesięcy.

Czy istnieje najlepsza praktyka ograniczania uruchomień Playbook do jednego komputera? Idealnie byłoby, gdyby podręczniki były nieszkodliwe, gdyby pominięto jakiś ważny szczegół.

joemaller
źródło

Odpowiedzi:

209

Okazuje się, że można wprowadzić nazwę hosta bezpośrednio do playbooka, więc uruchomienie playbooka hosts: imac-2.localbędzie działało poprawnie. Ale to trochę niezręczne.

Lepszym rozwiązaniem może być zdefiniowanie hostów podręcznika za pomocą zmiennej, a następnie przekazanie określonego adresu hosta przez --extra-vars:

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

Uruchamianie poradnika:

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

Jeśli {{ target }}nie jest zdefiniowane, poradnik nic nie robi. W razie potrzeby można również przekazać grupę z pliku hosts. Ogólnie rzecz biorąc, wydaje się to znacznie bezpieczniejszym sposobem na zbudowanie potencjalnie destrukcyjnego poradnika.

Poradnik przeznaczony dla jednego hosta:

$ 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

Poradnik z grupą gospodarzy:

$ 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

Zapominanie o zdefiniowaniu hostów jest bezpieczne!

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

playbook: user.yml

  play #1 ({{target}}): host count=0
joemaller
źródło
52
Można to rozwiązać w wersji 1.5.3 za pomocą opcji--limit office[0]
NG.
4
Zmienna musi być cytowana - tzn .: '{{ target }}'- według docs.ansible.com/…
Limbo Peng
9
Jest to odpowiedź „bezpieczna w razie awarii”, w przeciwieństwie do innych - jeśli coś pominiesz, nic nie da. Uruchamianie „tylko” jednego hosta za pomocą Ansible 1.7 run_oncemoże nadal być destrukcyjne, więc nie jest to dobry pomysł.
RichVel
4
Jeśli chcesz krótsze polecenie, -eto odpowiednik--extra-vars
William Turrell
1
Jeśli twoja konfiguracja ansible wymaga, aby hosty nie były puste ani niezdefiniowane, to działa zmienna połączona z filtrem jinja, taka jak:hosts: "{{ target | default('no_hosts')}}"
Zach Weg
178

Jest też urocza sztuczka, która pozwala określić jednego hosta w linii poleceń (lub, jak sądzę, wielu hostów), bez pośredniego spisu:

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

Zwróć uwagę na przecinek ( , ) na końcu; oznacza to, że jest to lista, a nie plik.

To nie ochroni Cię, jeśli przypadkowo przekażesz prawdziwy plik inwentarza, więc może nie być to dobre rozwiązanie tego konkretnego problemu. Ale to przydatna sztuczka!

Tybstar
źródło
2
To jest wspaniałe. Regularnie używam flagi -l, która działa z etc / ansible / hosts (która jest zapełniana za pomocą interfejsu API wykrywania EC2), ale czasami naprawdę potrzebuję tylko jednej maszyny. Dziękuję Ci!
Vic
3
Czy ta sztuczka powinna korzystać z pliku hosts? Używam hosty jako inwentaryzacji dynamiczny dla naszego systemu AWS EC2 i powraca: skipping: no hosts matched. Być może ta sztuczka już nie działa, ponieważ --limitdziała?
hamx0r
1
Ta sztuczka nie działała dla mnie. Ale to działało: $ ansible-playbook -kK --limit=myhost1 myplaybook.yml. Zobacz odpowiedź Marwana.
Donn Lee,
2
Należy wspomnieć, że aby to zadziałało, gospodarze muszą być skonfigurowani allw sztuce - zajęło mi to trochę czasu, aby dowiedzieć się ...
Remigius Stalder
83

To podejście zakończy się, jeśli więcej niż jeden host zostanie zapewniony przez sprawdzenie zmiennej play_hosts . Moduł nie jest używany do wyjścia, gdy pojedynczy warunek gospodarz nie jest spełniony. Poniższe przykłady używają pliku hosts z dwoma hostami alice i bob.

user.yml (podręcznik)

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

Uruchom poradnik bez filtrów hosta

$ 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

Uruchom poradnik na jednym hoście

$ 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!"
}
Marwan Alsabbagh
źródło
1
Zdecydowanie najlepsza --limitjest droga
berto
7
play_hostsjest przestarzałe w Ansible 2.2 i zastąpione przez ansible_play_hosts. Aby uruchomić na jednym hoście bez konieczności --limit, możesz użyć when: inventory_hostname == ansible_play_hosts[0].
Trevor Robinson
[WARNING]: conditional statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: {{ play_hosts|length }} == ''na Ansible 2.8.4.
Thomas
32

Jest IMHO wygodniejszy sposób. Rzeczywiście możesz interaktywnie poprosić użytkownika o maszynę, do której chce zastosować podręcznik, dzięki vars_prompt:

---

- hosts: "{{ setupHosts }}"
  vars_prompt:
    - name: "setupHosts"
      prompt: "Which hosts would you like to setup?"
      private: no
  tasks:
    […]
Buzut
źródło
2
Bardzo fajny. Ma to również tę zaletę, że podręcznik nie jest specyficzny dla pliku ekwipunku.
Erfan
2
Dzięki za edycję! Zastanawiałem się, dlaczego dane wejściowe były domyślnie traktowane jako „styl hasła”. Brakowało mi tego w dokumentach :)
Buzut
Czy hosty var można ustawić z wiersza poleceń, aby wyeliminować monit z tym podręcznikiem?
andig
1
@andig z --extra-varsnormalnym var w
książeczce
W rzeczywistości nie mogłem tego uruchomić - wydaje się, że {{ hosts }}jest sprawdzane przed wprowadzeniem wartości - czy jest jakiś specjalny trik?
Remigius Stalder
18

Aby rozwinąć odpowiedź Joemailera, jeśli chcesz mieć możliwość dopasowywania wzorców, aby dopasować dowolny podzbiór zdalnych maszyn (podobnie jak ansiblepolecenie), ale nadal chcesz bardzo utrudnić przypadkowe uruchomienie podręcznika na wszystkich maszynach, jest to co wymyśliłem:

Taki sam poradnik jak w innej odpowiedzi:

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

Miejmy następujące hosty:

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

Teraz, aby uruchomić polecenie na wszystkich urządzeniach, musisz w sposób oczywisty ustawić zmienną docelową na „wszystkie”

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

Aby ograniczyć go do określonego wzoru, możesz ustawić target=pattern_here

lub alternatywnie możesz zostawić target=alli dołączyć --limitargument, np .:

--limit imac-1*

to znaczy. ansible-playbook user.yml --extra-vars "target=all" --limit imac-1* --list-hosts

Co skutkuje w:

playbook: user.yml

  play #1 (office): host count=2
    imac-10.local
    imac-11.local
deadbeef404
źródło
To jest wzór, który podążyłem w ansible-django-postgres-nginx
Ajoy
13

Naprawdę nie rozumiem, jak wszystkie odpowiedzi są tak skomplikowane, sposobem na to jest po prostu:

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

checkTryb pozwala na uruchamianie w trybie na sucho, bez dokonywania jakichkolwiek zmian.

knocte
źródło
7
Prawdopodobnie dlatego, że zastanawiając się nad odpowiedziami, przeoczyłeś pytanie, które wymagało sposobu, aby zapobiec uruchomieniu, gdy parametry zostaną omyłkowo pominięte. Zasugerowałeś dodanie większej liczby parametrów, co jest niezgodne z wymaganiami.
techraf
2
Ach, jasne, ale jeśli ludzie głosują za mną, może to być dlatego, że są nowicjuszami Ansible (tak jak ja, kiedy pisałem swoją odpowiedź), którzy nawet nie wiedzą o fladze --check, więc myślę, że nadal jest to przydatne pod względem dokumentacji, ponieważ to pytanie może być bardzo zrozumiałe
Knocte
6

Użytkownicy AWS korzystający z zewnętrznego skryptu inwentaryzacyjnego EC2 mogą po prostu filtrować według identyfikatora instancji:

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

Działa to, ponieważ skrypt spisu tworzy grupy domyślne .

Szczery
źródło
4
Opcja --limit nie ogranicza się do EC2 i może być używana do hostowania / nazw grup twojego ekwipunku. Dzięki.
martinezdelariva
5

Mamy kilka ogólnych poradników, z których korzysta wiele zespołów. Posiadamy również pliki ekwipunku specyficzne dla środowiska, które zawierają wiele deklaracji grupowych.

Aby zmusić osobę dzwoniącą do playbooka do określenia grupy, z którą chcesz biegać, umieszczamy fikcyjny wpis na górze playbooka:

[ansible-dummy-group]
dummy-server

Następnie uwzględniamy następujące sprawdzenie jako pierwszy krok we wspólnym podręczniku:

- 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'

Jeśli serwer-atrapa pojawi się na liście hostów, dla których zaplanowano uruchomienie tego playbooka (ansible_play_batch), wówczas osoba dzwoniąca nie określiła grupy i wykonanie playbooka zakończy się niepowodzeniem.

mcdowellstl
źródło
ansible_play_batchwyświetla tylko bieżącą partię, więc podczas korzystania z grupowania jest to nadal niebezpieczne. Lepiej użyć ansible_play_hostszamiast tego.
Thomas
Poza tym ta sztuczka wydaje się najprostsza i najbliższa temu, o co pytano; Przyjmuję to!
Thomas
4

Od wersji 1.7 ansible ma opcję run_once . Sekcja zawiera także omówienie różnych innych technik.

Berend de Boer
źródło
4

To pokazuje, jak uruchomić podręczniki na samym serwerze docelowym.

Jest to nieco trudniejsze, jeśli chcesz korzystać z połączenia lokalnego. Ale powinno to być OK, jeśli użyjesz zmiennej dla ustawienia hosts i w pliku hosts utworzysz specjalny wpis dla localhost.

We (wszystkich) podręcznikach ustawiono linię hosts: na:

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

W pliku hostów zasobów reklamowych dodaj wpis dla hosta lokalnego, który ustawia połączenie na lokalne:

[localhost]
127.0.0.1  ansible_connection=local

Następnie w wierszu poleceń uruchom polecenia jawnie ustawiając cel - na przykład:

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

Będzie to również działać, gdy używasz ansible-pull:

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

Jeśli zapomnisz ustawić zmienną w wierszu polecenia, polecenie będzie bezpiecznie błąd (dopóki nie utworzysz grupy hostów o nazwie „no_hosts”!) Z ostrzeżeniem:

skipping: no hosts matched

Jak wspomniano powyżej, możesz kierować reklamy na jedną maszynę (o ile znajduje się ona w pliku hosts) za pomocą:

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

lub grupa z czymś takim jak:

$ ansible-playbook --extra-vars "target=web-servers" test.yml
bailey86
źródło
0

Mam skrypt otoki o nazwie zaopatrzenie zmusza cię do wybrania celu, więc nie muszę się nim zajmować gdzie indziej.

Dla tych, którzy są ciekawi, używam zmiennych ENV dla opcji, które wykorzystuje mój plik Vagrantfile (dodając odpowiedni ansible arg dla systemów chmurowych) i pozwalam przejść resztę ansible args. Gdy tworzę i udostępniam więcej niż 10 serwerów jednocześnie, włączam automatyczne ponawianie na serwerach, które uległy awarii (dopóki robione są postępy - zauważyłem, że tworząc 100 lub więcej serwerów naraz, kilka z nich zawiedzie za pierwszym razem ).

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
źródło
0

Nieco innym rozwiązaniem jest użycie specjalnej zmiennej, ansible_limitktóra jest zawartością --limitopcji CLI do bieżącego wykonywania Ansible.

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

Nie musisz tutaj definiować dodatkowej zmiennej, po prostu uruchom podręcznik z --limitflagą.

ansible-playbook --limit imac-2.local user.yml
Manolo
źródło