Ansible – zarządzanie wieloma hostami jednocześnie. Do działania wykorzystuje protokół SSH.
Ansible Management Node – miejsce, z którego uruchamiasz automatyzacje Ansible
Inventory – lista hostów zarządzanych przez Ansible (format txt lub bardziej zaawansowany YAML)
Playbook – scenariusz zawierający taski do wykonania na zdalnych serwerach
Task – pojedyncze zadanie wykonywane na serwerze
Jumphost – specjalna maszyna przez którą łączymy się do zdalnego serwera niedostępnego bezpośrednio ze świata
# Instalacja [RHEL]
Najpierw należy zainstalować repozytorium EPEL
subscription-manager repos –enable codeready-builder-for-rhel-9-$(arch)-rpms
dnf install https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm
A następnie jedyny pakiet do obsługi to: ansible
# Inventory
Domyślnie zlokalizowane w pliku /etc/ansible/hosts
Dostępne zmienne:
ansible_host – adres IP/DNS hosta
ansible_port – adres portu SSH
ansible_user – użytkownik, na jakiego ansible będzie się łączył, domyślnie root
ansible_become – informacja dla ansible, że użytkownik jest użytkownikiem sudo a nie rootem, więc każde polecenie musi zaczynać z sudo
ansible_ssh_extra_args – parametry doklejane do komendy ssh (np. w przypadku jumphosta)
Przykładowe inwentory:
[grupa_hostow]
web01 ansible_host=192.168.1.200 ansible_user=ubuntu ansible_become=true
web02 ansible_host=192.168.1.201 ansible_user=ubuntu ansible_become=true
[inna_grupa]
web03 ansible_host=192.168.1.202 ansible_port=10000
# Ustawienie, aby ansible domyślnie akceptował nowe klucze hostów
Sprawdźmy w pliku /etc/ansible/ansible.cfg jakie polecenie należy wykonać w celu wygenerowania konfigu (o ile plik jest pusty). Domyślnie jest to:
ansible-config init –disabled -t all > ansible.cfg
Należy zwrócić uwagę, aby być wewnątrz katalogu /etc/ansible.
Po wygenerowaniu konfiga wchodzimy ponownie do pliku, przechodzimy do sekcji ssh_connections i edytujemy zmienną ssh_common_args:
ssh_common_args= -o StrictHostKeyChecking=accept-new
# Logowanie do maszyn poprzez klucze SSH
Należy wygenerować klucz na nodzie:
ssh-keygen -t ed25519 -C ansible
A następnie skopiować go na hosty:
ssh-copy-id user@IP_hosta
# Wykonanie pojedynczego taska: moduły ad-hoc
Składnia wzorcowa:
ansible GDZIE -m JAKI_MODUŁ -a „ARGUMENTY DLA TEGO MODUŁU” (-f 1)
GDZIE:
all – wszystkie hosty w inventory
web01 – nazwa hosta zdefiniowana w inventory
-f – domyślnie ansible uruchamia się na 5 hostach jednocześnie, aby zmienić tę wartość należy wykorzystać ten przełącznik
Jeżeli nie podamy nazwy modułu wykorzysta domyślne moduł „command”
Moduły command, a shell:
command – wykona tylko prostą, jedną komendę
shell – ansible, uruchamia powłokę bash i dopiero wewnątrz niej daną komendę; wykorzystywany w przypadku złożonych komend (z wykorzystaniem pipe, && itp)
Sprawdźmy komunikację z powyżej utworzonym inventory wykorzystując ansiblowy moduł ping:
ansible all -m ping
ansible web01 -m ping
Aby wyłączyć wszystkie maszyny należy wykorzystać moduł shell i jako argument podać komendę wyłączającą:
ansible all -m shell -a „shutdown -h now”
Skopiowanie lokalnego pliku na hosta docelowego:
ansible all -m copy -a 'src=/root/plik dest=/home/ubuntu/plikdocelowy’
Sprawdzenie:
ansible all -a „ls -lA /home/ubuntu”
Instalacja pakietów:
ansible all -m package -a 'name=nazwa_pakietu state=present’
present – zainstaluj
absent – usuń
# Przykładowy plik napisany w języku YAML
—
– element1:
opis: dwa
opis2:
– jeden
– dwa
opis3:
a: b
c: d
…
Każdy plik YAML powinien się zaczynać — i kończyć …
Wcięcia mogą wynosić dowolną ilość spacji, ale należy zachować uszeregowanie.
Lista pracowników:
—
# pracownicy
– jkowalski:
imie: Jan
nazwisko: Kowalski
skills:
– ansible
– apache
– nginx
opis: |
to jest napis
i tak zostanie przekazany
opis2: >
to jest napis
który i tak będzie przekazany
w jednej linii
…
Do sprawdzenia poprawności plików YAML może posłużyć np. narzędzie yamllint (yamllint nazwa_pliku.yaml). Jeżeli nie zwraca wyniku, tzn poprawną składnię i brak błędów.
# Ansible facts, ansible magic variables
Facts – informacje, które ansible zbiera o naszej maszynie. Podczas uruchamiania playbooka wykorzystuje moduł setup. Aby sprawdzić co pobiera możemy użyć komendy:
ansible maszyna -m setup | less
Wszystkie te informacje możemy wykorzystać podczas budowania playbooka.
Ansible magic variables – informacje na temat samego Ansible
ansible maszyna -m debug -a 'var=mienna’
Przykłady zmiennych:
groups – grupy do jakich należy
inventory_hostname – nazwę jaką ma przyjętą w inventory
hostvars – informacje o innych maszynach (dzięki temu w playbooku możemy odwoływać się do innych maszyn)
„var=hostvars[’nazwa_maszyny’]” – informacje o konkretnej maszynie
Aby wykorzystać zmienne w playbooku wpisujemy je w {{ nazwa_zmiennej }}.
# Tworzenie playbooków
1. Dodanie maszyny lokalnej do inventories:
local ansible_connection=local (ansible nie będzie się łączył przez SSH tylko będzie wykonywał na niej polecenia lokalnie)
2. Utworzenie katalogu dla playbooka (w dowolnym miejscu) i wewnątrz niego podkatalog na klucze ssh (które w pierwszym tasku będziemy generować).
3. Utworzenie pliku startowego dla playbooka (przyjęte, że site.yaml)
4. Utworzenie zadania generującego klucze SSH
– name: generate SSH key
hosts: local
tasks:
– name: generate SSH key for jkowalski
openssh_keypair: (moduł do generowania kluczy)
path: „/root/playbook/ssh_keys/jkowalski” (ścieżka wewnątrz utworzonego przed chwilą katalogu)
type: ed25519
state: present
comment: „jkowalski key generated by ansible”
5. Uruchomienie playbooka:
ansible-playbook site.yaml
6. Rozszerzenie playbooka o zmienne. W tym celu utwórzmy katalogi host_vars (ze zmiennymi dla pojedynczych hostów) i group_vars (ze zmiennymi dla grup hostów)
7. Chcemy utworzyć użytkowników na wszystkich hostach w związku z tym w katalogu group_vars utwórzmy plik all o następującej zawartości:
ssh_user: jkowalski
8. Zmodyfikujmy playbook, aby korzystał ze zmiennych:
– name: generate SSH key
hosts: local
tasks:
– name: generate SSH key for {{ ssh_user }}
openssh_keypair: (moduł do generowania kluczy)
path: „/root/playbook/ssh_keys/{{ ssh_user }}”
type: ed25519
state: present
comment: „{{ ssh_user }} key generated by ansible”
9. Sprawmy, aby utworzyły jednocześnie kilku użytkowników. W tym celu wykorzystamy pętle. Przeróbmy najpierw plik ze zmiennymi all, aby zawierał listę użytkowników:
ssh_user:
– jkowalski
– jnowak
– anowak
A następnie w playbooku na końcu dopiszmy (na równi z nazwą taska):
loop: ” {{ ssh_user }}”
To utworzyło nam iteracyjną zmienną wewnętrzną, do której się odwołujemy pod nazwą item. Umożliwi to przejście przez wszystkie elementy z tablicy ssh_users. W związku z tym wszystkie powyższe wystąpienia modyfikujemy i finalnie playbook wygląda następująco:
– name: generate SSH key
hosts: local
tasks:
– name: „generate SSH key for ssh_users”
openssh_keypair: (moduł do generowania kluczy)
path: „/root/playbook/ssh_keys/{{ item }}”
type: ed25519
state: present
comment: „{{ item }} key generated by ansible”
loop: „{{ ssh_user }}”
10. Pogrupujmy hosty w inventory. Grupy zapisujemy w formacie: [nazwa_grupy]
11. Dodanie użytkowników na zdalnych maszynach. Dodajmy w tym celu nowy task do playbooka:
– name: Add ssh users to system
hosts: web,db
tasks:
– name: Add users
user:
name: „{{ item }}”
shell: /bin/bash
loop: „{{ ssh_user }}”
12. Wyślijmy teraz klucze SSH dla utworzonych użytkowników na maszyny. W tym celu wewnątrz zadania „Add ssh users to system” utwórzmy kolejny task:
– name: Add authorized_keys for users
authorized_key:
user: „{{ item }}”
state: present
key: „{{ lookup(’file’, '/root/playbook/ssh_keys/’ + item + ’.pub’) }}”
loop: „{{ ssh_user }}”
13. Zainstalujmy paczki na maszynach. Najpierw zmieńmy nazwę grupy tasków na „Tasks for web and db”. Następnie dodajmy nowy task dla tej grupy:
– name: Install bind-utils on CentOS
package:
name: bind-utils
state: present
when: ansible_facts[’os_family’] == „RedHat”
– name: Update apt cache
apt:
update_cache: yes
when: ansible_facts[’os_family’] == „Debian”
– name: Install bind-utils on Ubuntu
package:
name: bind9-utils
state: present
when: ansible_facts[’os_family’] == „Debian”
14. Zajmijmy się teraz plikami. Jeżeli nie określimy dokładnej ścieżki ansible domyślnie szuka plików katalogu files. Utwórz więc taki katalog, a wewnątrz niego dowolny plik z jakimś tekstem. Następnie do playbooka dodajmy task, który będzie kopiował ten plik na maszyny.
– name: „Copy file”
copy:
src: plik
dest: /tmp
owner: root
group: root
mode: u=rw,g=r,o=r
15. Plik dynamicznie modyfikowany na podstawie zdefiniowanych zmiennych. Utwórzmy katalog templates a wewnątrz niego plik z rozszerzeniem .j2
motd.j2:
Motd hosta
{{ ansible_host }}: {{ ansible_memtotal_mb }}
16. Dodanie hostsa. Wyszukuje w podanym pliku danego wyrażenia, jeżeli nie znajdzie to wpisuje.
– name: „Dodaj host Marian”
lineinfile:
path: /etc/hosts
search_string: 'marian’
line: 8.8.8.8 marian
owner: root
group: root
mode: '0644′
tags: hosts
Składnia taska jest identyczna jak w przypadku copy. Zmieniamy jedynie moduł „copy” na „template” oraz do pliku dopisujemy odpowiednie rozszerzenie.
17. Zajmijmy się teraz konfiguracją firewalla. W katalogu group_vars utwórzmy dwa pliki db oraz web.
db:
open_ports:
– 3306
– 22
web:
open_ports:
– 80
– 443
W playbooku dodajmy odpowiednie taski. (Wszystkie hosty mam w oparciu o Ubuntu, więc tylko takie tutaj przedstawię)
– name: „Firewall Ubuntu”
include_tasks: ubuntu_firewall.yaml (zainkluowanie zewnętrznego pliku z taskami)
when: ansible_facts[’os_family’] == „Debian”
tags: firewall_ubuntu
A następnie tworzymy zainkludowany plik z następującą zawartością:
– name: „Open ports”
ufw:
rule: allow
port: „{{ item }} „
proto: tcp
loop: „{{ open_ports }}”
tags: firewall
– name: „Default policy to deny”
ufw:
state: enabled
policy: deny
tags: firewall
# Wykonywanie pojedynczych tasków z playbooka
W tym celu należy wyedytować playbook i dla danego taska dodać dyrektywę tags: z nazwą tagu np.:
tags: kopiowanie
Wywołanie odbywa się poprzez polecenie:
ansible-playbook –tags nazwa_tagu site.yaml
# Instalacja Dockera i uruchomienie kontenera
Role bierzemy z Ansible Galaxy. Wyszukujemy danej roli i szukam w niej informacji jak ją zainstalować. Na przykład:
ansible-galaxy install geerlingguy.docker
Domyślnie instaluje dla katalogu domowego użytkownika. Musimy zainstalować je globalnie dodając przełącznik -p /etc/ansible/roles/
Przechodząc z Galaxy do GitHub repo znajdziemy cały manual dla danej roli.
Zainstalujmy Dockera na serwerach Web, w związku z tym w pliku group_vars/web wklejmy zmienne z manuala.
Następnie dodajmy do playbooka:
– name: Docker for web
hosts: web
roles:
– { role: 'geerlingguy.docker’, tags: 'docker’ }
* taski wykonują się po roli, aby wykonały się wcześniej musimy zamienić tasks na pre_tasks
Zainstalujemy tym sposobem dockera na maszynach z grupy hosts.
# Uruchomienie kontenera
Najpierw musimy doinstalować kilka rzeczy na maszynach.
– name: Install required packages
apt:
pkg:
– python3-pip
– python3-setuptools
state: latest
update_cache: true
A następnie zainstalujmy bibliotekę Docker na maszynach z wykorzystaniem narzędzia pip:
– name: Install docker python module
pip:
name: docker
Teraz możemy dodać zadania do obsługi Dockera. Pierwszy task pobiera obraz, a drugi uruchamia sam kontener.
– name: pull nginx image
community.docker.docker_image:
name: nginx:latest
source: pull
– name: run nginx container
community.docker.docker_container:
name: nginx
image: nginx:latest
state: started
ports:
– „80:80”
# Narzędzie do weryfikacji playbooków – ansible-lint
Wystarczy zainstalować je z repozytorium, a następnie wywołać jako argument podając nazwę pliku.
Pobierz hostname, zapisz go w zmiennej (register) a następnie wyświetl go w formie czystej (bez zbędnych danych) – zmienna.stdout. Następnie wrzuć go do pliku.
– tasks:
– name: Pokaz hostname
command: hostname
register: zmienna
– debug: msg=”{{ zmienna.stdout }}”
– lineinfile:
path: plik
line: „{{ zmienna.stdout }}”