OSMC 2015: Der Countdown läuft – nur noch 119 Tage

Gerhard Laußer mit “Monitoring von Netzwerkkomponenten mit check nwc health”

OSMC? Was soll das denn sein und wer sind die netten Menschen in diesen Videos? Die Open Source Monitoring Conference (kurz: OSMC) ist die internationale Plattform für alle an Open Source Monitoring Lösungen Interessierten, speziell Nagios und Icinga. Jedes Jahr gibt es hier die Möglichkeit sein Wissen über freie Monitoringsysteme zu erweitern und sich mit anderen Anwendern auszutauschen. Die Konferenz richtet sich besonders an IT-Verantwortliche aus den Bereichen System- und Netzwerkadministration, Entwicklung und IT-Management. Und die netten Menschen, die Ihr in unseren Videos zur OSMC seht, gehören dazu. 2015 wird die OSMC zum 10. Mal in Nürnberg stattfinden.

Schneller LIKEn

Nein, hier soll es nicht um Twitter, Instagram oder Youtube gehen, sondern um Datenbankabfragen in PostgreSQL wie diese:

blog # SELECT * FROM kunden WHERE vorname LIKE 'Ann%';

Diese Abfragen sind recht häufig anzutreffen, man denke z.B. an Drop-Down-Boxen, die z.B. per AJAX mit Vorschlägen gefüllt werden, sobald drei oder mehr Buchstaben eingegeben wurden.

Das Spielfeld

Unsere Beispieldaten enthalten 1.000.000 zufällig generierte Kunden in dieser Form und mit dieser Verteilung von Vornamen, die mit ‘Ann’ beginnen:

blog # \d kunden
                               Table "public.kunden"
┌────────────┬─────────┬───────────┬──────────┬────────────────────────────────────┐
│   Column   │  Type   │ Collation │ Nullable │              Default               │
├────────────┼─────────┼───────────┼──────────┼────────────────────────────────────┤
│ id         │ integer │           │ not null │ nextval('kunden_id_seq'::regclass) │
│ vorname    │ text    │           │ not null │                                    │
│ nachname   │ text    │           │ not null │                                    │
│ strasse    │ text    │           │ not null │                                    │
│ hausnummer │ integer │           │ not null │                                    │
│ plz        │ text    │           │ not null │                                    │
│ ort        │ text    │           │ not null │                                    │
│ bundesland │ text    │           │ not null │                                    │
└────────────┴─────────┴───────────┴──────────┴────────────────────────────────────┘
Indexes:
    "kunden_pkey" PRIMARY KEY, btree (id)
Check constraints:
    "kunden_plz_check" CHECK (length(plz) = 5)

blog # vorname,count(*) FROM kunden WHERE vorname LIKE 'Ann%' GROUP BY vorname;
┌───────────┬───────┐
│  vorname  │ count │
├───────────┼───────┤
│ Anni      │   963 │
│ Annabella │   984 │
│ Anne      │   965 │
│ Annalena  │   971 │
│ Annika    │  1017 │
│ Anna      │  1011 │
│ Ann       │  1003 │
│ Annelie   │   976 │
│ Annemarie │  1001 │
│ Annabell  │   996 │
└───────────┴───────┘
(10 rows)

Ein erster Versuch – “vanilla”

Schauen wir doch mal, wie unsere PostgreSQL-Datenbank eine Suche nach ‘Ann%’ bearbeitet:

blog # EXPLAIN (ANALYSE,COSTS) SELECT * FROM kunden WHERE vorname LIKE 'Ann%';
┌────────────────────────────────────────────────────────────────────┐
│                           QUERY PLAN                               │
├────────────────────────────────────────────────────────────────────┤
│ Seq Scan on kunden  (cost=0.00..24667.00 rows=10244 width=65)      |
|      (actual time=0.019..90.620 rows=9887 loops=1)                 │
│   Filter: (vorname ~~ 'Ann%'::text)                                │
│   Rows Removed by Filter: 990113                                   │
│ Planning time: 0.100 ms                                            │
│ Execution time: 90.956 ms                                          │
└────────────────────────────────────────────────────────────────────┘
(5 rows)

Ein Seq Scan, also der gefürchtete Sequential-Scan aka. Full table scan; alle Datensätze werden gelesen und ‘vorname’ mit ‘Ann%’ verglichen. Das ist sehr ineffektiv.

Ein Index muss her!

Die Lösung ist offensichtlich: wenn solche Abfragen häufig vorkommen, muss ein Index her. Der wird den Vorgang beschleunigen:

blog # CREATE INDEX vorname_btree_vanilla ON kunden (vorname);
blog # EXPLAIN (ANALYSE,COSTS) SELECT * FROM kunden WHERE vorname LIKE 'Ann%';
┌───────────────────────────────────────────────────────────────────────┐
│                            QUERY PLAN                                 │
├───────────────────────────────────────────────────────────────────────┤
│ Seq Scan on kunden  (cost=0.00..24667.00 rows=10244 width=65)         |      
|      (actual time=0.011..105.340 rows=9887 loops=1)                   │
│   Filter: (vorname ~~ 'Ann%'::text)                                   │
│   Rows Removed by Filter: 990113                                      │
│ Planning time: 0.195 ms                                               │
│ Execution time: 105.768 ms                                            │
└───────────────────────────────────────────────────────────────────────┘
(5 rows)

Uhm, Moment mal… warum nimmt meine Datenbank nicht den Index zu Hilfe?!? Geht es denn mit einzelnen Werten?

 blog # EXPLAIN (ANALYSE,COSTS) SELECT * FROM kunden WHERE vorname IN ('Anna','Anne','Annelie');
┌────────────────────────────────────────────────────────────────────────────────────────┐
│                              QUERY PLAN                                                │
├────────────────────────────────────────────────────────────────────────────────────────┤
│ Bitmap Heap Scan on kunden  (cost=59.83..6892.21 rows=2909 width=65)                   |
|      (actual time=1.275..5.054 rows=2952 loops=1)                                      │
│   Recheck Cond: (vorname = ANY ('{Anna,Anne,Annelie}'::text[]))                        │
│   Heap Blocks: exact=2656                                                              │
│   ->  Bitmap Index Scan on vorname_btree_vanilla  (cost=0.00..59.10 rows=2909 width=0) |
|      (actual time=0.652..0.652 rows=2952 loops=1)                                      │
│         Index Cond: (vorname = ANY ('{Anna,Anne,Annelie}'::text[]))                    │
│ Planning time: 0.136 ms                                                                │
│ Execution time: 5.292 ms                                                               │
└────────────────────────────────────────────────────────────────────────────────────────┘
(7 rows)

Ja, da wird der Index genommen, und die Ausführung ist auch gleich um Größenordnungen schneller.

Was ist also das Problem?

“C” und seine Spätfolgen – Schei* encoding!

Das Geheimnis liegt – wie so häufig – in der Lokalisierung. Btree-Indexe sind (für Text-Daten) auf das C-Locale hin optimiert. Wenn aber die Datenbank (wie heutzutage üblich!) mit en_US.UTF8 oder de_DE.UTF8 initialisiert wurde, müssen wir dem Index bei der Erstellung mitteilen, dass wir pattern operator-Aktionen ausführen können wollen. PostgreSQL kommt mit einem ganzen Haufen dieser Operator Classes.

Für unser TEXT-Feld ‘vorname’ nehmen wir text_pattern_ops. Nach der Erstellung testen wir, ob der Index unsere LIKE-Anfrage beschleunigt und verifizieren, dass auch weiterhin die klassischen <=, == und >= Vergleichsoperatoren funktionieren:

blog # DROP INDEX vorname_btree_vanilla ;
blog # CREATE INDEX vorname_btree_opclass ON kunden (vorname text_pattern_ops);
blog # EXPLAIN (ANALYSE,COSTS) SELECT * FROM kunden WHERE vorname LIKE 'Ann%';
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                                     QUERY PLAN                                          │
├─────────────────────────────────────────────────────────────────────────────────────────┤
│ Bitmap Heap Scan on kunden  (cost=129.19..10234.85 rows=10244 width=65)                 |
|       (actual time=5.327..16.083 rows=9887 loops=1)                                     │
│   Filter: (vorname ~~ 'Ann%'::text)                                                     │
│   Heap Blocks: exact=6830                                                               │
│   ->  Bitmap Index Scan on vorname_btree_opclass  (cost=0.00..126.62 rows=5820 width=0) |
|       (actual time=3.524..3.524 rows=9887 loops=1)                                      │
│         Index Cond: ((vorname ~>=~ 'Ann'::text) AND (vorname ~<~ 'Ano'::text))          │
│ Planning time: 0.378 ms                                                                 │
│ Execution time: 16.650 ms                                                               │
└─────────────────────────────────────────────────────────────────────────────────────────┘
(7 rows)

blog # EXPLAIN (ANALYSE,COSTS) SELECT * FROM kunden WHERE vorname IN ('Anna','Anne','Annelie');
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                                        QUERY PLAN                                       │
├─────────────────────────────────────────────────────────────────────────────────────────┤
│ Bitmap Heap Scan on kunden  (cost=59.83..6892.21 rows=2909 width=65)                    |
|       (actual time=1.233..5.001 rows=2952 loops=1)                                      │
│   Recheck Cond: (vorname = ANY ('{Anna,Anne,Annelie}'::text[]))                         │
│   Heap Blocks: exact=2656                                                               │
│   ->  Bitmap Index Scan on vorname_btree_opclass  (cost=0.00..59.10 rows=2909 width=0)  |
|       (actual time=0.634..0.634 rows=2952 loops=1)                                      │
│         Index Cond: (vorname = ANY ('{Anna,Anne,Annelie}'::text[]))                     │
│ Planning time: 0.135 ms                                                                 │
│ Execution time: 5.246 ms                                                                │
└─────────────────────────────────────────────────────────────────────────────────────────┘
(7 rows)

Wunderbar! Und 17ms klingt auch gleich viel besser als 100ms.

Geht da noch was?

Jedes Kind weiß, dass Indexe nur Anfragen wie LIKE ‘Ann%’ beschleunigen können. Für LIKE ‘%nna%’ gibt es leider keine Hilfe von der Datenbank. Ist ja auch irgendwie klar, der Btree muss ja von links nach rechts aufgebaut werden…

EXPLAIN (ANALYSE,COSTS) SELECT * FROM kunden WHERE vorname LIKE '%nna%';
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                                         QUERY PLAN                                      │
├─────────────────────────────────────────────────────────────────────────────────────────┤
│ Seq Scan on kunden  (cost=0.00..24667.00 rows=19022 width=65)                           |
|      (actual time=0.014..110.607 rows=11993 loops=1)                                    │
│   Filter: (vorname ~~ '%nna%'::text)                                                    │
│   Rows Removed by Filter: 988007                                                        │
│ Planning time: 0.131 ms                                                                 │
│ Execution time: 111.006 ms                                                              │
└─────────────────────────────────────────────────────────────────────────────────────────┘
(5 rows)

Aber stimmt das? Gibt es wirklich keine Möglichkeit, solche Abfragen zu beschleunigen?

PostgreSQL ist schier unfassbar erweiterbar, und unter anderem kommt es von Haus aus mit einer Extension pg_trgm, die wiederum operator classes für GIN und GiST Indexe mitbringt.

blog # CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE EXTENSION
blog # CREATE INDEX vorname_gin_trgm ON kunden USING GIN (vorname gin_trgm_ops);
blog # EXPLAIN (ANALYSE,COSTS) SELECT * FROM kunden WHERE vorname LIKE '%nna%';
┌──────────────────────────────────────────────────────────────────────────────────────────┐
│                                           QUERY PLAN                                     │
├──────────────────────────────────────────────────────────────────────────────────────────┤
│ Bitmap Heap Scan on kunden  (cost=183.42..13123.52 rows=19022 width=65)                  |
|       (actual time=5.049..15.935 rows=11993 loops=1)                                     │
│   Recheck Cond: (vorname ~~ '%nna%'::text)                                               │
│   Heap Blocks: exact=7670                                                                │
│   ->  Bitmap Index Scan on vorname_gin_trgm  (cost=0.00..178.67 rows=19022 width=0)      |   
|       (actual time=3.014..3.014 rows=11993 loops=1)                                      │
│         Index Cond: (vorname ~~ '%nna%'::text)                                           │
│ Planning time: 0.253 ms                                                                  │
│ Execution time: 16.488 ms                                                                │
└──────────────────────────────────────────────────────────────────────────────────────────┘
(7 rows)

pg_trgm kann noch mehr

Eine vermeintlich nette Spielerei, aber – wenn man es denn kennt – in vielen Situationen hilfreich, ist die Ähnlichkeitssuche, die pg_trgm in Form von Funktionen und Operatoren mitbringt:

blog # SELECT vorname, count(*) FROM kunden WHERE vorname % 'Nick' GROUP BY vorname ORDER BY similarity(vorname, 'Nick') DESC;
┌─────────┬───────┐
│ vorname │ count │
├─────────┼───────┤
│ Nick    │   995 │
│ Nico    │  1047 │
│ Nicole  │   977 │
│ Nicolas │  1026 │
└─────────┴───────┘
(4 rows)

Und auch hier beschleunigt der GIN-Index die Abfrage signifikant:

blog # EXPLAIN ANALYSE SELECT vorname, count(*) FROM kunden WHERE vorname % 'Nick' GROUP BY vorname ORDER BY similarity(vorname, 'Nick') DESC;
┌──────────────────────────────────────────────────────────────────────────────────────────┐
│                                          QUERY PLAN                                      │
├──────────────────────────────────────────────────────────────────────────────────────────┤
│ Sort  (cost=24761.50..24763.07 rows=630 width=18)                                        |
|      (actual time=915.696..915.697 rows=4 loops=1) .                                     │
│   Sort Key: (similarity(vorname, 'Nick'::text)) DESC                                     │
│   Sort Method: quicksort  Memory: 25kB                                                   │
│   ->  GroupAggregate  (cost=24716.83..24732.20 rows=630 width=18)                        |
|      (actual time=915.305..915.689 rows=4 loops=1)                                       │
│         Group Key: vorname                                                               │
│         ->  Sort  (cost=24716.83..24719.33 rows=1000 width=6)                            |
|      (actual time=915.162..915.305 rows=4045 loops=1)                                    │
│               Sort Key: vorname                                                          │
│               Sort Method: quicksort  Memory: 286kB                                      │
│               ->  Seq Scan on kunden  (cost=0.00..24667.00 rows=1000 width=6)            |
|      (actual time=0.650..914.065 rows=4045 loops=1)                                      │
│                     Filter: (vorname % 'Nick'::text)                                     │
│                     Rows Removed by Filter: 995955                                       │
│ Planning time: 0.296 ms                                                                  |
│ Execution time: 915.737 ms                                                               │
└──────────────────────────────────────────────────────────────────────────────────────────┘
(13 rows)

