Wybierz swój język

Podstawy automatyzacji z Ansible: Polecenia ad-hoc i moduły

Ansible udostępnia możliwość wysyłania doraźnych poleceń (ang. ad-hoc commands). Jest to bardzo proste, a jednocześnie otwiera wiele bardzo użytecznych możliwości. Dzięki nim, wygodnie możemy wydawać pojedyncze polecenia zarówno na jednym, jak i wielu węzłach. Wydawanie takich poleceń daje możliwość m.in. odpytywania o różne stany, przeprowadzenia wstępnej diagnozy i weryfikacji zgodności, zgrania konfiguracji i wybranych plików, zrestartowania wybranej usługi lub całego węzła, zarządzania pakietami oprogramowania i kontami użytkowników, aktualizacji oprogramowania, a nawet okresowej zmiany hasła.

Do wydawania doraźnych poleceń (ang. ad-hoc commands) służy poznane wcześniej polecenie "ansible".

$ ansible [pattern] -m [module] -a "[module options]"
$ ansible [pattern] --module-name [module] --args "[module options]"

  • [pattern] - wzorzec dopasowania do inwentarzu, który został dokładnie opisany w poprzednim artykule.
  • [module] - nazwa użytego modułu.
  • [module options] - opcje i argumenty dla użytego modułu.

Zapraszamy do Ten adres pocztowy jest chroniony przed spamowaniem. Aby go zobaczyć, konieczne jest włączenie w przeglądarce obsługi JavaScript. zainteresowanych rozwiązaniami Red Hat, w tym w szczególności Red Hat Enterprise Linux, Red Hat Satellite, Red Hat OpenShift Container Platform, Red Hat Ansible Automation Platform oraz Red Hat OpenStack Platform. Jesteśmy partnerem firmy Red Hat i za naszym pośrednictwem można zakupić ich produkty na polskim rynku


Nasz plik konfiguracyjny Ansible zakłada, że do wszystkich hostów łączymy się z użyciem użytkownika "ansible" bez podawania hasła. Wymaga to wygenerowania pary kluczy na zarządzającym hoście dla usługi SSH oraz wysłania klucza publicznego do odpowiedniego miejsca na koncie użytkownika "ansible" zarządzanych węzłów. Po zalogowaniu się eskalowane są uprawnienia do użytkownika "root" przy użyciu "sudo" bez podawania żadnego hasła. Do tego celu wymagana jest odpowiednia konfiguracja usługi "sudo" na zarządzanych węzłach. Aby skupić się tylko na działaniu Ansible, elementy te pomijamy.

[msleczek@vm0-net projekt_A]$ cat ansible.cfg 
[defaults]
inventory = ./inventory
remote_user = ansible
ask_pass = false

[privilege_escalation]
become = true
become_method = sudo
become_user = root
become_ask_pass = false
[msleczek@vm0-net projekt_A]$

Nasz plik inwentarza jest bardzo prosty. Znajdują się w nim 4 adresy IPv4 serwerów z systemem RHEL (Red Hat Enterprise Linux).

[msleczek@vm0-net projekt_A]$ cat inventory
10.8.232.121
10.8.232.122
10.8.232.123
10.8.232.124
[msleczek@vm0-net projekt_A]$

Do weryfikacji możliwości prawidłowego zarządzania tymi hostami można użyć modułu "ansible.builtin.ping". Sprawdzi on, czy da się do nich zalogować i ew. eskalować uprawnienia, a także weryfikuje dostępność i użyteczność interpretatora Python. W zasadzie to wymaga on, aby węzeł zdalny miał go zainstalowanego. Jeżeli wszystko przebiegnie prawidłowo, to w odpowiedzi domyślnie zwraca "pong". Jego odpowiednikiem dla systemów z rodziny Microsoft Windows jest moduł "ansible.windows.win_ping". Moduły te nie generują żadnych pakietów ICMP (służy do tego moduł "ansible.netcommon.net_ping"). 

[msleczek@vm0-net projekt_A]$ ansible all -m ansible.builtin.ping
10.8.232.122 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
10.8.232.123 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
10.8.232.121 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
10.8.232.124 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
[msleczek@vm0-net projekt_A]$

Do wykonania określonych zadań na hostach, Ansible używa modułów. Kiedy korzystamy z poleceń doraźnych (ang. ad-hoc commands) i nie podamy nazwy modułu, to domyślnie zostanie wykorzystany moduł "ansible.builtin.command". Wykonuje on plik binarny na zdalnym węźle. Na jego działanie nie mają wpływu żadne zmienne środowiskowe powłoki (ang. shell). Nie korzysta on w ogóle z powłoki zdalnego systemu. Uruchamia tylko wskazany plik binarny z zadanymi argumentami i zwraca nam sformatowany, po odpowiedniej interpretacji, wynik jego wykonania.

