
Rsyslog część druga – konfiguracja centralnego systemu logów z Ansible

Zgodnie z obietnicą z artykułu Rsyslog cz.1. dziś skupimy się na stworzeniu dwóch ról Ansible, które pozwolą nam wyskalować ustawienia logowania na dziesiątki, a nawet setki hostów. Artykuł ten ma też za zadanie zasymulować rzeczywiste tworzenie ról Ansible, które mogą być w prosty sposób wersjonowane, zmieniane i skalowane. Wygląd repozytorium Mając na uwadze dobre praktyki […]
Zgodnie z obietnicą z artykułu Rsyslog cz.1. dziś skupimy się na stworzeniu dwóch ról Ansible, które pozwolą nam wyskalować ustawienia logowania na dziesiątki, a nawet setki hostów. Artykuł ten ma też za zadanie zasymulować rzeczywiste tworzenie ról Ansible, które mogą być w prosty sposób wersjonowane, zmieniane i skalowane.
Wygląd repozytorium
Mając na uwadze dobre praktyki związane z Ansible, finalna wersja repozytorium będzie wyglądać w następujący sposób:
. ├── ansible.cfg ├── inventories │ └── prod │ └── hosts ├── playbooks │ ├── configure-rsyslog.yml │ └── roles -> ../roles ├── README.md └── roles ├── rsyslog_client │ ├── defaults │ │ └── main.yml │ ├── handlers │ │ └── main.yml │ ├── README.md │ └── tasks │ └── main.yml └── rsyslog_server ├── defaults │ └── main.yml ├── files │ └── external-rotate ├── handlers │ └── main.yml ├── README.md └── tasks └── main.yml
Tworzenie repozytorium gitowego
By przeprowadzić symulację tworzenia automatyzacji opartej na Ansible, pozwoliłem sobie stworzyć repozytorium Git. Znajduje się ono pod adresem: https://github.com/EuroLinux/ansible-rsyslog-example. Tworzenie repozytorium oraz używanie i zarządzanie Gitem wykracza poza zakres tego artykułu. Niemniej polecam kilka naszych artykułów na ten temat: Git podstawowe Narzędzie Pracy Developera część 1, Git podstawowe Narzędzie Pracy Developera część 2, Git podstawowe Narzędzie Pracy Developera część 3 czy Git Trójkątny Przepływ Pracy.
Przykładowe utworzenie repozytorium z dodaniem zdalnego serwera:
mkdir ansible-rsyslog-example cd ansible-rsyslog-example echo "# ansible-rsyslog-example" >> README.md git init git add README.md git commit -m "first commit" git remote add origin git@FIXME:FIXME/ansible-rsyslog-example.git git push origin master
Ansible – konfiguracja pliku inventory
Przed przystąpieniem do pisania roli należy zdefiniować hosty (maszyny fizyczne i wirtualne), na których będzie ona uruchomiona. Plik inventory, w języku polskim nazywany czasem plikiem inwentarza, został szerzej omówiony w tym artykule. Nasz plik inventory zawiera 2 systemy wysyłające logi oraz jeden serwer zbierający logi. Znajduje się on na ścieżce inventories/prod/hosts
i wygląda następująco:
[rsyslog-servers] 192.168.121.45 [rsyslog-clients] 192.168.121.24 192.168.121.132
Pozwala to na stworzenie własnej struktury z różnymi host_vars
i group_vars
ze względu na środowisko, na którym działamy. Biorąc pod uwagę wykładowy charakter tego artykułu, pozwoliłem sobie na użycie adresów IP zamiast nazw hostów.
Po zakończeniu konfiguracji inwentarza można dodać pliki do repozytorium, stworzyć migawkę i wysłać ją na zdalny serwer.
git add . git commit -m "Add inventory" git push origin master
Konfiguracja ansible.cfg
W pliku ansible.cfg znajdują się następujące dane konfiguracyjne, odpowiadające za ustawienie zdalnego użytkownika i eskalacji przywilejów:
[defaults] remote_user = ansible [privilege_escalation] become = True
Po dodaniu ansible.cfg ponownie można utworzyć migawkę gita.
git add . git commit -m "Add ansible.cfg" git push origin master
Rola serwera
Po przygotowaniu konfiguracji możemy stworzyć rolę. W tym celu w katalogu naszego repozytorium o nazwie roles
wywołujemy:
[Alex@Normandy ansible-rsyslog-example]$ mkdir roles [Alex@Normandy ansible-rsyslog-example]$ cd roles [Alex@Normandy roles]$ ansible-galaxy init rsyslog_server - rsyslog_server was created successfully
Osobiście mam w zwyczaju usuwać domyślne pliki, by nie zaśmiecać repozytorium. W związku z tym proponuję usunąć katalogi i pliki, których nie używamy. Można je oczywiście później w łatwy sposób odtworzyć.
Oto prosty jednolinijkowiec, który rekursywnie kasuje nieużywane katalogi:
rm -r rsyslog_server/{tests,vars,templates,meta}
Jako że wiemy, jakie serwisy będziemy konfigurować, zacznę od napisania dwóch prostych handlerów (uchwytów), które będą wykonywane, jeśli nastąpi zmiana. Znajdują się one na ścieżce roles/rsyslog_server/handlers/main.yml
.
# handlers file for rsyslog_server - name: restart rsyslog service: name: rsyslog state: restarted - name: restart firewalld service: name: firewalld state: restarted
Następnie proponuję stworzyć informację o porcie, na którym ma działać Rsyslog wraz z listą definiującą szablony. Każdy szablon (w rozumieniu Rsyslog nie Ansible) trzymany jest w słowniku (inaczej nazywanym mapą). Pierwszy element o kluczu def trzyma regułę definiującą sam wzorzec. Drugi element dest określa, z jakich obiektów i priorytetów będą zbierane dane. W poniższym przykładzie linie będą takie same jak w pierwszym artykule tej serii. Dodatkowo w celu ułatwienia (lepszym rozwiązaniem w przypadku serwera Rsyslog z dużą ilością zmian w konfiguracji będzie stworzenie odpowiedniego wzorca) dodałem także zmienione linie plików konfiguracyjnych.
Plik rsyslog_server/defaults/main.yml
:
--- # defaults file for rsyslog_server rsyslog_port: 514 rsyslog_templates: [ {def: "$template MojSzablon,\"/var/log/external/%HOSTNAME%.log\"", dest: "*.* -?MojSzablon"}, ] rsyslog_config_lines: [ { 'regex': '^$ModLoad imtcp', 'line': '$ModLoad imtcp'}, { 'regex': '^$InputTCPServerRun*', 'line': '$InputTCPServerRun {{rsyslog_port}}'}, ]
Proponuję utworzyć także plik zawierający ustawienia rotacji logów. Jest on oparty na /etc/logrotate.d/syslog
. Zawartość pliku roles/rsyslog_server/files/external-rotate
.
/var/log/external/\*.log { missingok sharedscripts postrotate /bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true endscript }
Finalnie możemy przystąpić do napisania naszych zadań (tasks) dla Ansible. Oto plik z zadaniami znajdujący się na ścieżce roles/rsyslog_server/tasks/main.yml
:
--- # tasks file for rsyslog_server - name: Install rsyslog yum: state: present name: rsyslog - name: Enable rsyslog service: name: rsyslog enabled: yes - name: Configure tcp port on firewalld firewalld: port: "{{rsyslog_port}}/tcp" permanent: yes state: enabled notify: restart firewalld - name: Configure the logrotation copy: src: external-rotate dest: /etc/logrotate.d/external-rotate owner: root group: root mode: 0644 - name: Enable tcp listening in /etc/rsyslog.conf lineinfile: path: /etc/rsyslog.conf regexp: "{{item.regex}}" line: "{{item.line}}" loop: "{{rsyslog_config_lines}}" notify: restart rsyslog - name: Remove templates.conf file: path: /etc/rsyslog.d/templates.conf state: absent - name: Add config lines blockinfile: create: yes path: /etc/rsyslog.d/templates.conf block: | {{item.def}} {{item.dest}} marker: "" loop: "{{rsyslog_templates}}" notify: restart rsyslog
By zakończyć tę część konfiguracji, pozostało nam jedynie napisanie playbooka. Będzie się on znajdował na ścieżce playbooks/rsyslog-server.yml
.
--- - name: Configure rsyslog server hosts: rsyslog-servers roles: - rsyslog_server
Zanim jednak wykonamy naszego playbooka, należy stworzyć dowiązanie symboliczne do katalogu zawierającego role:
[Alex@Normandy roles]$ ln -s ../roles roles
oraz przetestować nasz kod Ansible. Uruchamianie kodu Ansible bez wykonania minimalnych testów to proszenie się o kłopoty.
Niestety nie ma dobrego sposobu na zrobienie tzw. dry-run. W teorii istnieje możliwość uruchomienia playbooka z opcją --check
, by zebrać dane o wykonanych zadaniach oraz informacje o tym, czy zmienią one system. W praktyce jednak sposób ten z reguły nie działa i w wielu przypadkach nigdy nie zadziała poprawnie. Wynika to z prostego faktu, iż Ansible musiałoby wykonać kopię systemu, wprowadzić zmiany w celu zebrania danych, a następnie wrócić do oryginalnego stanu. Dla przykładu proszę sobie wyobrazić (playbook, który by to reprezentował, również byłby dziecinnie prosty) następujący scenariusz:
- Stwarzamy katalogi
/www/riposty/ansible/jest/gupi
. - Przywracamy lub nadajemy odpowiedni kontekst SELinuksowy na
www/riposty
oraz podpliki. - Stwarzamy plik
/www/riposty/ansible/jest/gupi/riposta
z zawartościąNo chyba Ty
. - Pobieramy dane z serwera
http://localhost/ansible/jest/gupi/riposta
.
W którym momencie Ansible zgłosi błąd? W zależności od tego, czy katalog /www/riposty/autor/jest/gupi
istnieje w kroku 2. lub 4. Oczywiście jest też tutaj pewne założenie, że system posiada serwer WWW, który wykorzystuje katalog /www/
jako główny katalog serwera.
Bardziej skomplikowanych scenariuszy rozłożonych na wiele hostów, z instalacją i konfiguracją oprogramowania, będących dynamicznie od siebie zależnych (np. instalacja aplikacji, a następnie migracja bazy danych) nie warto z reguły nawet rozważać.
Do testowania ról polecam użycie projektu Molecule. Dzięki niemu możemy testować role Ansible i, co bardzo ważne, tworzyć testy regresji dla infrastruktury. Z kolei w celu lintowania (oceny) kodu Ansible polecam użycie ansible-lint
. Więcej na temat tych narzędzi będzie można znaleźć na naszym blogu w najbliższych miesiącach. Zachęcam więc do jego regularnego odwiedzania lub zapisu na newsletter :)
Co zatem należy zrobić za każdym razem, kiedy chcemy przetestować playbooka? Otóż sprawdzić składnie. Odpowiada za to parametr --syntax-check
.
ansible-playbook --inventory inventories/prod playbooks/rsyslog-server.yml --syntax-check
Po pomyślnym przejściu testu można uruchomić rolę konfigurującą serwer.
ansible-playbook --inventory inventories/prod playbooks/rsyslog-server.yml --syntax-check
Poniżej przykładowe uruchomienie playbooka.
ansible-playbook --inventory inventories/prod playbooks/rsyslog-server.yml PLAY [Configure rsyslog server] ********************************************************* TASK [Gathering Facts] ****************************************************************** ok: [192.168.121.45] TASK [rsyslog_server : Install rsyslog] ************************************************* ok: [192.168.121.45] TASK [rsyslog_server : Enable rsyslog] ************************************************** ok: [192.168.121.45] TASK [rsyslog_server : Configure tcp port on firewalld] ********************************* changed: [192.168.121.45] TASK [rsyslog_server : Configure the logrotation] *************************************** changed: [192.168.121.45] TASK [rsyslog_server : Enable tcp listening in /etc/rsyslog.conf] *********************** changed: [192.168.121.45] => (item={u'regex': u'^\$ModLoad imtcp', u'line': u'$ModLoad imtcp'}) changed: [192.168.121.45] => (item={u'regex': u'^\$InputTCPServerRun*', u'line': u'$InputTCPServerRun 514'}) TASK [rsyslog_server : Remove templates.conf] ******************************************* ok: [192.168.121.45] TASK [rsyslog_server : Add config lines] ************************************************ changed: [192.168.121.45] => (item={u'dest': u'*.* -?MojSzablon', u'def': u'$template MojSzablon,"/var/log/external/%HOSTNAME%.log"'}) RUNNING HANDLER [rsyslog_server : restart rsyslog] ************************************** changed: [192.168.121.45] RUNNING HANDLER [rsyslog_server : restart firewalld] ************************************ changed: [192.168.121.45] PLAY RECAP ****************************************************************************** 192.168.121.45 : ok=10 changed=6 unreachable=0 failed=0
Po udanym zakończeniu playbooka proponuję zapisać zmiany w systemie kontroli wersji.
git add . git commit -m "rsyslog server role and playbook" git push origin master
Rola klienta
Rola klienta będzie ograniczać się do prostej reguły przekazywania logów po TCP. Jedyną ciekawą konstrukcją jest pętla po grupie pozwalająca na dodanie następnego serwera, do którego logi będą przekazywane, jeśli tylko pojawi się on w pliku inwentarza.
By stworzyć rolę rsyslog_client
proponuję wejść do katalogu ./roles
, a następnie stworzyć rolę i ponownie usunąć nieużywane pliki.
cd roles ansible-galaxy init rsyslog_client rm -rf rsyslog_client/{files/,meta/,templates/,tests/,vars/}
W pliku roles/rsyslog_client/defaults/main.yml
umieszczamy domyślny port.
--- # defaults file for rsyslog_server rsyslog_port: 514
W uchwytach (handlers) pod ścieżką roles/rsyslog_client/handlers/main.yml
tworzymy
identyczną jak w poprzedniej roli funkcję restartu rsysloga.
--- # handlers file for rsyslog_client - name: restart rsyslog service: name: rsyslog state: restarted
W końcu możemy napisać zadania w pliku roles/rsyslog_client/tasks/main.yml
.
--- # tasks file for rsyslog_client - name: Install rsyslog yum: state: present name: rsyslog - name: Enable rsyslog service: name: rsyslog enabled: yes - name: Remove forward.conf file: path: /etc/rsyslog.d/forward.conf state: absent - name: Add config lines lineinfile: create: yes line: "*.* @@{{item}}:{{rsyslog_port}}" path: /etc/rsyslog.d/forward.conf notify: restart rsyslog loop: "{{groups['rsyslog-servers'] }}"
Po takim zabiegu należy tylko dopisać prostego playbooka, który wykorzysta naszą rolę. Będzie się on znajdował na ścieżce playbooks/rsyslog-client.yml
.
--- - name: Configure rsyslog clients hosts: rsyslog-clients roles: - rsyslog_client
Następnie uruchamiany sprawdzenie składni:
ansible-playbook --inventory inventories/prod playbooks/rsyslog-client.yml --syntax-check playbook: playbooks/rsyslog-client.yml
By finalnie uruchomić playbooka:
ansible-playbook --inventory inventories/prod playbooks/rsyslog-client.yml PLAY [Configure rsyslog clients] ******************************************************** TASK [Gathering Facts] ****************************************************************** ok: [192.168.121.24] ok: [192.168.121.132] TASK [rsyslog_client : Install rsyslog] ************************************************* ok: [192.168.121.24] ok: [192.168.121.132] TASK [rsyslog_client : Enable rsyslog] ************************************************** ok: [192.168.121.24] ok: [192.168.121.132] TASK [rsyslog_client : Remove forward.conf] ********************************************* ok: [192.168.121.24] ok: [192.168.121.132] TASK [rsyslog_client : Add config lines] ************************************************ changed: [192.168.121.132] => (item=192.168.121.45) changed: [192.168.121.24] => (item=192.168.121.45) RUNNING HANDLER [rsyslog_client : restart rsyslog] ************************************** changed: [192.168.121.132] changed: [192.168.121.24] PLAY RECAP ****************************************************************************** 192.168.121.132 : ok=6 changed=2 unreachable=0 failed=0 192.168.121.24 : ok=6 changed=2 unreachable=0 failed=0
Po wprowadzonych zmianach możemy zapisać oraz wysłać zmiany na serwer gita.
git add . git commit -m "add rsyslog_client role and playbook" git push origin master
Test konfiguracji
Istnieje wiele narzędzi do testowania infrastruktury. Jednak ze względu na prostotę zagadnienia przetestujemy nasze rozwiązanie eksploracyjne. W tym celu wykorzystamy wiedzę o narzędziu logger
poznanym w artykule Rsyslog cz.1.
logger -p 0 "This is emergency from ${HOSTNAME}. Use Bat-Signal."
Na każdym z hostów (mają odpowiednio ustawione swoje nazwy loghost1, loghost2 i logserver) wykonujemy powyższą komendę. Następnie wystarczy sprawdzić, czy na serwerze logów znajdują się odpowiednie pliki.
[root@logserver ~]# ll /var/log/external/ total 200 -rw-------. 1 root root 692 May 22 10:25 loghost1.log -rw-------. 1 root root 709 May 22 10:25 loghost2.log -rw-------. 1 root root 67364 May 22 10:25 logserver.log
Playbook dla ról Ansible
Jak wcześniej wspomniałem, repozytorium znajduje się pod adresem: https://github.com/EuroLinux/ansible-rsyslog-example. Zdaję sobie sprawę, że ze względu na prostotę zagadnienia, wygląd repozytorium oraz szereg ustawień jest na wyrost. Niemniej jeszcze raz podkreślę, iż chciałem pokazać rzeczywisty sposób pracy z Ansible.
Jeśli artykuł Ci się spodobał, podziel się tym, co dobre ;-)