Donnerstag, Dezember 08, 2016

Microservice bauen

Microservice bauen

In Anlehnung an das Microservices Lab von Eberhard Wolff am 8.11.2016 auf der W-JAX in München.

Minimaler REST-Service bauen

Einen Spring-Boot "Hello World!"-REST-Service bauen. Das kann man mit Spring Initializr machen oder per Hand.


Wenn das Projekt mit Spring Initialzr erzeugt wurde, dann einfach downloaden, entpacken und in eine IDE deiner Wahl importieren.

Anschließend eine Klasse für den REST-Service implementieren:

@RestController
@EnableAutoConfigurationpublic class HelloWorldController {

  @RequestMapping(path = "/")
  public String helloWorld() {
    return "Hello World!";  }

}

Dann das Projekt bauen und testen:

curl localhost:8080

Als Antwort sollte dann

Hello World!

kommen.

Jetzt wird der Microservice als JAR-Datei erzeugt mvn package, gestartet java -jar target/hello-world-0.0.1-SNAPSHOT.jar und nochmal getestet.

Referenzen


Monitoring einbauen

Spring Boot bietet ein gutes Monitoring mit Hilfe der vorbereiteten Acutators. Einfach in die POM einbauen.

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Anwendung neu starten und abfragen:

curl localhost:8080/health | json_pp

Dann sollte so etwas rauskommen:

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   105    0   105    0     0   6794      0 --:--:-- --:--:-- --:--:--  7000
{
   "diskSpace" : {
      "total" : 243045949440,
      "free" : 223517011968,
      "status" : "UP",
      "threshold" : 10485760
   },
   "status" : "UP"
}


Ein weiterer interessanter Endpoint ist:

curl localhost:8080/metrics | json_pp

Dort kann man ablesen, welcher Service wie oft aufgerufen wurde.

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   838    0   838    0     0    838      0 --:--:-- --:--:-- --:--:--  818k
{
   "nonheap.init" : 2496,
   "nonheap.used" : 50635,
   "httpsessions.active" : 0,
   "gc.ps_marksweep.time" : 32,
   "httpsessions.max" : -1,
   "gc.ps_marksweep.count" : 1,
   "counter.status.200.info" : 1,
   "counter.status.200.beans" : 1,
   "gauge.response.metrics" : 0,
   "classes" : 6134,
   "heap.init" : 131072,
   "threads.daemon" : 21,
   "gauge.response.health" : 8,
   "systemload.average" : -1,
   "counter.status.200.health" : 3,
   "threads.totalStarted" : 27,
   "threads" : 23,
   "threads.peak" : 23,
   "mem.free" : 224805,
   "nonheap.committed" : 53696,
   "gauge.response.beans" : 38,
   "heap.used" : 55770,
   "gc.ps_scavenge.time" : 77,
   "counter.status.200.metrics" : 4,
   "instance.uptime" : 982970,
   "nonheap" : 0,
   "uptime" : 985825,
   "processors" : 4,
   "gauge.response.info" : 19,
   "gauge.response.root" : 255,
   "counter.status.200.root" : 1,
   "classes.unloaded" : 0,
   "mem" : 331211,
   "heap.committed" : 280576,
   "classes.loaded" : 6134,
   "gc.ps_scavenge.count" : 8,
   "heap" : 1853440
}

Die Actuator-Endpoints gehören zu den sog. Management-Services. Diese Management-Services können bei Bedarf auch abgesichert und / oder auf einen anderen Port gelegt werden.

Referenzen


Dockerfile bauen

Annahme: Wir laufen unter Linux und Docker ist bereits installiert.

Jetzt können wir die Anwendung in ein Docker Image einpacken. Dazu benötigen wir ein Dockerfile, das wir in das Verzeichnis hello-world legen. Die Datei heißt Dockerfile und hat folgenden Inhalt:

FROM openjdk:8
ADD target/hello-world-0.0.1-SNAPSHOT.jar .
CMD /usr/bin/java -Xmx400m -Xms400m -jar hello-world-0.0.1-SNAPSHOT.jar
EXPOSE 8080


Beschreibung:
(1) FROM beschreibt das Docker Image von dem wir ausgehen. Docker Hub bietet eine Reihe von vorbereitetes Images an. Wir benötigen nur ein, das schon ein Java drin hat. Ich habe mir hier für OpenJDK 8 entschieden.
(2) ADD beschreibt was zusätzlich in den Container eingepackt werden soll. In diesem Fall unsere fertige Applikation.
(3) CMD ist das Kommando, das beim Start des Containers ausgeführt werden soll. Bei uns soll automatisch die Applikation gestartet werden.
(4) EXPOSE beschreibt welcher Port des Containers nach außen verfügbar sein soll.

Das Docker Image wird jetzt mit folgendem Kommando gebaut, wobei wir uns eine Verzeichnisebene außerhalb unseres hello-world Projekts befinden.

sudo docker build -t <tag> <verzeichnis>
sudo docker build -t helloworld hello-world 

Ist die folgende Schreibweise eine Abkürzung oder gehört sie zu einer anderen Docker Version?

sudo docker --build=<tag> <verzeichnis>
sudo docker --build=helloworld hello-world

Jetzt lassen wir uns zeigen, ob Docker unser Image kennt

sudo docker images

Dann sollte folgendes gezeigt werden:

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
helloworld          latest              e00d323fce30        57 minutes ago      657.9 MB
openjdk             8                   861e95c114d6        4 weeks ago         643.2 MB


Wollen wir nochmal anschauen, was alles in unserem Image drin ist, geht das mit

sudo docker history <tag>
sudo docker history helloworld

Dann erscheint bei mir:

IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
e00d323fce30        57 minutes ago      /bin/sh -c #(nop)  EXPOSE 8080/tcp              0 B                
6f7c12b9ddac        57 minutes ago      /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "/usr/   0 B                
298395009277        57 minutes ago      /bin/sh -c #(nop) ADD file:aa4a274215e0f5109f   14.67 MB           
861e95c114d6        4 weeks ago         /bin/sh -c /var/lib/dpkg/info/ca-certificates   418.2 kB           
<missing>           4 weeks ago         /bin/sh -c set -x  && apt-get update  && apt-   351.5 MB           
<missing>           4 weeks ago         /bin/sh -c #(nop)  ENV CA_CERTIFICATES_JAVA_V   0 B                
<missing>           4 weeks ago         /bin/sh -c #(nop)  ENV JAVA_DEBIAN_VERSION=8u   0 B                
<missing>           4 weeks ago         /bin/sh -c #(nop)  ENV JAVA_VERSION=8u111       0 B                
<missing>           4 weeks ago         /bin/sh -c #(nop)  ENV JAVA_HOME=/usr/lib/jvm   0 B                
<missing>           4 weeks ago         /bin/sh -c {   echo '#!/bin/sh';   echo 'set    87 B               
<missing>           4 weeks ago         /bin/sh -c #(nop)  ENV LANG=C.UTF-8             0 B                
<missing>           4 weeks ago         /bin/sh -c echo 'deb http://deb.debian.org/de   55 B               
<missing>           4 weeks ago         /bin/sh -c apt-get update && apt-get install    1.287 MB           
<missing>           4 weeks ago         /bin/sh -c apt-get update && apt-get install    122.6 MB           
<missing>           4 weeks ago         /bin/sh -c apt-get update && apt-get install    44.3 MB            
<missing>           4 weeks ago         /bin/sh -c #(nop)  CMD ["/bin/bash"]            0 B                
<missing>           4 weeks ago         /bin/sh -c #(nop) ADD file:41ea5187c50116884c   123 MB             


Jetzt starten wir unser Docker Image mit dem Kommando:

sudo docker run <tag>
sudo docker run helloworld

Dann sollten gleich die Logausgaben unserer Anwendung erscheinen. Am Ende der Logausgaben sehen wir:

Tomcat started on port(s): 8080 (http)

Damit ist jetzt der Port 8080 im Container gemeint. Wir können also nicht mit "curl localhost:8080" auf die Anwendung zugreifen, weil die Anwendung jetzt im Container läuft.

Wenn wir jetzt auf unsere Anwendung zugreifen wollen, müssen wir zuerst die Instanz finden und dann die IP-Adresse herausfinden. Um zu sehen, welche Docker Images laufen, benutzen wir

sudo docker ps

Damit bekommen wir die laufenden Docker Images gezeigt:

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
17ef7c5b7462        helloworld          "/bin/sh -c '/usr/bin"   22 seconds ago      Up 21 seconds       8080/tcp            sad_minsky

Die Container ID ist jetzt besonders wichtig. Nur damit können wir auf die laufende Docker Instanz zugreifen. Wir wollen nun herausfinden, auf welcher IP-Adresse diese Instanz läuft:

sudo docker inspect --format '{{ .NetworkSettings.IPAddress }}' 17ef7c5b7462

Ich bekomme als Ergebnis

172.17.0.2

Jetzt kann ich mit

curl 172.17.0.2:8080 

auf meine Anwendung zugreifen.

Jetzt kann ich meinen Container wieder stoppen, auch nur mit der Container ID.

sudo docker stop 17ef7c5b7462



Keine Kommentare: