3-węzłowy zestaw repliki MongoDB z SystemD i metrykami w Telegraf / Grafana


Zestaw replik jest formą replikacji danych, dzięki czemu dane są przechowywane w więcej niż jednym węźle, co zapewnia trwałość danych. Ustawimy pierwszy węzeł jako węzeł główny, drugi i trzeci jako węzły drugorzędne. Stawianie replik zawsze zaleca się w liczbach nieparzystych powyżej 2. Jeden i drugi wymóg ma swoje przyczyny.

Co daje replikacja?

Wysoka dostępność: gdy jeden węzeł lub węzeł podstawowy ulegnie awarii, inny węzeł w klastrze może zostać przejąć jego obowiązki zapewniając ciągłość trwania usługi

Zwiększona wydajność odczytu: Zapytania odczytu obsługiwane są przez węzły slave. Im więcej węzłów tego typu tym wyższa wydajność całego klastra

Większa skalowalność (skalowanie horyzontalne): Nie jesteśmy ograniczeni do jednej maszyny (skalowanie wertykalne) a tym samym do ilości zasobów jakie może pomieścić.

Zwiększa gwarancję trwałości danych: Dane nie przebywają tylko w jednym miejscu ale są przechowywane w kilku jednocześnie, awaria jednego węzła nie skutkuje utratą żadnych danych.*

* Istnieją bardzo specyficzne testy które pokazują że w określonych warunkach, ta gwarancja nie jest utrzymana, jednak jest to bardzo specyficzny test pod wyśrubowane wymagania, polecam lekturę bloga Aphyra

Nieparzysta ilość węzłów

W zestawie replik cecha ta zapewnia osiągnięcie większości a tym samym consensusu w głosowaniu między węzłami w replice. W przypadku liczby parzystej istnieje szansa że głosy rozłożą się równomiernie i konsensus nie zostanie osiągnięty. Każdorazowe "głosowanie" zabiera czas na komunikację, a w systemach bazodanowych o wysokiej wydajności opóźnienia w wyborze nowego węzła głownego mogą mieć katastrofalne skutki.

Liczba węzłów powyżej 2

Wymóg ten podyktowany jest głównie względami praktycznymi z dwóch powodów. W przypadku awarii jednego z węzłów w 2-węzłowej replice, cały zestaw staje się bardzo wrażliwy na kolejną awarię do momentu odzyskania dostępności przez jeden z węzłów. Mając tylko jeden zestaw danych może to być niebezpieczne z punktów widzenia działania aplikacji. Ponadto w przypadku 2-węzłowych replik i konfiguracji master-slave niebezpieczne staje się zjawisko "network partition" kiedy oba węzły zachowują łączność z siecią, ale z jakiegoś powodu gubią to połączenie ze sobą. Taka sytuacja skutkuje, że każdy z węzłów uważa że został jedynym "zdrowym" w replice i sam siebie mianuje węzłem głównym. W tym momencie aplikacje wysyłające dane do bazy spowodują że dane w obu bazach przestaną być zgodne (zapisy z jednego węzła nie pojawią się w drugim i na odwrót). Dołożenie w takiej konfiguracji trzeciego węzła spowoduje obniżenie prawdopodobieństwa wystąpienia takiego zjawiska które dotknie wszystkie węzły naraz, tym samym jeżeli dwa węzły przestaną mieć ze sobą łączność, zawsze pozostanie ten trzeci który to potwierdzi.

W MongoDB istnieje twór węzłów arbitrażowych, które mają za zadanie pełnić właśnie rolę trzeciego lub kolejnego nieparzystego węzła. Jeżeli mamy zamiar postawić replikę na dwóch drogich maszynach, nie ma potrzeby stawiania trzeciej takiej samej tylko dla celów arbitra, instancja ta nie wymaga duzych zasobów i mały VPS wystarczy na jego potrzeby.

W dalszej części wpisu pokażę jak zainstalować i skonfigurować MongoDB 4.0.11 z SystemD w Ubuntu Server oraz podłączyć proces Telegrafa który będzie odczytywał dane telemetryczne procesów bazy i wysyłał do InfluxDB w celach monitorowania klastra. Całość zostanie opatrzona dashboardem w Grafanie.

Zaczynamy od pobrania binarek MongoDB przy pomocy wget na serwerze oraz binarki Telegrafa:

useradd -m mongodb;
cd /home/mongodb;
wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1804-4.0.11.tgz
tar --strip-components=1 -xvzf mongodb-linux-x86_64-ubuntu1804-4.0.11.tgz

useradd -m telegraf;
cd /home/telegraf;
wget https://dl.influxdata.com/telegraf/releases/telegraf-1.11.3_linux_amd64.tar.gz
tar xf telegraf-1.11.3_linux_amd64.tar.gz

Kolejnym krokiem jest dodanie obu procesów jako usługi SystemD, stworzymy dwa pliki które umieścimy w /etc/systemd/system:

## /etc/systemd/system/mongodb.service
[Unit]
Description=MongoDB
After=network.target

[Service]
User=mongodb
Group=mongodb
Restart=on-failure
ExecStart=/home/mongodb/bin/mongod --quiet --config /etc/mongodb/mongod.conf

[Install]
WantedBy=multi-user.target
## /etc/systemd/system/telegraf.service
[Unit]
Description=Reporting metrics into InfluxDB
Documentation=https://github.com/influxdata/telegraf
After=network.target

[Service]
ExecStart=/home/telegraf/telegraf/usr/bin/telegraf -config /home/telegraf/telegraf/etc/telegraf/telegraf.conf
Restart=on-failure
KillMode=control-group

[Install]
WantedBy=multi-user.target

Instalujemy usługi SystemD:

systemctl enable mongodb.service
systemctl enable telgraf.service

Ostatnim krokiem jest odpowiednia konfiguracja zarówno MongoDB oraz Telegraf. Do konfiguracji MongoDb użyjemy silnika WiredTiger oraz podstawowych ustawień kompresji danych oraz indeksów. Ważne jest postawienie procesów od razu w trybie repliki. MongoDB w trybie repliki należy przypiąć do interfejsu 0.0.0.0 z uwagi na potrzebę komunikacji z innymi maszynami, bardziej wymagający użytkownicy zapewne skofigurują odpowiednie reguły firewall'a pozwalające na komunikację na portach MongoDB tylko z odpowiednich adresów IP. Ponadto w trybie repliki MongoDB wymaga aby dostęp do bazy podlegał autentykacji.

Poniżej plik konfiguracyjny MongoDB

storage:
    dbPath: "/var/mongodb_data"
    directoryPerDB: true
    journal:
        enabled: true
    engine: "wiredTiger"
    wiredTiger:
        engineConfig: 
            cacheSizeGB: 2
            journalCompressor: zlib
            directoryForIndexes: true
        collectionConfig: 
            blockCompressor: zlib
        indexConfig:
            prefixCompression: true
systemLog:
   destination: file
   path: "/var/log/mongod.log"
   logAppend: true
   logRotate: rename
processManagement:
   fork: false
replication:
   replSetName: "rs0"
net:
   bindIp: 0.0.0.0
   port: 27017
   unixDomainSocket:
       enabled : true
security:
   keyFile: /home/mongodb/mongo.key

Konfiguracja Telegrafa w zakresie podstawowym, tzn. proces wysyła jedynie podstawowe metryki zasobów serwerowych oraz MongoDB:

[agent]
  interval = "10s"
  round_interval = true
  metric_batch_size = 1000
  metric_buffer_limit = 10000
  collection_jitter = "0s"
  flush_interval = "10s"
  flush_jitter = "0s"
  precision = ""
  debug = false
  quiet = false
  logfile = ""
  hostname = ""
  omit_hostname = false
###############################################################################
#                            OUTPUT PLUGINS                                   #
###############################################################################
[[outputs.influxdb]]
  urls = ["XXXXXXXXXXXXXXX"] # InfluxDB address
  database = "xxxxxxx" # database name
  retention_policy = ""
  write_consistency = "any"
  timeout = "5s"
  username = "xxxxxxxx" # user
  password = "xxxxxxxx" # password

###############################################################################
#                            INPUT PLUGINS                                    #
###############################################################################
[[inputs.system]]
[[inputs.disk]]
  ignore_fs = ["tmpfs", "devtmpfs"]
[[inputs.mem]]
[[inputs.diskio]]
[[inputs.net]]

[[inputs.mongodb]]
  servers = ["mongodb://127.0.0.1:27017"]
  gather_perdb_stats = true

Przyszedł czas na uruchomienie usług i weryfikację konfiguracji, usługi uruchamiamy komendami:

service mongodb start
service telegraf start

A następnie weryfikujemy działanie:

service mongodb status
service telegraf status

Jeżeli wszystko zadziałało, powinniśmy zobaczyć mniej więcej taki komunikat:

● mongodb.service - MongoDB
   Loaded: loaded (/etc/systemd/system/mongodb.service; enabled; vendor preset: enabled)
   Active: active (running) since Tue 2019-06-04 13:10:30 CEST; 2 months 1 days ago
 Main PID: 16935 (mongod)
    Tasks: 84 (limit: 4915)
   CGroup: /system.slice/mongodb.service
           └─16935 /home/mongodb/bin/mongod --quiet --config /home/mongod.conf

Teraz kiedy już wiadomo że wszystko działa, powtarzamy wszystkie kroki na pozostałych dwóch maszynach. Należy pamiętać o poprawnych ustawieniach firewalla, nic nie może blokować komunikacji na porcie (27017 w naszym przykładzie) pomiędzy maszynami, gdyż MongoDB używa tego portu do przesyłania danych replikacji. Logujemy się na dowolną maszynę i uruchamiamy mongo aby podłączyć się do bazy. Wykonujemy komendę gdzie zapis mongodb-node-X zastępujemy adresem IP maszyny:

rs.initiate()  
rs.add('mongodb-node-2:27017')
rs.add('mongodb-node-3:27017')

rs.status()

rs.status() w konsoli Mongo zwraca status repliki wraz ze szczegółami na temat działania poszczególnych węzłów repliki. Gratuluję właśnie uruchomiłeś replikę MongoDB! Pozostaje Ci jeszcze skonfigurować odpowiednio dashboard w Grafanie który zapewni Ci pogdląd na działanie całej bazy.

Piotr Osiński