Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ yq
**/.env-devel
**/.stack.*.yml
**/.stack.*.yaml
docker-compose.yml

stack.yml
stack_with_prefix.yml
docker-compose.simcore.yml
Expand Down
3 changes: 3 additions & 0 deletions scripts/common-services.Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
STACK_NAME = $(notdir $(shell pwd))
TEMP_COMPOSE=.stack.${STACK_NAME}.yaml
REPO_BASE_DIR := $(shell git rev-parse --show-toplevel)
2 changes: 2 additions & 0 deletions services/metabase/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
configure_metabase.sql
docker-compose.yml
17 changes: 17 additions & 0 deletions services/metabase/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
REPO_BASE_DIR := $(shell git rev-parse --show-toplevel)

include ${REPO_BASE_DIR}/scripts/common.Makefile
include ${REPO_BASE_DIR}/scripts/common-services.Makefile

.PHONY: up
up: ${TEMP_COMPOSE} ## Deploys metabase stack

${TEMP_COMPOSE}: docker-compose.yml .env
@${REPO_BASE_DIR}/scripts/docker-stack-config.bash -e .env $< > $@

docker-compose.yml: docker-compose.yml.j2 .env .venv
@$(call jinja, $<, .env, $@)

configure_metabase.sql: .env
@set -o allexport; source $<; set +o allexport; \
envsubst < [email protected] > $@
7 changes: 7 additions & 0 deletions services/metabase/configure_metabase.sql.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
CREATE USER ${METABASE_POSTGRES_USER} WITH PASSWORD '${METABASE_POSTGRES_PASSWORD}';

-- relies on readonly role aldready existing in the database
GRANT ${POSTGRES_DB}_readonly TO ${METABASE_POSTGRES_USER};

CREATE DATABASE ${METABASE_POSTGRES_DB}
WITH OWNER ${METABASE_POSTGRES_USER};
91 changes: 91 additions & 0 deletions services/metabase/docker-compose.yml.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# https://www.metabase.com/docs/v0.54/installation-and-operation/running-metabase-on-docker

# https://www.metabase.com/docs/v0.54/installation-and-operation/running-metabase-on-docker#example-docker-compose-yaml-file
services:
metabase:
image: metabase/metabase:v0.54.13.3
networks:
- public
- monitored
environment:
- MB_HEALTH_CHECK_LOGGING_ENABLED=false
# Avoid: site URL basename "/" does not match the actual basename
# https://www.metabase.com/docs/latest/configuring-metabase/environment-variables#mb_site_url
- MB_SITE_URL=https://${ADMIN_DOMAIN}/metabase/
# Metabase logs: https://www.metabase.com/docs/v0.54/configuring-metabase/log-configuration
- JAVA_OPTS=-Dlog4j.configurationFile=file:/etc/metabase/log4j2.xml
# https://www.metabase.com/docs/v0.54/installation-and-operation/configuring-application-database
- MB_DB_TYPE=postgres
- MB_DB_DBNAME=metabase
- MB_DB_HOST=${POSTGRES_HOST}
- MB_DB_PORT=${POSTGRES_PORT}
- MB_DB_USER=${METABASE_POSTGRES_USER}
- MB_DB_PASS=${METABASE_POSTGRES_PASSWORD}
# https://www.metabase.com/docs/v0.54/installation-and-operation/running-metabase-on-docker#setting-the-java-timezone
- JAVA_TIMEZONE=UTC
# https://www.metabase.com/docs/latest/installation-and-operation/observability-with-prometheus
- MB_PROMETHEUS_SERVER_PORT=9191
deploy:
# https://www.metabase.com/learn/metabase-basics/administration/administration-and-operation/metabase-at-scale
replicas: ${METABASE_REPLICAS}
update_config:
parallelism: 1
order: start-first
failure_action: rollback
delay: 30s
placement:
constraints:
- node.labels.ops==true
labels:
# prometheus only keeps jobs with `prometheus-job` label
- prometheus-job=metabase
# Set in `MB_PROMETHEUS_SERVER_PORT` environment variable
- prometheus-port=9191
# TODO: add prometheus metrics
- traefik.enable=true
- traefik.docker.network=${PUBLIC_NETWORK}
# router
- traefik.http.routers.metabase.rule=Host(`${ADMIN_DOMAIN}`) && PathPrefix(`/metabase`)
- traefik.http.routers.metabase.entrypoints=https
- traefik.http.routers.metabase.tls=true
- traefik.http.middlewares.metabase_stripprefixregex.stripprefixregex.regex=^/metabase
- traefik.http.routers.metabase.middlewares=ops_whitelist_ips@swarm, ops_gzip@swarm, ops_auth@swarm, metabase_stripprefixregex
# service
- traefik.http.services.metabase.loadbalancer.server.port=3000
- traefik.http.services.metabase.loadbalancer.healthcheck.path=/api/health
- traefik.http.services.metabase.loadbalancer.healthcheck.interval=10s
# GET method sometimes fails (`Request canceled before finishing`) log
# This does not happen with HEAD method. Official healthcheck documentation
# defines HEAD as well
- traefik.http.services.metabase.loadbalancer.healthcheck.method=HEAD
- traefik.http.services.metabase.loadbalancer.healthcheck.timeout=1s

