698 lines
38 KiB
Markdown
698 lines
38 KiB
Markdown
# 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`](../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 N` menjalankan profil `phaseN`. 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 dengan
|
|||
|
|
> `docker compose --profile dremio up -d dremio`. `bootstrap.sh` step 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:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
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
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
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` (atau `EXTERNAL_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 jalankan `scripts/render-config.sh` yang
|
|||
|
|
> menyalinnya ke `generated/host-<x>/config/` sambil mengganti hostname terdaftar:
|
|||
|
|
>
|
|||
|
|
> ```bash
|
|||
|
|
> ./scripts/render-config.sh --out generated/host-compute/config --dry-run \
|
|||
|
|
> --map postgres=10.20.0.12 --map nessie=10.20.0.10
|
|||
|
|
> ```
|
|||
|
|
>
|
|||
|
|
> Penggantian hanya pada konteks jaringan (setelah `//`, setelah `@`, atau `host:port`)
|
|||
|
|
> sehingga path filesystem seperti `/etc/trino/` dan baris komentar tidak ikut berubah.
|
|||
|
|
> Rencana lengkap: `reviews/sicabe-10-multi-host-installer-plan.md`.
|
|||
|
|
|
|||
|
|
> ⚠️ **Pilot multi-host di host yang SAMA tidak boleh dua stack berjalan bersamaan.**
|
|||
|
|
> Memisah network/port saja **tidak cukup** — RAM tidak akan cukup (host pilot 15 GB,
|
|||
|
|
> satu stack penuh ±10 GB). **Matikan stack lama lebih dulu** sebelum menyalakan hasil
|
|||
|
|
> `generated/host-<x>/`.
|
|||
|
|
|
|||
|
|
**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:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
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:
|
|||
|
|
> ```bash
|
|||
|
|
> 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.yml` memakai `${AIRFLOW_IMAGE:-sicabe/airflow:3.3.2-om203-ol}` —
|
|||
|
|
> jadi setelah `pull`, **pilih salah satu** dari dua opsi di atas; kalau tidak, compose
|
|||
|
|
> akan mencari tag `sicabe/...` yang belum ada di host itu.
|
|||
|
|
|
|||
|
|
> ⚠️ **`sicabe/airflow:3.3.2-om203-ol` dibangun lokal** (base `apache/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**:
|
|||
|
|
> ```bash
|
|||
|
|
> # 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 + shim `eventTime`**. 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.sh` membuatnya
|
|||
|
|
> bersama `openmetadata_lineage_api` (password = token bot `lineage-bot`,
|
|||
|
|
> `JWTTokenExpiry=Unlimited`).
|
|||
|
|
> - Aktif/mati: `AIRFLOW_OPENLINEAGE_CONFIG_CONN_ID` (default `openlineage_config`;
|
|||
|
|
> kosongkan untuk mematikan). ⚠️ Koneksinya **harus sudah ada** — pemuatan
|
|||
|
|
> konfigurasi provider tidak dibungkus try/except (bisa menggagalkan task).
|
|||
|
|
> - ⚠️ `AIRFLOW__OPENLINEAGE__EXECUTE_IN_THREAD=true` **wajib**; tanpa itu proses
|
|||
|
|
> emisi HANG lalu dimatikan dan tidak ada event yang keluar.
|
|||
|
|
> - ⚠️ Shim `ol_time_shim.ZuluHttpTransport` **wajib** untuk OM 2.0.3: provider
|
|||
|
|
> memakai `eventTime` berakhiran `+00:00`, sedangkan OM hanya menerima `Z`
|
|||
|
|
> (tanpa shim → `400 Invalid request format`).
|
|||
|
|
> - Lineage **tabel + kolom** muncul untuk task yang memakai **operator SQL**
|
|||
|
|
> (mis. `SQLExecuteQueryOperator`). Task dbt/Sling via `BashOperator` tidak
|
|||
|
|
> 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 MB
|
|||
|
|
> # di host lain
|
|||
|
|
> scp /tmp/airflow-image.tar <host>:/tmp/
|
|||
|
|
> ./scripts/prefetch-images.sh --load-airflow /tmp/airflow-image.tar
|
|||
|
|
> ```
|
|||
|
|
|
|||
|
|
### Menempatkan komponen ke beberapa host (Fase 1)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
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:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
./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 '~/sicabe-doc/deploy' # SSH user & tujuan
|
|||
|
|
./install.sh --sync --delete # hapus di tujuan berkas yang tak ada di sumber
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
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>`.
|
|||
|
|
|
|||
|
|
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`):
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
./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 exec` container harus dijalankan **DI
|
|||
|
|
> host pemilik container** — mis. `seed-source-postgres.sh` di host **`state`**,
|
|||
|
|
> `seed-starrocks.sh` di **`compute`**, `seed-native-dq.sh` di **`control`**. Bila dijalankan
|
|||
|
|
> di host lain, skrip keluar rapi (kode **3**) + instruksi, bukan error; `bootstrap.sh`
|
|||
|
|
> menampilkannya sebagai WARN. Gate (`verify-phase*`) otomatis **SKIP** cek container milik
|
|||
|
|
> host lain (bukan FAIL).
|
|||
|
|
>
|
|||
|
|
> ```bash
|
|||
|
|
> # 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 perlu `verify: false`. **`DBT_TRINO_HOST`** juga di-set ke IP
|
|||
|
|
> pemilik Trino sehingga dbt (di Airflow, host lain) menyambung lintas-host. Ubah alamat host
|
|||
|
|
> → `./install.sh --generate` → `--sync` → recreate `trino-coordinator` **dan** service Airflow.
|
|||
|
|
|
|||
|
|
> ⚠️ **Setelah mengubah file yang di-MOUNT (cert/config), container WAJIB di-restart manual.**
|
|||
|
|
> `./install.sh --host <x>` menjalankan `docker 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:
|
|||
|
|
> ```bash
|
|||
|
|
> # 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.** `.env` bundle kini menulis `*_HOST`
|
|||
|
|
> komponen **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_on` lintas host sudah dibuang generator —
|
|||
|
|
> penggantinya `preflight` yang **belum ada** (Fase 2), jadi urutan start antar host
|
|||
|
|
> masih manual.
|
|||
|
|
> dan `test-verification.sh` akan **melewati container** komponen tersebut, dan `bootstrap.sh`
|
|||
|
|
> beserta 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 contoh
|
|||
|
|
> `iceberg.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: database `sicabe_data` + tabel `public.orders` (6 baris). Ini yang
|
|||
|
|
> dibaca task `ingest_to_bronze` (Sling: `POSTGRES_DB.public.orders` →
|
|||
|
|
> `ICEBERG bronze.orders`). Tanpa ini DAG `sicabe_batch_daily` gagal dengan
|
|||
|
|
> pesan database `sicabe_data` tidak ditemukan.
|
|||
|
|
|
|||
|
|
> **Diagram alur instalasi** — `install.sh` (6 fase) → `bootstrap.sh` (9 step) → stack siap:
|
|||
|
|
> [Mermaid `sicabe-install-sequence.mmd`](../docs/diagrams/sicabe-install-sequence.mmd) ·
|
|||
|
|
> [versi interaktif `.drawio`](../docs/diagrams/sicabe-install-sequence.drawio).
|
|||
|
|
> Regenerasi `.drawio` dari `.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.md`](../docs/sicabe-install-phases-detail.md)
|
|||
|
|
>
|
|||
|
|
> **Memakai layanan existing?** Variabel `.env` yang 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 → Keycloak [`docs/keycloak-existing-requirements.md`](../docs/keycloak-existing-requirements.md) ·
|
|||
|
|
> OpenLDAP [`docs/openldap-existing-requirements.md`](../docs/openldap-existing-requirements.md).
|
|||
|
|
|
|||
|
|
Deploy per phase:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
./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-url` memakai `${ENV:KEYCLOAK_PUBLIC_URL}` (token/JWKS tetap
|
|||
|
|
ke `keycloak:8080`) — lihat `config/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_URL` masing-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.
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 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.json` **wajib 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 dengan `tls.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** — gejalanya `403 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 edge` di host non-ingress karena akan menggandakan rute.
|
|||
|
|
- Trino internal memakai HTTPS (sertifikat self-signed) -> Traefik memakai `serversTransport`
|
|||
|
|
dari `config/traefik/dynamic.yml` dengan `insecureSkipVerify` **hanya untuk Trino**.
|
|||
|
|
`serversTransports` **tidak bisa** didefinisikan lewat label Docker (menghasilkan error
|
|||
|
|
runtime `servers transport not found`).
|
|||
|
|
- `tls.mode: edge` membuat generator mengisi `KC_PROXY_HEADERS=xforwarded`,
|
|||
|
|
`TRINO_PROCESS_FORWARDED=true`, dan `KEYCLOAK_SSL_REQUIRED=external`. Sebaliknya pada
|
|||
|
|
`none` semuanya kosong/`false` — Trino dan Keycloak **menolak** header `X-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** (`nessie` DB, schema `nessie`,
|
|||
|
|
`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 `iceberg` via 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`) → grup `data-*`.
|
|||
|
|
- **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`) via `password-authenticator` file
|
|||
|
|
(`config/trino/password.db`, bcrypt; di-generate `bootstrap.sh`/`install.sh`,
|
|||
|
|
pra-syarat `htpasswd`). Otorisasi via OPA `data.users` (user→role).
|
|||
|
|
- **HTTPS-only**: HTTP polos **dimatikan** (`allow-insecure-over-http=false`) —
|
|||
|
|
`http://…:12081` tidak lagi di-expose. Semua akses via `https://localhost:12443`
|
|||
|
|
(self-signed → `curl -k` / terima peringatan browser).
|
|||
|
|
- ⚠️ **Web UI OAuth2 wajib HTTPS** — Trino mengalihkan `/ui/` ke
|
|||
|
|
`/ui/static/disabled.html` bila 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 volume** `s3_volume`.
|
|||
|
|
- Cluster = **FE + CN** (Compute Node, `ALTER SYSTEM ADD COMPUTE NODE`).
|
|||
|
|
- OIDC: `CREATE SECURITY INTEGRATION sr_oidc`; **LDAP group provider** `sr_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.json` `roles.<grup>.tables`.
|
|||
|
|
- **StarRocks**: external group `data-*` → role `sr_*` (`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_data` bersifat 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.
|
|||
|
|
- **`.env`** di-generate `install.sh` (JANGAN commit).
|
|||
|
|
- Container & project memakai **`sicabe-*`** (sama dengan `deploy-legacy/`).
|
|||
|
|
Karena nama container/project sama, **jangan jalankan `deploy/` dan
|
|||
|
|
`deploy-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`](../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](https://projectnessie.org/guides/iceberg-rest/)
|
|||
|
|
- [Trino OPA access control](https://trino.io/docs/current/security/opa-access-control.html)
|
|||
|
|
- [StarRocks shared-data](https://docs.starrocks.io/docs/deployment/deploy_shared_data_manually/)
|
|||
|
|
- [Dremio OSS](https://docs.dremio.com/)
|
|||
|
|
- [Sling Iceberg](https://docs.slingdata.io/connections/datalake-connections/iceberg)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
*deploy — hasil merge `deploy-legacy/` + 3 eksperimen (2026-09-10).*
|