Mittwoch, 30. Juni 2021

Docker - ein kleiner Schnellstart Workshop

Die Docker Dokumentation finde ich so umfangreich, ich weiß nicht wo ich anfangen soll. Deswegen habe ich mal einen kleinen Einstieg mit ein paar nützlichen Kommandos aufgeschrieben. Es ist meine Sicht der Dinge und soll keinesfalls ein: "wie mach ich es richtig" Dokument sein.

Ich habe diesen Workshop auf einem Raspberry Pi 3+ und auf einem Windows PC mit Docker Desktop ausprobiert. Selbstverständlich kann man auch ein vorhandes Linux System oder eine virtuelle Maschine verwenden.

Setup

Für die Raspberry Umgebung habe ich hier schon einen Artikel, unter Windows benötigt man WSL und Docker Desktop.

Wenn man noch docker-compose installiert hat, sollte man irgendwann auf docker compose v2 aktualisieren. Ich habe dazu ein Script gebaut. aus docker-compose wird damit docker compose!

Terminal auf: ssh, cmd, wsl oder Windows Terminal ist vom persönlichen Geschmack abhängig - docker läuft?

docker info

Container erzeugen

per Kommandozeile

Ich finde ein guter Einstieg und die Grundlage für späteren Überblick liefert Portainer. Es wird ein Docker Volume erzeugt und der Container mit einer Kommandozeile heruntergeladen, konfiguriert und gestartet.

docker volume create portainer_data
docker run -d -p 8000:8000 -p 9000:9000 --name=portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce
Jetzt öffnet man den Portainer mit http://dockerhostname:9000 vergibt ein admin Passwort und verbindet sich zur lokalen Dockerumgebung (den Hinweis zur anderen Schreibweise in der Windowsumgebung kann man ignorieren - der ist ev. "alt").
Man kann sich nach belieben umsehen und versuchen die Oberfläche zu verstehen. 

per Template

Man kann auch gleich per "Klick" den nächsten Container erzeugen. Linkes Menü / App Templates / Httpd auswählen und bei Actions / Deploy the Container drücken. Nach einer kurzen Wartezeit (Deployment in progress...) öffnet sich die Containers Liste und sieht in etwa so aus:
So jetzt erstmal einen Kaffee nehmen und alles etwas sacken lassen. Wir sehen hier ein paar wichtige Informationen und Bausteine des Containers im Überblick:
running - läuft, Image - der Grundbaustein, Published Ports - vorn steht der von außen Erreichbare.
Also den kurzerhand installierten HTTP Server testen:

http://dockerhostname:49153
Wenn da It Works erscheint, läuft der Server. Wo ist die Quelle dieser Seite? 
Vorn auf den (von docker zusammengewürfelten) Namen klicken, etwas nach unten scrollen im Feld Volumes sieht man das Volume im docker Host und den Pfad im Container. Zurück ins Terminal und mit diesen beiden Zeilen schreiben wir eine neue Startseite zum HTTP Server.

echo '<html><body><h1>Mein HTTP Server arbeitet!</h1></body></html>' > test.html
docker cp test.html containername:/usr/local/apache2/htdocs/index.html
Man kann natürlich einen kompletten Webauftritt dorthin kopieren. Einheitlich geht der Weg über docker cp. 
Man kann auch über den docker Host auf die Daten zugreifen, lediglich unter Windows geht das zumindest in der Variante etwas "mehrstufig" - ich habe da hier was gefunden. Der letzte dort beschriebene Weg funktioniert, klingt aber umständlich. Unten habe ich noch ein Beispiel wo einfach der lokale Windows Pfad gemappt wird.

Was haben wir gelernt? Docker verbindet sich mit dem Host (und damit zu uns) über Port mappings und Volume mounts. Der Container ist "nur" eine Definition und benötigt ein Image (quasi die Installations CD). Der Zugriff auf Container erfolgt über den Namen oder die ID und dem Befehl docker. Das sind die hauptsächlichen Komponenten in docker:
  • services
    • image
    • ports
    • volumes
    • networks
    • environment

per docker(-)compose

Mit docker compose v2 ändern sich alle Befehle auf docker compose! Ich habe noch nicht alles getestet!
Definitionen eines zweiten HTTP Servers über docker-compose. Dazu braucht man 3 Schritte
  1. Arbeitspfad
  2. Die Datei docker-compose.yml
  3. den Start Befehl

workpath='docker-httpd'
mkdir ~/$workpath
cd ~/$workpath
cat <<EOF > docker-compose.yml
version: '2'

services:
  http-server:
    image: httpd
    restart: always
    ports:
      - 9080:80
    volumes:
      - /usr/local/apache2/htdocs
    environment:
      - TZ=Europe/Berlin
EOF
docker-compose config
docker-compose up -d

Die Dashboard HTML Seite von Bastien Wirtz noch dazu?

cd ~/$workpath
cat <<EOF >> docker-compose.yml
  homer-dashboard:
    image: b4bz/homer:latest
    restart: always
    ports:
      - 9090:8080
    volumes:
      - ./homer/assets/:/www/assets
    environment:
      - TZ=Europe/Berlin
EOF

Meine Beispiele erzeugen die yml Datei per HereDoc, selbstverständlich kann man dafür seinen Lieblingseditor nehmen.  

Der Vorteil von docker-compose ist die einfache Handhabung mehrere services/container in einer Definition. Ein erneuter docker-compose up -d Befehl nach einer Konfigurationsänderung macht den betroffenen Container neu und führt einen Restart aus, die unveränderten Container bleiben unberührt und laufen ohne Unterbrechung weiter! Die yml Datei bleibt, die docker run Zeile ist quasi "weg" (Config laufender Container über inspect).
Die Doku ist umfangreich und nicht frei von Verwirrung. Das Format der yml Datei ist nicht sehr tolerant, die Fehlermeldung nicht immer hilfreich. Man muss die Blockbildung beachten: Immer zwei Leerzeichen am Zeilenanfang bilden einen neuen Block. Bestimmte Werte in der yml Datei werden entweder als Array (- variable=wert) oder Dictionary (variable: wert) geschrieben. Der Doppelpunkt mit Leerzeichen trennt den Variablennamen vom Wert. Der Doppelpunkt ohne Leerzeichen trennt die Ressource im Host von der Ressource im Container.

Beispiel conbeeII Stick

    volumes:
      - ./deconz:/root/.local/share/dresden-elektronik/deCONZ
    devices:
      - /dev/serial/by-id/usb-dresden_elektronik_ingenieurtechnik_GmbH_ConBee_II_DE1234567-if00:/dev/ttyACM0
Mit einer .env Datei können Variablennamen in der compose Datei ersetzt werden. Beispiel: Den Hostnamen in die .env schreiben:
echo HOST_HOSTNAME=$(hostname) > .env

In der yml Datei wird der Hostname dann in die Container Umgebung übernommen.

    environment:
      - TZ=Europe/Berlin
      - HOST_HOSTNAME=${HOST_HOSTNAME}
Den Erfolg prüft man mit dem docker-compose config Befehl!

Unter Windows 10 Docker kann man auch lokale Windows Ressourcen in den Container verbinden. Funktioniert im CMD Fenster!. Quelle.

docker run --restart=always -p 8090:8080 -v C:\homertest\:/www/assets b4bz/homer:latest
 

Update 

Die Container werden nicht durch lokale Updates gepflegt, man wirft sie normal weg und macht sie neu!
Ganz simpel in der compose Umgebung, einfach alles:

docker-compose pull
docker-compose up -d 
In der normalen docker Umgebung etwas aufwändiger.
Einzelnes/bestimmtes Images holen:
docker pull portainer/portainer-ce
Alle Images neu holen (Quelle):
for image in $(docker images --format "{{.Repository}}:{{.Tag}}" | grep -v '<none>'); do docker pull $image; done;
Nach dem das Image gezogen ist, kann man den Container anhalten, entfernen und neu machen. Die Daten (Volumes) bleiben erhalten.

docker stop portainer
docker rm portainer
docker run -d -p 8000:8000 -p 9000:9000 --name=portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce

Aufräumen

Achtung: die Befehle löschen die "Überbleibsel"! 
Bitte in der Doku schauen, was passiert! Ein paar Beispiele die ich gefunden habe:

docker system prune # räumt komplett auf
docker container prune # Remove all stopped containers
docker volume prune    # Remove all unused volumes
docker network prune   # Remove all unused networks
docker rm $(docker ps -aq --filter 'exited=0') #entfernt alle beendeten container
docker image prune     #entfernt alle nicht verwendeten Images
docker image prune -a  #fragt nicht nach

Dies und Das

Was Portainer kann, geht natürlich alles auch auf der Kommandozeile, mal ein paar nützliche Dinge

docker ps
docker inspect NameOderID
docker exec NameOderID date
docker exec -it NameOderID /bin/bash

Einzelnen Service/Container neu machen

Anders als beim docker Befehl wird bei docker-compose nicht der Name des Containers angegeben sondern der Name des Service, wenn man ihn weglässt werden alle Container mit einer Nachfrage entfernt!
Beispiele in neuer Form (docker compose vs docker-compose):

cd ~/$workpath
docker compose ps                  # um die Servicenamen anzeigen zu lassen
docker compose rm -s [ServiceName] # stoppt den Service vor dem entfernen
docker compose stop [ServiceName]  # stoppt nur
docker compose up -d               # macht die Container neu und startet sie

Ein bestimmtes Image verwenden

Per default wird immer der Tag latest verwenden. Viele Images haben kein speziellen Tag, wenn man ein Neues herunterlädt bekommt dies den Tag latest, das Alte hat dann kein Tag <none>. Um ein altes Image wieder zu verwenden kann man ihm einen eigene Tag geben. Mit dem ls Befehl holt man sich die ImageId und dann:

docker tag ImageId repository/imagename:neuerTag

Keine Kommentare: