
Jakie oprogramowanie może okazać się przydatne do monitorowania serwerów? Jak przygotować do tego maszyny? Jak krok po kroku uruchomić system do monitoringu serwerów przy użyciu Ansible?
Po zakończeniu fazy tworzenia i wdrożenia projektu informatycznego przychodzi faza utrzymania. W przypadku części serwerowej aplikacji musimy czuwać zarówno nad poprawnością działania, jak i nad wydajnością oraz zużytymi zasobami. Poprawność działania kontrolujemy dzięki monitoringowi logów, zaś zasoby i wydajność dzięki monitoringowi metryk samego serwera. Dostawcy serwerów, niezależnie czy to VPS, serwery dedykowane czy rozwiązania chmurowe przeważnie oferują taką możliwość i jest to bardzo wygodne. Jednak w momencie, gdy musimy korzystać z usług różnych dostawców lub posiadamy własne serwery, jesteśmy zmuszeni stworzyć własny system monitoringu.
Użyte oprogramowanie
Graylog
Doskonałym rozwiązaniem do monitoringu logów jest Graylog. Jest to narzędzie do gromadzenia i analizy logów. Umożliwia również zdefiniowanie alertów i na ich podstawie wysyłanie notyfikacji, które pozwalają znacząco zmniejszyć czas reakcji na awarię. Przyjmuje on dane z wielu popularnych narzędzi do wysyłania logów np. Filebeat. Dostępny jest w wersji zarówno open source jak i enterprise. Wersja open source w zupełności wystarcza nawet w przypadku zarządzania kilkudziesięcioma serwerami.
Prometheus
Z kolei rozwiązaniem do monitoringu metryk serwera jest Prometheus. Podobnie jak w poprzednim przypadku umożliwia definiowanie alertów oraz wysyłanie notyfikacji. Posiada wiele gotowych “exporterów”, dzięki czemu możemy monitorować nie tylko parametry samej maszyny jak np. zużycie procesora, ale również metryki konkretnych usług, jak baza MySQL. Jest rozwiązaniem w pełni open source.
Grafana
Wadą Prometheus`a jest to, że ma bardzo ubogi interfejs graficzny, przez co analiza danych jest niezbyt wygodna. Tu z pomocą przychodzi nam Grafana. Jest to narzędzie umożliwiające wizualizację danych. Wykorzystuje dane z zewnętrznej bazy, takiej jak właśnie Prometheus i jest w pełni open source.
Ansible
Bardzo wygodnym narzędziem do automatyzacji pracy z serwerami jest Ansible. Dostarcza wiele często używanych komend takich jak operacje na plikach czy kontrola popularnych usług. Komendy te mogą zostać zapisane w skrypcie zwanym “playbook” i używane w odniesieniu do wielu serwerów (nawet jednocześnie). Nieodłączną jego częścią jest Galaxy. Można tam znaleźć “role”, czyli gotowe komponenty odpowiadające za określone zadania np. Instalacja i konfiguracja Prometheusa. Podobnie jak Graylog występuje w wersji open source i płatnej.
W tym poradniku pokażę, jak skonfigurować serwer do monitoringu oraz wysłać z maszyny podstawowe logi i metryki. Cała konfiguracja będzie wykonywana przy użyciu Ansible. Użyję do tego 2 maszyn wirtualnych, z systemem Debian 10.
Przygotowanie
Na początku musimy przygotować nasze maszyny. Ponieważ Ansible wywołuje wszystkie komendy używając protokołu SSH, nie musimy instalować żadnego dodatkowego oprogramowania na naszych maszynach. Musimy jedynie dodać maszyny do “inwentarza” Ansible, czyli pliku hosts. Jeśli korzystamy z kluczy SSH, należy dodać nasz klucz prywatny do agenta SSH przy użyciu komendy ssh-add. W przeciwnym razie musimy dodać hasło w polu ansible_password. Praktycznie wszystkie komendy, których będziemy używać będą wymagały uprawnień roota. Jeśli na maszynach jest włączone hasło sudo, musimy je podać w polu ansible_become_pass. W moim przypadku plik hosts wygląda następująco:
monitoring-client ansible_host=192.168.2.2 ansible_user=vagrant
monitoring-host ansible_host=192.168.2.3 ansible_user=vagrant
Po dodaniu maszyn możemy sprawdzić poprawność konfiguracji używając polecenia ansible -m ping all. Jeśli konfiguracja jest poprawna, powinniśmy otrzymać następujący wynik:
[email protected]:~/ansible-monitoring$ ansible -m ping all
monitoring-client | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
monitoring-host | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
Instalacja Prometheusa i Grafany
Zaczniemy od instalacji Prometheusa. Ponieważ działa on na zasadzie pull based (klient, czyli monitorowana maszyna, udostępnia metryki, a host, okresowo je pobiera), zaczniemy od konfiguracji klienta. Do konfiguracji użyjemy gotowej roli z Galaxy dostarczonej przez Cloud Alchemy. Polecam przejrzeć repozytoria na GitHubie, można tam znaleźć wiele przydatnych ról do Prometheusa. Instalujemy ją poleceniem ansible-galaxy install cloudalchemy.node-exporter.
Następnie utworzymy playbook do konfiguracji klienta. Stworzymy go w nowym katalogu client_playbook, a w nim plik install.yml. Każdy playbook zaczyna się od —. Dalej jest lista “play”. Każdy “play” ma m.in. swoje maszyny, zmienne i polecenia. W naszym przypadku wystarczy jeden “play”. Na początku podajemy maszyny wobec których ma być uruchomiony, w polu hosts:
---
- hosts:
- monitoring-client
Ponieważ polecenia wymagają uprawnień root musimy włączyć eskalację uprawnień. Robimy to przez pole become, a w polu become_user podajemy z prawami jakiego użytkownika mają być wykonywane komendy
---
- hosts:
- monitoring-client
become: true
become_user: root
Każdy “play” ma pole tasks które zawiera listę poleceń do wykonania. Każde polecenie posiada m.in. pole name w którym powinien być krótki opis oraz pole zależne od komendy. My potrzebujemy wywołać rolę cloudalchemy.node-exporter. Role wywołuje się poleceniem import_role z polem import_role.name zawierającym nazwę roli. W naszym przypadku będzie to więc wyglądało w następujący sposób:
tasks:
- name: Install Prometheus exporter
import_role:
name: cloudalchemy.node-exporter
Role możemy konfigurować przez zmienne. W tym przypadku może to być np. podanie adresu na jakim ma nasłuchiwać exporter, czy wyłączenie poszczególnych modułów z metrykami (jeśli nie używamy supervisord możemy wyłączyć jego metryki itp.). Zmienne mogą być podawane na wiele różnych sposobów, jednak obowiązuje ścisła hierarchia tzn. domyślne zmienne roli są nadpisywane przez zmienne podane w playbooku, zmienne w playbooku są nadpisywane przez dodatkowe zmienne podane przy uruchamianiu komendy. Na ten moment wystarczy nam domyślna konfiguracja.
Po stworzeniu naszego playbooka, uruchamiamy go komendą ansible-playbook client_playbook/install.yml:
[email protected]:~/ansible-monitoring$ ansible-playbook client_playbook/install.yml
PLAY [monitoring-client] ***************************************************************************************************************************************************************
TASK [Gathering Facts] *****************************************************************************************************************************************************************
ok: [monitoring-client]
...
RUNNING HANDLER [cloudalchemy.node-exporter : restart node_exporter] *******************************************************************************************************************
changed: [monitoring-client]
PLAY RECAP *****************************************************************************************************************************************************************************
monitoring-client : ok=18 changed=9 unreachable=0 failed=0 skipped=5 rescued=0 ignored=0
Exporter już działa. Niestety nie ma on żadnych mechanizmów autoryzacji, więc musimy stworzyć reguły firewalla, tak aby nikt niepowołany nie miał dostępu do metryk. Użyjemy do tego iptables. Zezwolimy na połączenia do portu 9100 z adresu hosta (IP 192.168.2.3), oraz z naszego komputera (w moim przypadku 192.168.0.108). Wszystkie pozostałe połączenia będą zablokowane. Zrobimy to dzięki poleceniu iptables dostarczonemu z Ansible:
- name: Allow monitoring host connection
iptables:
chain: INPUT
protocol: tcp
destination_port: 9100
source: 192.168.2.3
jump: ACCEPT
flush: true
- name: Allow local machine connection
iptables:
chain: INPUT
protocol: tcp
source: 192.168.0.108
destination_port: 9100
jump: ACCEPT
flush: true
- name: Disallow others
iptables:
chain: INPUT
protocol: tcp
destination_port: 9100
jump: ACCEPT
flush: true
Parametry odpowiadają parametrom polecenia iptables uruchamianego z terminala (https://linux.die.net/man/8/iptables). Jedynie parametr flush: true oznacza, że po dodaniu reguły należy ją zapisać. Ponownie uruchamiamy playbooka komendą ansible-playbook client_playbook/install.yml. Ogromną zaletą Ansible jest to, że w przeciwieństwie do skryptów np. Fabric nie musimy się martwić w jakim stanie jest maszyna. Rola sprawdzi stan maszyny i wprowadzi tylko te zmiany, które są wymagane. Podobnie w przypadku iptables. Przy pierwszym uruchomieniu reguły zostaną dodane na koniec(-A), a przy kolejnych uruchomieniach nic się nie zmieni, ponieważ takie reguły już istnieją. Po uruchomieniu skryptu możemy sprawdzić działanie exportera, otwierając URL http://<adres maszyny>:9100.
Teraz możemy przejść do konfiguracji serwera, który będzie zbierał i agregował metryki. Tworzymy nowy plik z playbookiem: host_playbook/install.yml. Tak jak w poprzednim przypadku użyjemy gotowej roli. Po zainstalowaniu jej komendą ansible-galaxy install cloudalchemy.prometheus musimy podać kilka parametrów konfiguracyjnych. Dla lepszej czytelności, podamy je w pliku vars/prometheus_vars.yml. W polu prometheus_targets.node podajemy monitorowane serwery:
prometheus_targets:
node:
- targets:
- 192.168.2.2:9100
labels:
alisa: Client
W polu labels podajemy tagi dla danej celu. W naszym przypadku jest to alias. Następnie podamy konfigurację do Alertmanagera, co umożliwi wysyłanie notyfikacji:
prometheus_alertmanager_config:
- scheme: http
path_prefix: /
static_configs:
- targets: ["127.0.0.1:9093"]
Następnie dodamy prosty alarm, sygnalizujący, że procesor naszej maszyny jest obciążony. Reguły alarmów ustawiamy w polu prometheus_alert_rules. Każda reguła musi mieć następujące pola:
- alert w którym jest jej nazwa
- expr w którym jest warunek wystąpienia alarmu
- for określa jak długo ma być spełniony warunek, żeby wysłać powiadomienie
- labels zawiera tagi alertu, jak np. severity określające poziom alarmu
- annotations zawiera szablon treści powiadomienia
Nasza reguła będzie wyglądała w następujący sposób:
prometheus_alert_rules:
- alert: CriticalCPULoad
expr: '100 - (avg by (instance) (irate(node_cpu_seconds_total{job="node",mode="idle"}[2m])) * 100) > 96'
for: 2m
labels:
severity: critical
annotations:
description: "{% raw %}{{ $labels.instance }} of job {{ $labels.job }} has Critical CPU load for more than 2 minutes.{% endraw %}"
summary: "{% raw %}Instance {{ $labels.instance }} - Critical CPU load{% endraw %}"
Ta struktura jest dokładnie taka sama jak podawana w plikach konfiguracyjnych Prometheusa, dzięki temu konfigurując go ręcznie możemy jej użyć “tak jak jest”.
Po utworzeniu pliku ze zmiennymi dołączamy go do playbooka w polu vars_files. Następnie dodajemy uruchomienie roli cloudalchemy.prometheus. Nasz plik ma następującą zawartość:
---
- hosts:
- monitoring-host
become: true
become_user: root
vars_files:
- vars/prometheus_vars.yml
tasks:
- name: Install prometheus
include_role:
name: cloudalchemy.prometheus
Po uruchomieniu komendą ansible-playbook host_playbook/install.yml możemy sprawdzić działanie Prometheusa otwierając w przeglądarce adres http://<adres maszyny>:9090.
Gdy już mamy działającego Prometheusa przystępujemy do instalacji Alertmanagera, który odpowiada za wysyłanie powiadomień z aktywnymi alarmami. Użyjemy do tego roli cloudalchemy.alertmanager. Konfigurację podamy w osobnym pliku vars/alertmanager_vars.yml. Umożliwia on wysyłanie notyfikacji na wiele różnych sposobów m. in. na e-mail, Hipchat, Slack itp., a także skonfigurowanie własnego webhooka. W naszym przykładzie użyjemy Slacka. URL do Slacka podajemy w polu alertmanager_slack_api_url. Następnie w alertmanager_receivers ustalamy sposób wysyłania powiadomień, czyli odbiorcę. Każdy odbiorca ma pole name z unikalną nazwą oraz pola zależne od typu. W naszym przypadku jest to slack_configs. Plik z konfiguracją wygląda w następujący sposób:
alertmanager_slack_api_url: *****************************************
alertmanager_receivers:
- name: slack
slack_configs:
- send_resolved: true
channel: '#monitoring'
text: "{%raw%}Summary: {{ .CommonAnnotations.summary }}\nDescription: {{ .CommonAnnotations.description }}{%endraw%}"
title_link: http://192.168.2.3:9090/alerts
Definiujemy tu kanał na jaki mają zostać wysłane powiadomienia (channel), treść powiadomienia (text), oraz link, który ma zostać otwarty po kliknięciu na powiadomienie (title_link). Bardzo przydatnym parametrem jest send_resolved, który sprawia, że otrzymujemy nie tylko notyfikację o wystąpieniu alertu, ale też o jego zakończeniu
Następnym krokiem jest dodanie “route” czyli reguł na jakich mają być wysyłane powiadomienia. Definiujemy tu następujące parametry:
- group_by – podajemy jakie pola mają być używane do grupowania wiadomości, tak żeby Alertmanager nie wysyłał kilku wiadomości na raz
- group_wait – w tym polu podajemy, ile czasu ma czekać przed wysłaniem notyfikacji w oczekiwaniu na inne wiadomości z grupy
- repeat_interval – jaki czas ma minąć przed ponownym wysłaniem notyfikacji
- receiver – nazwa odbiorcy, do którego odnosi się konfiguracja
alertmanager_route:
group_by: ['alertname','cluster','service']
group_wait: 30s
repeat_interval: 4h
receiver: slack
Tak jak poprzednio dołączamy plik w polu vars_files i dodajemy rolę.
---
- hosts:
- monitoring-host
become: true
become_user: root
vars_files:
- vars/prometheus_vars.yml
- vars/alertmanager_vars.yml
tasks:
- name: Install prometheus
include_role:
name: cloudalchemy.prometheus
- name: Install alert manager
include_role:
name: cloudalchemy.alertmanager
Następnie uruchamiamy playbooka. Po instalacji, możemy sprawdzić działanie alarmów przy użyciu polecenia stress. Po 2 minutach otrzymamy notyfikację, że CPU naszej maszyny jest obciążone.
Nasz system zbiera już metryki oraz wysyła powiadomienia. Teraz przystępujemy do instalacji i konfiguracji Grafany czyli panelu do przeglądania danych z naszych serwerów. Użyjemy do tego roli cloudalchemy.grafana. Tworzymy więc plik konfiguracyjny vars/grafana_vars.yml. Pierwszym krokiem jest podanie danych administratora w polu grafana_security. Będziemy używać tych danych do logowania. Następnie podajemy źródło danych z którego mają być pobierane metryki w polu grafana_datasources. Zawiera on następujące pola:
- name – nazwa źródła danych
- type – typ źródła danych, w naszym przypadku prometheus
- url – adres dostępowy do danych
- basicAuth – dane autoryzacyjne do danych. Grafana wykorzystuje zapytania AJAX do źródła danych więc potrzebujemy dostępu do danych z maszyny, na której otwieramy Grafanę. Niestety Prometheus nie ma wbudowanych mechanizmów kontroli dostępu, dobrą praktyką jest skonfigurowanie reverse proxy np. NGINX i ograniczenie dostępu do danych. My na razie to zostawimy i ograniczymy dostęp jedynie do określonych adresów IP.
Grafana umożliwia tworzenie bardzo zaawansowanych paneli z wykresami, listami, filtrami itp. Posiada również bazę gotowych paneli. W tym przykładzie użyjemy panelu dedykowanego dla Prometheusa. Dashbordy do zainstalowania podajemy w polu grafana_dashboards. Podajemy tam id panelu (dashboard_id), wersję (revision_id) oraz źródło danych do którego ma się odnosić (datasource). Jako że dashboard potrzebuje dodatkowo pluginu wyświetlającego wykresy kołowe, dodajemy go w polu grafana_plugins. Nasz plik wygląda następująco:
grafana_security:
admin_user: grafana
admin_password: grafana
grafana_datasources:
- name: prometheus
type: prometheus
url: 'http://192.168.2.3:9090'
basicAuth: false
grafana_dashboards:
- dashboard_id: 10645
revision_id: 1
datasource: prometheus
grafana_plugins:
- grafana-piechart-panel
Pozostaje nam jedynie dodać plik do pola vars_files oraz dodanie roli do pliku install.
---
- hosts:
- monitoring-host
become: true
become_user: root
vars_files:
- vars/prometheus_vars.yml
- vars/alertmanager_vars.yml
- vars/grafana_vars.yml
tasks:
- name: Install prometheus
include_role:
name: cloudalchemy.prometheus
- name: Install alert manager
include_role:
name: cloudalchemy.alertmanager
- name: Install grafana
include_role:
name: cloudalchemy.grafana
Po uruchomieniu playbooka możemy sprawdzić działanie Grafany otwierając URL http://<adres maszyny>:3000 i logując się przy użyciu skonfigurowanych wcześniej danych.
Mamy już działający system do monitoringu metryk. Jest to bardzo ważna część utrzymania serwera, ponieważ nawet przy doskonale zoptymalizowanej aplikacji, zbudowanej z zachowaniem dobrych praktyk dotyczących bezpieczeństwa, może zabraknąć miejsca na dysku, lub z powodu dużego ruchu CPU może być przeciążone. W kolejnej części pokażę, jak zainstalować Graylog oraz stworzyć rolę Ansible do konfiguracji Filebeata, który będzie wysyłał logi do naszego serwera.