Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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 < $@.template > $@
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
43 changes: 43 additions & 0 deletions services/metabase/logging_configuration.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?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>

<!-- This file appender is provided as an example -->
<!--
<RollingFile name="FILE" fileName="${logfile.path}/metabase.log" filePattern="${logfile.path}/metabase.log.%i">
<Policies>
<SizeBasedTriggeringPolicy size="500 MB"/>
</Policies>
<DefaultRolloverStrategy max="2"/>
<PatternLayout pattern="%d [%t] %-5p%c - %m%n">
<replace regex=":basic-auth \\[.*\\]" replacement=":basic-auth [redacted]"/>
</PatternLayout>
</RollingFile>
-->
</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