feat(infra): стенд целиком — Kafka, Airflow 3, Superset и мониторинг одной командой
Зачем: рядом с кластером ClickHouse не хватало остальной платформы, а поднимать её по кускам — значит каждый раз вспоминать порядок. Теперь `make up` даёт стенд целиком, а `make smoke` честно отвечает, работает он или нет. Что: - Kafka в режиме KRaft (без ZooKeeper), Postgres под метаданные, Airflow 3.3 четырьмя сервисами и Superset 6.1 с драйверами ClickHouse; - мониторинг: Prometheus снимает метрики с обеих нод ClickHouse и keeper, Grafana получает подготовленный источник данных; - подключение Airflow ведёт на ноду 1, подключение Superset — на ноду 2: ловушка правильных ошибок, забытый `ON CLUSTER` виден в дашборде сам; - `scripts/stand-smoke.sh` — сквозная проверка из 24 пунктов: топик в Kafka, цели Prometheus, запуск примера DAG через API Airflow, проверка подключения Superset и расход памяти против порога 3,4 ГБ; - `make config-test` — статические ворота: Compose, синтаксис Bash и Python, стражи README; стражи smoke проверяют, что отчёт краснеет на сломанном стенде и зеленеет после восстановления; - решения записаны в `docs/adr/0001-stand-services.md`, состав стенда и порядок работы — в README. Проверка: на чистых томах `make clean` → `cp .env.example .env` → `make up` (1 мин 51 с) → `make smoke` — 24 пройдено, 0 ошибок, 2171 MiB. Перезапуск `make down` → `make up` → `make smoke` — 24/0. Также зелены `make config-test` (3/0 и 5/0), `make smoke-cluster` (8/8) и `make smoke-guards` (3/0 и 3/0). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Executable
+31
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
install -d -o airflow -g root -m 0775 /opt/airflow/auth
|
||||
|
||||
# SimpleAuthManager не умеет создавать пользователей через CLI: пароль хранится в JSON.
|
||||
runuser -u airflow -- python - <<'PY'
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
passwords_file = Path(os.environ["AIRFLOW__CORE__SIMPLE_AUTH_MANAGER_PASSWORDS_FILE"])
|
||||
passwords_file.write_text(
|
||||
json.dumps(
|
||||
{os.environ["AIRFLOW_ADMIN_USER"]: os.environ["AIRFLOW_ADMIN_PASSWORD"]},
|
||||
ensure_ascii=False,
|
||||
)
|
||||
+ "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
PY
|
||||
|
||||
runuser -u airflow -- airflow db migrate
|
||||
runuser -u airflow -- airflow connections delete clickhouse_default >/dev/null 2>&1 || true
|
||||
runuser -u airflow -- airflow connections add clickhouse_default \
|
||||
--conn-type generic \
|
||||
--conn-host clickhouse-01 \
|
||||
--conn-port 8123 \
|
||||
--conn-login default \
|
||||
--conn-schema default \
|
||||
--conn-description "ClickHouse, нода 1; типизированный провайдер появится на этапе ETL"
|
||||
@@ -0,0 +1,11 @@
|
||||
<clickhouse>
|
||||
<!-- Встроенная точка метрик проверена на образе ClickHouse 26.3.17.56. -->
|
||||
<prometheus>
|
||||
<endpoint>/metrics</endpoint>
|
||||
<port>9363</port>
|
||||
<metrics>true</metrics>
|
||||
<events>true</events>
|
||||
<asynchronous_metrics>true</asynchronous_metrics>
|
||||
<errors>true</errors>
|
||||
</prometheus>
|
||||
</clickhouse>
|
||||
@@ -6,6 +6,15 @@
|
||||
</logger>
|
||||
<listen_host>0.0.0.0</listen_host>
|
||||
|
||||
<!-- Метрики keeper; схема проверена на образе ClickHouse 26.3.17.56. -->
|
||||
<prometheus>
|
||||
<endpoint>/metrics</endpoint>
|
||||
<port>9363</port>
|
||||
<metrics>true</metrics>
|
||||
<events>true</events>
|
||||
<asynchronous_metrics>true</asynchronous_metrics>
|
||||
</prometheus>
|
||||
|
||||
<keeper_server>
|
||||
<tcp_port>9181</tcp_port>
|
||||
<server_id>1</server_id>
|
||||
@@ -24,4 +33,3 @@
|
||||
</raft_configuration>
|
||||
</keeper_server>
|
||||
</clickhouse>
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
apiVersion: 1
|
||||
|
||||
datasources:
|
||||
- name: Prometheus
|
||||
uid: prometheus
|
||||
type: prometheus
|
||||
access: proxy
|
||||
url: http://prometheus:9090
|
||||
isDefault: true
|
||||
editable: false
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Один сервер, но отдельные владельцы и базы не смешивают метаданные приложений.
|
||||
psql --set=ON_ERROR_STOP=1 \
|
||||
--set=airflow_user="$AIRFLOW_METADATA_USER" \
|
||||
--set=airflow_password="$AIRFLOW_METADATA_PASSWORD" \
|
||||
--set=superset_user="$SUPERSET_METADATA_USER" \
|
||||
--set=superset_password="$SUPERSET_METADATA_PASSWORD" \
|
||||
--username "$POSTGRES_USER" \
|
||||
--dbname postgres <<'SQL'
|
||||
SELECT format('CREATE ROLE %I LOGIN PASSWORD %L', :'airflow_user', :'airflow_password')
|
||||
WHERE NOT EXISTS (SELECT FROM pg_roles WHERE rolname = :'airflow_user') \gexec
|
||||
|
||||
SELECT format('CREATE ROLE %I LOGIN PASSWORD %L', :'superset_user', :'superset_password')
|
||||
WHERE NOT EXISTS (SELECT FROM pg_roles WHERE rolname = :'superset_user') \gexec
|
||||
|
||||
SELECT format('CREATE DATABASE airflow OWNER %I', :'airflow_user')
|
||||
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'airflow') \gexec
|
||||
|
||||
SELECT format('CREATE DATABASE superset OWNER %I', :'superset_user')
|
||||
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'superset') \gexec
|
||||
SQL
|
||||
@@ -0,0 +1,18 @@
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
scrape_timeout: 10s
|
||||
|
||||
scrape_configs:
|
||||
- job_name: clickhouse
|
||||
static_configs:
|
||||
- targets:
|
||||
- clickhouse-01:9363
|
||||
- clickhouse-02:9363
|
||||
labels:
|
||||
role: server
|
||||
- job_name: clickhouse-keeper
|
||||
static_configs:
|
||||
- targets:
|
||||
- clickhouse-keeper:9363
|
||||
labels:
|
||||
role: keeper
|
||||
@@ -0,0 +1,12 @@
|
||||
ARG SUPERSET_BASE_IMAGE
|
||||
FROM ${SUPERSET_BASE_IMAGE}
|
||||
|
||||
USER root
|
||||
|
||||
# В основном образе нет ни драйвера ClickHouse, ни драйвера Postgres.
|
||||
RUN . /app/.venv/bin/activate \
|
||||
&& uv pip install --no-cache \
|
||||
"clickhouse-connect==1.6.0" \
|
||||
"psycopg2-binary==2.9.12"
|
||||
|
||||
USER superset
|
||||
@@ -0,0 +1,14 @@
|
||||
database_name: ClickHouse
|
||||
sqlalchemy_uri: clickhousedb://default@clickhouse-02:8123/default
|
||||
uuid: 4b8f2c6e-1d3a-4f5b-9c7d-2e8a1f0b3c5d
|
||||
cache_timeout: null
|
||||
expose_in_sqllab: true
|
||||
allow_run_async: false
|
||||
allow_ctas: false
|
||||
allow_cvas: false
|
||||
allow_dml: false
|
||||
allow_file_upload: false
|
||||
impersonate_user: false
|
||||
extra:
|
||||
allows_virtual_table_explore: true
|
||||
version: 1.0.0
|
||||
@@ -0,0 +1,3 @@
|
||||
version: 1.0.0
|
||||
type: Database
|
||||
timestamp: '2026-07-30T00:00:00+00:00'
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
superset db upgrade
|
||||
if python - <<'PY'
|
||||
import os
|
||||
|
||||
from sqlalchemy import create_engine, text
|
||||
|
||||
engine = create_engine(os.environ["SUPERSET__SQLALCHEMY_DATABASE_URI"])
|
||||
with engine.connect() as connection:
|
||||
exists = connection.scalar(
|
||||
text("SELECT EXISTS (SELECT 1 FROM ab_user WHERE username = :username)"),
|
||||
{"username": os.environ["SUPERSET_ADMIN_USER"]},
|
||||
)
|
||||
raise SystemExit(0 if exists else 1)
|
||||
PY
|
||||
then
|
||||
superset fab reset-password \
|
||||
--username "$SUPERSET_ADMIN_USER" \
|
||||
--password "$SUPERSET_ADMIN_PASSWORD"
|
||||
else
|
||||
superset fab create-admin \
|
||||
--username "$SUPERSET_ADMIN_USER" \
|
||||
--password "$SUPERSET_ADMIN_PASSWORD" \
|
||||
--firstname Superset \
|
||||
--lastname Admin \
|
||||
--email admin@example.invalid
|
||||
fi
|
||||
superset init
|
||||
superset import-directory /app/import --overwrite
|
||||
@@ -0,0 +1,17 @@
|
||||
"""Минимальные настройки локального учебного Superset."""
|
||||
|
||||
import os
|
||||
|
||||
SQLALCHEMY_DATABASE_URI = os.environ["SUPERSET__SQLALCHEMY_DATABASE_URI"]
|
||||
SECRET_KEY = os.environ["SUPERSET_SECRET_KEY"]
|
||||
|
||||
SESSION_COOKIE_NAME = "superset_session"
|
||||
SESSION_COOKIE_SECURE = False
|
||||
SESSION_COOKIE_HTTPONLY = True
|
||||
SESSION_COOKIE_SAMESITE = "Lax"
|
||||
|
||||
WTF_CSRF_ENABLED = True
|
||||
CACHE_CONFIG = {
|
||||
"CACHE_TYPE": "SimpleCache",
|
||||
"CACHE_DEFAULT_TIMEOUT": 300,
|
||||
}
|
||||
Reference in New Issue
Block a user