Folyamat figyelő állapotváltozás-detektálással: dmon
A sorozat utolsó projektje a legegyszerűbb ötlettel indult: tudni akarom hogy a szerveremen futnak-e azok a folyamatok amiknek futniuk kell. Az sshd, a podman, a specifikus szolgáltatások. Ha valamelyik leáll, akarok róla tudni.
A dmon nem csinál bonyolult dolgot. Beolvas egy YAML konfigot a figyelt folyamatokkal, és egy végtelen ciklusban ellenőrzi őket. Az első futásnál kiírja az aktuális állapotot. Utána már csak a változásokat jelzi – ha valami elindul vagy leáll.
Állapotváltozás-detektálás
A legtöbb monitor tool minden ellenőrzésnél kiírja a teljes állapotot. A dmon csak a változásokat. Ehhez egy prev map tárolja az előző állapotot:
type StateChange struct {
Name string
Running bool
Changed bool
First bool
}
Három eset van: az első futás (First), amikor még nincs előzmény. Az állapotváltozás (Changed), amikor a jelenlegi állapot különbözik az előzőtől. És a változatlan állapot, amit nem kell kiírni.
Ez a minta a monitoring eszközök alapja – nem a pillanatnyi állapot a fontos, hanem a változás.
A –once flag
Az eredeti verzió csak folyamatosan futott. A production ready verzió kapott egy --once flag-et ami egyszer lefuttatja az ellenőrzést és kilép. Ez cron-ból vagy más scriptekből hasznos:
dmon -once --no-color || ntfy pub monitoring "dmon: process down"
Az --once az sshping exit code mintáját követi – a kimenet nem csak olvasható, hanem scriptelhető is.
A checker interface
A dmon a pgrep -x paranccsal ellenőrzi a folyamatokat. A tesztekben nem akarok valódi folyamatokat keresni, ezért a checker interface absztrahálja:
type checker interface {
isRunning(name string) bool
}
A mockChecker egy map-ből olvassa hogy melyik folyamat “fut”:
chk := mockChecker{status: map[string]bool{
"sshd": true,
"nginx": false,
}}
Ez lehetővé teszi hogy a teljes állapotváltozás-detektálást teszteljük: első futás, változás, leállás, változatlan állapot. Minden eset lefuttatható egy milliszekundum alatt, pgrep nélkül.
A checkAll függvény
A logika a checkAll függvényben van – megkapja a folyamatok listáját, az előző állapotot, és a checker-t. Visszaad egy StateChange slice-ot:
func checkAll(processes []string, prev map[string]bool, chk checker) []StateChange
Ez a sorozat tanulságainak összefoglalása: a logika egy tiszta függvényben van ami bemenetet kap és kimenetet ad. Nem olvas globális változót, nem hív meg közvetlenül külső parancsot, nem ír a konzolra. Tesztelhető, olvasható, cserélhető.
Kilenc projekt, egy minta
A dmon-nal a werkstatt sorozat véget ér. Kilenc projekt, mindegyik más Go koncepciót tanított:
Az envcheck a fájl I/O-t és a flag csomagot. A stackctl a HTTP szervert és a JSON struktúrákat. A netmapper a goroutine-okat és channel-eket. A deployer az interface-alapú dependency injection-t. A portspy a párhuzamos scanning mintát. Az sshping a dialer interface-t és az exit code-okat. A hookrelay a POST kezelést és a JSON Lines logolást. A kvault az RWMutex-et és a CRUD ciklust. A dmon az állapotváltozás-detektálást és a checker interface-t.
De az igazi tanulság nem az egyes koncepciók, hanem a minta ami ismétlődik: interface az absztrakcióhoz, flag a konfigurációhoz, tesztek a megbízhatósághoz. Ez a három elem teszi a scriptet eszközzé.
Hogyan próbáld ki?
git clone https://github.com/brtkcs/werkstatt-tools.git
cd werkstatt-tools/dmon
go mod tidy
Hozz létre egy dmon.yaml-t:
interval: 5
processes:
- sshd
- podman
Aztán:
go run main.go
Vagy egyszer futtatva:
go run main.go -once
A teljes forráskód a werkstatt-tools repóban.