A konténerek üzeneteinek naplózása

Egy alkalmazás üzeneteinek megjelenítése nem csak a fejlesztési fázisban, hanem a későbbi, éles üzemben is fontos. Ezek rögzítése a működésük követéséhez, a futásidőben történt események elemzéséhez és az esetleges hibák kezeléséhez is elengedhetetlen lehet. Egy konténerben futó alkalmazás nyomon követését azonban nagyban megnehezíti, hogy az rendszerint a háttérben fut, így nem áll rendelkezésre egy olyan konzol, vagy egyéb felület, ahol az üzenetek azonnal megjelennének. Ezért bonyolultabb módszerhez kell folyamodnunk, mint egy hagyományos, konzolban futó program esetében. Mivel egy háttérben futó konténerből származó üzenetek ilyen módon elvesznek, egy konténerizált alkalmazás esetén a gyűjtésükhöz további lépéseket kell tenni. Ebben a fejezetben megismerjük a Docker naplózási lehetőségeit, az üzenetek összegyűjtésének folyamatát és kezelésének néhány módját.

Szükséges eszközök:

Windows, MacOS vagy Linux operációs rendszerű számítógép, telepített Docker szoftverrel. Az önálló feladatok megoldásához Virtualbox vagy más virtualizációs szoftver.

Feldolgozási idő:

Kb. 2 óra. Gyakorlati feladatok megoldására további 3 óra.

Az eseménynapló

Képzeljük el a következő történetet: megbízást kaptunk egy REST API szerver alkalmazás elkészítésére, amit a specifikációnak megfelelően el is készítettünk. A program számos klienstől fogad kéréseket, melyekre a funkcióinak megfelelően válaszokat küld. A fejlesztési folyamat végén az alkalmazás a teszteket sikeresen teljesítette, és bár nem tapasztaltunk hibát, az üzembe helyezés után a felhasználók jelzik, hogy az nem működik megfelelően: néha, de naponta csak egy-két esetben érvénytelen választ ad vissza bizonyos klienseknek. Az alkalmazás már egy konténerben fut egy internet szolgáltató szerverén, az esetleges üzenetek nem látszanak, és a forráskódot újra átnézve fogalmunk sincs arról, hogy mi lehet a hiba oka. Arra gyanakszunk, hogy esetleg valamilyen hibás kérés lehet a ludas, de ehhez tetten kellene érni egy konkrét esetet.

Az ilyen típusú problémák megoldására az operációs rendszerek gyakran saját, beépített szolgáltatást kínálnak. A Windowsok esetében ez az Eseménynapló, a Unixoknál pedig a syslog szoftver valamelyik változata (Rsyslog, syslog-ng, journalctl stb.). Ezek a nekik küldött üzeneteket egy naplóba helyezik (idegen szóval logolják), a gyakorlatban azokat valamilyen adatstruktúrába vagy egyszerű szövegfájlba írják. Az üzenetek küldésére a programozási nyelvek gyakran szabványos könyvtárakat kínálnak, amelyek pl. egy Syslog szerverrel nem csak helyi, hanem internetes kapcsolaton keresztül is kommunikálni tudnak. A példára visszatérve, amennyiben vannak logok, azok elemzésével a működés során keletkezett üzenetek egyszerűen rögzíthetők így a későbbiek során visszakereshetők.

Megjegyzés

A naplókat nem csak hibák keresésére lehet használni, azok a normál, üzemszerű események rögzítését is szolgálhatják. Ilyen lehet pl. egy szoftverben a felhasználók be- és kijelentkezéseinek, valamint az abban végzett tevékenységeinek rögzítése, egy levélküldő szoftverben az beérkező és elküldött levelek folyamatának naplózása, vagy egy fájlszerveren a felhasználók által elvégzett műveletek (fájlok és könyvtárak létrehozása, másolása, törlése stb.) rögzítése. Az ilyen típusú eseménynaplók számos vitás esetben segíthetnek egy incidens pontos körülményeinek utólagos tisztázásában.

Egy konténerben futó alkalmazás által kiírt üzenetek megtekintéséhez a docker logs <konténer_id> parancs használatos. Ennek kipróbálásához egy kis Python alkalmazást készítünk: ez három másodpercenként véletlenszámokat generál, makd eldönti, hogy azok párosak vagy páratlanok. Először készítsünk egy miniprojektet, futtassuk a programot natív módban, és csak ezt követően konténerizáljuk! Végül készítsünk hozzá egy docker-compose fájlt is! A feladatok ismeretében projektet az alábbi könyvtárstruktúrában építjük fel:

koczka@columbo:~/counter$ tree
.
├── counter
│   ├── counter.py
│   └── Dockerfile
├── docker-compose.yaml
└── run

A projekt könyvtárban létrehoztam egy counter nevű mappát, ebben hozom létre a Python programot counter.py néven. A program forrása az alábbi:

import time
import random
min = -10
max = 10
sleepPeriod = 3

def main():
  while True:
    number = random.randint(min, max)
    if number % 2 == 0:
      print(f"{number} páros", flush=True)
    else:
      print(f"{number} páratlan", flush=True)
    time.sleep(sleepPeriod)

if __name__ == "__main__":
    main()

A programban a kiírást végző sorok egy extra paramétert is tartalmaznak, az flush=True-ra azért van szükség, hogy a kimenet ne egy átmeneti gyorsítótárba kerüljön, hanem azonnal jelenjen meg a standard outputon. A konténerizáció előtt érdemes a működést ellenőrizni a python3 counter.py paranccsal. Ha minden rendben van, látszanak a generált számok és a paritásuk megállapítása is helyes, készítsünk egy Dockerfile-t is az alábbi tartalommal:

FROM python:3
WORKDIR /
ADD counter.py .
CMD ["python3","counter.py"]

Építsük fel a konténert a docker build -t counter . paranccsal, majd indítsuk el egy háttérben futó szolgáltatásként! Kényelmi okból használjuk az --rm paramétert is:

koczka@columbo:~/counter/counter$ docker run -d --rm --name=counter counter
c08aedcfa54a0fc2dd7336cc846708ac89dc485e30e97bad4e3203e359a550ef

A python alkalmazás megkezdi a számok generálását és a paritás meghatározását, de ebből mit sem látunk, hiszen a program a háttérben fut. Most kap szerepet a már említett docker logs parancs, amivel a szóban forgó konténer üzenetei utólag is megjeleníthetők:

koczka@columbo:~/counter/counter$ docker logs counter
7 páratlan
-8 páros
7 páratlan
...
0 páros

Ha az üzeneteket folyamatosan nyomon szeretnénk követni, a Unixok tail -f parancsához hasonlóan a -f kapcsolót kell használni:

koczka@columbo:~/counter/counter$ docker logs -f counter
7 páratlan
-8 páros
7 páratlan
7 páratlan
4 páros
-7 páratlan
-10 páros
9 páratlan
-9 páratlan

A -f hatására a parancs futása a logüzenetek kiírása után nem áll le, hanem vár az újabb és újabb sorokra: így a működés egyszerűen és valós időben nyomon követhető. A parancs működése a már ismert Ctrl-C megnyomásával állítható le.

Naplózás a syslog rendszerbe

A Syslog a Unixokban működő naplózási rendszer, amelyet minden olyan esetben célszerű használni, amikor egy alkalmazás eseményeit különösebb fejlesztési munka nélkül szeretnénk megőrizni. A syslogba történő naplózáshoz két módszer közül választhatunk, képessé tehetjük erre magát az alkalmazást vagy a konténer definíciójában határozzuk meg az üzenetek naplózásának célját. Bár a Python egyszerű módszert kínál az üzenetek syslogba küldésére, ez csak a natív környezetben futtatáskor lehet hatékony. Ha az alkalmazás konténerben fut, a logolást célszerű a konténer beállításival intézni, mivel ez általánosan használható módszert nyújt. Ebben az esetben az alkalmazás üzeneteit csak a standard outputra és a hibacsatornára érdemes küldeni.

A syslog üzenetek kezelését legegyszerűbben úgy valósíthatjuk meg, ha egy docker-compose.yaml fájlt készítünk a konténerünk futtatására, amit az applikáció könyvtára feletti szinten helyezünk el. Ez egyetlen szolgáltatást definiál, amit a counter könyvtárban szereplő Dockerfile alapján kell felépíteni és indítani.

version: "3"
services:
  app:
    build: counter
    logging:
      driver: "syslog"
      options:
        syslog-address: "tcp://127.0.0.1:514"

