W tej serii artykułów pokazuję jak stworzyłem w swoim labie automatyzację procesu cyklicznego odtwarzania Cisco ISE w postaci maszyny wirtualnej. W pierwszym artykule przedstawiłem Ci projekt. z jakich komponentów korzystam. Opisałem też jak wygląda konfiguracja Jenkinsa, a dokładniej jak utworzyłem w nim nowy projekt. Spójrzmy teraz dokładniej jakie dodatkowe komponenty są potrzebne, aby wykonanie playbooków Ansible przez Jenkinsa było możliwe.
Spis treści
- Część I: Opis projektu
- Część II: Przygotowanie środowiska Jenkins i Ansible
- Część III: Sprawdzamy datę ostatniego backupu
- Część IV: Sprawdzamy ważność licencji trial na Cisco ISE
- Część V: Zatrzymujemy ISE i wyłączamy maszynę wirtualną
- Część VI: Zero Touch Provisioning
- Część VII: Odtworzenie konfiguracji z kopii zapasowej
Konfiguracja Jenkinsa
Żeby wszystkie elementy ze sobą mogły działać tworząc jeden pipeline CI/CD, to “surowy” Jenkins wymaga lekkiego podrasowania. Wynika to z tego, że czysta instalacja nie zawiera wszystkich niezbędnych bibliotek Python czy pakietów systemowych. Nie uda się więc bez dodatkowych narzędzi wykonać playbook Ansible. Używam instalacji Jenkinsa w postaci kontenerów Dockera. Obrazy buduję sam, zatem mam kontrolę nad tym, jakie dodatkowe elementy zostaną doinstalowane na etapie budowania obrazu. Sam proces budowy kontroluję przez dodanie własnych wpisów do oryginalnego Dockerfile pochodzącego z repozytorium Jenkinsa.
Na początek używając menedżera pakietów apk z systemu Alpine Linux, instaluję różne pakiety, takie jak: shadow, gcc, libffi-dev, python3-dev, linux-headers, musl-dev, openssl-dev, make, automake, openrc, ruby, docker, py-pip, rust, cargo, oraz certbot i openssl. Te pakiety są niezbędne do uruchomienia różnych języków programowania, zarządzania bibliotekami, szyfrowania, a także do działania narzędzi takich jak Docker.
Instalacja pakietów Pythona odbywa się za pomocą narzędzia pip. Musze używać flagi –break-system-packages, która pozwala ignorować zainstalowane pakiety systemowe w przypadku konfliktu. Pakiety Pythona zainstalowane w ten sposób to między innymi: wheel, cryptography, ansible, Jinja2, robotframework, requests, PyVmomi, ciscoisesdk, oraz paramiko. Pakiet PyVmomi to biblioteka do obsługi API VMware vSphere, ciscoisesdk, paramiko oraz requests będą używane do połączenia z Cisco ISE zarówno do API jak i do konsoli.
Instalacja poszczególnych pakietów bibliotek odbywa się w kilku krokach po to bym miał większą kontrolę nad procesem budowania. Powstałe w tym procesie artefakty mogę potencjalnie dalej używać do zbudowania innych obrazów wynikowych.
RUN apk add shadow gcc libffi-dev python3-dev linux-headers musl-dev \ openssl-dev make automake openrc ruby RUN apk add docker py-pip rust cargo RUN pip install wheel --break-system-packages RUN pip install cryptography --break-system-packages RUN usermod -aG docker jenkins RUN pip install ansible --break-system-packages \ && pip install Jinja2 --break-system-packages \ && pip install robotframework --break-system-packages RUN pip3 install requests PyVmomi ciscoisesdk --break-system-packages RUN apk add certbot openssl RUN pip3 install paramiko --break-system-packages RUN groupmod -g 999 docker
Konfiguracja Jenkinsa - pipeline
W moim projekcie całość operacji nie będzie zarządzana w samym Ansible. Nie dlatego, że nie jest to wykonalne (bo jest). Do bardziej zaawansowanych projektów staram się używać dedykowanych narzędzi CI/CD oferujących często większe możliwości. Czasem jest to pipeline skonfigurowany na poziomie GitLab-a, tym razem jednak wybór padł na Jenkinsa, którego używam na przykład do automatyzacji budowania obrazów Dockera.
Jenkins ma za zadanie wywoływać kolejne playbooki Ansible realizujące mniejsze zadania, sprawdzać ich stan i na podstawie ustalonego scenariusza (pipeline) wykonywać kolejne akcje. Poszczególne playbooki jak i definicja samego pipeline Jenkinsa utrzymuję w repozytorium GitLaba. Dodatkowo Jenkins przechowuje wszystkie wrażliwe informacje, na przykład loginy i hasła. Sam Jenkins działa jako kontener Dockera i zawiera zainstalowane wszystkie niezbędne biblioteki Pythona do obsługi API Cisco ISE czy VMware ESXi, a także oczywiście samo Ansible.
Konfiguracja Jenkinsa, a dokładniej definicja scenariusza przechowywana jest w pliku Jenkinsfile w głównym folderze projektu. Dodając kolejne zadania do scenariusza będę pokazywał ich definicję w pliku konfiguracyjnym w kolejnych etapach (stages). Pierwszym z nich jest pobranie aktualnej wersji projektu z repozytorium.
stage('Clone repository') { steps { checkout scm } }
ansible.cfg
Do projektu przygotowałem dedykowaną konfigurację dla Ansible. Plik nosi nazwę ansible.cfg i musi znajdować się w głównym folderze projektu. Konfiguracja ta zmienia nieco domyślny sposób wyświetlania informacji na konsoli, ale też zmienia domyślne parametry połączenia i utrzymywania sesji z urządzeniami
[defaults] enable_plugins = auto, yaml, host_list ; inventory plugins and order used forks = 5 ; Concurrent processes allowed for hosts host_key_checking = false ; Avoid SSH `known_hosts` key checking interpreter_python = auto_silent ; Silence warning about Python environment callbacks_enabled = ansible.posix.profile_tasks ; Show task execution times [callback_profile_tasks] task_output_limit = 50 ; Default: 20 sort_order = none ; { descending (default) | ascending | none } [persistent_connection] connect_timeout = 60 ; Idle connection timeeout. Default: 30s command_timeout = 600 ; Per-command timeout. Default: 30s [ssh_connection] pipelining = False ; improves performance; conflicts with `become`
Najważniejsze są zmiany związane z connection_timeout i command_timeout. Ponieważ część poleceń na Cisco ISE wykonuje się bardzo długo, to nie chcemy by połączenie zostało zerwane przed ich zakończeniem. Wyłączam też pipelining, gdyż powoduje on problemy z podnoszeniem uprawnień na konsoli Cisco ISE.