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