Example / template: virtual company network in a docker-compose structure with Traefik, Matrix, Kimai, Open Project, DokuWiki, Gogs, MatterMost, Minecraft

The below is a docker-compose file, in combination with a traefik configuration. I have been building it over the last months. I put it on the net, because I would have wished for something like this when I started a few months ago 🙂 .

For me it was especially difficult to understand how to “wire” the containers via Traefik to the outside and via Docker-Compose to the inside. I hope to give you a template with the file set to help you understand what you are looking for in the help files of Docker and Traefik.

Goal

  • Provision of modular add-on “apps” for a small company (project management, time recording, etc.)
  • Low administration effort
  • Provision of professional, Internet-accessible, company infrastructure
  • Allow fast testing of applications – I didn’t know what I needed, wanted to test it
  • Encrypt all communication with https

Solution

  • Description of servers and their networking as docker-compose-file. The simulated network consists of 9 virtual subnets and 12 virtually interconnected servers without having to pull a wire
  • One virtual server per app (example: op.my-company.de is answered by Docker-Container from OpenProject, kb.my-company.de is answered by Docker-Container from DokuWiki, etc.)

.

Targets / Non-targets of this post

I would like to give a set of files that “works” to people who have similar goals in Docker/Traefik etc. . Whoever wants to get acquainted should be able to see where you can/must go.

I can give small help with the below construction. But I can’t keep anyone from reading the documentation about Docker, Traefik and the other containers.

If you would like to commission my engineering office to set up a comparable infrastructure for your company, I would be happy to help you. The basic structure can be set up for other companies within 1-2 working days. I can recommend this in so far as it drastically shortens your training period. The conceptual part of the training, in which you have to get used to the functionality of the whole system, is omitted.

Implemented approaches

  • Simple ticket system, preferably with project management and time recording functions; here I played with OpenProject, but did not use its time recording afterwards. Instead, I used Kimai. A knowledge base is implemented wiki-based; here I tested Media-Wiki, found it too cumbersome and used DokuWiki.
  • An internal Team-Messaging-System; here I found Matrix/Synapse very interesting, which is used e.g. by the German Federal Armed Forces or the French State. For team messaging, I nowadays consider the Office365 solutions to be more suitable. So de facto the results of the matrix tests are only used for a protected chat environment in a private context.

    Reasons among others: Matrix is really technically great in many technical things. But the current state of the art of the GUI-technology in such systems is, in my opinion, that you can “collapse” answers to a post or that answers are displayed collapsed from the beginning. Matrix does not offer this.

    This causes that Matrix becomes quickly confusing. In my opinion it works only in a small scale. It is e.g. not possible to exchange messages on different topics in one channel. If someone comes back after a 3 day “matrix break” he finds an unsorted mess of text messages per channel, which are not sorted by threads. Matrix does not have message threads, although it has been desired for years. As technically beautiful as I find Matrix, they can’t be our messenger in the long run if it stays that way. Mattermost has it in the roadmap at the end of the year – if Matrix doesn’t have it then, we might jump over it. Zulip I will also check it out.
  • I have also tested – as an alternative to Matrix as the Team-Messaging-System – Mattermost. I liked it very much. It was surprisingly similar to Slack. But it was not so good that I would have migrated the participants of the running Matrix installation to a new system. Also Mattermost offers no function to separate threads in channels.
  • What Minecraft as game has to do with the row of company containers? I am a self-dependend father of several boys. I promote Minecraft as a computer game to the boys, because they become wonderfully creative. At the same time I want to prevent the boys from playing together with other adults. Besides, if a child’s house suddenly breaks in Minecraft, I want to know that it was one of our players so I can get it under control. Building Minecraft was easy. So the guys have their own “server”, play in their own world.

Implemented approaches

  • Simple ticket system, preferably with project management and time recording functions; here I played with OpenProject, but did not use its time recording afterwards. Instead, I used Kimai. A knowledge base is implemented wiki-based; here I tested Media-Wiki, found it too cumbersome and used DokuWiki.
  • An internal Team-Messaging-System; here I found Matrix/Synapse very interesting, which is used e.g. by the German Federal Armed Forces or the French State. For team messaging, I nowadays consider the Office365 solutions to be more suitable. So de facto the results of the matrix tests are only used for a protected chat environment in a private context.

    Reasons among others: Matrix is really technically great in many technical things. But the current state of the art of the GUI-technology in such systems is, in my opinion, that you can “collapse” answers to a post or that answers are displayed collapsed from the beginning. Matrix does not offer this.

    This causes that Matrix becomes quickly confusing. In my opinion it works only in a small scale. It is e.g. not possible to exchange messages on different topics in one channel. If someone comes back after a 3 day “matrix break” he finds an unsorted mess of text messages per channel, which are not sorted by threads. Matrix does not have message threads, although it has been desired for years. As technically beautiful as I find Matrix, they can’t be our messenger in the long run if it stays that way. Mattermost has it in the roadmap at the end of the year – if Matrix doesn’t have it then, we might jump over it. Zulip>/strong>I will also check it out.

  • I have also tested – as an alternative to Matrix as the Team-Messaging-System – Mattermost. I liked it very much. It was surprisingly similar to Slack. But it was not so good that I would have migrated the participants of the running Matrix installation to a new system. Also Mattermost offers no function to separate threads in channels.
  • What Minecraft as game has to do with the row of containers? I am an independent father of several boys. I promote Minecraft as a computer game to the boys, because they become wonderfully creative. At the same time I want to prevent the boys from playing together with other adults. Besides, if a child’s house suddenly breaks in Minecraft, I want to know that it was one of our players so I can get it under control. Building Minecraft was easy. So the guys have their own “server”, play in their own world.

    –> Adding Minecraft into it was easy. So the guys have their own “server”, play in their own world.

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 )

Core functions of the following file:

  • Monitoring the port clearances of started Docker containers via the socket of the Docker service (see below, docker.sock).
  • Reroute all accesses to the virtual hosts (as defined as labels in docker-compose) to the containers.
  • Receive http requests; redirect http requests to https
  • For https accesses on-the-fly via Let’s-Encrypt certificates, store and manage them, handle encryption… forward unencrypted traffic to port 80 of the containers.
  • Cool here: Traefik does everything fully automatic. Hardly any explanation is needed.
  • It’s difficult: Traefik does everything fully automatic. Nobody explains, because it is so simple (????)…

    I am desperate when setting up the system, that the setting up of Traefik was apparently sooooooo simple, that it apparently did not need any explanation. I was sitting in front of a jumble of colorful help pages for Traefik and didn’t even know what the vocabulary of my problems in Traefik was, so I couldn’t search for the solutions.

    Find below a complete system consisting of Traefik configuration and a 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

This entry was posted in Uncategorized and tagged , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *