diff --git a/deployment/README.md b/deployment/README.md new file mode 100644 index 0000000..2259ecc --- /dev/null +++ b/deployment/README.md @@ -0,0 +1,100 @@ +# Deploying an OpenBikeSensor Portal with Docker + +## Introduction + +The main idea of this document is to provide an easy docker-based +production-ready setup of the openbikesensor portal. It uses the [the traefik +proxy](https://doc.traefik.io/traefik/) as a reverse proxy, which listens +on port 80 and 443. Based on some labels, traefik routes the domains to the +corresponding docker containers. + +## Before Getting Started + +The guide and example configuration assumes one domain, which points to the +server's IP address. This documentation uses `portal.example.com` as an +example. The API is hosted at `https://portal.example.com/api`, while the main +frontend is reachable at the domain root. + +## Steps + +### Clone the repo + +Create a folder somewhere in your system, we'll name it `$ROOT` from now on. + +Clone the repository to `$ROOT/source`. Ensure you also cloned the submodules, +as described in the main [README](../README.md). + +```bash +mkdir -p /opt/openbikesensor +cd /opt/openbikesensor +git clone https://github.com/openbikesensor/portal source/ +``` + +### Configure `traefik.toml` + +```bash +mkdir -p config/ +cp source/deployment/examples/traefik.toml config/traefik.toml +vim config/traefik.toml +``` + +Configure your email in the `config/traefik.toml`. This email is uses by +Let's Encrypt to send you some mails regarding your certificates. + +### Configure `docker-compose.yaml` + +```bash +cp source/deployment/examples/docker-compose.yaml docker-compose.yaml +vim docker-compose.yaml +``` + +Change the domain where it occurs, such as in `Host()` rules. + +### Configure frontend + +```bash +cp source/frontend/config.json.example config/frontend.json +vim frontend/src/config.json +``` + +* Change all URLs to your domain +* Create a UUID by using `uuidgen` and set the `clientId` + +### Configure API + +```bash +cp source/api/config.json.example config/api.json +vim config/api.json +``` + +* Change all URLs to your domain +* Generate and set a random `cookieSecret` (for example with `uuidgen`) +* Generate and set a random `jwtSecret` (for example with `uuidgen`) +* Configure you SMTP mail server +* Set the `clientId` for the `oAuth2Client` of the portal (from step 3) + +### Build container and run them + +```bash +docker-compose up -d +``` + +The services are being built the first time this is run. It can take some +minutes. + +## Miscellaneous + +### Logs + +To read logs, run + +```bash +docker-compose logs -f +``` + +If something went wrong, you can reconfigure your config files and rerun: + +```bash +docker-compose build +docker-compose up -d +``` diff --git a/deployment/examples/docker-compose.yaml b/deployment/examples/docker-compose.yaml new file mode 100644 index 0000000..69f4834 --- /dev/null +++ b/deployment/examples/docker-compose.yaml @@ -0,0 +1,125 @@ +version: '3' + +networks: + gateway: + external: true + name: gateway + backend: + internal: true + +services: + mongo: + image: mongo + tty: true + volumes: + - ./data/mongo:/data/db + restart: on-failure + networks: + - backend + + redis: + image: redis + volumes: + - ./data/redis:/data + command: redis-server --appendonly yes + restart: on-failure + networks: + - backend + + api: + image: obs-api + build: + context: ./source/api + volumes: + - ./data/api-data:/data + - ./config/api-config.json:/opt/obs/api/config.json + environment: + - MONGODB_URL=mongo://mongo/obs + restart: on-failure + labels: + - traefik.http.middlewares.obsapi-prefix.stripprefix.prefixes=/api + - traefik.http.middlewares.obsapi-wellknown.replacepathregex.regex=^/\.well-known/oauth-authorization-server/api$$ + - traefik.http.middlewares.obsapi-wellknown.replacepathregex.replacement=/.well-known/oauth-authorization-server + - traefik.http.routers.obsapi.rule=Host(`portal.example.com`) && (PathPrefix(`/api/`) || Path(`/.well-known/oauth-authorization-server/api`)) + - traefik.http.routers.obsapi.entrypoints=websecure + - traefik.http.routers.obsapi.tls=true + - traefik.http.routers.obsapi.tls.certresolver=leresolver + - traefik.http.routers.obsapi.middlewares=obsapi-prefix@docker,obsapi-wellknown@docker + - traefik.docker.network=gateway + networks: + - gateway + - backend + + worker: + image: obs-api + build: + context: ./source/api + volumes: + - ./data/api-data:/data + - ./config/api-config.json:/opt/obs/api/config.json + links: + - mongo + - redis + restart: on-failure + command: + - npm + - run + - start:worker + networks: + - backend + # Not requred for traefik, but to reach overpass-api.de + - gateway + + frontend: + image: obs-frontend + build: + context: ./source/frontend + dockerfile: Dockerfile-prod + links: + - api + restart: on-failure + labels: + - traefik.http.routers.obsfrontend.rule=Host(`portal.example.com`) + - traefik.http.routers.obsfrontend.entrypoints=websecure + - traefik.http.routers.obsfrontend.tls=true + - traefik.http.routers.obsfrontend.tls.certresolver=leresolver + - traefik.docker.network=gateway + networks: + - gateway + - backend + + traefik: + image: traefik:2.4.8 + restart: always + ports: + - "80:80" + - "443:443" + # The Web UI (enabled by [api] in traefik.toml) + # - "8080:8080" + + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - ./config/traefik.toml:/traefik.toml + - ./config/usersfile:/usersfile + - ./data/acme.json:/acme.json + + networks: + - gateway + + labels: + # global redirect from http to https + - "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)" + - "traefik.http.routers.http-catchall.entrypoints=web" + # Define middlewares to be used + - "traefik.http.routers.http-catchall.middlewares=redirect-http-to-https" + # Configure middlewares + - "traefik.http.middlewares.redirect-http-to-https.redirectscheme.scheme=https" + + # Show Traefik Dashboard. Enable the dashboard in traefik.toml if you use these. + # - "traefik.http.routers.traefik.rule=Host(`traefik.example.com`)" + # - "traefik.http.routers.traefik.service=api@internal" + # - "traefik.http.routers.traefik.tls=true" + # - "traefik.http.routers.traefik.entrypoints=websecure" + # - "traefik.http.routers.traefik.tls.certresolver=leresolver" + # - "traefik.http.routers.traefik.middlewares=basic-auth" + # - "traefik.http.middlewares.basic-auth.basicauth.usersfile=/usersfile" diff --git a/deployment/examples/traefik.toml b/deployment/examples/traefik.toml new file mode 100644 index 0000000..a4b1f5e --- /dev/null +++ b/deployment/examples/traefik.toml @@ -0,0 +1,29 @@ +# https://doc.traefik.io/traefik/v2.4/routing/entrypoints/ +[entryPoints] + [entryPoints.web] + address = ":80" + + [entryPoints.web.http] + [entryPoints.web.http.redirections] + [entryPoints.web.http.redirections.entryPoint] + to = "websecure" + scheme = "https" + + [entryPoints.websecure] + address = ":443" + +# Enable API +[api] + dashboard = false + +# Enable docker backend +[providers.docker] + network = "gateway" + +# https://doc.traefik.io/traefik/v2.4/https/acme/#configuration-examples +[certificatesResolvers.leresolver.acme] + email = "info@example.com" + storage = "acme.json" + [certificatesResolvers.leresolver.acme.httpChallenge] + # used during the challenge + entryPoint = "web"