eBdesk SiCABE — deploy (Installer Hasil Merge Eksperimen)
SiCABE (Solusi Integrasi Cerdas Adaptif Big Data Enterprise) adalah platform
data modern 5-lapisan (lakehouse) untuk analisis harga dan logistik kebutuhan
pokok. Folder deploy/ adalah installer hasil merge dari:
deploy-legacy/— base installer lama (yang sudah berjalan baik),experiments/nessie-dremio/— Nessie (ganti Polaris) + OPA + Dremio,experiments/starrocks-minio/— StarRocks shared-data (FE + Compute Node),experiments/starrocks-oidc/— LDAP group provider.
deploy-legacy/(base lama) tidak diubah — tetap menjadi referensi/rollback. Dokumen rencana & mapping:../reviews/sicabe-07-merge-experiments-plan.md.
Arsitektur
L1 INGESTION Airflow (base) ── dbt/Sling ──► Trino
L2 STORAGE MinIO (base) + Nessie (JDBC2/PostgreSQL) + OpenLDAP (identitas)
L3 PROCESSING Trino 483 ──► Nessie catalog; authz Trino ──► OPA
L4 SERVING StarRocks shared-data (FE + CN) | Dremio OSS 25.2.0
L5 GOVERNANCE Keycloak (user lokal) · OpenMetadata · Grafana
IDENTITY OpenLDAP (sumber grup untuk Trino & StarRocks)
DATABASE PostgreSQL shared: keycloak | nessie | airflow | openmetadata_db
Dependency Graph (urutan deploy)
Phase 1: Keycloak + OpenLDAP ──────── (tidak ada dependensi)
Phase 2: MinIO ◄──── Nessie ───────── (MinIO dulu, baru Nessie)
Phase 3: Trino ◄──── Nessie + OPA ─── (katalog Nessie, authz OPA)
Phase 4: StarRocks FE + CN (+ Dremio opt-in) ── (shared-data; engine paralel)
Phase 5: Airflow ◄──── Postgres ───── (PostgreSQL sebagai backend DB)
Phase 6: OpenMetadata ◄──── Grafana ─
Matriks Install (Fase × Layer)
A — Service yang baru diinstall per fase:
| Fase | Database | Identity | L1 Ingestion | L2 Storage & Catalog | L3 Processing | L4 Serving | L5 Governance |
|---|---|---|---|---|---|---|---|
| Phase 1 | postgres |
openldap |
— | — | — | — | keycloak |
| Phase 2 | — | — | — | minio, minio-init*, nessie |
— | — | — |
| Phase 3 | — | — | — | — | opa, trino |
— | — |
| Phase 4 | — | — | — | — | — | starrocks-fe, starrocks-cn |
— |
| Phase 5 | — | — | airflow-init, airflow-webserver, airflow-dag-processor, airflow-scheduler |
— | — | — | — |
| Phase 6 | — | — | — | — | — | — | openmetadata-opensearch, openmetadata-migrate*, openmetadata-server, grafana |
* one-shot (selesai lalu exit): minio-init (buat bucket), openmetadata-migrate (migrasi schema OM).
B — Kondisi aktif kumulatif setelah fase N (full deploy ./install.sh):
| Fase | Database | Identity | L1 Ingestion | L2 Storage & Catalog | L3 Processing | L4 Serving | L5 Governance |
|---|---|---|---|---|---|---|---|
| 1 | postgres |
openldap |
— | — | — | — | keycloak |
| 2 | postgres |
openldap |
— | minio, nessie |
— | — | keycloak |
| 3 | postgres |
openldap |
— | minio, nessie |
opa, trino |
— | keycloak |
| 4 | postgres |
openldap |
— | minio, nessie |
opa, trino |
starrocks-fe, starrocks-cn |
keycloak |
| 5 | postgres |
openldap |
airflow-* |
minio, nessie |
opa, trino |
starrocks-* |
keycloak |
| 6 | postgres |
openldap |
airflow-* |
minio, nessie |
opa, trino |
starrocks-* |
keycloak, openmetadata-*, grafana |
./install.sh --phase Nmenjalankan profilphaseN. Phase 1–4 kumulatif; phase 5–6 lebih sempit (disarankan full deploy berurutan).bootstrap.sh(9 step) dijalankan setelah install.Dremio bersifat opt-in: tidak ikut
--profile all/phaseN(installer-nya menolak heap < 4 GB sehingga boros RAM di dev). Aktifkan dengandocker compose --profile dremio up -d dremio.bootstrap.shstep 9 akan dilewati bila container Dremio tidak berjalan.
Stack
| Layer | Komponen | Versi | Catatan |
|---|---|---|---|
| Database | PostgreSQL | 15 | 1 instance, DB terpisah: keycloak/nessie/airflow/openmetadata_db |
| L2 Catalog | Project Nessie | 0.108.4-java | JDBC2 di PostgreSQL; Iceberg REST /iceberg/ |
| L2 Storage | MinIO | RELEASE.2025-09-07 | Bucket iceberg-data (warehouse + StarRocks) |
| L3 Processing | Trino | 483 | Katalog iceberg via Nessie |
| L3 Authz | OPA | latest | Deny-by-default; policy Rego + ACL config/opa/data.json |
| L4 Serving | StarRocks | 4.0.13 | shared-data (FE + CN) |
| L4 Serving | Dremio OSS (opt-in) | 25.2.0 | Engine paralel; auth lokal |
| L5 IAM | Keycloak | 26.7 | OIDC; user lokal |
| Identity | OpenLDAP | 1.5.0 | Sumber grup (data-admin, dst.) |
| L1 Ingestion | Airflow | 3.3.2 | Sling → Nessie Iceberg REST; image ber-plugin OpenMetadata (/pluginsv2/); provider MySQL (koneksi ke server MySQL eksternal) |
| L5 Metadata | OpenMetadata | 2.0.3 | Konektor Trino + Airflow; jalur DQ = Great Expectations (§8.9) |
| L5 Search | OpenSearch | 3.8.0 | Backend pencarian OM 2.0.x (security plugin dimatikan) |
| L5 Observability | Grafana | 13.1 | Provisioning file |
Kebutuhan CPU (AVX2)
Hanya satu komponen di stack ini yang mensyaratkan AVX2: StarRocks CN (BE). Build
resmi StarRocks memakai USE_AVX2=ON, dan CPU tanpa AVX2 membuat container-nya gagal start
dengan SIGILL (illegal instruction) — bukan sekadar lambat, dan saat ini tidak ada paket
resmi non-AVX2. StarRocks FE tidak terpengaruh (berbasis JVM). AVX-512 tidak wajib di
komponen mana pun.
Komponen lain berjalan di x86-64 biasa: Trino (hanya butuh 64-bit Linux + Java 25), OpenSearch (kita hanya memakai pencarian teks/BM25 — AVX2/AVX-512 hanya relevan untuk plugin k-NN yang tidak dipakai), OpenMetadata/Keycloak/Nessie (JDK 21), Airflow (CPython 3.11), serta MinIO/PostgreSQL/Grafana/Prometheus. Dremio opt-in tidak mendokumentasikan syarat ISA.
Periksa CPU calon server sebelum deploy:
grep -o -m1 avx2 /proc/cpuinfo # satu-satunya gate nyata di stack ini
lscpu | grep -o -E 'avx2|avx512[a-z]*|sse4_2|bmi2|popcnt' | sort -u
Tanpa AVX2, opsinya: lewati Fase 4 (StarRocks) dan pakai Dremio sebagai engine serving, atau
kompilasi StarRocks sendiri dengan build.sh --without-avx2. Catatan arsitektur: ppc64le
hanya didukung Trino, PostgreSQL, Prometheus, dan Airflow — komponen lain butuh x86-64 atau ARM64.
Peta Port (prefix 12xxx)
Aturan: host port = 12 + 3 digit port kanonik. Port internal container tidak berubah.
| Service | Internal | Host |
|---|---|---|
| Grafana | 3000 | 12300 |
| Dremio Flight | 32010 | 12011 |
| Dremio JDBC | 31010 | 12010 |
| StarRocks FE HTTP | 8030 | 12030 |
| Keycloak | 8080 | 12080 |
| Airflow | 8080 | 12082 |
| OpenMetadata | 8585 | 12085 |
| Nessie | 19120 | 12120 |
| OPA | 8181 | 12181 |
| Trino HTTPS | 8443 | 12443 |
| OpenSearch | 9200 | 12920 |
| MinIO API | 9000 | 12900 |
| MinIO Console | 9001 | 12901 |
| StarRocks FE MySQL | 9030 | 12930 |
| Dremio UI | 9047 | 12947 |
| OpenLDAP | 389 | 12389 |
Prasyarat
| Tool | Minimal | Catatan |
|---|---|---|
| Docker Engine | 24.x+ | docker --version |
| Docker Compose | v2.x+ | docker compose version |
| OpenSSL | 1.1+ | generate password .env |
| curl | 7.x+ | bootstrap & test |
| python3 | 3.8+ | parsing JSON |
| MinIO Client (mc) | latest | opsional |
| MySQL Client | 8.x+ | opsional (StarRocks) |
Quick Start
cd deploy
chmod +x install.sh bootstrap.sh test-verification.sh
# 1. Deploy 6 fase (~15 menit)
./install.sh
# 2. Bootstrap & integrasi (Keycloak, OpenLDAP, MinIO, StarRocks, Airflow, OM)
./bootstrap.sh
# 3. Verifikasi
./scripts/verify-phase1-iam.sh # gate kontrak IAM (Phase 1) — sebelum lanjut fase berikutnya
./scripts/verify-phase2-storage.sh # gate kontrak storage & catalog (Phase 2)
./scripts/verify-phase3-processing.sh # gate kontrak processing: Trino + OPA (Phase 3)
./scripts/verify-phase4-serving.sh # gate kontrak serving: StarRocks shared-data (Phase 4)
./scripts/verify-phase5-ingestion.sh # gate kontrak ingestion: Airflow + connections (Phase 5)
./scripts/verify-phase6-governance.sh # gate kontrak governance: OpenMetadata + Grafana (Phase 6)
# 3b. Setelah restart StarRocks FE — pulihkan dynamic config (auth chain OIDC)
./scripts/starrocks-reapply-dynamic-config.sh
# 3c. Isi katalog OpenMetadata (agent ingestion) — tanpa ini halaman Explore kosong
./scripts/ingest-openmetadata.sh
./test-verification.sh
./test-verification.sh --layer L2 # MinIO + Nessie
./test-verification.sh --layer E2E # End-to-end smoke test
# 3d. ALUR DATA (urutan ini penting)
# Ingest DULU, baru DAG:
./scripts/ingest-openmetadata.sh --all # buat entitas tabel di katalog OM
docker exec sicabe-airflow-webserver airflow dags test sicabe_batch_daily
#
# Mengapa urutannya begitu: action GE (OpenMetadataValidationAction) menempelkan
# test suite/test case ke ENTITAS TABEL di OM. Sebelum ingest, entitas itu belum ada,
# sehingga hasil Great Expectations tidak muncul di OM walau tugas DAG-nya sukses.
# Catatan: bagian LINEAGE dari --all membaca artefak dbt (target/manifest.json) yang
# baru ada SETELAH DAG berjalan — untuk lineage utuh jalankan `--dbt-only` sekali lagi
# setelah DAG, atau ulangi `--all`.
#
# 4. (Opsional) Seed tabel contoh Iceberg — namespace bronze + bronze.orders
./scripts/seed-sample-data.sh
# 5. (Opsional) Seed SUMBER PostgreSQL untuk DAG — sicabe_data.public.orders
# WAJIB bila ingin menjalankan DAG 'sicabe_batch_daily' (task ingest_to_bronze)
./scripts/seed-source-postgres.sh
Memakai komponen yang sudah ada (Keycloak/LDAP/MinIO/Nessie/Trino/OPA, atau Postgres milik DBA)? Arahkan endpoint-nya di
.env—KEYCLOAK_URL,LDAP_URL,S3_ENDPOINT,NESSIE_URL,TRINO_URL,OPA_URL(atauEXTERNAL_SERVICES="postgres ...").install.sh
Menempatkan komponen kita di host lain (multi-host). Beda dari komponen EXISTING:
di sini komponen tetap milik klaster ini, hanya dipindah host. Override variabel
*_HOST di .env dengan alamat host pemiliknya — default-nya nama container, sehingga
single-host tidak berubah:
| Variabel | Default | Untuk |
|---|---|---|
POSTGRES_HOST |
postgres |
Keycloak, Nessie, Airflow |
MINIO_HOST |
minio |
Nessie, minio-init |
KEYCLOAK_HOST |
keycloak |
Keycloak backend URL, Grafana OAuth |
OPENMETADATA_HOST |
openmetadata-server |
OM_API_URL, SERVER_HOST_API_URL |
AIRFLOW_WEBSERVER_HOST |
airflow-webserver |
AIRFLOW__WEBSERVER__BASE_URL, pipeline client |
STARROCKS_FE_HOST / STARROCKS_CN_HOST |
starrocks-fe / starrocks-cn |
registrasi CN, healthcheck |
OPENSEARCH_HOST |
openmetadata-opensearch |
ELASTICSEARCH_HOST OpenMetadata |
File
config/**yang dibaca container (.properties/.yaml/.cfg/.json/.rego) tidak bisa ekspansi env. Untuk multi-host jalankanscripts/render-config.shyang menyalinnya kegenerated/host-<x>/config/sambil mengganti hostname terdaftar:./scripts/render-config.sh --out generated/host-compute/config --dry-run \ --map postgres=10.20.0.12 --map nessie=10.20.0.10Penggantian hanya pada konteks jaringan (setelah
//, setelah@, atauhost:port) sehingga path filesystem seperti/etc/trino/dan baris komentar tidak ikut berubah. Rencana lengkap:reviews/sicabe-10-multi-host-installer-plan.md.
⚠️ Satu host = satu stack aktif. Stack single-host dan multi-host tidak untuk berjalan bersamaan di host yang sama — matikan stack lama lebih dulu sebelum menyalakan hasil
generated/host-<x>/. Berdampingan tidak cukup hanya dipisah network/port: pada host kecil hal itu juga menabrak RAM (pilot 15 GB, satu stack penuh ±10 GB).
Trino dipisah coordinator/worker. Sejak 2026-09-28 Trino bukan lagi satu service:
trino-coordinator (port host 12443, HTTPS + OAuth2/Basic, client-facing) dan
trino-worker (tanpa port host; mengeksekusi task, discovery.uri ke coordinator).
Klien tidak berubah (TRINO_URL tetap https://localhost:12443). Untuk restart:
./install.sh --restart trino-coordinator (atau trino-worker). Perubahan nama
service mewajibkan regenerasi sertifikat: ./scripts/generate-trino-keystore.sh
(SAN memuat trino-coordinator), lalu restart coordinator.
Menyiapkan server baru (Ubuntu 24.04)
Dua script, dijalankan di tiap server sebelum instalasi:
sudo ./scripts/prepare-host.sh --dry-run # lihat rencananya dulu (tidak mengubah apa pun)
sudo ./scripts/prepare-host.sh # paket apt + Docker + sysctl + precheck
./scripts/prefetch-images.sh # tarik semua image lebih dulu
prepare-host.sh memasang paket yang benar-benar dipakai script deploy
(htpasswd/apache2-utils, ldapsearch/ldap-utils, klien mysql, openssl, curl,
python3+PyYAML), memasang Docker Engine + compose v2 (lewati dengan --no-docker),
menetapkan vm.max_map_count=262144 (syarat keras OpenSearch), dan memeriksa
AVX2 (wajib starrocks-cn), jam/NTP, RAM, disk, serta port 12xxx yang akan dipakai.
Flag --swap-off (opsional) mematikan swap sesuai rekomendasi OpenSearch.
prefetch-images.sh mengambil daftar image dari docker compose config --images
(tidak di-hardcode, jadi selalu sinkron dengan compose). Default: 14 image ≈ 6,2 GB
(terukur). Pilihan: --all (default) · --host <nama> (hanya image host itu, dari
generated/host-<x>/) · --list (lihat saja).
Tiga image dikecualikan secara default dan ditampilkan sebagai catatan ("dikecualikan: …") supaya tidak terkesan terlupa:
| Dikecualikan | Alasan | Mengaktifkan |
|---|---|---|
dremio/dremio-oss:25.2.0 |
Dremio opt-in di compose (default nonaktif) | --with-dremio |
apache/airflow:slim-3.3.2-python3.11 |
hanya base untuk build lokal Airflow | --with-base |
sicabe/airflow:3.3.2-om203-ol |
dibangun lokal; sejak 2026-10-02 juga tersedia di registry | docker pull dari registry (di bawah), atau build-sekali-salin |
📌 Sejak 2026-10-02 image ini tersedia di registry internal — jadi host lain bisa menariknya, tidak harus membangun:
docker pull registry.sicabe.com/airflow:3.3.2-om203-ol # opsi A (disarankan): arahkan compose ke nama registry — tanpa retag export AIRFLOW_IMAGE=registry.sicabe.com/airflow:3.3.2-om203-ol # opsi B: beri nama yang diharapkan compose docker tag registry.sicabe.com/airflow:3.3.2-om203-ol sicabe/airflow:3.3.2-om203-ol⚠️
docker-compose.ymlmemakai${AIRFLOW_IMAGE:-sicabe/airflow:3.3.2-om203-ol}— jadi setelahpull, pilih salah satu dari dua opsi di atas; kalau tidak, compose akan mencari tagsicabe/...yang belum ada di host itu.
⚠️
sicabe/airflow:3.3.2-om203-oldibangun lokal (baseapache/airflow:slim-3.3.2-python3.11), jadi tidak tersedia di Docker Hub. ✅ Sejak 2026-10-02 fetch bisa lewat registry internal (lihat blok di atas); alur build-sekali-salin di bawah tetap tersedia untuk host yang tidak punya akses registry:# di SATU host docker build -t sicabe/airflow:3.3.2-om203-ol -f docker/airflow/Dockerfile.332 docker/airflow📌 Sejak BATCH B (review R18, 2026-10-01) varian image berdampingan:
Dockerfile.332(+requirements.332.{in,lock}) = Airflow 3.3.2 / Python 3.11 — yang dipakai sekarang;Dockerfile.slim(+requirements.{in,lock}) tetap disimpan sebagai jalur rollback ke Airflow 3.3.1. 📌 2026-10-02 — adopsi opsi C: tag-ol= image di atas + provider OpenLineage + shimeventTime. OpenLineage AKTIF mengirim lineage task Airflow ke OpenMetadata (rute langsung, tanpa Marquez).
- Transport dibaca dari KONEKSI Airflow
openlineage_config(bukan.env), sehingga token tidak tersebar di berkas../bootstrap.shmembuatnya bersamaopenmetadata_lineage_api(password = token botlineage-bot,JWTTokenExpiry=Unlimited).- Aktif/mati:
AIRFLOW_OPENLINEAGE_CONFIG_CONN_ID(defaultopenlineage_config; kosongkan untuk mematikan). ⚠️ Koneksinya harus sudah ada — pemuatan konfigurasi provider tidak dibungkus try/except (bisa menggagalkan task).- ⚠️
AIRFLOW__OPENLINEAGE__EXECUTE_IN_THREAD=truewajib; tanpa itu proses emisi HANG lalu dimatikan dan tidak ada event yang keluar.- ⚠️ Shim
ol_time_shim.ZuluHttpTransportwajib untuk OM 2.0.3: provider memakaieventTimeberakhiran+00:00, sedangkan OM hanya menerimaZ(tanpa shim →400 Invalid request format).- Lineage tabel + kolom muncul untuk task yang memakai operator SQL (mis.
SQLExecuteQueryOperator). Task dbt/Sling viaBashOperatortidak menghasilkan lineage dari jalur ini — dbt tetap dari ingestion dbt.- ⚠️ OM 2.0.3 mencatat error berulang
null value in column "updatedat"saat mencoba auto-create entitas pipeline (bug OM; tidak berbahaya).- Rincian:
experiments/lineage-airflow-om/README.md. Rollback:sicabe/airflow:3.3.2-om203(tanpa OpenLineage). ./scripts/prefetch-images.sh --save-airflow /tmp/airflow-image.tar # ±835 MBdi host lain
scp /tmp/airflow-image.tar :/tmp/ ./scripts/prefetch-images.sh --load-airflow /tmp/airflow-image.tar
Menempatkan komponen ke beberapa host (Fase 1)
cp cluster.example.yml cluster.yml # sesuaikan address tiap host
./install.sh --generate # → generated/host-<x>/ (gitignored)
./install.sh --generate --blueprint b5 # preset 5 host (blueprint B)
./install.sh --sync # salin deploy/ (termasuk .env) ke host lain (dari control)
./install.sh --sync --host state --dry-run # lihat saja (tanpa menyalin)
./install.sh --host compute --dry-run # lihat perintahnya tanpa menjalankan
Distribusi ke host lain (--sync). Dijalankan di host control (master installer).
Menyalin seluruh deploy/ — termasuk .env dan generated/ — ke tiap host lain di
cluster.yml via rsync over SSH:
./install.sh --sync # ke SEMUA host lain (host ini dideteksi dari alamat lokal)
./install.sh --sync --host state # hanya satu host
./install.sh --sync --user admin --dest '~/lain/deploy' # SSH user & tujuan (timpa default)
./install.sh --sync --delete # hapus di tujuan berkas yang tak ada di sumber
Tujuan default = cermin lokasi repo ini. Nilai --dest diturunkan dari letak install.sh
di bawah $HOME (sicabe_default_dest), jadi nama repo tidak dipatok:
| Repo di host control | Tujuan --sync default di host lain |
|---|---|
~/sicabe-doc/deploy |
~/sicabe-doc/deploy |
~/sicabe-core/deploy |
~/sicabe-core/deploy |
Ini membuat repo distribusi bersih (mis. sicabe-core — hanya folder deploy/, tanpa
docs/, reviews/, experiments/) bekerja tanpa --dest. deploy/ self-contained:
semua script memakai SCRIPT_DIR, dan bind-mount compose relatif (./config/…) — jadi nama
atau lokasi repo tidak berpengaruh. Timpa tujuan dengan --dest '~/…/deploy' bila perlu.
Host yang sedang dijalankan dikecualikan otomatis (kecocokan hostname -I dengan
address di inventory); bila tak cocok, ia menyinkron ke semua host. .env wajib ikut
(rahasia bersama — OIDC client secret harus identik lintas host). Setelah sync, di tiap host
jalankan ./scripts/prefetch-images.sh --host <nama> lalu ./install.sh --host <nama>.
Model distribusi: control = sumber tunggal · host lain = PENERIMA (bukan working tree git).
--syncmenimpa berkas yang di-track git dideploy/host tujuan dengan versi control, sehinggagit statusdi sana selalu kotor dangit pullakan ditolak ("local changes would be overwritten"). Jangangit pulldi host tujuan — perbarui di control lalu--syncsaja (clone repo di host tujuan cukup sekali, untukprepare-host.sh). Gunakan--sync --deleteagar berkas yang dihapus/di-rename di repo ikut terhapus di tujuan (.git&outputdikecualikan, jadi keduanya aman).
Tanpa cluster.yml, installer berperilaku persis seperti single-host (tidak ada
yang di-generate). Hasil generate berisi per host: docker-compose.yml (hanya service
milik host itu), .env (endpoint → alamat host), dan config/ ter-render.
Uji: ./scripts/test-inventory.sh (validasi inventory) · ./scripts/test-generate.sh
(semantik hasil generate vs single-host). Rencana lengkap:
reviews/sicabe-10-multi-host-installer-plan.md.
Endpoint per host di skrip (multi-host). Skrip gate/verifikasi memakai helper
sicabe_load_env/sicabe_local_url/sicabe_local_host, jadi jalankan dengan env host agar
endpoint menunjuk ALAMAT HOST PEMILIK (bukan localhost):
./scripts/verify-phase3-processing.sh --env-file generated/host-compute/.env
./scripts/verify-phase6-governance.sh --env-file generated/host-control/.env
./install.sh --status --host compute # status + endpoint host compute
--env-file juga dihormati lewat env SICABE_ENV_FILE — dipakai otomatis oleh
bootstrap.sh --host, sehingga sub-script (seed-*/ingest-*/reapply) mewarisi env host
dan tidak lagi menimpanya dengan deploy/.env. Single-host tidak berubah (tetap
localhost) tanpa opsi ini.
Skrip host-scoped (multi-host). Skrip yang
docker execcontainer harus dijalankan DI host pemilik container — mis.seed-source-postgres.shdi hoststate,seed-starrocks.shdicompute,seed-native-dq.shdicontrol. Bila dijalankan di host lain, skrip keluar rapi (kode 3) + instruksi, bukan error;bootstrap.shmenampilkannya sebagai WARN. Gate (verify-phase*) otomatis SKIP cek container milik host lain (bukan FAIL).# di host state: ./scripts/seed-source-postgres.sh --env-file generated/host-state/.env # di host compute: ./scripts/seed-starrocks.sh --env-file generated/host-compute/.env
TLS Trino lintas-host. Saat
--generate, keystore + cert Trino dibangkitkan sekali dengan SAN alamat IP tiap host, lalu disalin identik ke tiap bundle (keypair harus sama lintas host). Jadi klien yang terhubung via IP (mis. ingestion OpenMetadata) tetap memverifikasi TLS — tak perluverify: false.DBT_TRINO_HOSTjuga di-set ke IP pemilik Trino sehingga dbt (di Airflow, host lain) menyambung lintas-host. Ubah alamat host →./install.sh --generate→--sync→ recreatetrino-coordinatordan service Airflow.
⚠️ Setelah mengubah file yang di-MOUNT (cert/config), container WAJIB di-restart manual.
./install.sh --host <x>menjalankandocker compose up -d, yang hanya recreate bila definisi compose berubah — bukan bila isi file bind-mount berubah. Contoh: regenerasi cert Trino →docker restart sicabe-trino-coordinator(di host Trino) dan service Airflow (di host control). Cek cepat: fingerprint cert yang disajikan vs yang dipakai container harus sama:# disajikan (dari host lain): echo | openssl s_client -connect <ip-trino>:8443 -servername trino 2>/dev/null | openssl x509 -noout -fingerprint -sha256 # dipakai (di container airflow): docker exec sicabe-airflow-webserver openssl x509 -in /etc/ssl/trino/trino.crt -noout -fingerprint -sha256
Endpoint komponen SE-HOST memakai nama service.
.envbundle kini menulis*_HOSTkomponen se-host sebagai nama service (mis.OPENMETADATA_HOST=openmetadata-server) dan komponen beda host sebagai alamat IP. Sebab port container (mis. OM 8585) tidak diterbitkan di host; memakai IP host justru "connection refused" (regresi OpenLineage).
⚠️
./install.sh --host <nama>dijalankan DI host itu, dan hanya setelah stack lama dimatikan bila host-nya sama.depends_onlintas host sudah dibuang generator — penggantinyapreflightyang belum ada (Fase 2), jadi urutan start antar host masih manual. dantest-verification.shakan melewati container komponen tersebut, danbootstrap.shbeserta gate fase memakai endpoint yang sama. Detail:docs/sicabe-install-phases-detail.md§2.8–§2.12.
scripts/seed-sample-data.sh(idempoten) membuat namespace + tabel contohiceberg.bronze.orders(6 baris) agar catalog tidak kosong dan langsung terlihat di Trino, Dremio, dan StarRocks.
scripts/seed-source-postgres.sh(idempoten) menyiapkan sumber di PostgreSQL: databasesicabe_data+ tabelpublic.orders(6 baris). Ini yang dibaca taskingest_to_bronze(Sling:POSTGRES_DB.public.orders→ICEBERG bronze.orders). Tanpa ini DAGsicabe_batch_dailygagal dengan pesan databasesicabe_datatidak ditemukan.
Diagram alur instalasi —
install.sh(6 fase) →bootstrap.sh(9 step) → stack siap: Mermaidsicabe-install-sequence.mmd· versi interaktif.drawio. Regenerasi.drawiodari.mmd:bash docs/diagrams/generate-drawio-from-mermaid.sh sicabe-install-sequence.mmd
Detail per fase & bootstrap step (prasyarat/dependency, container yang dinyalakan, aksi configure/seed/verify tiap step):
docs/sicabe-install-phases-detail.mdMemakai layanan existing? Variabel
.envyang perlu ditambahkan: Keycloak/LDAP → §2.8 · MinIO/S3 & Nessie → §2.9 pada dokumen yang sama. Untuk IAM existing (dikelola pihak lain/tanpa akses admin): daftar permintaan ke admin → Keycloakdocs/keycloak-existing-requirements.md· OpenLDAPdocs/openldap-existing-requirements.md.
Deploy per phase:
./install.sh --phase 1 # Keycloak + OpenLDAP + PostgreSQL
./install.sh --phase 2-4 # MinIO + Nessie + Trino + OPA + StarRocks FE+CN
./install.sh --status
./install.sh --destroy
# Dremio opt-in — tidak ikut phase mana pun (butuh RAM ~4 GB lebih)
docker compose --profile dremio up -d dremio
URL browser-facing (issuer OIDC, redirect, UI)
Enam variabel berisi URL yang dilihat browser, bukan lalu lintas container:
KEYCLOAK_PUBLIC_URL, GRAFANA_PUBLIC_URL, OPENMETADATA_PUBLIC_URL,
AIRFLOW_PUBLIC_URL, TRINO_PUBLIC_URL, MINIO_CONSOLE_PUBLIC_URL.
| Kondisi | Nilai *_PUBLIC_URL |
|---|---|
tls.mode: none (default) |
http://<alamat host yang menjalankan komponen>:<port> — diisi otomatis oleh install.sh --generate, sama di semua bundle |
tls.mode: edge / full |
https://<label>.<domain> dari cluster.domain |
tanpa cluster.yml (single-host) |
http://localhost:<port> |
Keycloak 26.x (hostname v2): yang menentukan issuer, discovery, dan redirect adalah
KC_HOSTNAME, dan isinya URL lengkap (http://host:port) — diambil dari
KEYCLOAK_PUBLIC_URL. Jangan pakai KC_HOSTNAME_URL: opsi hostname v1 itu dihapus di
Keycloak 26.0.0 sehingga tidak berefek; KC_HOSTNAME_BACKEND_URL bahkan bukan opsi
Keycloak. KC_HOSTNAME_BACKCHANNEL_DYNAMIC=true membuat container lain tetap bisa
memakai nama internal keycloak:8080 untuk token/JWKS.
Akses HTTP dari alamat non-lokal (mis. Tailscale). sslRequired adalah atribut
realm (default Keycloak external) dan dinilai dari alamat sumber koneksi, bukan
header Host. Menurut Keycloak, "lokal" = loopback + RFC1918 (10/8, 172.16/12,
192.168/16) + link-local + IPv6 fc00::/7 — sehingga 100.64.0.0/10 (CGNAT/Tailscale)
dianggap eksternal dan admin console gagal dengan HTTPS required, padahal akses via
localhost normal. Keycloak 26 tidak punya opsi server-level untuk ini (tidak ada
KC_HTTPS_REQUIRED), jadi harus per realm: bootstrap.sh --step 1 menyetelnya untuk
master dan realm Sicabe dari KEYCLOAK_SSL_REQUIRED (none saat tls.mode: none,
external saat ada TLS/Traefik).
Mengubah alamat publik berarti mengubah iss → semua konsumen harus ikut dan
semua pengguna login ulang:
- Trino:
issuer+auth-urlmemakai${ENV:KEYCLOAK_PUBLIC_URL}(token/JWKS tetap kekeycloak:8080) — lihatconfig/trino/config-coordinator.properties; - redirect URI + web origin klien: disinkronkan otomatis oleh
bootstrap.sh step 1(digabung, idempoten — termasuk client lama yang masih localhost-only); - Grafana (
GF_SERVER_ROOT_URL) dan Airflow (AIRFLOW__WEBSERVER__BASE_URL) mengikuti*_PUBLIC_URLmasing-masing.
Gejala bila salah: browser dibuang ke localhost, atau login ditolak dengan
Invalid parameter: redirect_uri.
Ingress + TLS (langkah 2 / T-20)
Traefik berjalan hanya pada profile edge, dan hanya ikut ke bundle host saat
tls.mode != none. Setiap host menjalankan Traefik-nya sendiri, dengan rute diambil dari
label container (provider docker) — jadi daftar rute = label pada layanan.
# 1. inventory: tls.mode: edge (domain: sicabe.com)
# 2. install.sh --generate && install.sh --sync -> distribusi bundle ke host lain
# 3. isi kredensial DNS di .env tiap host (contoh Cloudflare: token Zone:DNS:Edit)
# CF_DNS_API_TOKEN=...
# 4. jalankan ingress
docker compose --profile all --profile edge up -d
| Variabel | Arti |
|---|---|
CLUSTER_DOMAIN |
domain rute Host() (label dari PUBLIC_URL_SERVICES: kc/grafana/om/airflow/trino/minio) — hanya di host ingress |
tls.ingress_host |
(inventory, opsional) host yang menjalankan Traefik; default pemilik Keycloak |
TRAEFIK_BIND_ADDR |
alamat bind port 80/443 — diisi generator dengan alamat host (bukan semua antarmuka) |
TRAEFIK_ACME_CASERVER |
staging secara default; produksi: https://acme-v02.api.letsencrypt.org/directory |
CF_DNS_API_TOKEN |
kredensial DNS-01 Cloudflare |
Catatan penting:
- Tanpa kredensial DNS, Traefik tetap start dan memakai sertifikat self-signed bawaan,
jadi routing masih bisa diuji:
curl -k -H 'Host: kc.<domain>' https://<host>/. Log ACME akan menyebut variabel yang kurang secara eksplisit. acme.jsonwajib mode 0600 (Traefik menolak start bila terlalu terbuka). Compose memakai named volume, sehingga Traefik membuatnya sendiri dengan mode benar.- Ingress dijalankan SATU host saja (default: host yang menjalankan Keycloak —
control; bisa dipatok dengantls.ingress_host). Alasannya bukan hanya kesederhanaan: apex (sicabe.com) dan wildcard (*.sicabe.com) memakai nama TXT DNS-01 yang sama (_acme-challenge.<domain>), sehingga beberapa Traefik yang menerbitkan wildcard yang sama akan saling bertabrakan — gejalanya403 Incorrect TXT record ... (and 1 more). Batas Let's Encrypt juga hanya 5 sertifikat per himpunan nama per minggu. - Host ingress mengambil rute komponen lokal dari label container, dan rute komponen di
host lain dari
config/traefik/dynamic.yml(provider file) yang dibangkitkan generator — mis.trino→https://<alamat-compute>:12443,minio→http://<alamat-state>:12901. Traefik hanya melihat container di host-nya sendiri, jadi rute lintas host wajib lewat berkas dinamis. - Karena ingress terpusat, A-record cukup satu wildcard
*.sicabe.com→ alamat host ingress (tidak perlu per label). Port 80/443 hanya perlu dibuka di host ingress. - Label
traefik.*tetap tertulis di semua bundle (inert tanpa Traefik) — tidak berbahaya, tapi jangan menjalankan--profile edgedi host non-ingress karena akan menggandakan rute. - Trino internal memakai HTTPS (sertifikat self-signed) -> Traefik memakai
serversTransportdariconfig/traefik/dynamic.ymldenganinsecureSkipVerifyhanya untuk Trino.serversTransportstidak bisa didefinisikan lewat label Docker (menghasilkan error runtimeservers transport not found). tls.mode: edgemembuat generator mengisiKC_PROXY_HEADERS=xforwarded,TRINO_PROCESS_FORWARDED=true, danKEYCLOAK_SSL_REQUIRED=external. Sebaliknya padanonesemuanya kosong/false— Trino dan Keycloak menolak headerX-Forwarded-*dari klien, jadi host bisa dipalsukan tidak.
Detail per Komponen
Phase 1 — Keycloak + OpenLDAP
- Realm
sicabe; OIDC clients:trino,starrocks,airflow,openmetadata,grafana. - Users lokal:
admin,engineer,analyst,viewer. - OpenLDAP = sumber kebenaran grup (
data-admin,data-engineer,data-analyst,data-viewer). User tidak difederasikan — autentikasi via Keycloak, resolusi grup via LDAP (opsi 1). - Akses:
http://localhost:12080/admin/sicabe/console.
Phase 2 — Storage: MinIO + Nessie
- MinIO bucket
iceberg-data(dipakai Nessie warehouse + StarRocks shared-data). - Nessie menyimpan version store di PostgreSQL (
nessieDB, schemanessie,NESSIE_VERSION_STORE_TYPE=JDBC2). Tabel Nessie di-auto-create. - Warehouse:
s3://iceberg-data/warehouse; S3 MinIO path-style, kredensial server-side. - Akses:
http://localhost:12120(/api/v2,/iceberg/).
Phase 3 — Processing: Trino + OPA
- Katalog
icebergvia Nessie (iceberg.catalog.type=nessie). - Authz OPA (
access-control.name=opa, deny-by-default). Policy:config/opa/policies/trino.rego; ACL:config/opa/data.json. - Group provider LDAP (
group-provider.name=ldap) → grupdata-*. - Auth hybrid (
http-server.authentication.type=oauth2,password):- End user: OAuth2 SSO Keycloak (Web UI + JDBC) di
https://localhost:12443. - Service/pipeline: HTTP Basic service account (
svc_dbt,svc_seed,svc_airflow,svc_om) viapassword-authenticatorfile (config/trino/password.db, bcrypt; di-generatebootstrap.sh/install.sh, pra-syarathtpasswd). Otorisasi via OPAdata.users(user→role).
- End user: OAuth2 SSO Keycloak (Web UI + JDBC) di
- HTTPS-only: HTTP polos dimatikan (
allow-insecure-over-http=false) —http://…:12081tidak lagi di-expose. Semua akses viahttps://localhost:12443(self-signed →curl -k/ terima peringatan browser). - ⚠️ Web UI OAuth2 wajib HTTPS — Trino mengalihkan
/ui/ke/ui/static/disabled.htmlbila request tidak secure.
Phase 4 — Serving: StarRocks (shared-data) + Dremio
StarRocks shared-data:
- FE
run_mode=shared_data,cloud_native_storage_type=S3; data tabel internal disimpan di MinIO via storage volumes3_volume. - Cluster = FE + CN (Compute Node,
ALTER SYSTEM ADD COMPUTE NODE). - OIDC:
CREATE SECURITY INTEGRATION sr_oidc; LDAP group providersr_ldap;authentication_chain = native,sr_oidc;GRANT role TO EXTERNAL GROUP. - External catalog
iceberg_catalog→ Nessie (http://nessie:19120/iceberg/). - Akses:
mysql -h localhost -P 12930 -u root -p.
Dremio OSS:
- Engine paralel di atas Nessie yang sama; auth lokal (first-run admin via UI).
- Source Nessie: endpoint
http://nessie:19120/api/v2, MinIO path-style (fs.s3a.path.style.access=true,dremio.s3.compat=true). - Akses:
http://localhost:12947. Detail:config/dremio/README.md.
Phase 5 — Ingestion: Airflow
- Sling menarik PostgreSQL → Bronze (Iceberg via Nessie Iceberg REST).
- dbt via Trino: Bronze → Silver → Gold. Great Expectations validasi.
- Akses:
http://localhost:12082.
Phase 6 — Governance: OpenMetadata + Grafana
- OpenMetadata: DB
openmetadata_db; konektor Trino + Airflow. - Grafana: datasource provisioning file. Akses
http://localhost:12300.
RBAC
| User | Password | Role | Akses |
|---|---|---|---|
admin |
KEYCLOAK_ADMIN_PASSWORD |
admin | Penuh (OPA superuser, StarRocks sr_admin) |
engineer |
engineer123 |
engineer | OPA write+ddl, StarRocks sr_engineer |
analyst |
analyst123 |
analyst | OPA select (per-kolom), StarRocks sr_analyst |
viewer |
viewer123 |
viewer | Terbatas (StarRocks tanpa grant → fail-closed) |
Pemetaan grup → akses:
- Trino/OPA: grup LDAP
data-*→config/opa/data.jsonroles.<grup>.tables. - StarRocks: external group
data-*→ rolesr_*(GRANT ... TO EXTERNAL GROUP).
Perbedaan dari deploy-legacy/
| Aspek | deploy-legacy/ (base lama) |
deploy/ (merge) |
|---|---|---|
| Iceberg catalog | Polaris | Nessie (JDBC2/Postgres) |
| Trino authz | rules.json |
OPA |
| Trino group provider | plugin Keycloak | LDAP |
| StarRocks mode | shared-nothing (FE+BE) | shared-data (FE+CN) |
| StarRocks storage | disk lokal | S3/MinIO (storage volume) |
| Engine paralel | — | Dremio OSS |
| Identitas | user+grup di Keycloak | user di Keycloak, grup di OpenLDAP |
| Project/network | sicabe / 172.28.0.0/16 |
sicabe / 172.28.0.0/16 (sama) |
| Host port | 3xxx/8xxx/9xxx | 12xxx |
Catatan Penting
run_mode=shared_databersifat final — tidak bisa diubah setelah first start;deploy/mulai dari volume bersih.- Nessie tidak mendukung Iceberg spec v3 → selalu
format-version=2. - Dremio OSS memakai auth lokal & penyimpanan volume (bukan PostgreSQL).
- Tulis lintas engine (Trino/StarRocks/Dremio) di Nessie sebaiknya bergantian, bukan simultan.
.envdi-generateinstall.sh(JANGAN commit).- Container & project memakai
sicabe-*(sama dengandeploy-legacy/). Karena nama container/project sama, jangan jalankandeploy/dandeploy-legacy/bersamaan —deploy-legacy/hanya arsip/referensi. - Host port tetap 12xxx.
Troubleshooting (ringkas)
| Gejala | Solusi |
|---|---|
| Nessie unhealthy | Cek DB nessie + schema nessie ada; cek NESSIE_DB_PASSWORD |
| Trino query ditolak semua | Cek OPA reachable (curl localhost:12181/v1/data/trino/allow); cek config/opa/data.json |
| StarRocks CN tidak terdaftar | docker logs sicabe-starrocks-cn; SHOW COMPUTE NODES di FE |
| StarRocks S3 400/301 | Pastikan path-style di storage volume + cn.conf/env S3 |
| Login OIDC StarRocks gagal | required_issuer harus host yang dipakai login (http://localhost:12080/...); redirect URI client starrocks harus memuat http://starrocks-fe:8030/api/oauth2 |
| Dremio tidak melihat Nessie | Buat source Nessie manual (first-run); MinIO path-style + dremio.s3.compat=true |
| Sling gagal ke katalog | Sling tidak mendukung catalog_type=nessie; gunakan catalog_type: rest + rest_uri: http://nessie:19120/iceberg/ |
Detail tambahan: experiments/*/README.md dan config/*/README.md.
Referensi
Catatan versi & perilaku terverifikasi —
docs/sicabe-catatan-versi-dan-perilaku.md: hal-hal yang pernah menggigit dan nilai yang benar (Keycloak 26 hostname/sslRequired/cluster health, Traefik v3 label/file-provider/ACME, token DNS-01 Cloudflare, env OM & MinIO), termasuk Traefik membuang container yang unhealthy yang membuat komponen hilang dari ingress tanpa error.
- Rencana merge:
../reviews/sicabe-07-merge-experiments-plan.md - Project Nessie
- Trino OPA access control
- StarRocks shared-data
- Dremio OSS
- Sling Iceberg
deploy — hasil merge deploy-legacy/ + 3 eksperimen (2026-09-10).