# https://www.metabase.com/learn/metabase-basics/administration/administration-and-operation/metabase-in-production
resources:
limits:
memory: 2G
cpus: "2.0"
reservations:
memory: 1G
cpus: "1.0"
healthcheck:
test: curl --fail -I http://localhost:3000/api/health || exit 1
interval: 15s
timeout: 5s
retries: 5
configs:
- source: logging_configuration
target: /etc/metabase/log4j2.xml

configs:
logging_configuration:
file: ./logging_configuration.xml
name: {{ STACK_NAME }}_logging_configuration_{{ "./logging_configuration.xml" | sha256file | substring(0,10) }}

networks:
public:
external: true
name: ${PUBLIC_NETWORK}
monitored:
name: ${MONITORED_NETWORK}
external: true
30 changes: 30 additions & 0 deletions services/metabase/logging_configuration.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Original file: https://github.com/metabase/metabase/blob/v0.54.14.3/resources/log4j2.xml -->

<Configuration>
<Appenders>
<Console name="STDOUT" target="SYSTEM_OUT" follow="true">
<PatternLayout pattern="%date %level %logger{2} :: %message %notEmpty{%X}%n%throwable">
<replace regex=":basic-auth \\[.*\\]" replacement=":basic-auth [redacted]"/>
</PatternLayout>
</Console>
</Appenders>

<Loggers>
<Logger name="com.mchange" level="ERROR"/>
<Logger name="liquibase" level="INFO"/>
<Logger name="metabase" level="INFO"/>
<Logger name="metabase-enterprise" level="INFO"/>
<Logger name="metabase.metabot" level="INFO"/>
<Logger name="metabase.plugins" level="INFO"/>
<Logger name="metabase.query-processor.async" level="INFO"/>
<Logger name="metabase.server.middleware" level="INFO"/>
<Logger name="org.quartz" level="INFO"/>
<Logger name="net.snowflake.client.jdbc.SnowflakeConnectString" level="ERROR"/>
<Logger name="net.snowflake.client.core.SessionUtil" level="FATAL"/>

<Root level="WARN">
<AppenderRef ref="STDOUT"/>
</Root>
</Loggers>
</Configuration>
16 changes: 16 additions & 0 deletions services/metabase/template.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
STACK_NAME=${STACK_NAME}

METABASE_REPLICAS=${METABASE_REPLICAS}

ADMIN_DOMAIN=${ADMIN_DOMAIN}

PUBLIC_NETWORK=${PUBLIC_NETWORK}
MONITORED_NETWORK=${MONITORED_NETWORK}

POSTGRES_HOST=${POSTGRES_HOST}
POSTGRES_PORT=${POSTGRES_PORT}
POSTGRES_DB=${POSTGRES_DB}

METABASE_POSTGRES_USER=${METABASE_POSTGRES_USER}
METABASE_POSTGRES_PASSWORD=${METABASE_POSTGRES_PASSWORD}
METABASE_POSTGRES_DB=${METABASE_POSTGRES_DB}
Loading