Ebben a fájlban a logging blokkban határozzuk meg a konténer üzeneteinek célját, amely a gép syslog szolgáltatása lesz. Mivel a syslog üzenetek tetszőleges gépre küldhetők, ezért a cél gép host nevét vagy IP címét kell megadni, és meg kell határozni a küldés protokollját is, ami TCP vagy UDP lehet. A példában az előbbit használjuk,kiegészítve a syslog szolgáltatás port számával, ami alapértelmezésben 514. A logging definíciójában egyébként nem csak syslog állítható be az üzenetek céljaként, a Docker számos más lehetőséget, pl. egyszerű JSON fájlok használatát is lehetővé teszi. A syslog használata azért célszerű, mert azon túl, hogy szinte minden Unix rendszeren rendelkezésre áll, számos kiegészítő feladatot is megoldhat, pl. a nagy mennyiségű logüzenetek rendszeres archiválását és a feleslegessé vált régi üzenetek törlését.

Az újraépítést követően a logok ellenőrzése rendkívül egyszerűvé válik, általában a /var/log/syslog fájl tartalmát kell ellenőriznünk – ennek megfelelő sorai tartalmazzák majd a konténer üzeneteit. Mivel ebben a fájlban számos más program napló üzenetei is megjelennek, ezért célszerű a kimenetet a grep 698ad624af21 szűrővel a kívánt sorokra szűkíteni (ahol a 698ad624af21 értelemszerűen a vizsgált konténer ID-je).

koczka@columbo:~/counter$ cat /var/log/syslog | grep 698ad624af21
Jun 18 18:13:34 localhost 698ad624af21[1030]: -10 páros
Jun 18 18:13:37 localhost 698ad624af21[1030]: -3 páratlan
Jun 18 18:13:40 localhost 698ad624af21[1030]: 7 páratlan
Jun 18 18:13:43 localhost 698ad624af21[1030]: 7 páratlan
Jun 18 18:13:46 localhost 698ad624af21[1030]: -4 páros
Jun 18 18:13:49 localhost 698ad624af21[1030]: -3 páratlan
Jun 18 18:13:52 localhost 698ad624af21[1030]: -10 páros

A teljességhez hozzátartozik, hogy egy Linux rendszer esetén az syslog alapértelmezett beállításai nem teszik lehetővé ezeknek az üzeneteknek a fogadását, ehhez a rendszerben néhány módosítást kell végezni. A szolgáltatás beállításait a /etc/rsyslog.conf fájl tartalmazza, amelyben a TCP kommunikáció alapértelmezésben ki van kommentezve, ezeket az alábbi példában olvasható módon be kell kapcsolni:

# provides TCP syslog reception
module(load="imtcp")
input(type="imtcp" port="514")

Ezt követően pedig az új beállításokkal újra kell indítani a szolgáltatást. Értelemszerűen, a konfiguráció módosítása és a syslog újraindítása is csak rendszergazdai jogosultságok megléte mellett hajtható végre:

sudo systemctl restart rsyslog

A syslog számos további beállítást tesz lehetővé. Képes az egyes alkalmazásokból származó log üzenetek szétválogatására, így azok önálló fájlokban rögzíthetők. A Logrotate programcsomaggal a logok „forgathatók”, azaz időről időre archiválhatók, és adott mennyiség vagy idő lejárta után törölhetők. Ezek már nem kapcsolódnak szorosan a konténerek témaköréhez, de érdemes lehet ezek kipróbálására egy virtuális gépen időt szánni, a Logrotate működésre bírása és minden apró csínjának-bínjának kiismerése magányos informatikusok számára vidám karácsony esti elfoglaltság lehet.

Összefoglalás

Ebben a fejezetben a konténerek kezelésének egy gyakorlati aspektusát, az üzenetek gyűjtésének módszerét ismertük meg. Bár egy fejlesztő számára ez nem feltétlenül tűnik hangsúlyos témakörnek, a gyakorlatban, főleg a hibakereséskor és az események naplózásában fontos szerepe van. A naplózás beállítását gyakran nem a fejlesztő, hanem a rendszer üzemeltetője végzi, az igények megfogalmazásakor fontos, hogy tisztában legyünk ennek módjával, a felhőszolgáltatók rendszereiben ezek pedig csak akkor fognak működni, ha a docker-compose.yaml fájlunk eleve tartalmazza a szükséges beállításokat.

Feladatok

  • Készíts egy Python Flask REST API szervizt, amely egy SQLite3 adatbázis egy tábláján CRUD műveleteket valósít meg! Naplózd az egyes eseményeket a syslog szolgáltatás segítségével!

  • Telepíts egy Ubuntu Linux LTS szervert egy virtuális gépbe! Futtasd ezen az elkészült konténert!

  • Állítsd be a syslog szervert úgy, hogy az fogadjon log üzeneteket az 514-es TCP porton! Konfiguráld be úgy a REST API szolgáltatást, hogy logolja az összes beérkező kérést és az arra adott válaszokat!