1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
"""Browser-facing Grafana for the private scale-to-zero ClickHouse endpoint.
Create a Modal Secret named ``atop-grafana`` before deploying. It needs:
``CLICKHOUSE_HOST`` (hostname only), ``MODAL_KEY``, ``MODAL_SECRET``, and
the Pocket ID Generic OAuth settings, including ``GF_SERVER_ROOT_URL``. A
separate ``atop-grafana-clickhouse-auth`` Secret must contain the nonempty
``CLICKHOUSE_GRAFANA_PASSWORD`` for the read-only ``grafana_reader`` user.
Deploy with ``modal deploy modal_grafana.py``.
"""
import subprocess
import time
import modal
APP_NAME = "atop-grafana"
DATA_VOLUME_NAME = "atop-grafana-data"
IDLE_SECONDS = 300
app = modal.App(APP_NAME)
data_volume = modal.Volume.from_name(DATA_VOLUME_NAME, create_if_missing=True)
grafana_secret = modal.Secret.from_name(
"atop-grafana",
required_keys=[
"CLICKHOUSE_HOST", "MODAL_KEY", "MODAL_SECRET", "GF_SERVER_ROOT_URL",
"GF_AUTH_GENERIC_OAUTH_CLIENT_ID", "GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET",
"GF_AUTH_GENERIC_OAUTH_AUTH_URL", "GF_AUTH_GENERIC_OAUTH_TOKEN_URL",
"GF_AUTH_GENERIC_OAUTH_API_URL", "GF_AUTH_GENERIC_OAUTH_JWK_SET_URL",
],
)
clickhouse_auth_secret = modal.Secret.from_name(
"atop-grafana-clickhouse-auth",
required_keys=["CLICKHOUSE_GRAFANA_PASSWORD"],
)
image = (
modal.Image.debian_slim(python_version="3.12")
.apt_install("adduser", "ca-certificates", "curl", "libfontconfig1", "musl")
.run_commands(
"curl --fail --location --silent --show-error "
"https://dl.grafana.com/oss/release/grafana_11.6.0_amd64.deb "
"--output /tmp/grafana.deb "
"&& echo 'c5159ce58c1eb7b4a47474951f0334055dd1a4b3325ffc432ef1761da6474b2a /tmp/grafana.deb' "
"| sha256sum --check "
"&& apt-get update "
"&& apt-get install --yes --no-install-recommends /tmp/grafana.deb "
"&& /usr/share/grafana/bin/grafana cli --homepath=/usr/share/grafana "
"--pluginsDir=/opt/grafana/plugins "
"plugins install grafana-clickhouse-datasource"
)
.add_local_dir("grafana/provisioning", "/etc/grafana/provisioning")
)
@app.function(
image=image,
secrets=[grafana_secret, clickhouse_auth_secret],
volumes={"/var/lib/grafana": data_volume},
min_containers=0,
max_containers=1,
scaledown_window=IDLE_SECONDS,
timeout=15 * 60,
env={
"GF_SERVER_HTTP_ADDR": "0.0.0.0",
"GF_PATHS_DATA": "/var/lib/grafana",
"GF_PATHS_LOGS": "/var/log/grafana",
"GF_PATHS_PLUGINS": "/opt/grafana/plugins",
"GF_DATABASE_WAL": "false",
"GF_PATHS_PROVISIONING": "/etc/grafana/provisioning",
"GF_AUTH_DISABLE_LOGIN_FORM": "true",
"GF_AUTH_GENERIC_OAUTH_ENABLED": "true",
"GF_AUTH_GENERIC_OAUTH_NAME": "Pocket ID",
"GF_AUTH_GENERIC_OAUTH_SCOPES": "openid profile email offline_access",
"GF_AUTH_GENERIC_OAUTH_ALLOW_SIGN_UP": "true",
"GF_AUTH_GENERIC_OAUTH_AUTO_LOGIN": "true",
"GF_AUTH_GENERIC_OAUTH_USE_PKCE": "true",
"GF_AUTH_GENERIC_OAUTH_USE_REFRESH_TOKEN": "true",
"GF_AUTH_GENERIC_OAUTH_VALIDATE_ID_TOKEN": "true",
},
)
@modal.concurrent(max_inputs=100)
@modal.web_server(3000, startup_timeout=120)
def grafana() -> None:
"""Run a public Grafana login page; its data-source credentials stay server-side."""
grafana_process = subprocess.Popen([
"/usr/share/grafana/bin/grafana", "server",
"--homepath=/usr/share/grafana", "--config=/etc/grafana/grafana.ini",
])
time.sleep(1)
if grafana_process.poll() is not None:
raise RuntimeError(f"Grafana exited during startup ({grafana_process.returncode})")
|