Übung: Docker

Vorbereitung

  1. Auf dem eigenen Rechner: Installieren Sie Docker Desktop
  2. Linux-Labor: folgen Sie dieser Anleitung.

PHP-Applikation

In dieser Übung werden Sie eine eigene Applikation in ein Container-Image verpacken und als neuen Container starten.

  1. Dockerfile schreiben:

    FROM php:apache
    COPY src/ /var/www/html/
    
    
  2. Applikation schreiben (src/index.php):

    <? phpinfo() ?>
    
    
  3. Image bauen:

    $ docker build -t php-image .
    
  4. Container starten:

    $ docker run -d --name php-container --publish 80:80 php-image
    
  5. Applikation testen:

    $ curl http://$(docker-machine ip default):80
    
  6. Container stoppen:

    $ docker kill php-container
    

node.js Applikation

Ähnlich dem PHP-Container können Sie auch eine node.js-Applikation als Image paketieren und als Container ausführen.

Dockerfile schreiben

FROM node:alpine
WORKDIR /usr/src/app
ADD . /usr/src/app
CMD ["node","hello.js"]

Applikation schreiben:

var http = require("http");
var os = require("os");
var hostname = os.hostname();
const port = process.env.PORT || 3000;

http.createServer(function (request, response) {
  console.log(new Date().toISOString() + " " + request.method + " " + request.url);
  response.writeHead(200, {'Content-Type': 'text/plain'});
  response.end('Hello from ' + hostname + '\n');
}).listen(port);

console.log("Server listening on port " + port);

Image bauen

$ docker build --tag hello-node .

Container starten

$ docker run -it --rm --name hello-node-1 --publish 8081:8081 --env PORT=8081 hello-node

Applikation testen

$ curl http://$(docker-machine ip default):8081

Beobachten Sie in der Ausgabe des Containers, dass für jeden HTTP-Request eine neue Log-Zeile erzeugt wird.

Container stoppen

$ docker kill hello-node-1

Fragen zur Kontrolle

  1. Wie wirkt sich die Änderung der Variable PORT aus?

Multi-Container

Viele Anwendungen bestehen aus mehreren Containern. In dieser Übung werden wir eine Ruby-Applikation und einen Postgres-Container zusammen deployen.

Anleitung

Überprüfen Sie die Verfügbarkeit von compose als Teil der Docker-Installation:

$ docker compose version

sollte eine Ausgabe ähnlich der folgenden erzeugen:

Docker Compose version vX.Y.Z

Dockerfile schreiben

FROM ruby:alpine

RUN apk add --no-cache                         \
      build-base                               \
      git                                      \
      postgresql-dev                           \
  && rm -rf /var/cache/apk/*                   \
  && rm -rf /usr/local/lib/ruby/gems/*/cache/* \
  && rm -rf ~/.gem

RUN gem install bundler --no-document
RUN bundle config --global silence_root_warning 1

WORKDIR /app

# Get the app's source code @master.
# You should actually checkout a specific branch or tag in order to get reproducibility.
RUN git clone https://github.com/uhlig-it/journal.git /app
RUN bundle config set --local without 'development test'
RUN bundle config set jobs $(nproc)
RUN bundle install

CMD rake db:migrate && bundle exec ruby app.rb -o 0.0.0.0

Wir kapseln für diese Übung die gleiche Ruby-App wie in der Vagrant-Übung, aber diesmal in einem Container. Zur Vereinfachung der Übung wird beim Bau des Docker-Images der neueste Code (master-Branch) geklont. In der Realität sollten Sie stattdessen eine wohldefinierte Version (Commit-SHA oder Tag) auschecken.

docker-compose.yml schreiben

In dieser Datei beschreiben Sie das Zusammenwirken (‘Orchestrierung’) mehrerer Container. Legen Sie einen Container für die Datenbank und einen für die App an. Ergänzen Sie die Stellen, die mit TODO gekennzeichnet sind.

services:
  web:
    build: .
    # TODO: exposed port, environment variables
  db:
    image: 'postgres:alpine'
    # TODO: environment variables

Erster Start

  1. Starten Sie den Datenbank-Container mit:
  $ docker compose up -d db
  1. Starten Sie den Web-Container:
  $ docker compose up -d web
  1. Geben Sie die URL zur App auf der Konsole mit echo http://$(docker-machine ip default):4567 aus und öffnen Sie danach diese URL im Browser.

  2. Beobachten Sie die Logs, während Sie mit dem Browser HTTP-Anfragen starten:

  $ docker compose logs -f

Alternativ können Sie die Anfragen auch in einem zweiten Terminal-Fenster starten:

  $ curl http://$(docker-machine ip default):4567

Fehlermeldungen

  • ERROR: Service 'web' failed to build: ADD failed: stat /mnt/sda1/var/lib/docker/tmp/docker-builder594177452/journal: no such file or directory

    Dieser Fehler tritt auf, wenn die Applikation, die in dem Dockerfile-Statement ADD journal /app referenziert wurde, nicht vorhanden ist. Fügen Sie die App zu dem Ordner hinzu, der das Dockerfile enthält (siehe oben).

Fragen zur Kontrolle

  1. Wie können Sie sicherstellen, dass der Datenbank-Container auf jeden Fall vor dem Web-Container gestartet wird?

  2. Wie könnte eine alternative Lösung aussehen, bei der die Reihenfolge der Container-Starts keine Rolle spielt? Bedenken Sie, dass es zur Laufzeit durchaus zu vorübergehendem Ausfall des Datenbank-Containers kommen kann. Dabei soll der Web-Container weiterlaufen und eine Fehlermeldung anzeigen (statt kommentarlos zu stoppen).

  3. Wie könnte die vorhandene Lösung skalieren? Was wäre notwendig, um z.B. mehrere Instanzen des web containers zu starten?

    Vergleichen Sie dazu auch den Ansatz aus der Musterlösung Virtuelle Maschinen.

Literatur

letzte Änderung: 12. Dezember 2023