Docker

Warning

MAD’s Docker support is community driven and untested by MAD’s core developers!

Set up Docker

If you do not have a clue about docker, you maybe want to check out:

First of all, you have to install Docker CE and docker-compose on your system.

These sites are well documented and if you follow the install instructions, you are good to go.

Setup MAD and RocketMAD database.

In this section we explain how to setup MAD and a RocketMAD database using docker-compose.

Preparations

You can just copy & paste this to do what is written below:

mkdir MAD-docker && \
cd MAD-docker && \
mkdir mad && \
mkdir mad/configs && \
mkdir rocketdb && \
touch rocketdb/my.cnf && \
touch docker-compose.yml && \
cd mad/configs/ && \
wget -O config.ini https://raw.githubusercontent.com/Map-A-Droid/MAD/async/configs/config.ini.example && \
cd ../../

This will:

  1. Create a directory MAD-docker.

  2. Create a file docker-compose.yml.

  3. Create a directory MAD-docker/mad. (here we store MAD related stuff)

  4. Create a directory MAD-docker/mad/configs. (here we store config files for MAD). Here you store your config.ini.

  5. Create a directory MAD-docker/rocketdb. (here we store config files for MariaDB). Here you store your my.cnf.

Your directory should now look like this:

MAD-docker/
  docker-compose.yml
  mad/
  rocketdb/
    my.cnf
  configs/
    config.ini

Writing the MariaDB config file

Fill rocketdb/my.cnf file with the following content.

[mysqld]
innodb_buffer_pool_size=1G

You should align this setting with you available memory. It should probably not exceed 50% of your available memory.

Decrease VM swappiness

sysctl -w vm.swappiness=1

For further details have a look at https://mariadb.com/kb/en/configuring-swappiness/

Writing the docker-compose file

We use docker-compose to deploy and manage our services.

Fill docker-compose.yml with the following content. Below we explain the details of every service.

version: '2.4'
services:
  mad:
    container_name: pokemon_mad
    image: ghcr.io/map-a-droid/mad:async
    restart: always
    volumes:
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
      - ./mad/configs/config.ini:/usr/src/app/configs/config.ini
      - ./volumes/mad/personal_commands:/usr/src/app/personal_commands
      - ./volumes/mad/files:/usr/src/app/files
      - ./volumes/mad/logs:/usr/src/app/logs
      - ./volumes/mad/apks:/usr/src/app/temp/mad_apk
      - ./volumes/mad/plugins:/usr/src/app/plugins
    depends_on:
      - rocketdb
    ports:
      - "8080:8080"
      - "8000:8000"
      - "5000:5000"

  rocketdb:
    container_name: pokemon_rocketdb
    image: mariadb:10.4
    restart: always
    command: ['mysqld', '--character-set-server=utf8mb4', '--collation-server=utf8mb4_unicode_ci', '--innodb_file_per_table=1', '--event-scheduler=ON', '--sql-mode=NO_ENGINE_SUBSTITUTION']
    environment:
      MYSQL_ROOT_PASSWORD: StrongPassword
      MYSQL_DATABASE: rocketdb
      MYSQL_USER: rocketdb
      MYSQL_PASSWORD: AnotherStrongPassword
      TZ: Europe/Berlin
    volumes:
      - ./volumes/rocketdb:/var/lib/mysql
      - ./rocketdb:/etc/mysql/mariadb.conf.d
    networks:
      - default

The docker-compose file defines a set of services.

“mad” service

The “mad” service is a docker-container based on the image ghcr.io/map-a-droid/mad:async , which is automatically built by GitHub whenever a push to the async happens, using this Dockerfile.

In the docker image, the whole MAD repository is located in /usr/src/app.

Volumes:

  • The volumes define what is mounted into the docker-container.

  • On one hand we mount the configuration file (config.ini).

  • On the other hand we “mount out” the files/directories produced by MAD, such as the directory “logs” and also the “files” directory, which contains all position files and stats. As usual, volumes are needed for everything you do not want to lose after you take the docker-container down.

Ports:

“rocketdb” service

The “rocketdb” service is docker-container based on mariadb:10.4. It will start a MariaDB database server and automatically create the defined used MYSQL_USER with password MYSQL_PASSWORD.

Your job here is to set secure passwords for MYSQL_ROOT_PASSWORD and MYSQL_PASSWORD.

The database is reachable in the default network as rocketdb, so in your config.ini it looks like this:

dbip: rocketdb                      # IP adress or hostname of the mysql server
dbusername: rocketdb                 # USERname for database
dbpassword: AnotherStrongPassword    # Password for that username
dbname: rocketdb                     # Name of the database

“redis” service

There’s an optional way to implement a cache layer between the data from the devices and MAD itself. To add that to your stack, just add the following lines to your compose file:

redis:
  container_name: pokemon_cache
  image: redis:latest

Make sure to set cache_host to redis in the MAD config.ini. The port can stay on default.

Database deployment

Let’s deploy the database, shall we? Just execute:

docker-compose up -d rocketdb

This will start the “rocketdb” service, take a look at the logs:

docker-compose logs -f rocketdb

and verify that the database was initialized without problems.

Installing a webfrontend

Add a webfrontend like RocketMAD or PMSF to your setup by just adding another container to the docker-compose.yml.

Warning

Make sure to adjust the config files just like the MAD config.

RocketMAD

rocket-mad:
  container_name: pokemon_rocketmad
  image: ghcr.io/cecpk/rocketmad:master
  restart: always
  volumes:
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
      - ./RocketMAD/config/:/usr/src/app/config/
  depends_on:
      - rocketdb
  networks:
      - default
  ports:
      - "5500:5000"

Create a new directory and download the basic config file into it: mkdir RocketMAD && cd RocketMAD && wget -O config.ini https://raw.githubusercontent.com/cecpk/RocketMAD/master/config/config.ini.example. This docker-compose file will expose RocketMAD on port 5500, but the internal routing is still on port 5000, so don’t change that in the config.

PMSF

pmsf:
  container_name: pokemon_pmsf
  build:
      context: ./PMSF
  restart: always
  volumes:
      - ./PMSF/access-config.php:/var/www/html/config/access-config.php
      - ./PMSF/config.php:/var/www/html/config/config.php
  depends_on:
      - rocketdb
  networks:
      - default
  ports:
      - "80:80"

Download the three required files from the PMSF repository:

mkdir PMSF && \
cd PMSF && \
wget https://raw.githubusercontent.com/pmsf/PMSF/master/Dockerfile && \
wget -O config.php https://raw.githubusercontent.com/pmsf/PMSF/develop/config/example.config.php && \
wget -O access-config.php https://raw.githubusercontent.com/pmsf/PMSF/develop/config/example.access-config.php

PMSF will run on port 80. Consider using some sort of reverse proxy!

Re-build the container for updating PMSF: docker-compose build pmsf.

Note

For more informations and a best practice example, check out the docker-compose used here

Using Traefik 2 as router

If you use Docker, we recommend to use Traefik 2 as router. It is easy to configure, easy to use and it handles alot of things for you, like SSL certificates, service discovery, load balancing. We will not explain, how you deploy a Traefik on your server, but we give you a production ready example for your docker-compose.yml, In this example, we assume:

  • your Traefik is connected to a docker-network proxy,

  • your domain is example.com and

  • you use a config similar to this:

api:
  dashboard: true

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false
    network: proxy


entryPoints:
  web:
    address: :80
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https

  websecure:
    address: :443
    http:
      tls:
        certResolver: letsEncResolver


certificatesResolvers:
  letsEncResolver:
    acme:
      email: bree@example.com
      storage: acme.json
      httpChallenge:
        entryPoint: web

We define the labels as follows:

version: '2.4'
services:
  mad:
    container_name: pokemon_mad
    image: ghcr.io/map-a-droid/mad:async
    init: true
    restart: always
    volumes:
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
      - ./mad/configs/config.ini:/usr/src/app/configs/config.ini
      - ./volumes/mad/files:/usr/src/app/files
      - ./volumes/mad/logs:/usr/src/app/logs
    depends_on:
      - rocketdb
    networks:
      - default
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
      - "traefik.http.routers.madmin.rule=Host(`madmin.example.com`)"
      - "traefik.http.routers.madmin.service=madmin"
      - "traefik.http.services.madmin.loadbalancer.server.port=5000"
      - "traefik.http.routers.pogodroid.rule=Host(`pogodroid.example.com`)"
      - "traefik.http.routers.pogodroid.service=pogodroid"
      - "traefik.http.services.pogodroid.loadbalancer.server.port=8000"
      - "traefik.http.routers.rgc.rule=Host(`rgc.example.com`)"
      - "traefik.http.routers.rgc.service=rgc"
      - "traefik.http.services.rgc.loadbalancer.server.port=8080"

  rocketdb:
    container_name: pokemon_rocketdb
    image: mariadb:10.3
    restart: always
    command: ['mysqld', '--character-set-server=utf8mb4', '--collation-server=utf8mb4_unicode_ci', '--innodb_file_per_table=1', '--event-scheduler=ON', '--sql-mode=NO_ENGINE_SUBSTITUTION']
    environment:
      MYSQL_ROOT_PASSWORD: StrongPassword
      MYSQL_DATABASE: rocketdb
      MYSQL_USER: rocketdb
      MYSQL_PASSWORD: AnotherStrongPassword
      TZ: Europe/Berlin
    volumes:
      - ./volumes/rocketdb:/var/lib/mysql
    networks:
      - default

networks:
  proxy:
    external: true
Using these labels, traefik now will:
  • route https://madmin.example.com to port 5000 (MADmin Flask app).

  • route https://pogodroid.example.com to port 8000 (Pogodroid listener).

  • route https://rgc.example.com to port 8080 (RGC listener).

Deploy MAD

To deploy MAD you just execute

docker-compose up -d mad

Look at the logs with:

docker-compose logs -f mad

Go to http://your-domain.com:5000 and check if the MADmin is running.

Useful commands

Some useful commands to maintain MAD + DB

Dump DB:

docker-compose exec -T rocketdb /usr/bin/mysqldump -uroot -pStrongPassword rocketdb  > $(date +"%Y-%m-%d")_rocketmap_backup.sql

Restore DB:

cat <backup>.sql | docker-compose exec -T rocketdb /usr/bin/mysql -uroot -pStrongPassword rocketdb

MySQL CLI:

docker-compose exec rocketdb /usr/bin/mysql -uroot -pStrongPassword rocketdb

Restarting every container:

docker-compose down && docker-compose up -d && docker-compose logs -f

That will first stop every running container and then start the whole stack again in detached mode (-d). The last command is showing the logs.

Further useful Docker tools:

  • Router: Traefik is recommended, which is really easy to use and also runs as Docker container. To secure the docker-socket (which traefik has access to) we recommend the docker-socket-proxy by Tecnativa.

  • Automatic updates: Watchtower is a useful tool which will update your docker-services once there are newer images available

Further steps

Review and implement anything related to the security section