Przykłady jawnego i niejawnego wykorzystania modułu "ansible.builtin.command" widać poniżej:

[msleczek@vm0-net projekt_A]$ ansible 10.8.232.123 -m ansible.builtin.command -a "df -h"
10.8.232.123 | CHANGED | rc=0 >>
Filesystem                      Size   Used   Avail  Use% Mounted on
devtmpfs                        440M      0    440M    0% /dev
tmpfs                           456M      0    456M    0% /dev/shm
tmpfs                           456M   6.2M    450M    2% /run
tmpfs                           456M      0    456M    0% /sys/fs/cgroup
/dev/mapper/rhel_vm3--net-root   21G   1.8G     20G    9% /
/dev/sda1                      1014M   185M    830M   19% /boot
tmpfs                            92M      0     92M    0% /run/user/1000
tmpfs                            92M      0     92M    0% /run/user/1001
[msleczek@vm0-net projekt_A]$ ansible all -a "uptime"
10.8.232.121 | CHANGED | rc=0 >>
 15:02:29 up 1 day, 20:38, 2 users, load average: 0.07, 0.02, 0.00
10.8.232.123 | CHANGED | rc=0 >>
 15:02:14 up 1 day, 19:58, 2 users, load average: 0.34, 0.08, 0.03
10.8.232.122 | CHANGED | rc=0 >>
 15:03:08 up 1 day, 20:15, 2 users, load average: 0.15, 0.03, 0.01
10.8.232.124 | CHANGED | rc=0 >>
 15:01:25 up 1 day, 19:18, 2 users, load average: 0.00, 0.00, 0.00
[msleczek@vm0-net projekt_A]$ ansible 10.8.232.123 -a "free -h"
10.8.232.123 | CHANGED | rc=0 >>
         total    used     free     shared    buff/cache    available
Mem:     911Mi    212Mi    249Mi    6.0Mi     450Mi         547Mi
Swap:    2.0Gi    0.0Ki    2.0Gi
[msleczek@vm0-net projekt_A]$

Kiedy po zalogowaniu się do systemu za pomocą SSH wpisujemy polecenia, wszystko jest najpierw interpretowane przez powłokę. To jest powodem dla którego, kiedy chcemy przekazać jako argument polecenia, któryś ze specjalnych znaków powłoki, jak m.in. "<", ">", "|", ";" czy "&", to musimy go poprzedzać znakiem backslash "\" lub użyć odpowiednich komentarzy.

Moduł "ansible.builtin.command" nie korzysta z powłoki, stąd każdy ze specjalnych znaków powłoki zostanie przekazany wprost do polecenia jako argument. Zatem, nie jest możliwe korzystanie z operatorów i potoków, jakie udostępnia powłoka.

Widać to w przykładzie poniżej, gdzie zarówno "|", jak i "grep" oraz "sda1" zostały przekazane jako argumenty do polecenia "df". Kiedy podajemy dodatkowe argumenty dla polecenia "df", to pokazuje ono informacje o systemach plików, na których znajdują się pliki, będące zadanymi argumentami polecenia. Tak wygląda wynik użycia tego modułu dla polecenia "df -h | grep sda1":

[msleczek@vm0-net projekt_A]$ ansible 10.8.232.123 -m ansible.builtin.command -a "df -h | grep sda1"
10.8.232.123 | FAILED | rc=1 >>
df: '|': No such file or directory
df: grep: No such file or directory
df: sda1: No such file or directorynon-zero return code
[msleczek@vm0-net projekt_A]$ df -h /boot /root
Filesystem                      Size   Used   Avail   Use% Mounted on
/dev/sda1                      1014M   185M    830M    19% /boot
/dev/mapper/rhel_vm0--net-root   29G   2.0G     28G     7% /
[msleczek@vm0-net projekt_A]$

Aby lepiej i szybciej orientować się w tym co się wydarzyło oraz odpowiednio ukierunkować naszą uwagę, Ansible używa różnych kolorów w zwracanych wynikach. Są to m.in.:

  • "nie udało się wykonać zadania",
  • "udało się wykonać zadanie",
  • "nie trzeba było nic robić".

"command', "shell" czy "raw"

Moduł "ansible.builtin.command" nie korzysta z powłoki i nie ma dostępu do żadnych zmiennych środowiskowych powłoki użytkownika, jak dla przykładu "$HOSTNAME". Stąd, jego wykonanie nie może na nich bazować. Ma to jednak zaletę, którą jest brak negatywnego wpływy tych zmiennych i ustawień środowiskowych na wydawane polecenia. To jest też powodem, dla którego jest najbezpieczniejszym i zalecanym sposobem wydawania doraźnych poleceń.

W wielu miejscach, bez żadnej weryfikacji piszą o tym, że dla modułu "ansible.builtin.command" nie będzie dostępna zmienna środowiskowa "$HOME". Akurat niektóre zmienne środowiskowe, jak "$HOME", "$SHELL" czy "$PATH", są ustawiane przez program "login" w systemie GNU/Linux i dostępne dla procesu "sshd" już w trakcie nawiązywania sesji SSH. Jest do nich dostęp jeszcze zanim wykonamy jakiekolwiek polecenie czy aktywowana zostanie powłoka. Stąd moduł "ansible.builtin.command", jak najbardziej ma do nich dostęp. Natomiast inne zmienne aktywowane są dopiero po aktywowaniu powłoki użytkownika, jak dla przykładu "$HOSTNAME" i stąd nie są one dostępne dla modułu "ansible.builtin.command".

Tam, gdzie potrzebne nam potoki, operatory lub zmienne środowiskowe powłoki użytkownika, powinniśmy korzystać z modułu "ansible.builtin.shell". Moduł ten domyślnie stosuje powłokę "/bin/sh", ale da się to zmienić. Jednocześnie warto pamiętać o tym, że wynik działania tego modułu może zależeć od wartości zmiennych środowiskowych powłoki. Zatem, osoba modyfikująca pliki środowiskowe powłoki może celowo lub niecelowo przyczynić się do czegoś, co niekoniecznie powinno się wydarzyć.

Dlatego do poleceń doraźnych zalecamy korzystanie z modułu "ansible.builtin.command" wszędzie tam, gdzie to możliwe.

Powłoka może być aktywowana na różne sposoby, a to ma wpływ na to, w jaki sposób ustawiane są zmienne środowiskowe. Jeżeli w trakcie logowania dochodzi do aktywowania interaktywnej powłoki lub robimy to ręcznie wydając polecenie "bash --login" lub "bash -l", to wykonywany jest najpierw plik "/etc/profile", a potem pierwszy dostępny plik z listy o kolejności: "~/.bash_profile", "~/.bash_login" i "~/.profile". Dla powłoki interaktywnej, nie będącej powłoką logowania, jest wykonywany tylko plik "~/.bashrc". Natomiast jest jeszcze powłoka nieinteraktywna ("bash -c"), do której uzyskuje dostęp Ansible poprzez moduł "shell". Wykonuje ona przy starcie plik wskazany zmienną "$BASH_ENV". Zmienna ta jest domyślnie pusta, stąd wiele osób jest zdziwione, kiedy zadaje modułowi "shell" używania powłoki bash, a ona nie widzi zmiennych zdefiniowanych w swoich plikach środowiskowych.

Moduły "ansible.builtin.command" i "ansible.builtin.shell" wymagają posiadania interpretatora Python na zarządzanym węźle. Nie zawsze jest to możliwe, chociaż większość systemów serwerowych posiada go już zaraz po instalacji systemu bazowego. Podobnie się to ma do rozsądnych urządzeń sieciowych. Niemniej, na pewno natrafimy na jakieś starsze urządzenia i systemy, gdzie użycie interpretatora Python nie będzie możliwe, a mimo to chcielibyśmy zarządzać nimi z użyciem tego samego narzędzia, jakim jest Ansible. Jest to możliwe dzięki modułowi "ansible.builtin.raw", który omija cały podsystem modułów Ansible. Wydaje on zadane polecenia wprost po nawiązaniu połączenia SSH, a następnie odsyła do nas wynik. Nie wykonuje żadnej próby interpretacji tego wyniku, w tym sprawdzenia błędów. Dostaniemy z powrotem cokolwiek zostanie wyrzucone na wyjściu STDERR i STDOUT.

Użycie modułu "ansible.builtin.shell" i "ansible.builtin.raw" dla naszego systemu dało niżej podobny wynik. W obu przypadkach zadane polecenie zostało wykonane w powłoce "/bin/sh". Więcej różnic powinno dać się zaobserwować w innych przykładach.

[msleczek@vm0-net projekt_A]$ ansible 10.8.232.123 -m ansible.builtin.shell -a "df -h | grep sda1"
10.8.232.123 | CHANGED | rc=0 >>
/dev/sda1 1014M 185M 830M 19% /boot
[msleczek@vm0-net projekt_A]$ ansible 10.8.232.123 -m ansible.builtin.raw -a "df -h | grep sda1"
10.8.232.123 | CHANGED | rc=0 >>
/dev/sda1 1014M 185M 830M 19% /boot
Shared connection to 10.8.232.123 closed.

[msleczek@vm0-net projekt_A]$


Red Hat Ansible Automation Platform zawiera w sobie szereg dodatkowych komponentów, które sprawiają, iż nawet najbardziej złożona automatyzacja może być wdrażana wygodnie, efektywnie i skalowanie w ramach średnich i większych organizacji oraz przedsiębiorstw. Zainteresowanych tą platformą Ten adres pocztowy jest chroniony przed spamowaniem. Aby go zobaczyć, konieczne jest włączenie w przeglądarce obsługi JavaScript..

Jesteśmy oficjalnym partnerem firmy Red Hat i za naszym pośrednictwem można zakupić ich produkty na polskim rynku.


Uzyskiwanie dodatkowych informacji o module

Niektóre moduły Ansible wymagają podania dodatkowych argumentów, a niektóre nie. Jeżeli nie jesteśmy pewni tego w jaki sposób należy posługiwać się danym modułem, najlepiej posłużyć się poleceniem "ansible-doc".

Przykład informacji jakie udostępnia dla zadanego modułu widać poniżej:

[msleczek@vm0-net projekt_A]$ ansible-doc ansible.builtin.ping
> ANSIBLE.BUILTIN.PING    (/usr/lib/python3.12/site-packages/ansible/modules/ping.py)

        A trivial test module, this module always returns `pong' on successful contact. It does not make sense
       
in playbooks, but it is useful from `/usr/bin/ansible' to verify the ability to login and that a usable
        Python is configured. This is NOT ICMP ping, this is just a trivial test module that requires Python on
        the remote-node. For Windows targets, use the
[ansible.windows.win_ping] module instead. For Network
        targets, use the [ansible.netcommon.net_ping] module instead.

ADDED IN: historical

OPTIONS (= is mandatory):

- data
        Data to return for the `ping' return value.
        If this parameter is set to `crash', the module will cause an exception.
        default: pong
        type: str

ATTRIBUTES:

        check_mode:
          description: Can run in check_mode and return changed status prediction without
            modifying target
          support: full
        diff_mode:
          description: Will return details on what has changed (or possibly needs changing
            in check_mode), when in diff mode
          support: none
        platform:
          description: Target OS/families that can be operated against
          platforms: posix
          support: N/A

SEE ALSO:
      * Module ansible.netcommon.net_ping
      * Module ansible.windows.win_ping


AUTHOR: Ansible Core Team, Michael DeHaan

EXAMPLES:

# Test we can logon to 'webservers' and execute python with json lib.
# ansible webservers -m ansible.builtin.ping

- name: Example from an Ansible Playbook
  ansible.builtin.ping:

- name: Induce an exception to see what happens
  ansible.builtin.ping:
    data: crash


RETURN VALUES:
- ping
        Value provided with the `data' parameter.
        returned: success
        sample: pong
        type: str
[msleczek@vm0-net projekt_A]$

Zgodnie z "ansible-doc" dla modułu "ansible.builtin.ping", domyślną wartość "pong" da się zmienić z użyciem argumentu "data".

[msleczek@vm0-net projekt_A]$ ansible 10.8.232.124 --module-name ansible.builtin.ping
10.8.232.124 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
[msleczek@vm0-net projekt_A]$ ansible 10.8.232.124 --module-name ansible.builtin.ping --args data=JESTEM
10.8.232.124 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "JESTEM"
}
[msleczek@vm0-net projekt_A]$

Powyżej używamy rozszerzonych argumentów, gdzie "-a" jest równoważne z "--args", a "-m" z "--module-name".

Listę obsługiwanych przez nasz system modułów Ansible można sprawdzić za pomocą:

$ ansible-doc -l

Na liście widoczne są także własnoręcznie dodane moduły. Ilość dostępnych modułów zależna jest od dodatkowych kolekcji, jakie zostały zainstalowane na węźle zarządzającym. Poniżej widać zrobiony przez nas moduł "networkers_pl.cisco.cisco_webex", który umożliwia wysłanie wiadomości m.in. do pokoju w Cisco Webex. O ile lista ta jest dość długa, to można ją przeszukiwać:

[msleczek@vm0-net projekt_A]$ ansible-doc -l | grep webex
networkers_pl.cisco.cisco_webex               Send a webexmsg to a Cisco Webex Room or Individual 
[msleczek@vm0-net projekt_A]$

Istnieje też możliwość wygenerowania przykładowego skrawka konfiguracji dla danego modułu, który posłużyć nam może jako wstęp do dalszej parametryzacji jego ustawień. Służy do tego polecenie:

$ ansible-doc -s <moduł>

Tutaj widzimy przykład dostępnych opcji dla naszego modułu "networkers_pl.cisco.cisco_webex":

[msleczek@vm0-net projekt_A]$ ansible-doc -s networkers_pl.cisco.cisco_webex
- name: Send a webexmsg to a Cisco Webex Room or Individual.
  networkers_pl.cisco.cisco_webex:
     personal_token:  # (required) Personal access token required to validate the Webex API.
     recipient_id:    # (required) The unique identifier associated with `recipient_type'.
     recipient_type:  # (required) Messages can be sent to a room or individual (by ID or E-Mail).
     webexmsg:        # (required) The webexmsg you would like to send.
     webexmsg_type:   # Specifies how you would like the webexmsg formatted.
[msleczek@vm0-net projekt_A]$ 

Zadania Ansible mogą być wykonywane doraźnie lub cyklicznie, według ustalonego harmonogramu. Dzięki możliwości wysyłania wiadomości do Cisco Webex możemy szybko powiadomić odpowiedni zespół zarówno na temat tego co zostało wykonane, jak i wszelkich niepowodzeń czy wykrytych niezgodności.


Zapraszamy do Ten adres pocztowy jest chroniony przed spamowaniem. Aby go zobaczyć, konieczne jest włączenie w przeglądarce obsługi JavaScript. zainteresowanych rozwiązaniami Red Hat, w tym w szczególności Red Hat Enterprise Linux, Red Hat Satellite, Red Hat OpenShift Container Platform, Red Hat Ansible Automation Platform oraz Red Hat OpenStack Platform. Jesteśmy partnerem firmy Red Hat i za naszym pośrednictwem można zakupić ich produkty na polskim rynku


Dodatkowe opcje polecenia "ansible"

Zajmiemy się teraz kilkoma użytecznymi opcjami polecenia "ansible". W tym celu cofniemy się o jeden poziom w strukturze drzewa katalogowego tak, aby przestały działać nasze domyślne wartości ustawień z pliku "ansible.cfg".

[msleczek@vm0-net projekt_A]$ ansible --version | grep "config file"
 config file = /home/msleczek/projekt_A/ansible.cfg
[msleczek@vm0-net projekt_A]$ cd ..
[msleczek@vm0-net ~]$ ansible --version | grep "config file"
 config file = /home/msleczek/.ansible.cfg
[msleczek@vm0-net ~]$ ansible 10.8.232.124 -a whoami -i projekt_A/inventory
10.8.232.124 | UNREACHABLE! => {
    "changed": false,
    "msg": "Failed to connect to the host via ssh: Ten adres pocztowy jest chroniony przed spamowaniem. Aby go zobaczyć, konieczne jest włączenie w przeglądarce obsługi JavaScript.: Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password).",
    "unreachable": true
}
[msleczek@vm0-net ~]$

Powyżej widać błąd, gdyż domyślnie Ansible stara się użyć w trakcie logowania do zdalnego hosta nazwy użytkownika, który uruchomił to polecenie. Wcześniej w tym celu korzystaliśmy ze statycznie wskazanego użytkownika "ansible". Możemy go wskazać poleceniu "ansible" ręcznie, za pomocą opcji "-u" lub "--user", co będzie miało nadrzędny priorytet.

[msleczek@vm0-net ~]$ ansible 10.8.232.124 -a whoami -i projekt_A/inventory -u ansible
10.8.232.124 | CHANGED | rc=0 >>
ansible
[msleczek@vm0-net ~]$ ansible 10.8.232.124 -a whoami -i projekt_A/inventory -u ansible -b
10.8.232.124 | CHANGED | rc=0 >>
root
[msleczek@vm0-net ~]$ ansible 10.8.232.124 -a whoami -i projekt_A/inventory --user ansible --become
10.8.232.124 | CHANGED | rc=0 >>
root
[msleczek@vm0-net ~]$

Po wskazaniu prawidłowego użytkownika widać, że nie dostajemy błędu i polecenie "whoami" wskazuje na "ansible". Kiedy dodamy opcję "-b" lub "--become", to polecenie "whoami" zostanie wydane dopiero po eskalacji uprawnień. W wyniku tego, w dwóch kolejnych przykładach powyżej, zwraca użytkownika "root".

W niektórych przypadkach bardziej czytelne lub użyteczne może być umieszczenie całego wyniku w jednym wierszu. Dokonać tego można za pomocą opcji "-o" lub "--one-line". Zostało to zaprezentowane poniżej:

[msleczek@vm0-net projekt_A]$ ansible all -a "cat /etc/redhat-release"
10.8.232.122 | CHANGED | rc=0 >>
Red Hat Enterprise Linux release 8.2 (Ootpa)
10.8.232.124 | CHANGED | rc=0 >>
Red Hat Enterprise Linux release 8.2 (Ootpa)
10.8.232.123 | CHANGED | rc=0 >>
Red Hat Enterprise Linux release 8.2 (Ootpa)
10.8.232.121 | CHANGED | rc=0 >>
Red Hat Enterprise Linux release 8.2 (Ootpa)
[msleczek@vm0-net projekt_A]$ ansible all -a "cat /etc/redhat-release" --one-line
10.8.232.121 | CHANGED | rc=0 | (stdout) Red Hat Enterprise Linux release 8.2 (Ootpa)
10.8.232.122 | CHANGED | rc=0 | (stdout) Red Hat Enterprise Linux release 8.2 (Ootpa)
10.8.232.123 | CHANGED | rc=0 | (stdout) Red Hat Enterprise Linux release 8.2 (Ootpa)
10.8.232.124 | CHANGED | rc=0 | (stdout) Red Hat Enterprise Linux release 8.2 (Ootpa)
[msleczek@vm0-net projekt_A]$ ansible all -a "cat /etc/redhat-release" -o
10.8.232.121 | CHANGED | rc=0 | (stdout) Red Hat Enterprise Linux release 8.2 (Ootpa)
10.8.232.124 | CHANGED | rc=0 | (stdout) Red Hat Enterprise Linux release 8.2 (Ootpa)
10.8.232.123 | CHANGED | rc=0 | (stdout) Red Hat Enterprise Linux release 8.2 (Ootpa)
10.8.232.122 | CHANGED | rc=0 | (stdout) Red Hat Enterprise Linux release 8.2 (Ootpa)
[msleczek@vm0-net projekt_A]$