blog # CREATE INDEX vorname_gin_trgm ON kunden USING GIN (vorname gin_trgm_ops);
blog # EXPLAIN ANALYSE SELECT vorname, count(*) FROM kunden WHERE vorname % 'Nick' GROUP BY vorname ORDER BY similarity(vorname, 'Nick') DESC;
┌──────────────────────────────────────────────────────────────────────────────────────────┐
│                                             QUERY PLAN                                   │
├──────────────────────────────────────────────────────────────────────────────────────────┤
│ Sort  (cost=3164.18..3165.75 rows=630 width=18)                                          |
|      (actual time=32.510..32.510 rows=4 loops=1)                                         │
│   Sort Key: (similarity(vorname, 'Nick'::text)) DESC                                     │
│   Sort Method: quicksort  Memory: 25kB                                                   │
│   ->  HashAggregate  (cost=3127.01..3134.88 rows=630 width=18)                           |
|      (actual time=32.497..32.503 rows=4 loops=1)                                         │
│         Group Key: vorname                                                               │
│         ->  Bitmap Heap Scan on kunden  (cost=75.75..3122.01 rows=1000 width=6)          |
|      (actual time=5.993..31.447 rows=4045 loops=1)                                       │
│               Recheck Cond: (vorname % 'Nick'::text)                                     │
│               Rows Removed by Index Recheck: 13010                                       │
│               Heap Blocks: exact=9174                                                    │
│               ->  Bitmap Index Scan on vorname_gin_trgm                                  |
|      (cost=0.00..75.50 rows=1000 width=0) (actual time=4.976..4.976 rows=17055 loops=1)  │
│                     Index Cond: (vorname % 'Nick'::text)                                 │
│ Planning time: 0.123 ms                                                                  │
│ Execution time: 32.563 ms                                                                │
└──────────────────────────────────────────────────────────────────────────────────────────┘
(13 rows)

Fazit

Dass PostgreSQL nicht von Haus aus alle LIKE-Anfragen per Index beschleunigt, sorgt gerne für Irritationen. Auf der anderen Seite öffnen sich, sobald man anfängt, sich mit dem Thema auseinanderzusetzen, ganz neue Möglichkeiten, die man in dieser Form bei anderen DBMS nicht findet. Und wir haben noch gar nicht über eine echte Volltextsuche gesprochen!

P.S.: pg_trgm kann kein Chinesisch!

Wenn nicht-alphanumerische Buchstaben in’s Spiel kommen (oder pg_trgm zu langsam ist…) sollte man einen Blick auf PGroonga werfen, das in diesen Bereichen glänzt.

Über den Author

Gunnar “Nick” Bluth hat seine Liebe zu relationalen Datenbanken Ende des letzten Jahrtausends entdeckt. Über MS Access und MySQL 3.x landete er sehr schnell bei PostgreSQL und hat nie zurückgeschaut, zumindest nie ohne Schmerzen. Er verdient seine Brötchen seit beinahe 20 Jahren mit FOSS (Administration, Schulungen, Linux, PostgreSQL). Gelegentlich taucht er auch tiefer in die Programmierung ein, so als SQL-Programmierer bei der Commerzbank oder in App-Nebenprojekten

Das neue User Interface von Icinga DB

Während mit Icinga DB sehr viel unter der Haube passiert ist, kommt mit dem Release auch ein rundum neu geschriebenes Monitoring Modul für Icinga Web 2. In diesem Zuge erhielt auch das User Interface ein ausführliches Redesign. In diesem Blogpost werden die wichtigsten Änderungen erklärt.

Listen

Wie im bewährten Monitoring Modul gibt es im Monitoring Modul für Icinga DB viele Listenansichten. Daher wurden diese komplett überarbeitet. Allen Listenelementen liegt eine grundlegende einheitliche Anatomie zugrunde.

ListItem Anatomy.jpg

Anatomie eines Listenelements

Visual

Für jedes Listenelement wird ein so genanntes Visual verwendet. Dieses dient dazu, in langen Listen bestimmte Elemente hervorzuheben bzw. einen intuitiven Überblick geben, welchen Zustand das zugrunde liegende Objekt hat. Bei Host- bzw. Servicelisten wird dadurch beispielsweise der State dargestellt. So wird in der Übersicht unmittelbar ersichtlich, bei welchen Objekten Probleme vorliegen.

Title

Der Titel beschreibt ergänzend kurz zusammengefasst den Zustand des Listenelements. So enthält er beispielsweise die Info, dass ein Host gerade Down ist. Während das Visual einen intuitiven Eindruck gibt, erklärt der Titel, was genau passiert ist.

Meta

Der Metabereich ist für zusätzliche Informationen vorgesehen. In der Regel werden hier Zeitangaben des Listenelements angezeigt. In Host und Servicelisten steht hier, wie lange sich das Objekt bereits im entsprechenden Zustand befindet.

Caption

Der Caption-Bereich enthält detaillierte Informationen zum Listenelement. Dies kann im Falle von Hosts und Services der Plugin Output sein. Bei Kommentaren und Downtimes werden hier die Kommentartexte des Users angezeigt. Um die Listendarstellung kompakt und einheitlich zu halten, werden lange Texte auf eine oder mehrere Zeilen gekürzt.

Klickbare Elemente

Viele der Listenelemente enthalten neben dem Hauptelement zwei oder mehrere klickbare Elemente. Alle Teile des Listenelements, die auf weitere Detailinformationen verweisen sind eindeutig hervorgehoben. Dadurch ist sofort erkennbar, hinter welchen Textteilen sich Zusatzinformationen befinden.

Overdue Checks in Host- und Service Listen

In Host und Servicelisten werden Overdue Checks nun besonders auffällig hervorgehoben. Dadurch ist auf den ersten Blick sofort ersichtlich, welche Objekte möglicherweise nicht mehr aktuell sind.

Artboard Copy.jpg

Das neue Icinga DB Design: Hostliste und Detailbereich

Detailgrad der Listenansichten wählen

Der Detailgrad der Host- und Servicelisten ist nun wählbar. Die Standardansicht zeigt den Titel und einen zweizeiligen Plugin Output. In der detaillierten Ansicht wird der gesamt Plugin Output angezeigt. Will man einen größeren Überblick bekommen gibt es außerdem die Minimalansicht. Hier wird in einer Zeile der Plugin Output angeschnitten, wenn genügend Platz vorhanden ist. Dafür sieht man auf einem Bildschirm wesentlich mehr Listenelemente als in den anderen Darstellungen.

State Change Visual in History- und Notification Elementen

In History und Notification Listen sind unter anderem Statewechsel-Elemente zu finden. Hier wird im Visual der Wechsel nun auf den ersten Blick deutlich gemacht. Neben dem aktuellen State wird gleichzeitig auch der vorherige State ersichtlich.

State Changes sind in den Notificationlisten nun besser ersichtlich.

Detailansichten

Optimierte Headerbereiche

Die Headerbereiche der einzelnen Objekttypen erhalten ein neues Design. Während die herkömmlichen Headerbereiche sehr viel Platz brauchten, sind die Informationen kompakter.

Graphen

Die Detailansichten der Elemente waren bisher sehr textlastig. Nach dem Redesign sind die Detailbereiche deutlich visueller angelegt. Nun werden anstatt der bloßen Auflistung Informationen kombiniert und Zusammenhänge dargestellt.

