docker run -d --rm -p 8080:8080 effectivetrainings/docker-health
Die Sourcen sind unter : https://github.com/effective-docker/docker-healthcheck.git
Seit Docker 1.12 gibt es die Möglichkeit, Health-Checks für Container zu definieren
Health-Checks sind vor allem zusammen mit Swarm hochinteressant: https://docs.docker.com/engine/reference/builder/#/healthcheck
Anwendungen zu deployen ist einfach, und Docker Container zu starten ist noch einfacher. Ein einfaches docker run und schon sind wir live. Das funktioniert auch eine ganze Zeit bis zum ersten Problem - sei es nun eine Lastspitze, ein Bug, ein Amoklaufender Prozess oder einfach ein temporäres Netzwerkproblem. Die Anwendung ist nicht erreichbar, unsere E-Commerce-Plattform ist für die Kunden nichts weiter als eine 500 - Wir arbeiten an einer Lösung und bis wir diese gefunden haben ist die Bestellung meistens schon bei der Konkurrenz gelandet.
Was machen wir also? Wir designen unser System so sicher und gut, dass einfach keine Fehler auftreten? Das klingt verlockend, ist aber schlichtweg nicht möglich und sogar gefährlich, da wir uns dann in falscher Sicherheit wiegen. Wenn dann doch ein Problem auftritt sind wir nicht dafür gewappnet.
Tipp
|
Besser ist es, die Anwendung gleich von Anfang so zu bauen dass Fehler akzeptiert werden und die Anwendung entsprechend darauf reagiert. Resilient Software ist hier das wichtige Stichwort. |
Die Anwendung ist eine Spring Boot Anwendung mit Actuator.
Wichtig
|
Spring Boot Actuator bietet eine ganze Menge Endpoints um den Zustand der Anwendung zu überwachen. Die Anwendung gibt beispielsweise über den Endpunkt "/blog/health" den Status zurück, ob sie healthy ist. Healthy ist natürlich abhängig davon ob die verwendeten Upstream Services und die Infrastruktur-Komponenten verfügbar sind. Beispielsweise sollte die Anwendung nur healthy sein, wenn eine verwendete Datenbank vorhanden ist. |
Als nächstes starten wir die Anwendung.
docker run -d --rm -p 8080:8080 effectivetrainings/docker-health
Nach kurzer Zeit können wir über einen cURL die Healthyness der Anwendung abfragen.
{
"status":"UP",
"static":{"status":"UP"},
"diskSpace": {"status":"UP","total":67371577344,"free":61355114496,"threshold":10485760}
}
Hier sehen wir die HealthChecks der Anwendung. Selbst geschrieben ist der Static-Health-Check, mit dem die Anwendung einfach statisch in einen Healthy- oder Unhealthy-Zustand gesetzt werden kann.
curl "http://localhost:8080/environment/health?status=false"
curl "http://localhost:8080/health"
{
"status": "DOWN",
"static":{"status":"DOWN"},
"diskSpace":{"status":"UP","total":67371577344,"free":61354893312,threshold":10485760}
}
Das Problem ist, der Container ist per Definition bisher immer noch gesund, obwohl die Anwendung vielleicht nicht mal antwortet. Die Docker Engine weiß nichts vom Health-Status der Anwendung. Der Container selbst hat bis jetzt noch keinen Health-State.
Damit die Engine auf eine Änderung im Health-Status reagieren kann muss der Container selbst auch wissen, ob er Healthy ist oder nicht. Und genau hier kommen die neuen Health-Checks ins Spiel.
Health Checks sind essentiell für die Anwendung - sowohl für das Monitoring, den Betrieb als auch für beispielsweise Service Discovery in verteilten Umgebungen. Dabei kann sich ein Health-Check ganz unterschiedlich gestalten, beispielsweise kann es für eine Oracle ein einfaches SQL Statement sein wie select count from dual, ein HTTP-Status Code eines Actuator-Endpoints oder ein bash-script das nur prüft, ob ein bestimmter Prozess noch lebt.
Für jeden Container kann separat definiert werden, wie ein Health-Check ausgeführt werden kann. Für die Überwachung der Spring-Boot Anwendung beispielsweise bietet sich die Abfrage des Health-Endpoint mittels cURL an.
Für den Health-Check sind folgende Parameter für docker run hinzugekommen. Analog könnten die Health-Checks im Dockerfile definiert werden.
--health-cmd string (1)
--health-interval duration (default 0s) (2)
--health-retries int (3)
--health-timeout duration (default 0s) (4)
Kommando für den Health-Check (Bash, Python..)
Zeit zwischen Health-Checks, standardmäßig alle 30 sekunden.
Retries bevor ein Container als unhealthy deklariert wird
Timeout für den Health-Check
Ein einfacher Health-Check für die Spring-Boot Anwendung wäre beispielsweise folgender cURL-Aufruf.
Tipp
|
Ein Health-Check liefert einen boolschen Wert - 0 oder 1. 0 ⇒ healthy, 1 ⇒ unhealthy. |
--health-cmd curl -f http://localhost:8080/health | exit 1 (1)
Mit curl -f zwingt curl, den Fehlercode 1 im Falle eines HTTP-Errors zurückzuliefern, andernfalls den Code 0.
Starten wir den Container mit aktiviertem Health-Check.
docker run -p 8080:8080 -d --name health-check --rm --health-cmd "curl -f http://localhost:8080/health || exit 1" effectivetrainings/docker-health
Die Instruktion im Dockerfile wäre analog.
HEALTHCHECK CMD curl -f http://localhost:8080/health || exit 1;
Achtung
|
Achtung - der Health-Check wird im Container ausgeführt, nur der interne Container ist also wichtig - nicht der gemappte. |
Mit docker inspect healthcheck sehen wir jetzt einen Health-Status für den Container.
"Health": {
"Status": "starting",
"FailingStreak": 0,
"Log": []
}
Parallel ist es recht spannend, sich den Docker Event Stream ausgeben zu lassen.
docker events
2017-01-03T22:10:18.137182990+01:00 container exec_start: /bin/sh -c curl -f http://localhost:8080/health 1a2bfc354a3eb75c41e8822620c85b7920ba0ebb9103aa481b090da6ce137037 (image=effectivetrainings/docker-health, name=health-check)
2017-01-03T22:10:18.527115972+01:00 container health_status: healthy 1a2bfc354a3eb75c41e8822620c85b7920ba0ebb9103aa481b090da6ce137037 (image=effectivetrainings/docker-health, name=health-check)
Was aber passiert jetzt, wenn wir den Container auf unhealthy setzen? Je nach eingestelltem Interval dauert es jetzt kurz, bis die Engine das Problem entdeckt.
curl "localhost:8080/environment/health?status=false"
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1a2bfc354a3e effectivetrainings/docker-health "java -jar /app.jar" 3 minutes ago Up 3 minutes (unhealthy) 0.0.0.0:8080->8080/tcp health-check
Ein Scheduler wie Docker Swarm könnte jetzt beispielsweise den Container einfach neustarten, in der Hoffnung, dass das hilft.
Mit Container-Health Checks können relativ einfach Checks implementiert werden, die einen Scheduler unterstützen können die richtigen Entscheidungen zu treffen.
Welcher Container wird jetzt neugestartet?
Wohin soll deployt werden
Docker Swarm beispielsweise macht sich den Health-Check zu Nutze und routet nur Requests zu Containern, die healthy sind. Außerdem versucht Swarm, Container neu zu deployen, wenn Sie in den Status unhealthy wechseln.
Wollen Sie mehr erfahren? Ich biete Consulting / Training für Docker. Schauen Sie doch mal vorbei!