DeploymentΒΆ
b3lb is distributed as Docker image and the following deployment blueprint is based on Docker Compose. As reverse proxy traefik is used. The following templates are provided:
docker-compose.yml
The compose file deploying all services on a single node. For scale-out you need some deployment environment allowing you to scale the components.
conf.d/traefik/config.yml
The traefik configuration.
conf.d/traefik/traefik.yml
Static middlewares for traefik used in the compose file.
Hint
You need at least to change the options tagged with TODO:
in the following templates. You might use the templates with Jinja2 and set the variables ansible_fqdn
, api_base_domain
, assets_domain
, tsig_key
and tsig_secret
appropriately. For single domain name setups you need to update the traefik rules labels and change the certificat resolver setting to traefik.http.routers.*.tls.certResolver=acmeTLS
.
version: '3'
services:
# reverse proxy
traefik:
image: traefik:2.5.6
read_only: true
ports:
- 80:80
- 443:443
volumes:
- ./conf.d/traefik/config.yml:/etc/traefik/config.yml:ro
- ./conf.d/traefik/traefik.yml:/etc/traefik/traefik.yml:ro
- ./data.d/traefik/acme:/etc/traefik/acme
- /var/run/docker.sock:/var/run/docker.sock
environment:
RFC2136_DNS_TIMEOUT: 21
# TODO: nameserver to be used for DNS-01 challenge
RFC2136_NAMESERVER: 192.0.2.53
RFC2136_TSIG_ALGORITHM: hmac-sha512.
# TODO: tsig key used for auth
RFC2136_TSIG_KEY: {{ tsig_key }}
RFC2136_TSIG_SECRET: {{ tsig_secret }}
labels:
- traefik.enable=true
# HOST: publish traefik dashboard
- traefik.http.routers.traefik.entrypoints=https
- traefik.http.routers.traefik.rule=Host(`{{ ansible_fqdn }}`)
- traefik.http.routers.traefik.middlewares=management-chain@file
- traefik.http.routers.traefik.tls=true
- traefik.http.routers.traefik.tls.options=default
- traefik.http.routers.traefik.tls.certResolver=acmeTLS
- traefik.http.routers.traefik.service=api@internal
# HOST: publish traefik ping service
- traefik.http.routers.traefik-ping.entrypoints=https
- traefik.http.routers.traefik-ping.rule=Host(`{{ ansible_fqdn }}`) && PathPrefix(`/ping`)
- traefik.http.routers.traefik-ping.middlewares=endpoint-chain@file
- traefik.http.routers.traefik-ping.tls=true
- traefik.http.routers.traefik-ping.tls.options=default
- traefik.http.routers.traefik-ping.tls.certResolver=acmeTLS
- traefik.http.routers.traefik-ping.service=ping@internal
# HOST: publish traefik prometheus metrics
- traefik.http.routers.prometheus.entrypoints=https
- traefik.http.routers.prometheus.rule=Host(`{{ ansible_fqdn }}`) && PathPrefix(`/metrics`)
- traefik.http.routers.prometheus.middlewares=management-chain@file
- traefik.http.routers.prometheus.tls=true
- traefik.http.routers.prometheus.tls.options=default
- traefik.http.routers.prometheus.tls.certResolver=acmeTLS
- traefik.http.routers.prometheus.service=prometheus@internal
networks:
- rp
restart: always
logging: &default_logging
driver: "json-file"
options:
max-size: "1M"
max-file: "5"
# b3lb frontend
django:
image: quay.io/ibh/b3lb:2.2.2
env_file:
- ./conf.d/b3lb/env
labels:
# TENANT: /bigbluebutton/api
# /b3lb/t/TENANT/bbb/api
- traefik.enable=true
- traefik.http.routers.api.entrypoints=https
- traefik.http.routers.b3lb-api.rule=(HostRegexp(`{[a-z0-9-]+}.{{ api_base_domain }}`) && PathPrefix(`/bigbluebutton/api/`)) || (Host(`{{ api_base_domain }}`) && PathPrefix(`/b3lb/t/{[a-z0-9-]+}/bbb/api/`))
- traefik.http.routers.api.middlewares=endpoint-chain@file
- traefik.http.routers.api.tls=true
- traefik.http.routers.api.tls.options=default
- traefik.http.routers.api.tls.certResolver=acmeDNS
- "traefik.http.routers.api.tls.domains[0].main={{ api_base_domain }}"
- "traefik.http.routers.api.tls.domains[0].sans=*.{{ api_base_domain }}"
- traefik.http.routers.api.service=api
- traefik.http.services.api.loadbalancer.server.port=8000
# TENANT: /b3lb/t/TENANT/logo
# /b3lb/t/TENANT/slide
- traefik.http.routers.b3lb-assets.entrypoints=https
- traefik.http.routers.b3lb-assets.rule=Host(`{{ api_base_domain }}`) && Path(`/b3lb/t/{[a-z0-9-]+}/logo`, `/b3lb/t/{[a-z0-9-]+}/slide`)
- traefik.http.routers.b3lb-assets.middlewares=endpoint-chain@file
- traefik.http.routers.b3lb-assets.tls=true
- traefik.http.routers.b3lb-assets.tls.options=default
- traefik.http.routers.b3lb-assets.tls.certResolver=acmeDNS
- traefik.http.routers.b3lb-assets.service=b3lb-assets
- traefik.http.services.b3lb-assets.loadbalancer.server.port=8000
- "traefik.http.routers.b3lb-assets.tls.domains[0].main={{ api_base_domain }}"
- "traefik.http.routers.b3lb-assets.tls.domains[0].sans=*.{{ api_base_domain }}"
# GLOBAL: /b3lb/ping
# TENANT: /b3lb/ping
- traefik.http.routers.api-ping.entrypoints=https
- traefik.http.routers.api-ping.rule=(HostRegexp(`{{ api_base_domain }}`) || HostRegexp(`{tenant:[a-z0-9-]+}.{{ api_base_domain }}`)) && Path(`/b3lb/ping`)
- traefik.http.routers.api-ping.middlewares=endpoint-chain@file
- traefik.http.routers.api-ping.tls=true
- traefik.http.routers.api-ping.tls.options=default
- traefik.http.routers.api-ping.tls.certResolver=acmeDNS
- "traefik.http.routers.api-ping.tls.domains[0].main={{ api_base_domain }}"
- "traefik.http.routers.api-ping.tls.domains[0].sans=*.{{ api_base_domain }}"
- traefik.http.routers.api-ping.service=api-ping
- traefik.http.services.api-ping.loadbalancer.server.port=8000
# TENANT: /b3lb/stats
# TENANT: /b3lb/metrics
- traefik.http.routers.stats.entrypoints=https
- traefik.http.routers.b3lb-stats.rule=(HostRegexp(`{[a-z0-9-]+}.{{ api_base_domain }}`) && Path(`/b3lb/stats`, `/b3lb/metrics`)) || (Host(`{{ api_base_domain }}`) && Path(`/b3lb/t/{[a-z0-9-]+}/stats`, `/b3lb/t/{[a-z0-9-]+}/metrics`))
- traefik.http.routers.stats.middlewares=endpoint-chain@file
- traefik.http.routers.stats.tls=true
- traefik.http.routers.stats.tls.options=default
- traefik.http.routers.stats.tls.certResolver=acmeDNS
- traefik.http.routers.stats.service=stats
- traefik.http.services.stats.loadbalancer.server.port=8000
- "traefik.http.routers.stats.tls.domains[0].main={{ api_base_domain }}"
- "traefik.http.routers.stats.tls.domains[0].sans=*.{{ api_base_domain }}"
# GLOBAL: /admin/ /files/ /b3lb/metrics
- traefik.http.routers.admin.entrypoints=https
- traefik.http.routers.b3lb-admin.rule=Host(`{{ api_base_domain }}`) && (PathPrefix(`/admin/`, `/files/`) || Path(`/b3lb/metrics`))
- traefik.http.routers.admin.middlewares=management-chain@file
- traefik.http.routers.admin.tls=true
- traefik.http.routers.admin.tls.options=default
- traefik.http.routers.admin.tls.certResolver=acmeDNS
- traefik.http.routers.admin.service=admin
- traefik.http.services.admin.loadbalancer.server.port=8000
- "traefik.http.routers.admin.tls.domains[0].main={{ api_base_domain }}"
- "traefik.http.routers.admin.tls.domains[0].sans=*.{{ api_base_domain }}"
networks:
- rp
- lb
restart: always
logging:
<<: *default_logging
# static assets: logos, slides and Django admin
static:
image: quay.io/ibh/b3lb-static:2.2.2
labels:
# Django admin static assets
- traefik.enable=true
- traefik.http.routers.static.entrypoints=https
- traefik.http.routers.static.rule=Host(`{{ api_base_domain }}`) && PathPrefix(`/static/`)
- traefik.http.routers.static.middlewares=management-chain@file,static-strip
- traefik.http.routers.static.tls=true
- traefik.http.routers.static.tls.options=default
- traefik.http.routers.static.tls.certResolver=acmeDNS
- traefik.http.middlewares.static-strip.stripprefix.prefixes=/static
- traefik.http.services.static.loadbalancer.server.port=8001
- "traefik.http.routers.static.tls.domains[0].main={{ api_base_domain }}"
- "traefik.http.routers.static.tls.domains[0].sans=*.{{ api_base_domain }}"
networks:
- rp
restart: always
logging:
<<: *default_logging
# celery scheduling
celery-beat:
image: quay.io/ibh/b3lb:2.2.2
command: celery-beat
env_file:
- ./conf.d/b3lb/env
networks:
- lb
restart: always
logging:
<<: *default_logging
# celery worker
celery-tasks:
image: quay.io/ibh/b3lb:2.2.2
#
# ---==] PyPy [==---
#
# Consider to change to PyPy if the processing speed is to slow.
# You need to replace the image and add a reasonable high cgroup
# memory limit:
#
# image: quay.io/ibh/b3lb-pypy:2.2.2
# mem_limit: 10g
#
# ---==] PyPy [==---
#
command: celery-tasks
env_file:
- ./conf.d/b3lb/env
networks:
- lb
restart: always
logging:
<<: *default_logging
# cache
redis:
image: redis:6.0.16-alpine
# TODO: Adjust max memory!
# TODO: Set your redis secret!
command: redis-server --maxmemory 4096mb --maxmemory-policy volatile-lfu --requirepass {{ redis_secret }}
networks:
- lb
restart: always
logging:
<<: *default_logging
networks:
# b3lb internal
lb:
# reverse proxy
rp:
http:
middlewares:
# add security related http headers
# https://doc.traefik.io/traefik/middlewares/headers/#configuration-options
security-headers:
headers:
frameDeny: true
sslRedirect: true
browserXssFilter: true
contentTypeNosniff: true
forceSTSHeader: true
stsSeconds: 31536000
stsIncludeSubdomains: true
stsPreload: true
# prevent search enginge indexing
x-robots-tag:
headers:
customResponseHeaders:
X-Robots-Tag: "noindex, nofollow, noarchive, nosnippet, notranslate, noimageindex"
# list of ip prefixes allowed to access management and metrics
mgmt-whitelist:
ipWhiteList:
sourceRange:
# TODO: Add your management ip prefixes!
- 127.0.0.0/8
# middleware chain used for public endpoints
endpoint-chain:
chain:
middlewares:
- security-headers
- x-robots-tag
# middleware chain used for management endpoints
management-chain:
chain:
middlewares:
- security-headers
- x-robots-tag
- mgmt-whitelist
tls:
options:
# TLS settings
default:
sniStrict: true
minVersion: VersionTLS12
preferServerCipherSuites: true
cipherSuites:
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
curvePreferences:
- X25519
- CurveP521
- CurveP384
# disable traefik call home
global:
checkNewVersion: false
sendAnonymousUsage: false
# enable traefik dashboard
api:
dashboard: true
# fix traefik's dashboard privacy issue
# (https://github.com/traefik/traefik/issues/7699)
pilot:
dashboard: false
# enable traefik ping handler
ping:
manualRouting: true
# enable traefik prometheus metrics export
metrics:
prometheus:
manualRouting: true
# entrypoints for http and https
entryPoints:
http:
address: ":80"
http:
redirections:
entryPoint:
to: https
scheme: https
https:
address: ":443"
http:
tls:
options: default
# add docker and file providers
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
watch: true
exposedByDefault: false
# Needs to match the network name created by
# docker-compose!
network: b3lb_rp
file:
filename: /etc/traefik/config.yml
# frontend certificates
certificatesResolvers:
# required for wildcard DNS entries
acmeDNS:
acme:
# TODO: Adding an email address is required!
#email:
storage: /etc/traefik/acme/acmeDNS.json
dnsChallenge:
provider: rfc2136
# sufficient for single domain name setups
acmeTLS:
acme:
# TODO: Adding an email address is required!
#email:
storage: /etc/traefik/acme/acmeTLS.json
tlsChallenge: {}
# use default logging
log: {}
# enable access logging only for failed or high latency requests
accessLog:
filters:
statusCodes:
- "400-499"
- "500-599"
retryAttempts: true
minDuration: "500ms"