DevEnv: Docker und AppServer

Docker ist aktuell in aller Munde und das Ökosystem um Docker wächst kontinuierlich weiter. Somit lohnt sich mal ein genauerer Blick. Ich werde hier schildern, wie ein Docker Image mit einem Application Server erstellt werden kann. Als Application Server werde ich WildFly verwenden, jedoch ist jegliche andere Software denkbar.

Überblick

Im einem Softwareprojekt strebt man an, dass alle Umgebungen (Entwicklung, Integration und Produktion) die selben Systeme, Versionen und Einstellungen besitzen. Dies kann man mit sehr genauen Installations- und Konfigurations-Anleitung bewerkstelligen. Jedoch ist der manuelle Aufwand hoch, genauso auch sehr fehleranfällig. Eine andere Möglichkeit ist es, immer das selbe VM Image zu verwenden. Dies kann einheitlich erstellt werde und dann auf allen Umgebungen verwendet werden. Ein solches VM Image beinhaltet jedoch neben der für das Projekt benötigte Software auch mind. das Betriebssystem. Dieser Sachverhalt verursacht ein verhältnismäßig zu großes Artefakt, was dann auch nicht schnell mal auf einer neuen Umgebung kopiert und bereitgestellt werden kann. Hier kommt dann Docker ins Spiel [1]. Docker kapselt die Software in einem Container und erwartet auf dem Host-System eine Docker Engine um den Container zum Laufen zu bringen. D.h. der Container beinhaltet nur die „projekt-relevanten“ Softwareprodukte. Dieser Container ist leichtgewichtiger als ein komplettes VM Image. Einmal erstellt, können diese Docker Container überall eingesetzt werden können, wo ein Docker Engine zur Verfügung steht – für Windows-User gibt es hier auch eine Lösung ;-).

Im weiteren Verlauf werde ich die Einrichtung eines Docker Container beschreiben, welches einen Application Server (hier: WildFly) beinhaltet. Dieser Container wird nur den App Server und die restliche Software beinhalten (JDK etc.). In weiteren Beiträgen werde ich dann darauf eingehen, wie diese Docker Instanz mit einer Docker DB Instanz verknüpft werden kann, oder wie die Konfiguration, welche umgebungsabhängig ist, verwaltet werden kann. Das Ökosystem ist hier umfangreich, so dass hier mehrere Beiträge folgen werden.

Zusammenfassend für diesen Beitrag bedeutet das Ziel

  • Überblick in Docker
  • Erstellung eines Docker Image mit Wildfly

Vorbedingungen

  • Ubuntu als Host-System

Ausgrenzungen

Installation

Um Docker zu verwenden, benötigt man für das Host-System, wo Docker laufen soll eine entsprechende Docker Engine. Mittels folgendem Befehl wird die aktuellste Version (1.6.0, Stand April 2015, hier in Ubuntu 14.10) installiert:

$ wget -qO- https://get.docker.com/ | sh

Nach der Installation kann geben wir testweise die Version aus:

$ sudo docker --version
Docker version 1.6.0, build 4749651

Wie man erkennen kann, wird für die Nutzung sudo-Rechte benötigt. Um Docker mit einem Nicht-Root-User zu benutzen, müssen wir den User zur „docker“-Gruppe hinzufügen:

sudo usermod -aG docker vmuser

Einrichtung

Bevor wir mit der Einrichtung eines neuen Docker Containers beginnen, noch ein paar einleitende Worte. Im Docker Kontext gibt es mindestens folgende zwei Komponenten:

  • Image: Ein Docker Image ist die Komponente, welches das System/Applikation beschreibt und alle Softwareinstallationen beinhaltet. Ein Image kann als Template angesehen werden, welches für die Erstellung eines Docker Container verwendet wird. Jegliche Änderungen an ein existierendes Image resultiert in ein neues Image.
  • Container: Ein Docker Container, wird aus einem Image erstellt und symbolisiert die laufende Plattform, welches die Applikation beinhaltet. Jeder Docker Container kann als geschlossenes/isoliertes System angesehen werden.

Ein Docker Image besteht aus einer Menge von Layer. Solch ein Layer wird durch jegliche Änderung/Aktion erstellt. D.h. eine Befehl-Ausführung, Variablen-Änderung oder das Hinzufügen einer Datei führt zu einem neuen Layer. Die Summe dieser Layer beschreiben das Docker Image, welches in einem „Dockerfile“ festgehalten werden. Um ein Docker Image zu aktualisieren, reicht es, nur die neusten Layer mitzuteilen, anstatt das ganze Image nochmal zu übertragen. Das hat den großen Charme im Gegensatz zu einem VM Image.

Wir werden im weiteren Verlauf ein eigenes Docker Image erstellen. Als Basis brauchen wir ein minimales Betriebssystem z.B. Ubuntu, CentOS. Unter Docker Hub [2] kann nach existierenden Images gesucht werden. Diese Systeme sind jedoch gut über 100MB groß. Mit busybox [3] oder der Weiterentwicklung alpine [4] bekommen wir ein minimales System mit einer Größe von 5MB. Nachtrag: Alpine scheint aktuell noch nicht optimal zu laufen. Deswegen wird im weiteren Verlauf wieder busybox verwendet.

Das Docker Image wird mittels der Konfigurationsdatei Dockerfile beschrieben:

$ mkdir -p docker/wildfly
$ cd docker/wildfly/
$ touch Dockerfile
# Use busybox as base OS/image
FROM progrium/busybox

MAINTAINER Hafid Haddouti <code@haddouti.com>

# Install cUrl
RUN opkg-install curl

# change into the tmp directory
RUN mkdir -p /tmp/a1
WORKDIR /tmp/a1