Certyfikowane kolekcje, role i moduły do Ansible firmy Red Hat oraz innych znanych producentów dostępne są ramach subskrypcji Red Hat Ansible Automation Platform. Zainteresowanych zakupem tej platformy Ten adres pocztowy jest chroniony przed spamowaniem. Aby go zobaczyć, konieczne jest włączenie w przeglądarce obsługi JavaScript..

Jesteśmy oficjalnym partnerem firmy Red Hat i za naszym pośrednictwem można zakupić ich produkty na polskim rynku.


Moduły i idempotentność

O ile stosowanie modułów "ansible.builtin.command", "ansible.builtin.shell" i "ansible.builtin.raw" jest łatwe i wygodne, to tam gdzie się da powinniśmy ich unikać. Docelowo powinniśmy stosować dedykowane i wyspecjalizowane do określonych zadań moduły, których celem jest doprowadzenie urządzeń, systemów, plików, aplikacji bądź usług do żądanego stanu, który opisywany jest w sposób deklaratywny (ang. declarative language). Oznacza to, że tylko określamy stan jaki chcemy by dany obiekt przyjął, bez opisywania w szczegółach w jaki sposób ma zostać to zrealizowane.

Doprowadzeniem go do określonego stanu zajmują się już wyspecjalizowane moduły. W zależności od tego, w jakim stanie początkowym jest dany obiekt, użyty moduł może wykonać na nim więcej lub mniej różnych operacji, czy nawet nie wykonać żadnej, jeżeli nie jest to potrzebne. Podejście takie charakteryzuje się idempotentnością (ang. idempotent), czyli możliwość wielokrotnego uruchomienia tego samego modułu bez zmiany stanu końcowego.

Oznacza to też to, że możemy bez obaw wielokrotnie wykonywać ten sam wyspecjalizowany moduł na danym obiekcie. Jeżeli jest on już w żądanym stanie, to nic się nie stanie, a jeżeli nie, to zostanie do niego doprowadzony.

Należy pamiętać o tym, że idempotentność jest zagwarantowana tylko jeżeli korzystamy z wyspecjalizowanych i wbudowanych modułów. Jeżeli tworzymy je sami lub korzystamy z innych możliwości, jak dla przykładu wysyłanie poleceń przy użyciu modułów "ansible.builtin.command", "ansible.builtin.shell" czy "ansible.builtin.raw", to o idempotentność musimy zadbać sami.


Ansible obsługuje bardzo dużą ilość wyspecjalizowanych modułów, których liczba ciągle rośnie. Red Hat udostępnia też dodatkowe, certyfikowane przez Red Hat kolekcje, role, moduły i wtyczki w ramach swojego Automation Hub. Wiele z nich umożliwia wygodną automatyzację rozwiązań innych znanych producentów (ang. third-party vendors). Dodatkowo, my też udostępniamy naszym klientom własne, jak wspomniany moduł "networkers_pl.cisco.cisco_webex".

W związku z bardzo dużą ilością modułów Ansible, w tym miejscu przejdziemy tylko przez kilka wybranych.

Zdarza się, że aktualizujemy cały plik. Może on zawierać listę serwerów NTP, klucze do uwierzytelnienia, certyfikaty lub zmienne środowiskowe. Ansible nadaje się do tego idealnie. Zanim coś zrobi sprawdza, czy plik istnieje i jego zawartość jest odpowiednia. Jeżeli tak, to nic nie robi. Jeżeli nie, to wykonuje stosowne operacje. Jest też w stanie wykonać kopię poprzedniej wersji pliku.

Załóżmy, że administrujemy bardzo dużą ilością serwerów i chcemy na nich wszystkich zaktualizować lub ujednolicić zmienne środowiskowe. Na wszelki wypadek, chcemy też zrobić kopię poprzedniej wersji pliku z tymi zmiennymi tam, gdzie jego zawartość była inna od zadanej. Do tego celu posłużymy się modułem "ansible.builtin.copy".

[msleczek@vm0-net projekt_A]$ cat environment
LANG=en_US.utf-8
LC_ALL=en_US.utf-8

