Beispiel / Vorlage: virtuelles Firmennetzwerk in einer docker-compose-Struktur mit Traefik, Matrix, Kimai, Open Project, DokuWiki, Gogs, MatterMost, Minecraft

Das untere ist ein Docker-Compose-File, im Zusammenspiel mit einer Traefik-Konfiguration. Der Aufbau entstand bei mir über die letzten Monate. Ich stelle ihn ins Netz, weil ich mir so etwas gewünscht hätte, als ich vor ein paar Monaten begann 🙂 .

Für mich war es insbesondere schwierig, zu verstehen, wie man die Container über Traefik nach außen und über Docker-Compose nach innen „verdrahtet“. Ich hoffe, mit dem Dateiset eine Vorlage zu geben, die Euch hilft, in den Hilfedateien von Docker und Traefik zu verstehen, wonach Ihr sucht.

Ziel

  • Bereitstellung modular hinzufügbarer „Apps“ für eine kleine Firma (Projektverwaltung, Zeiterfassung, etc.)
  • Geringer Administrationsaufwand
  • Bereitstellung professioneller, aus dem Internet erreichbarer, Firmeninfrastruktur
  • Möglichkeit schneller Tests von Anwendungen – ich wusste nicht, was ich brauche, wollte testen
  • Verschlüsselung aller Kommunikation mit https

Lösung

  • Beschreibung von Servern und deren Vernetzung als Docker-Compose-File. Das simulierte Netzwerk besteht aus 9 virtuellen Subnetzen und 12 virtuell zusammengestöpselten Servern ohne einen Draht zu ziehen.
  • Je ein virtueller Server pro App (Beispiel: op.my-company.de wird beantwortet durch Docker-Container von OpenProject, kb.my-company.de wird beantwortet durch Docker-Container von DokuWiki, etc.)

Ziele / Nicht-Ziele dieses Posts

Ich möchte Personen, die sich mit ähnlicher Zielsetzung in Docker/Traefik etc einarbeiten, ein Set Dateien geben, das „funktioniert“. Wer sich einarbeiten möchte, soll sehen können, wo man hin kann/muss.

Ich kann kleine Hilfestellungen zum unteren Aufbau geben. Ich kann aber niemanden davor bewahren, sich im Zweifel in die Dokumentation zu Docker, zu Traefik und und der anderen Container einzulesen.

Wer mein Ingenieurbüro beauftragen möchte, eine hierzu vergleichbare Infrastruktur für seine Firma aufzuziehen, dem helfe ich gern. Die Grundstruktur lässt sich auch sicherlich ungefähr innerhalb 1-2 Arbeitstagen für andere Firmen aufsetzen. Ich kann das insofern empfehlen, als dass sich Eure Einarbeitungszeit dabei drastisch verkürzt. Der konzeptionelle Teil der Einarbeitung, in dem man sich in die Funktionsweisen des Gesamtsystems einarbeiten muss, entfällt.