# create base directory for Wildfly
RUN mkdir -p /opt/jboss

# #############################################
# JDK

# Java variables for JDK 8
ENV JAVA_VERSION_MAJOR 8
ENV JAVA_VERSION_MINOR 45
ENV JAVA_VERSION_BUILD 14
ENV JAVA_BASE_URL http://download.oracle.com/otn-pub/java/jdk/${JAVA_VERSION_MAJOR}u${JAVA_VERSION_MINOR}-b${JAVA_VERSION_BUILD}/
ENV JAVA_FILE jdk-${JAVA_VERSION_MAJOR}u${JAVA_VERSION_MINOR}-linux-x64
ENV JAVA_CHECKSUM 1ad9a5be748fb75b31cd3bd3aa339cac

# Retrieve and install JDK
RUN curl -vkLH "Cookie: oraclelicense=accept-securebackup-cookie" ${JAVA_BASE_URL}${JAVA_FILE}.tar.gz -o ${JAVA_FILE}.tar.gz
RUN echo "${JAVA_CHECKSUM} ${JAVA_FILE}.tar.gz" > ${JAVA_FILE}.md5.txt
RUN md5sum -c ${JAVA_FILE}.md5.txt \
&& gunzip ${JAVA_FILE}.tar.gz \
&& tar -xf ${JAVA_FILE}.tar -C /opt \
&& rm *-linux-x64.tar* \
&& ln -s /opt/jdk1.${JAVA_VERSION_MAJOR}.0_${JAVA_VERSION_MINOR} /opt/jdk

# Set JAVA environment
ENV JAVA_HOME /opt/jdk
ENV PATH $PATH:$JAVA_HOME/bin

VOLUME ["/opt/jdk"]

# #############################################
# Wildfly

# Wildfly variables
ENV WILDFLY_VERSION 9.0.0.Beta2
ENV WILDFLY_FILE wildfly-${WILDFLY_VERSION}
ENV WILDFLY_DIR /opt/jboss/wildfly

# Retrieve and extract Wildfly into the WILDFLY_DIR
RUN mkdir -p ${WILDFLY_DIR}
RUN curl http://download.jboss.org/wildfly/${WILDFLY_VERSION}/${WILDFLY_FILE}.tar.gz | gunzip | tar -x \
&& cp -R ${WILDFLY_FILE}/* ${WILDFLY_DIR}

# Set the JBOSS_HOME env variable
ENV JBOSS_HOME ${WILDFLY_DIR}

# Expose the ports: 8080 default http, 9990 management port
EXPOSE 8080
EXPOSE 9990

# Add an admin user and pw
RUN /opt/jboss/wildfly/bin/add-user.sh admin admin2015 --silent

# Overwrite the default entrypoint and command/parameters with the wildfly
ENTRYPOINT ["/opt/jboss/wildfly/bin/standalone.sh"]
CMD ["-b", "0.0.0.0", "-bmanagement", "0.0.0.0"]

# ##############################################
# Clean up
RUN rm -R /tmp/a1

Mit folgendem Befehl wird dann das Docker Image gebaut, in dem das Dockerfile verwendet wird:

$ docker build -t dev.center-of.info/wildfly:v1 .

Dieser Befehl kann mehrmals aufgerufen werden. Docker merkt, welche Anweisungen neu hinzugekommen sind und führt dann nur noch diese aus, die anderen werden aus dem Cache verwendet. Dies ist in der Ausgabe zu erkennen:

Step 4 : ENV JAVA_VERSION_MAJOR 8
---&gt; <strong>Using cache</strong>
---&gt; <strong>1043413c0ce5</strong>

Gestartet kann nun unsere neues Docker Image wie folgt:

$ docker run --rm -it -p 8080:8080 -p 9990:9990 dev.center-of.info/wildfly:v1

`Dabei bedeuten die Parameter:

  • -it: interaktiv mit pseudo TTY
  • -p: Publiziert den Port vom Container zum Host
  • --rm: Löscht den Container, falls der schon existiert. Das reduziert während der Entwicklung die Anzahl der erstellten Container.

Wildfly ist nun im Browser unter http://localhost:8080 bzw. http://localhost:9990 zu erreichen. Für die Management-Konsole wurde admin:admin2015 als Nutzer hinterlegt.

Fazit

Das hier aufgeführte Dockerfile habe ich real in 3 Teile aufgeteilt

  • base: welches das Betriebssystem und allg. Informationen beinhaltet
  • jdk8: Ein Docker Image, welches auf base basiert und Oracle JDK 8 bereitstellt.
  • Wildfly9: Ein Docker Image, welches auf jdk8 basiert und WildFly 9 bereitstellt.

Mit dieser Modularisierung lassen sich die Docker Images für weitere Projekte wiederverwenden. So kann in wenigen Schritten ein wiederverwendbarer Docker Container bereitgestellt werden.

Informationen bzgl. Deployment und Konfiguration folgen in den nächsten Beiträgen.

Die Dockerfiles sind bei Bitbucket zu finden:

https://bitbucket.org/playsphere/docker-files.git

Referenzen

[1] Docker: https://www.docker.com/

[2] Docker Hub: https://hub.docker.com/

[3] BusyBox: https://github.com/progrium/busybox

[4] Docker Alpine: https://github.com/gliderlabs/docker-alpine

[5] Blog-Eintrag von Peter Roßbach zu Docker und Microservices: http://www.infrabricks.de/blog/2014/12/19/docker-microservice-basis-mit-apache-tomcat-implementieren/

comment

Comments

arrow_back

Previous

DevEnv: Docker und Elasticsearch

Next

JEE: Arquillian
arrow_forward