Modaldialoge für schnelle Aktionen

Wollte man bisher aus dem Detailbereich einen Kommentar anlegen oder eine Downtime setzen wurde der Dialog in einer weiteren Spalte angezeigt, so dass die Inhalte der linken Spalte verloren gingen. Im neuen Monitoring Modul gibt es nun ein Modal-Element für kurze Interaktionsdialoge. Für Aktionen im Detailbereich erscheint nun ein Modaldialog. Dadurch bleibt die linke Listenspalte und somit der Kontext besser erhalten.

 

Florian Strohmaier
Florian Strohmaier
Senior UX Designer

Mit seinen Spezialgebieten UI-Konzeption, Prototyping und Frontendentwicklung unterstützt Florian das Dev-Team bei NETWAYS. Trotz seines Design-Backgrounds fühlt er sich auch in der Technik zuhause. Gerade die Kombination aus beidem hat für ihn einen besonderen Reiz.

STARFACE 6.7.0.22 – SIP Probleme beheben

Mit der neuen STARFACE Version 6.7.0.22 sind einige neue Funktionen und Verbesserungen in die Telefonanlage eingezogen. Leider hat sich hier aber ein kleiner Fehler eingeschlichen, welcher Benutzer mit einem SIP-Anschluss betrifft.

Das Problem tritt auf, wenn im SIP Benutzernamen ein “+” beinhaltet ist. Mit der neuen Version der Software, wird dieses durch “%2B” ersetzt, wodurch eine Anmeldung beim SIP-Provider nicht mehr möglich ist. Die Leitung steckt dadurch im Status “Not registered” fest. Ob man von diesem Fehler betroffen ist, kann man im PBX-Log in der Telefonanlage im Webfrontend unter

Admin > Server > Log-Datei > PBX

einsehen. Hier tritt dann eine Fehlermeldung auf, die bspw. wie folgt aussieht:

[Oct 24 11:40:34] WARNING[362] chan_sip.c: Probably a DNS error for registration to %2B499119288544@+499119288544, trying REGISTER again (after 20 seconds)

Das Problem ist STARFACE bekannt und wird in einer nächsten Version behoben. Als Workaround findet man auf dem STARFACE-Support GitHub Account ein Shell-Script, welches die Einträge in der Datenbank ändert, die Asterisk-Konfiguration anpasst und den Dienst neu startet:

#!/bin/bash

# Revert URLencode in the database
/usr/bin/psql asterisk -c “UPDATE providerconfig SET register = REPLACE(register,’%2B’,’+’);”
/usr/bin/psql asterisk -c “UPDATE providerconfig SET username = REPLACE(username,’%2B’,’+’);”

# Replace all instanced of “%2B” in the sip.conf, while creating a backup (“_SC3960.bak”)
/bin/sed -i_SC3960.bak “s/%2B/+/g” /etc/asterisk/sip.conf

# Show the difference
echo “Here’s what we’ve done:”
/usr/bin/diff -U0 /etc/asterisk/sip.conf_SC3960.bak /etc/asterisk/sip.conf

# Reload the Asterisk SIP config to reregister all lines
/usr/sbin/asterisk -r -x “sip reload”

Damit der Vorgang dann beschleunigt wird, kann die STARFACE anschließend neu gestartet werden. Nun sollte die Anlage sich wieder ordnungsgemäß verbinden können.

Wichtig dabei ist jedoch, dass die Konfiguration über das Web für die Leitungen nicht angepasst wird – danach besteht das Problem wieder und der Workaround muss erneut angewendet werden.

Bei Fragen hierzu steht unser Shop-Team natürlich gerne zur Verfügung und freut sich auf Ihre Kontaktaufnahme!

Christian Stein
Christian Stein
Lead Senior Account Manager

Christian kommt ursprünglich aus der Personalberatungsbranche, wo er aber schon immer auf den IT Bereich spezialisiert war. Bei NETWAYS arbeitet er als Senior Sales Engineer und berät unsere Kunden in der vertrieblichen Phase rund um das Thema Monitoring. Gemeinsam mit Georg hat er sich Mitte 2012 auch an unserem Hardware-Shop "vergangen".

GitLab CI Runners with Auto-scaling on OpenStack

 

With migrating our CI/CD pipelines from Jenkins to GitLab CI in the past months, we’ve also looked into possible performance enhancements for binary package builds. GitLab and its CI functionality is really really great in this regard, and many things hide under the hood. Did you know that “Auto DevOps” is just an example template for your CI/CD pipeline running in the cloud or your own Kubernetes cluster? But there’s more, the GitLab CI runners can run jobs in different environments with using different hypervisors and the power of docker-machine.

One of them is OpenStack available at NWS and ready to use. The following examples are from the Icinga production environment and help us on a daily basis to build, test and release Icinga products.

 

Preparations

Install the GitLab Runner on the GitLab instance or in a dedicated VM. Follow along in the docs where this is explained in detail. Install the docker-machine binary and inspect its option for creating a new machine.

curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | sudo bash
apt-get install -y gitlab-runner
  
curl -L https://github.com/docker/machine/releases/download/v0.16.2/docker-machine-`uname -s`-`uname -m` -o /usr/local/bin/docker-machine
chmod +x /usr/local/bin/docker-machine
  
docker-machine create --driver openstack --help

Next, register the GitLab CI initially. Note: This is just to ensure that the runner is up and running in the GitLab admin interface. You’ll need to modify the configuration in a bit.

gitlab-runner register \
  --non-interactive \
  --url https://git.icinga.com/ \
  --tag-list docker \
  --registration-token SUPERSECRETKEKSI \
  --name "docker-machine on OpenStack" \
  --executor docker+machine \
  --docker-image alpine

 

Docker Machine with OpenStack Deployment

Edit “/etc/gitlab-runner/config.toml” and add/modify the “[[runners]]” section entry for OpenStack and Docker Machine. Ensure that the MachineDriver, MachineName and MachineOptions match the requirements. Within “MachineOptions”, add the credentials, flavors, network settings just as with other deployment providers. All available options are explained in the documentation.

vim /etc/gitlab-runner/config.toml

  [runners.machine]
    IdleCount = 4
    IdleTime = 3600
    MaxBuilds = 100
    MachineDriver = "openstack"
    MachineName = "customer-%s"
    MachineOptions = [
      "openstack-auth-url=https://cloud.netways.de:5000/v3/",
      "openstack-tenant-name=1234-openstack-customer",
      "openstack-username=customer-login",
      "openstack-password=sup3rS3cr3t4ndsup3rl0ng",
      "openstack-flavor-name=s1.large",
      "openstack-image-name=Debian 10.1",
      "openstack-domain-name=default",
      "openstack-net-name=customer-network",
      "openstack-sec-groups="mine",
      "openstack-ssh-user=debian",
      "openstack-user-data-file=/etc/gitlab-runner/user-data",
      "openstack-private-key-file=/etc/gitlab-runner/id_rsa",
      "openstack-keypair-name=GitLab Runner"
    ]

The runners cache can be put onto S3 granted that you have this service available. NWS luckily provides S3 compatible object storage.

  [runners.cache]
    Type = "s3"
    Shared = true
    [runners.cache.s3]
      ServerAddress = "s3provider.domain.localdomain"
      AccessKey = "supersecretaccesskey"
      SecretKey = "supersecretsecretkey"
      BucketName = "openstack-gitlab-runner"

Bootstrap Docker in the OpenStack VM

Last but not least, these VMs need to be bootstrapped with Docker inside a small script. Check the “–engine-install-url” parameter in the help output:

root@icinga-gitlab:/etc/gitlab-runner# docker-machine create --help
  ...
  --engine-install-url "https://get.docker.com"							Custom URL to use for engine installation 

You can use the official way of doing this, but putting this into a small script also allows customizations like QEMU used for Raspbian builds. Ensure that the script is available via HTTP e.g. from a dedicated GitLab repository 😉

#!/bin/sh
#
# This script helps us to prepare a Docker host for the build system
#
# It is used with Docker Machine to install Docker, plus addons
#
# See --engine-install-url at docker-machine create --help

set -e

run() {
  (set -x; "$@")
}

echo "Installing Docker via get.docker.com"
run curl -LsS https://get.docker.com -o /tmp/get-docker.sh
run sh /tmp/get-docker.sh

echo "Installing QEMU and helpers"
run sudo apt-get update
run sudo apt-get install -y qemu-user-static binfmt-support

Once everything is up and running, the GitLab runners are ready to fire the jobs.

 

Auto-Scaling

Jobs and builds are not run all the time, and especially with cloud resources, this should be a cost-efficient thing. When building Icinga 2 for example, the 20+ different distribution jobs generate a usage peak. With the same resources assigned all the time, this would tremendously slow down the build and release times. In that case, it is desirable to automatically spin up more VMs with Docker and let the GitLab runner take care of distributing the jobs. On the other hand, auto-scaling should also shut down resources in idle times.

By default, one has 4 VMs assigned to the GitLab runner. These builds run non-privileged in Docker, the example below also shows another runner which can run privileged builds. This is needed for Docker-in-Docker to create Docker images and push them to GitLab’s container registry.

root@icinga-gitlab:~# docker-machine ls
NAME                                               ACTIVE   DRIVER      STATE     URL                      SWARM   DOCKER     ERRORS
runner-privileged-icinga-1571900582-bed0b282       -        openstack   Running   tcp://10.10.27.10:2376           v19.03.4
runner-privileged-icinga-1571903235-379e0601       -        openstack   Running   tcp://10.10.27.11:2376           v19.03.4
runner-non-privileged-icinga-1571904408-5bb761b5   -        openstack   Running   tcp://10.10.27.20:2376           v19.03.4
runner-non-privileged-icinga-1571904408-52b9bcc4   -        openstack   Running   tcp://10.10.27.21:2376           v19.03.4
runner-non-privileged-icinga-1571904408-97bf8992   -        openstack   Running   tcp://10.10.27.22:2376           v19.03.4
runner-non-privileged-icinga-1571904408-97bf8992   -        openstack   Running   tcp://10.10.27.22:2376           v19.03.4

Once it detects a peak in the pending job pipeline, the runner is allowed to start additional VMs in OpenStack.

root@icinga-gitlab:~# docker-machine ls
NAME                                               ACTIVE   DRIVER      STATE     URL                      SWARM   DOCKER     ERRORS
runner-privileged-icinga-1571900582-bed0b282       -        openstack   Running   tcp://10.10.27.10:2376           v19.03.4
runner-privileged-icinga-1571903235-379e0601       -        openstack   Running   tcp://10.10.27.11:2376           v19.03.4
runner-non-privileged-icinga-1571904408-5bb761b5   -        openstack   Running   tcp://10.10.27.20:2376           v19.03.4
runner-non-privileged-icinga-1571904408-52b9bcc4   -        openstack   Running   tcp://10.10.27.21:2376           v19.03.4
runner-non-privileged-icinga-1571904408-97bf8992   -        openstack   Running   tcp://10.10.27.22:2376           v19.03.4
runner-non-privileged-icinga-1571904408-97bf8992   -        openstack   Running   tcp://10.10.27.23:2376           v19.03.4

...

runner-non-privileged-icinga-1571904534-0661c396   -        openstack   Running   tcp://10.10.27.24:2376           v19.03.4
runner-non-privileged-icinga-1571904543-6e9622fd   -        openstack   Running   tcp://10.10.27.25:2376           v19.03.4
runner-non-privileged-icinga-1571904549-c456e119   -        openstack   Running   tcp://10.10.27.27:2376           v19.03.4
runner-non-privileged-icinga-1571904750-8f6b08c8   -        openstack   Running   tcp://10.10.27.29:2376           v19.03.4

 

In order to achieve this setting, modify the runner configuration and increase the limit.

vim /etc/gitlab-runner/config.toml

[[runners]]
  name = "docker-machine on OpenStack"
  limit = 24
  output_limit = 20480
  url = "https://git.icinga.com/"
  token = "supersecrettoken"
  executor = "docker+machine"

This would result in 24 OpenStack VMs after a while, and all are idle 24/7. In order to automatically decrease the deployed VMs, use the OffPeak settings. This also ensures that resources are available during workhours while spare time and weekend are considered “off peak” with shutting down unneeded resources automatically.

    OffPeakTimezone = "Europe/Berlin"
    OffPeakIdleCount = 2
    OffPeakIdleTime = 1800
    OffPeakPeriods = [
      "* * 0-8,22-23 * * mon-fri *",
      "* * * * * sat,sun *"
    ]

Pretty neat functionality 🙂

 

Troubleshooting & Monitoring

“docker-machine ls” provides the full overview and tells whenever e.g. a connection to OpenStack did not work, or if the VM is currently unavailable.

root@icinga-gitlab:~# docker-machine ls
NAME                                               ACTIVE   DRIVER      STATE     URL                      SWARM   DOCKER     ERRORS
runner-privileged-icinga-1571900582-bed0b282       -        openstack   Error                                      Unknown    Expected HTTP response code [200 203] when accessing [GET https://cloud.netways.de:8774/v2.1/servers/], but got 404 instead

In case you have deleted the running VMs to start fresh, provisioning might take a while and the above can be a false positive. Check the OpenStack management interface to see whether the VMs booted correctly. You can also remove a VM with “docker-machine rm <id>” and run “gitlab-runner restart” to automatically provision it again.

Whenever the VM provisioning fails, a gentle look into the syslog (or runner log) unveils what’s the problem. Lately we had used a wrong OpenStack flavor configuration which was fixed after investigating in the logs.

Oct 18 07:08:48 3 icinga-gitlab gitlab-runner[30988]:  #033[31;1mERROR: Error creating machine: Error in driver during machine creation: Unable to find flavor named 1234-customer-id-4-8#033[0;m  #033[31;1mdriver#033[0;m=openstack #033[31;1mname#033[0;m=runner-non-privilegued-icinga-1571375325-3f8176c3 #033[31;1moperation#033[0;m=create

Monitoring your GitLab CI runners is key, and with the help of the REST API, this becomes a breeze with Icinga checks. You can inspect the runner state and notify everyone on-call whenever CI pipelines are stuck.

 

Conclusion

Developers depend on fast CI feedback these days, speeding up their workflow – make them move fast again. Admins need to understand their requirements, and everyone needs a deep-dive into GitLab and its possibilities. Join our training sessions for more practical exercises or immediately start playing in NWS!

Über Physik, Erdnuss-Snacks und Schlangenöl aka Datenbanken

Achtung ein Hinweis in eigener Sache, dieser Artike kann Spuren von Polemik und rant enthalten!

Neal Stephenson hat in seinem wunderbaren Roman Cryptonomicon eine kleine Randgeschichte über eine Bandsäge eingebaut. Der für mich schönste Satz darin ist:

Anecdotes about accidents involving the bandsaw were told in hushed voices and not usually commingled with other industrial-accident anecdotes.

An diesen Satz muss ich immer denken, wenn es auf PostgreSQL-Konferenzen in den abendlichen War-Story-Runden zu Geschichten über MySQL oder MariaDB kommt, zugegeben hauptsächlichen deren Migration zu PostgreSQL.
Bei Entity–Attribute–Value (EAV) Schemas, nicht getesteten Backups, Prozeduren, die nicht maskierte Benutzereingaben als Tabellennamen für dynamisches SQL hernehmen etc. kann man sagen sie hätten es wissen können bzw. müssen. (Halt die üblichen Katastrophen, die man sich in gemütlicher Runde so erzählt, Datenbanker sind halt schon ein komischer Haufen.)

Das ist bei MySQL wie auch diversen anderen Datenbanken irgendwie anders, und ich frage mich immer wieder, warum ist das so?

Es hat evtl. etwas mit einer grundsätzlichen Bereitschaft zu tun, über physikalische Gegebenheiten nachzudenken.

Wenn jemand eine PostgreSQL-Mailingliste anschreibt und erklärt, dass Oracle eine Anfrage in 0,009 ms beantwortet, für die PostgreSQL über zwei Minuten benötigt, dann ist diese Frage erst einmal legitim.

Wenn dann aber nach und nach herauskommt, dass es um eine Tabelle mit 9.649.110 Zeilen und rund 3,5GB geht, die mittels SELECT * FROM table1; komplett gelesen wird, müsste einem doch sofort klar sein, das Ersteres nicht sein kann? Das brandneue DDR5-RAM schafft gerade mal 5,2 GT/s (Megatransfer), und da ist noch keine Netzwerkverbindung im Spiel. In 0,000009 Sekunden schafft selbst Licht “nur” rund 2,7km.

Wie können solche offensichtlichen Mess- oder Denkfehler nicht sofort auffallen? Die 2,7 km muss man ja nicht parat haben, aber dass 0,000009 Sekunden ein sehr kurzer Zeitraum sind, um 3,5GB Daten zu übertragen, liegt doch eigentlich für ITler nah?

Was mich zum Thema MM (Multi-Master-Replication) oder politisch korrekt Multi-Primary, Write-Anywhere etc. bringt…

PostgreSQL-Umsteiger von MySQL, MariaD, CockroachDB, Oracle,… fragen immer wieder (z.B. im Telegram-/Slack-Channel, den Mailinglisten, Seminaren), wie man denn einen MM-Cluster aufbaut.

Der Dialog läuft üblicherweise so ab:
Q: “Ich komme von und habe dort einen Multimaster-Cluster. Wie baue ich dasselbe mit PostgreSQL auf?”
A: “Gar nicht. Wahrscheinlich willst du das aber auch gar nicht.”
Q: “Doch, doch. Ich brauche die Hochverfügbarkeit und die Performance (ein Knoten allein schafft meine Schreiblast nicht)!”
A: “MM kostet Performance und verbessert deine Verfügbarkeit nicht wirklich. Benutze einfach eine klassische Replikation, ggfs. mit Lese-Lastverteilung.”

Verdeutlichen wir uns kurz, was es für eine Datenbank bedeutet, auf mehr als einem Clusterknoten schreibenden Zugriff zu erlauben:

  • Sequenzen (für Primärschlüssel bzw. “auto-increment/identity” Spalten) müssen synchronisiert werden, damit keine IDs doppelt vergeben werden
  • es muss sichergestellt werden, dass bei z.B. DELETE-Statements die referentielle Integrität erhalten bleibt; es muss also die Möglichkeit geben,
    clusterweit Datensätze oder sogar ganze Tabellen zu LOCKen
  • wenn auf mehreren Knoten derselbe Datensatz verändert wird, muss eine Konfliktbehandlung erfolgen. Wenn die nicht dem Zufall, sprich dem Replikations-Lag überlassen werden soll, werden clusterweite Transaktions-IDs benötigt
  • und zum Thema Schreiblast: Ziel ist es ja weiterhin, einen einheitlichen und konsistenten Datenbestand zu haben, die Schreiblast pro Knoten bleibt also unverändert!

Je mehr Knoten beteiligt sind, desto mehr Koordination ist erforderlich. Und jede dieser Aktivitäten benötigt Zeit, mindestens 1-2 Netzwerk-Roundtrips.

Je weiter meine Knoten voneinander entfernt sind, desto mehr Zeit und damit Performance geht allein schon durch pure Physik verloren!

Für PostgreSQL gibt bzw. gab es einige FOSS-Varianten, die MM beherrschen (Postgres-XC, Postgres-XL, BDR). Und 2ndQuadrant, deren derzeitiger Maintainer, sagt sinngemäß:

Die Einschränkungen und zusätzlichen Maßnahmen, die MM mit sich bringt bzw. erfordert, sind so eklatant, dass MM wahrscheinlich nie Einzug in den PostgreSQL-Core kommen wird. Und so ein Cluster ist explizit nicht für Hochverfügbarkeit gedacht, sondern um z.B. logisch weitgehend voneinander getrennte Datenbestände, z.B. Verkäufe und Lagerbestände in verschiedenen Ländern, in den jeweils anderen Standorten verfügbar zu machen.

Der Aufwand ist also durchaus erheblich, und mit ein klein wenig Nachdenken sollten sich die Zusammenhänge und Gründe auch jedem Interessenten erschließen.

Oder man macht sich einfach keine Gedanken und setzt bidirektionale Replikation auf, “was soll schon passieren” und “war ganz einfach, mit diesem Tutorial aus dem Netz”…

Multimaster-Clustering ist ein wenig wie im Dunkeln, bei Regen, mit 250 km/h über die Autobahn zu fahren. Als Beifahrer…

  1. Man wünscht sich auf jeden Fall, dass die Person am Steuer nicht nachtblind (sprich: “has RTFM”) ist
  2. das Auto und seine Grenzen sehr genau kennt (pun intended!)
  3. ein oder besser mehrere Fahrsicherheitstrainings hinter sich hat.

Und trotzdem ist man letztlich Faktoren überlassen, auf die man keinen Einfluss hat. Meist geht es ja auch gut…

Hier schließt sich der Kreis zur Bandsäge von oben, denn gewisse DBMS (Datenbank Management Systeme) stehen ja schon ohne bidirektionale Replikation nicht unbedingt im Ruf, Datenintegrität besonders hoch zu bewerten (https://bugs.mysql.com/bug.php?id=11472).

Zur Performance:

Ich erinnere mich, dass Oracle seinerzeit für ihr RAC angegeben hat, dass jeder neue Knoten 90% der Leistung des vorherigen Knotens bringt*. Was ein sensationell guter Wert wäre, aber eben alles andere als lineare Skalierung (linear steigt lediglich der Preis…):


WITH perf AS (
SELECT nodes, 0.9^(nodes-1) AS nextnodes_performance
FROM generate_series(1,8) AS nodes
)
SELECT *
,avg(nextnodes_performance) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
AS average_performance
,nodes * avg(nextnodes_performance) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
AS overall_performance
FROM perf;
┌───────┬───────────────────────┬────────────────────────┬────────────────────────┐
│ nodes │ nextnodes_performance │ average_performance │ overall_performance │
├───────┼───────────────────────┼────────────────────────┼────────────────────────┤
│ 1 │ 1.0000000000000000 │ 1.00000000000000000000 │ 1.00000000000000000000 │
│ 2 │ 0.9000000000000000 │ 0.95000000000000000000 │ 1.90000000000000000000 │
│ 3 │ 0.8100000000000000 │ 0.90333333333333333333 │ 2.70999999999999999999 │
│ 4 │ 0.7290000000000000 │ 0.85975000000000000000 │ 3.43900000000000000000 │
│ 5 │ 0.6561000000000000 │ 0.81902000000000000000 │ 4.09510000000000000000 │
│ 6 │ 0.5904900000000000 │ 0.78093166666666666667 │ 4.68559000000000000002 │
│ 7 │ 0.5314410000000000 │ 0.74529014285714285714 │ 5.21703099999999999998 │
│ 8 │ 0.4782969000000000 │ 0.71191598750000000000 │ 5.69532790000000000000 │
└───────┴───────────────────────┴────────────────────────┴────────────────────────┘

* vielleich waren es auch 80% oder 95% oder die Aussage war einfach kompletter Humbug?

Zur Verfügbarkeit:

Ein anderes gerne vorgebrachtes Argument, z.B. auf galeracluster.com ist “Transparent to Applications: Required no or minimal changes to the application”.

Ich habe mich immer gefragt, wie Oracle beim RAC das TCP-Protokoll aushebelt, eine Netzwerkverbindung bricht nun einmal ab, wenn der Host offline geht. Bis mir mal jemand gesteckt hat, dass man auf ein RAC mit einer anderen Client-Bibliothek zugreift. Mit anderen Worten: der Verbindungsabbruch wird nicht an das Programm durchgereicht, sondern das Statement (bzw. die Transaktion?) wird auf einer neuen Verbindung wiederholt. Ich unterstelle einfach mal, dass Galera etc. ähnlich arbeiten.

Können das die modernen Connection-Pools, ORMs etc. nicht auch alle schon mehr oder weniger selbsttätig oder mit “minimal changes”? Eine Applikation sollte es auf jeden Fall können. Aber gut, es sollte auch keine Applikationen mehr geben, die mehr als ein Statement außerhalb einer Transaktion durchführen…!

Auf jeden Fall rücken solche Aussagen die entsprechenden Produkte m. E. in die Nähe eines Worts, das im Titel dieses Beitrags steht…

Der klassische Ansatz:

Der Vollständigkeit halber sei noch kurz umrissen, wie eine hochverfügbare Cluster-Lösung mit PostgreSQL oder auch anderen DBMS aussehen kann.

  • ein Knoten (“Primary” oder non-PC “master”, P) erlaubt lesende und schreibende Zugriffe (R/W)
  • n andere Knoten (“Secondaries” oder “slaves”, S1,…,Sn) erhalten die Änderungen von P und erlauben lesende Zugriffe (RO)
  • zusätzlich landen alle Änderungen in einem Archiv, aus dem jeder beliebige Zeitpunkt wieder hergestellt werden kann (PITR)
  • Sn können synchron oder asynchron replizieren (bei synchroner Replikation sollte n > 1 sein!)

Der Zugriff auf den Knoten P kann auf verschiedene Arten erfolgen:

  • Clients erhalten die IPs aller Knoten und können selbst festlegen, ob sie Schreib-Lesezugriff benötigen
  • P hat eine zusätzliche virtuelle IP-Adresse, unter der er angesprochen wird (gerne zusammen mit P in einer Resource-Group der eingesetzten HA-Lösung)
  • ein vorgeschaltetes Tool, z.B. HAProxy weiß, welcher Knoten gerade P ist

Zusätzlich können Clients entscheiden, ob ihre jeweilige Aktivität nur lesend oder schreibend ist und sich entsprechend mit P oder einem Sn unterhalten, um die Gesamt-Performance zu verbessern.

Manche Pooler, z.B. PGPool-II können diese Unterscheidung sogar selber mehr oder weniger gut treffen. Vorsicht aber mit z.B. schreibenden Triggern!

Dass eine Client-Software eine halbwegs funktionale Fehlerbehandlung haben sollte, vielleicht sogar mit ein wenig Wartezeit vor’m retry, versteht sich von selbst.

Multi-Master-Datenbankcluster bringen einige Aspekte mit, die zu wenig oder zumindest selten beachtet werden. Manche sind ausgesprochen gefährlich!

Eine klassische Replikationslösung bietet annähernd dieselbe Verfügbarkeit, i.A. deutlich bessere Performance und bereitet auf Dauer wesentlich weniger Kopfschmerzen.

Wie man ein solches Cluster aufbaut, lernt man in unserem Kurs PostgreSQL Fundamentals.

Gunnar “Nick” Bluth hat seine Liebe zu relationalen Datenbanken Ende des letzten Jahrtausends entdeckt. Über MS Access und MySQL 3.x landete er sehr schnell bei PostgreSQL und hat nie zurückgeschaut, zumindest nie ohne Schmerzen. Er verdient seine Brötchen seit beinahe 20 Jahren mit FOSS (Administration, Schulungen, Linux, PostgreSQL). Gelegentlich taucht er auch tiefer in die Programmierung ein, so als SQL-Programmierer bei der Commerzbank oder in App-Nebenprojekten.

Partition voll, Wo?

Dieses Problem kennt bestimmt jeder SysAdmin, das Monitoring meldet Partition fast vollgelaufen, gut die Partition weiß man schon mal, laut Monitoring Icinga


Das heißt aber noch nicht automatisch WO die Partition vollgelaufen ist. In Zeiten wo Storage-Systeme, SSD’s und Speichermedien immer größer werden, werden auch die Dateien immer größer und Speicherplatz nicht unendlich ist, werde ich heute mal mit ein paar einfachen Shell-Kommandos auf einem Linux-System zeigen, wie schnell man das Verzeichnis ermittelt, das vollgelaufen ist.
Tool meiner Wahl ist dafür:
du -> Disk Usage
Größe gesamt summiert
du -hs /
49G /

Möchte ich mir einen Überblick über das File-System mit allen Ordnern sehen, damit ich weiß in welchen dieser Ordner am vollsten ist:
du -hx / -d1
36M /etc
43M /root
47G /var
3,2M /tmp
1,4G /usr
300M /boot
1,9M /home
0 /media
0 /mnt
22M /opt
0 /srv
48G /

Der Schalter -h (human readable) und -d1 (nur die erste Ordner-Reihe ohne Unterverzeichnisse), Schalter -x, (nur in diesem Filesystem suchen)

Erkenntnis: Aha, im Verzeichnis /var sind sehr viele Daten, also Schlussfolgerung, in diesem Verzeichnis weiter suchen.
# du -hx /var -d1
4,0K /var/tmp
31G /var/lib
211M /var/log
16G /var/adm
119M /var/cache
8,0K /var/db
47G /var

Das ganze kann ich jetzt soweit fortführen, bis ich das gewünschte Verzeichnis gefunden habe und entscheiden, was gelöscht / ausgelagert werden kann oder welche Anwendung ich optimieren muss, um den Speicherplatz wieder freizugeben.

In meinem Fall, war es die MySQL-Datenbank, die mir hier den Platz raubt.
# du -hx /var/lib/ -d1 | grep mysql
31G /var/lib/mysql

Lösung: z.B. VM / Partition vergrößern, Datenbank auslagern auf externen Storage.

Dieses Tool kann noch mehr, am besten ausprobieren(learining by doing), die Man-Page man du verrät noch mehr.

Wir bieten auch Trainings zu vielen Themen rund um Open Source an, Schau mal rein.

Johannes Carraro
Johannes Carraro
Support Engineer

Bevor Johannes bei NETWAYS anheuerte war er knapp drei Jahre als Systemadministrator in Ansbach tätig. Seit Februar 2016 verstärkt er nun unser Managed Services Team als Systems Engineer. In seiner Freizeit spielt Johannes E-Gitarre in einer Metalband, bastelt an Linux Systemen zuhause herum und ertüchtigt sich beim Tischtennisspielen im Verein, bzw. Mountainbiken, Inlinern und nicht zuletzt Skifahren.

Die richtigen Alarmierungslösungen für eure IT-Infrastruktur

Jeder wünscht sich für seine IT-Infrastruktur einen störungsfreien Ablauf. Um hier immer sorgenfrei die Umgebungsbedingungen zu prüfen und zu überwachen, ist es wichtig, die richtigen Alarmierungslösungen zu haben. Der Hersteller HW group hat sich auf das Thema Umgebungsmonitoring spezialisiert und bietet eine Vielzahl an Lösungen für Alarmweiterleitung und Störmeldungen.

Eine der gängigsten Lösungen ist das HWg-Ares12 GSM-Thermometer. Das Ares ist ein GSM-gestütztes Überwachungssystem. Es ist ideal für die Überwachung von abgelegenen Bereichen ohne Internetzugang. Es kann bei einer vordefinierten Schwellwertüberschreitung SMS versenden, Anrufe tätigen (ruft nur an, keine Ansage) oder E-Mails senden. Zusätzlich sind die Daten noch im SensDesk-Portal abrufbar (mind. GPRS Datentarif erforderlich).
Das Ares 12 hat 2 Sensoranschlüsse und kann bis zu 14 logische Sensoren verwalten (entweder mit Kombisensor oder T-Box) und zwei digitale Eingänge. Das Gerät wird einfach und ohne zusätzliche Software per USB oder aus dem Online-Portal SensDesk konfiguriert.

Wer alles gleich im Set haben möchte, kann sich das STE2 ansehen, dies ist ein WLAN– und netzwerkfähiges Überwachungsgerät mit 2 Anschlüsse für Temperatur- und Luftfeuchtesensoren (3 Sensoren maximal möglich – Einsatz von T-Box2) und 2 integrierte potentialfreie Kontakte. Das Gerät hat volle SNMP Kompatibilität und ist DHCP- fähig. Die Stromversorgung erfolgt hier über das Netzteil oder PoE.
Dieses Set enthält bereits einen Temperatursensor mit 3m Länge.

Immer noch unsicher was in eure Umgebung passt? Ruft uns im NETWAYS Online Shop an, wir helfen euch gerne weiter! Auf unserer Demowebseite könnt ihr ebenfalls noch mehr über die Funktionalität des STE2 erfahren und ausprobieren.

 

 

 

MySQL-Datenbanken verwalten mit Sequel Pro

Eines meiner meist genutzten Apps am Mac ist Sequel Pro. Das kann man kennen, muss man aber nicht. Daher liest man – wenn man möchte – in den folgenden Zeilen eine kurze Vorstellung.
(mehr …)

Florian Strohmaier
Florian Strohmaier
Senior UX Designer

Mit seinen Spezialgebieten UI-Konzeption, Prototyping und Frontendentwicklung unterstützt Florian das Dev-Team bei NETWAYS. Trotz seines Design-Backgrounds fühlt er sich auch in der Technik zuhause. Gerade die Kombination aus beidem hat für ihn einen besonderen Reiz.

ReaR mal anders

Bereits vor ein paar Jahren habe ich einen Blogpost zur Disaster Recovery Lösung Relax-and-Recover (kurz: ReaR) geschrieben. Vor Kurzem hatte ich ein Anwendungsbeispiel, in dem ich ebenfalls auf die in vielen Fällen bewährte Lösung zugreifen wollte: Ziel war es, unsere Schulungsnotebooks auch außerhalb des Headquarters nach erfolgtem Training möglichst automatisch auch für nichttechnische Anwender auf den Auslieferungszustand zurück zu setzen. Bisher werden die Notebooks mittels Foreman jedes Mal neu provisoniert, was v.a. einiges an unnötiger Zeit frisst.
Demzufolge lag der Ansatz nahe, das mit ReaR zu lösen. Da ich auf zusätzliche Medien wie USB-Sticks verzichten wollte und das Ganze auch offline funktionieren soll, bleibt nur die lokal verbaute Platte als Speicherort für Rescueimage und Backupdateien übrig. Zudem sollte noch ein Booteintrag für die Rücksetzung erstellt werden. Die ReaR-Konfiguration in “/etc/rear/local.conf” dazu sieht so aus:
OUTPUT=ISO
OUTPUT_URL=file:///backupshare
BACKUP=NETFS
BACKUP_URL=file:///backupshare
GRUB_RESCUE=1

Problem dabei ist, dass die Backupdateien als Archiv (backup.tar.gz) in einer der Partitionen auf der Festplatte (/dev/sdaX) liegen. Beim Wiederherstellungsvorgang löscht ReaR leider standardmäßig alle Laufwerksinformationen und erstellt diese neu, sodass die Backupdateien in dem Fall verloren gehen. Mit dem Parameter PRE_RECOVERY_SCRIPT kann man den Backupshare zumindest mounten und das Backuparchiv in das Filesystem des Rescueimages kopieren.
Ein anderer Ansatz ist die Backupdateien direkt im IOS-Image des Rescuesystems abzulegen, das geht mit folgender Konfiguration:
OUTPUT=ISO
OUTPUT_URL=file:///backupshare
BACKUP=NETFS
BACKUP_URL=iso://backup
GRUB_RESCUE=1

Auch hier zeigen sich allerdings in der Praxis Probleme. Je nach Größe der Backupdateien wächst das ISO-Image dadurch natürlich entsprechend an. Außerdem verwendet das von uns auf den Schulungsnotebooks eingesetzte Betriebssystem, CentOS 7, zum Erstellen des ISO’s in der Standardinstallation genisoimage. Hier besteht eine feste Grenze von 4GB pro Image. Diese lässt sich zwar mit ISO_MAX_SIZE bei ReaR fix konfigurieren, führt aber dazu, dass die Backupdateien im Rescueimage aufgrund des Abbruchs eben nicht vollständig enthalten sind. Indem man genisoimage gegen das nachzuinstallierende xorriso austauscht und den symbolischen Link für mkisofs anpasst, lässt sich die Begrenzung jedoch umgehen. Je nach Größe der Backupdateien macht das allerdings oft nur bedingt Sinn.
In unserem Fall hat sich gezeigt, dass ReaR für das spezielle Anwendungsszenario der lokalen Wiederherstellung von Notebooks leider nicht die ideale Wahl ist, da die Software ursprünglich für andere Anwendungszwecke konzipiert wurde. Die Suche nach der optimalen Lösung dauert daher aktuell noch an…

Markus Waldmüller
Markus Waldmüller
Lead Senior Consultant

Markus war bereits mehrere Jahre als Sysadmin in Neumarkt i.d.OPf. und Regensburg tätig. Nach Technikerschule und Selbständigkeit ist er nun Anfang 2013 bei NETWAYS als Lead Senior Consultant gelandet. Wenn er nicht gerade die Welt bereist, ist der sportbegeisterte Neumarkter mit an Sicherheit grenzender Wahrscheinlichkeit auf dem Mountainbike oder am Baggersee zu finden.