Umgesetzte Ansätze

  • Einfaches Ticketsystem, gern mit Projektverwaltungs- und Zeiterfasungsfunktionen; hier spielte ich mit OpenProject; dessen Zeiterfassung nutzte ich hinterher aber nicht. Stattdessen kommt Kimai zum Einsatz.
  • Eine Knowledge-Base auf Wiki-Basis; hier testete ich Media-Wiki, fand es zu umständlich und nahm DokuWiki.
  • Ein internes Team-Messaging-System; hier fand ich Matrix/Synapse sehr interessant, was z.B. von der Bundeswehr oder dem französischen Staat genutzt wird. Für das Team-Messaging halte ich inzwischen die Office365-Lösungen für passender. De facto kommen die Ergebnisse der Matrix-Tests aber nur für eine geschützte Chat-Umgebung im privaten Umfeld zum Einsatz.

    Gründe u.a: Matrix ist in vielen technischen Dingen wirklich technisch toll. Den aktuellen Stand der GUI-Technik bei solchen Systemen stellt es aber m.E. dar, dass man Antworten zu einem Beitrag „zusammenklappen“ kann bzw. Antworten von vornherein zusammengeklappt angezeigt bekommt. Matrix bietet das nicht.

    Das bewirkt, dass Matrix schnell unübersichtlich wird. Es funktioniert m.E. nur im kleinen Rahmen. Es ist z.B. kein inhaltlicher Austausch in einem Kanal zu verschiedenen Themen möglich, wenn jemand nach 3 Tagen „Matrix-Pause“ wieder einsteigt und pro Kanal einen unsortierten Wust von Textnachrichten vorfindet, die nicht nach Threads sortiert sind. Matrix hat das nicht, obwohl es seit Jahren gewünscht wird. So technisch schön ich Matrix finde, so wenig können sie auf Dauer unser Messenger sein, wenn das so bleibt. Mattermost hat es zu Ende des Jahres in der Roadmap – wenn Matrix es dann nicht hat, springen wir evtl. rüber. Zulip werde ich mir auch noch ansehen.
  • Als Erfahrung ferner: Matrix ist in vielen technischen Dingen wirklich technisch toll. Den aktuellen Stand der GUI-Technik bei solchen Systemen stellt es aber m.E. dar, dass man Antworten zu einem Beitrag „zusammenklappen“ kann bzw. Antworten von vornherein zusammengeklappt angezeigt bekommt. Matrix bietet das nicht.
  • Mattermost habe ich – als Alternative zu Matrix als Team-Messaging-System – ebenfalls getestet. Es gefiel mir sehr gut. Es war überraschend ähnlich zu Slack. Es war aber nicht so gut, dass ich die Teilnehmer der laufenden Matrix-Installation auf ein neues System herübermigriert hätte. Auch Mattermost bietet keine Funktion, Threads in Kanälen zu trennen.
  • Was Minecraft als Spiel in der Reihe der Container zu suchen hat? Ich bin selbstständiger Vater mehrerer Jungs. Ich fördere bei den Jungs Minecraft als Computerspiel, weil sie wunderschön kreativ dabei werden. Gleichzeitig möchte ich verhindern, dass die Jungs mit anderen Erwachsenen zusammen spielen. Außerdem: wenn in Minecraft auf einmal z.B. ein Haus eines Kinders kaputt ist, will ich wenigstens wissen, dass es einer der unserigen Spieler war, damit ich das unter Kontrolle bekomme.

    –> Minecraft dazu zu bauen, war einfach. So haben die Jungs einen eigenen „Server“, spielen in ihrer eigenen Welt.

Dockerfile (/var/docker_data/composed/docker-compose.yml)

version: "3.7"
networks:
  private_network:
    ipam:
      driver: default
      config:
      - subnet: 192.168.200.0/24

  kimai_network:
    ipam:
      driver: default
      config:
      - subnet: 192.168.201.0/24

  mattermost_network:
    ipam:
      driver: default
      config:
      - subnet: 192.168.202.0/24

  op_network:
    ipam:
      driver: default
      config:
      - subnet: 192.168.203.0/24

  kb_network:
    ipam:
      driver: default
      config:
      - subnet: 192.168.204.0/24

  gogs_network:
    ipam:
      driver: default
      config:
      - subnet: 192.168.205.0/24

  synapse_network:
    ipam:
      driver: default
      config:
      - subnet: 192.168.206.0/24

  gitlab_network:
    ipam:
      driver: default
      config:
      - subnet: 192.168.207.0/24

  public_network:
    driver: bridge

services:

  traefik:

    image: traefik:latest

    container_name: traefik

    ports:
      - "80:80"
      - "8080:8080"
      - "443:443"

    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /var/docker_data/traefik:/etc/traefik

# block "labels" is not really needed and without function here
    labels:
      - "traefik.http.routers.traefik.rule=Host(`traefik.my-company.de`)"
      - "traefik.http.routers.traefik.entrypoints=https,http"
      - "traefik.http.routers.traefik.tls=true"
      - "traefik.http.routers.traefik.tls.certresolver=traefikresolver"

# let Traefik snif in all networks; target: recognize ports which are opened
# and must be forwarded from outside to the containers
    networks:
      - private_network
      - kimai_network
      - mattermost_network
      - op_network
      - kb_network
      - gogs_network
      - synapse_network
      - public_network

  myprivatechat:
    image: matrixdotorg/synapse
    restart: always
    volumes:
      - /var/docker_data/myprivatechat:/data
# "Ports" is not needed because Traefik does the routing from
# the virtual servers to the containers
#    ports:
#      - "80:80"
#      - "443:443"
    labels:
      - "traefik.http.routers.myprivatechat.rule=Host(`wir.myprivatechat.de`)"
      - "traefik.http.routers.myprivatechat.entrypoints=https,http"
      - "traefik.http.routers.myprivatechat.tls=true"
      - "traefik.http.routers.myprivatechat.tls.certresolver=myprivatechatresolver"
#      - "traefik.http.routers.matrix.port=8008"

    container_name: myprivatechat

    depends_on:
      - db

    networks:
      - synapse_network

  op:
    image: openproject/community:10
    restart: always

# "Ports" is not needed because Traefik does the routing from
# the virtual servers to the containers
#    ports:
#      - "80:80"
#      - "443:443"
    labels:
      - "traefik.http.routers.op.rule=Host(`op.my-company.de`)"
      - "traefik.http.routers.op.entrypoints=https,http"
      - "traefik.http.routers.op.tls=true"
      - "traefik.http.routers.op.tls.certresolver=opresolver"
#      - "traefik.http.routers.matrix.port=8008"

    environment:
      - SECRET_KEY_BASE=your_SECRET_KEY_BASE

    volumes:
      - /var/docker_data/op/pgdata:/var/openproject/pgdata
      - /var/docker_data/op/assets:/var/openproject/assets

    container_name: op

    networks:
      - op_network

  kb:
    image: 'docker.io/bitnami/dokuwiki:0.20180422.202005011246-debian-10-r67'
    restart: always

# "Ports" is not needed because Traefik does the routing from
# the virtual servers to the containers
#    ports:
#      - "80:80"
#      - "443:443"
    labels:
      - "traefik.http.routers.kb.rule=Host(`kb.my-company.de`)"
      - "traefik.http.routers.kb.entrypoints=https,http"
      - "traefik.http.routers.kb.tls=true"
      - "traefik.http.routers.kb.tls.certresolver=kbresolver"
#      - "traefik.http.routers.matrix.port=8008"

    volumes:
      - /var/docker_data/kb:/bitnami/dokuwiki

    container_name: kb

    networks:
      - kb_network

  gitlab:
    image: gitlab/gitlab-ce:latest
    restart: "no"
    hostname: gitlab.my-company.de

# gitlab did not work on strato v-servers
# the "container" is currently primarily an
# example for "commenting out" a container
# --> set start command for container to
# the directly exiting "true"
    command: "true"
    entrypoint: "true"

    environment:
      - GITLAB_OMNIBUS_CONFIG="external_url 'https://gitlab.my-company.de/'"

# "Ports" is not needed because Traefik does the routing from
# the virtual servers to the containers
#    ports:
#      - "2222:22"
#      - "443:443"
    labels:
      - "traefik.http.routers.gitlab.rule=Host(`gitlab.my-company.de`)"
      - "traefik.http.routers.gitlab.entrypoints=https,http"
      - "traefik.http.routers.gitlab.tls=true"
      - "traefik.http.routers.gitlab.tls.certresolver=gitlabresolver"
#      - "traefik.http.routers.matrix.port=8008"

    volumes:
      - /var/docker_data/gitlab/data:/var/opt/gitlab
      - /var/docker_data/gitlab/logs:/var/log/gitlab
      - /var/docker_data/config:/etc/gitlab

    container_name: gitlab

    networks:
      - gitlab_network

  db:
    image: postgres:alpine

    environment:
      - POSTGRES_USER=synapse
      - POSTGRES_PASSWORD=YOUR_MATRIX-DB_PW
      # ensure the database gets created correctly
      # https://github.com/matrix-org/synapse/blob/master/docs/postgres.md#set-up-database
      - POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C
#    ports:
#      - "127.0.0.1:5432:5432"
    volumes:
      # You may store the database tables in a local folder..
      - /var/docker_data/myprivatechatDB:/var/lib/postgresql/data
      # .. or store them on some high performance storage for better results
      # - /path/to/ssd/storage:/var/lib/postgresql/data

    networks:
      - synapse_network

  gogs:
    image: gogs/gogs
    container_name: gogs

    volumes:
      - /var/docker_data/gogs/repositories:/home/git/gogs-repositories
      - /var/docker_data/gogs/data:/data

# SSH port must be routed b the ports statement
# web port is done by Traefik (see below)
    ports:
#      - "443:3000"
      - "10022:22"

    networks:
      - gogs_network

    depends_on:
      - gogsdb

    labels:
      - "traefik.http.routers.gogs.rule=Host(`gogs.my-company.de`)"
      - "traefik.http.routers.gogs.entrypoints=https,http"
      - "traefik.http.routers.gogs.tls=true"
      - "traefik.http.routers.gogs.tls.certresolver=gogsresolver"
      - "traefik.http.routers.gogs.service=gogs@docker"
      - "traefik.http.services.gogs.loadbalancer.server.port=3000"

  gogsdb:
    image: mysql:5.7.16
    container_name: gogsdb
    environment:
#      - MYSQL_ALLOW_EMPTY_PASSWORD=yes
      - MYSQL_ROOT_PASSWORD=your-gogsPW
      - MYSQL_DATABASE=gogs
      - MYSQL_USER=gogs
      - MYSQL_PASSWORD=your-gogsPW

    volumes:
      - /var/docker_data/gogsdb:/var/lib/mysql

    expose:
      - "3306"

    networks:
      - gogs_network

    labels:
      - "traefik.enable=false"

  minecraft:
    image: itzg/minecraft-bedrock-server
    container_name: minecraft
    environment:
      - EULA=TRUE
      - VERSION=latest
      - GAMEMODE=creative
      - SERVER-NAME=your-minecraft-server-name
      - MOTD="Willkommen im xxxxxxx"
      - PUBLIC=false

    labels:
      - "traefik.enable=false"

    ports:
      - "19132:19132/udp"

    volumes:
      - /var/docker_data/minecraft:/data

  kimai_sqldb:
    image: mysql:5.7
    environment:
      - MYSQL_DATABASE=kimai
      - MYSQL_USER=kimaiuser
      - MYSQL_PASSWORD=your_kimaipassword
      - MYSQL_ROOT_PASSWORD=your_rootPassword
    volumes:
      - /var/docker_data/kimaidb/:/var/lib/mysql
    command: --default-storage-engine innodb
    restart: unless-stopped
    healthcheck:
      test: mysqladmin -pyour_kimaipassword ping -h localhost
      interval: 20s
      start_period: 10s
      timeout: 10s
      retries: 3

    networks:
      - kimai_network

    labels:
      - "traefik.enable=false"

  kimai:
    image: bkraul/kimai2:nginx-alpine
    environment:
      # web server
      - PHP_DATE_TIMEZONE=Europe/Berlin
      # kimai2
      - APP_ENV=prod
      - DATABASE_URL=mysql://kimaiuser:your_kimaipassword@kimai_sqldb/kimai
      - MAILER_FROM=kimai@mydomain.com
      - MAILER_URL=smtp://validsmtpurl
      - APP_SECRET=your_SuperSecret
      # initial admin user
      - APP_ADMIN_USER=master
      - APP_ADMIN_EMAIL=master@mydomain.com
      - APP_ADMIN_PASS=your_masterPassword
      - APP_LOG=0
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - ./local.yaml:/app/config/packages/local.yaml
#    ports:
#      - '8001:80'
    restart: unless-stopped
    depends_on:
      - kimai_sqldb

    labels:
      - "traefik.http.routers.kimai.rule=Host(`kimai.my-company.de`)"
      - "traefik.http.routers.kimai.entrypoints=https,http"
      - "traefik.http.routers.kimai.tls=true"
      - "traefik.http.routers.kimai.tls.certresolver=kimairesolver"

    networks:
      - kimai_network

  kimai_postfix:
    image: catatnight/postfix:latest
    environment:
      - maildomain=kimai.local
      - smtp_user=kimai:kimai
    restart: unless-stopped

    networks:
      - kimai_network

    labels:
      - "traefik.enable=false"

Traefik ( /var/docker_data/traefik/traefik.toml )

Kernfunktionen des folgenden Files:

  • Beobachten der Portfreigaben gestarteter Docker-Container über den Socket des Docker-Services (siehe unten, docker.sock).
  • Alle Zugriffe auf die virtuellen Hosts (wie definiert als labels in docker-compose) umleiten auf die Container.
  • Entgegennehmen von http-Anfragen; http-Anfragen auf https umleiten
  • Bei https-Zugriffen on-the-fly per Let’s-Encrypt-Zertifikate beantragen, speichern und verwalten, Verschlüsselung erledigen… unverschlüsselten Verkehr auf Port 80 der Container weitergeben.
  • Toll dabei: Traefik erledigt alles vollautomatisch. Muss man kaum erklären.
  • Schwierig dabei: Traefik erledigt alles vollautomatisch. Erklärt auch keiner, weil es ja so einfach (???) ist…

    Ich bin beim Aufsetzen des Systems daran verzweifelt, dass das Aufsetzen von Traefik anscheinend sooooooo einfach war, dass es anscheinend keiner Erklärung bedurfte. Ich saß aus meiner Sicht vor einem Wust quitschbunt bebilderter Hilfeseiten zu Traefik und wusste nichtmal, wie das Vokabular meiner Probleme in Traefik lautete, so dass ich die Lösungen nicht suchen konnte.

    Unten finden Interessierte einmal ein Gesamtsystem aus Traefik-Konfiguration und Docker-Compose-File.
logLevel = "DEBUG"

defaultEntryPoints = ["http", "https"]

[entryPoints]

  [entryPoints.http]
  address = ":80"

  [entryPoints.http.http]
    [entryPoints.http.http.redirections]
      [entryPoints.http.http.redirections.entryPoint]
        to = "https"
        scheme = "https"

  [entryPoints.https]
  address = ":443"

[api]
dashboard = true
insecure = false

[certificatesresolvers.traefikresolver.acme]
email = "post@your_private_hp.de"
storage = "/etc/traefik/traefik/acme.json"
[certificatesresolvers.traefikresolver.acme.httpChallenge]
entryPoint = "http"

#onHostRule = true
#acmeLogging =true

[acme.httpChallenge]
entryPoint = "http"

[certificatesresolvers.myprivatechatresolver.acme]
email = "post@your_private_hp.de"
storage = "/etc/traefik/myprivatechat/acme.json"
[certificatesresolvers.myprivatechatresolver.acme.httpChallenge]
entryPoint = "http"

[certificatesresolvers.mattermostnmresolver.acme]
email = "post@your_private_hp.de"
storage = "/etc/traefik/mattermostnm/acme.json"
[certificatesresolvers.mattermostnmresolver.acme.httpChallenge]
entryPoint = "http"

[certificatesresolvers.kimairesolver.acme]
email = "post@your_private_hp.de"
storage = "/etc/traefik/kimai/acme.json"
[certificatesresolvers.kimairesolver.acme.httpChallenge]
entryPoint = "http"

[certificatesresolvers.opresolver.acme]
email = "post@your_private_hp.de"
storage = "/etc/traefik/op/acme.json"
[certificatesresolvers.opresolver.acme.httpChallenge]
entryPoint = "http"

[certificatesresolvers.kbresolver.acme]
email = "post@your_private_hp.de"
storage = "/etc/traefik/kb/acme.json"
[certificatesresolvers.kbresolver.acme.httpChallenge]
entryPoint = "http"

[certificatesresolvers.gitlabresolver.acme]
email = "post@your_private_hp.de"
storage = "/etc/traefik/gitlab/acme.json"
[certificatesresolvers.gitlabresolver.acme.httpChallenge]
entryPoint = "http"

[certificatesresolvers.gogsresolver.acme]
email = "post@your_private_hp.de"
storage = "/etc/traefik/gogs/acme.json"
[certificatesresolvers.gogsresolver.acme.httpChallenge]
entryPoint = "http"

[providers.docker]
  endpoint = "unix:///var/run/docker.sock"
#  exposedByDefault = false
Dieser Beitrag wurde unter Uncategorized abgelegt und mit , , , , , , , , , verschlagwortet. Setze ein Lesezeichen auf den Permalink.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.