[msleczek@vm0-net projekt_A]$ ansible all -m ansible.builtin.command -a 'cat /etc/environment' -o
10.8.232.122 | CHANGED | rc=0 | (stdout)
10.8.232.124 | CHANGED | rc=0 | (stdout) LANG=en_US.utf-8\nLC_ALL=en_US.utf-8
10.8.232.123 | CHANGED | rc=0 | (stdout) LANG=en_US.utf-8\nLC_ALL=en_US.utf-8
10.8.232.121 | CHANGED | rc=0 | (stdout)
[msleczek@vm0-net projekt_A]$ ansible all -m ansible.builtin.copy -a "src=./environment dest=/etc/environment owner=root group=root mode=0644 backup=yes"
10.8.232.124 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "checksum": "0fa89fe380b8dc91e6bb56679f04a8759d5500d6",
    "dest": "/etc/environment",
    "gid": 0,
    "group": "root",
    "mode": "0644",
    "owner": "root",
    "path": "/etc/environment",
    "secontext": "system_u:object_r:etc_t:s0",
    "size": 37,
    "state": "file",
    "uid": 0
}
10.8.232.123 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "checksum": "0fa89fe380b8dc91e6bb56679f04a8759d5500d6",
    "dest": "/etc/environment",
    "gid": 0,
    "group": "root",
    "mode": "0644",
    "owner": "root",
    "path": "/etc/environment",
    "secontext": "system_u:object_r:etc_t:s0",
    "size": 37,
    "state": "file",
    "uid": 0
}
10.8.232.121 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "backup_file": "/etc/environment.36908.2025-06-07@17:48:44~",
    "changed": true,
    "checksum": "0fa89fe380b8dc91e6bb56679f04a8759d5500d6",
    "dest": "/etc/environment",
    "gid": 0,
    "group": "root",
    "md5sum": "f14f07073c6735cc233511404a860097",
    "mode": "0644",
    "owner": "root",
    "secontext": "system_u:object_r:etc_t:s0",
    "size": 37,
    "src": "/home/ansible/.ansible/tmp/ansible-tmp-1588358960.2073026-15260-256434093911547/source",
    "state": "file",
    "uid": 0
}
10.8.232.122 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "backup_file": "/etc/environment.13295.2025-06-07@17:49:22~",
    "changed": true,
    "checksum": "0fa89fe380b8dc91e6bb56679f04a8759d5500d6",
    "dest": "/etc/environment",
    "gid": 0,
    "group": "root",
    "md5sum": "f14f07073c6735cc233511404a860097",
    "mode": "0644",
    "owner": "root",
    "secontext": "system_u:object_r:etc_t:s0",
    "size": 37,
    "src": "/home/ansible/.ansible/tmp/ansible-tmp-1588358960.2120671-15262-147935961544848/source",
    "state": "file",
    "uid": 0
}
[msleczek@vm0-net projekt_A]$ ansible all -m ansible.builtin.command -a 'cat /etc/environment' -o
10.8.232.122 | CHANGED | rc=0 | (stdout) LANG=en_US.utf-8\nLC_ALL=en_US.utf-8
10.8.232.121 | CHANGED | rc=0 | (stdout) LANG=en_US.utf-8\nLC_ALL=en_US.utf-8
10.8.232.123 | CHANGED | rc=0 | (stdout) LANG=en_US.utf-8\nLC_ALL=en_US.utf-8
10.8.232.124 | CHANGED | rc=0 | (stdout) LANG=en_US.utf-8\nLC_ALL=en_US.utf-8
[msleczek@vm0-net projekt_A]$

Widać powyżej, że na dwóch serwerach Ansible nie musiało nic robić. Na pozostałych dwóch doprowadziło zawartość żądanego pliku do odpowiedniej zawartości.

Zamiast wskazywać plik źródłowy opcją "src" modułu "ansible.builtin.copy", da się wprost zdefiniować zawartość jaką powinien posiadać plik. Służy do tego celu opcja "content" modułu "ansible.builtin.copy", której zastosowanie widać poniżej:

[msleczek@vm0-net projekt_A]$ ansible 10.8.232.121 -m ansible.builtin.copy -a 'content="\nSerwer obsługiwany przez networkers.pl\n\n" dest=/etc/motd'
10.8.232.121 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": true,
    "checksum": "0bcc7021129a6c1564e9d26983b7ad52c710ec28",
    "dest": "/etc/motd",
    "gid": 0,
    "group": "root",
    "md5sum": "2fda36d59048989bd169842be2c843e1",
    "mode": "0644",
    "owner": "root",
    "secontext": "system_u:object_r:etc_t:s0",
    "size": 42,
    "src": "/home/ansible/.ansible/tmp/ansible-tmp-1588359294.0033875-15420-226606317748867/source",
    "state": "file",
    "uid": 0
}
[msleczek@vm0-net projekt_A]$

Poniżej można zobaczyć co po tej zmianie widzi użytkownik logujący się do naszych serwerów:

ms-net:~ msleczek$ ssh -l msleczek 10.8.232.121
Ten adres pocztowy jest chroniony przed spamowaniem. Aby go zobaczyć, konieczne jest włączenie w przeglądarce obsługi JavaScript.'s password:

Serwer obsługiwany przez networkers.pl

Last login: Sat Jun 7 15:37:10 2025 from 10.8.64.128
[msleczek@vm1-net ~]$

Załóżmy teraz, że chcemy utworzyć na wszystkich zarządzanych serwerach katalog o określonych uprawnieniach, gdzie będziemy w przyszłości wrzucali kopie zapasowe wybranych plików. Do tego celu użyjemy modułu "ansible.builtin.file":

[msleczek@vm0-net projekt_A]$ ansible 10.8.232.121 -m ansible.builtin.file -a "path=/root/backup state=directory mode=700"
10.8.232.121 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": true,
    "gid": 0,
    "group": "root",
    "mode": "0700",
    "owner": "root",
    "path": "/root/backup",
    "secontext": "unconfined_u:object_r:admin_home_t:s0",
    "size": 6,
    "state": "directory",
    "uid": 0
}
[msleczek@vm0-net projekt_A]$

Dużo też zależy od naszej pomysłowości i znajomości tego czym administrujemy i chcemy automatyzować. Dla przykładu, tym samym modułem możemy wyłączyć pamiętanie historii wydawanych poleceń w bazie MySQL/MariaDB:

[msleczek@vm0-net projekt_A]$ ansible 10.8.232.121 -m ansible.builtin.file -a "src=/dev/null dest=/root/.mysql_history state=link"
10.8.232.121 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": true,
    "dest": "/root/.mysql_history",
    "gid": 0,
    "group": "root",
    "mode": "0777",
    "owner": "root",
    "secontext": "unconfined_u:object_r:admin_home_t:s0",
    "size": 9,
    "src": "/dev/null",
    "state": "link",
    "uid": 0
}
[msleczek@vm0-net projekt_A]$

Na skutek wykonania dwóch powyższych zadań Ansible, na serwerze pojawił się katalog "/root/backup" oraz dowiązanie symboliczne "/root/.mysql_history" prowadzące do "/dev/null":

[root@vm1-net ~]# ls -ald .mysql_history backup
drwx------. 2 root root 6 Jun 7 17:58  backup
lrwxrwxrwx. 1 root root 9 Jun 7 17:59  .mysql_history -> /dev/null
[root@vm1-net ~]#

Na koniec zachęcamy do zapoznania się z modułem "ansible.builtin.setup", który zbiera i udostępnia bardzo dużą ilość informacji na temat każdego z zarządzanych węzłów. Informacji tych jest tak bardzo dużo, że nie umieszczamy ich w tym miejscu. Odsyłamy do sprawdzenia tego samodzielnie. Posłużyć się można do tego poleceniem:

$ ansible all -m ansible.builtin.setup

Udostępniane przez moduł "ansible.builtin.setup" informacje można wygodnie filtrować i zawężać za pomocą odpowiednich podopcji. Najlepiej w tym celu zapoznać się z "ansible-doc" dla tego modułu. Poniżej został pokazany przykład filtra, który pozwala sprawdzić rodzinę systemu operacyjnego oraz typ procesora:

[msleczek@vm0-net projekt_A]$ ansible 10.8.232.121 -m ansible.builtin.setup -a filter="ansible_os_family"
10.8.232.121 | SUCCESS => {
    "ansible_facts": {
        "ansible_os_family": "RedHat",
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false
}
[msleczek@vm0-net projekt_A]$ ansible 10.8.232.121 -m ansible.builtin.setup -a filter="ansible_processor"
10.8.232.121 | SUCCESS => {
    "ansible_facts": {
        "ansible_processor": [
            "0",
            "GenuineIntel",
            "Intel Xeon E312xx (Sandy Bridge)"
        ],
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false
}
[msleczek@vm0-net projekt_A]$

Polecenia doraźne (łac. ad-hoc) tak samo łatwo wykonuje się na jednym, jak i setkach węzłów.


Zapraszamy do Ten adres pocztowy jest chroniony przed spamowaniem. Aby go zobaczyć, konieczne jest włączenie w przeglądarce obsługi JavaScript. zainteresowanych rozwiązaniami Red Hat, w tym w szczególności Red Hat Enterprise Linux, Red Hat Satellite, Red Hat OpenShift Container Platform, Red Hat Ansible Automation Platform oraz Red Hat OpenStack Platform. Jesteśmy partnerem firmy Red Hat i za naszym pośrednictwem można zakupić ich produkty na polskim rynku.


Przed kolejną porcją wiedzy zachęcamy do przećwiczenia i utrwalenia tej poznanej tutaj. Skorzystaj z naszych ćwiczeń!