feat: add IaC layer with shared var/t1 and var/t3 data paths
Ansible provisions the k3d cluster and Flux controllers; Terraform modules deploy the simulated fleet and ground warehouse. Compose and k3d share var/t1 (live lake) and var/t3 (warehouse). The prototype gains a live mode fed by the explorer read-only SQL API.
This commit is contained in:
@@ -0,0 +1,273 @@
|
||||
# Ground warehouse segment (T3, docs/03 + docs/06) — the stationary,
|
||||
# long-lifecycle half of the system and therefore the most natural Terraform
|
||||
# territory. Runs in the same k3d cluster as the fleet, in its own namespace
|
||||
# and with its own state.
|
||||
|
||||
locals {
|
||||
labels = { "app.kubernetes.io/part-of" = "swarm-ground" }
|
||||
}
|
||||
|
||||
resource "kubernetes_namespace" "ground" {
|
||||
metadata {
|
||||
name = var.namespace
|
||||
labels = local.labels
|
||||
}
|
||||
}
|
||||
|
||||
# --- MinIO object store -------------------------------------------------------
|
||||
|
||||
resource "kubernetes_secret" "minio" {
|
||||
metadata {
|
||||
name = "minio-credentials"
|
||||
namespace = kubernetes_namespace.ground.metadata[0].name
|
||||
}
|
||||
data = {
|
||||
MINIO_ROOT_USER = var.minio_root_user
|
||||
MINIO_ROOT_PASSWORD = var.minio_root_password
|
||||
}
|
||||
}
|
||||
|
||||
resource "kubernetes_persistent_volume_claim" "minio" {
|
||||
metadata {
|
||||
name = "minio-data"
|
||||
namespace = kubernetes_namespace.ground.metadata[0].name
|
||||
}
|
||||
spec {
|
||||
access_modes = ["ReadWriteOnce"]
|
||||
resources {
|
||||
requests = { storage = var.minio_storage }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "kubernetes_deployment" "minio" {
|
||||
metadata {
|
||||
name = "minio"
|
||||
namespace = kubernetes_namespace.ground.metadata[0].name
|
||||
labels = local.labels
|
||||
}
|
||||
|
||||
spec {
|
||||
replicas = 1
|
||||
strategy {
|
||||
type = "Recreate" # RWO volume
|
||||
}
|
||||
selector {
|
||||
match_labels = { app = "minio" }
|
||||
}
|
||||
template {
|
||||
metadata {
|
||||
labels = merge(local.labels, { app = "minio" })
|
||||
}
|
||||
spec {
|
||||
container {
|
||||
name = "minio"
|
||||
image = "minio/minio:RELEASE.2024-06-13T22-53-53Z"
|
||||
args = ["server", "/data", "--console-address", ":9001"]
|
||||
|
||||
env_from {
|
||||
secret_ref {
|
||||
name = kubernetes_secret.minio.metadata[0].name
|
||||
}
|
||||
}
|
||||
# Air-gap hygiene: no update checks
|
||||
env {
|
||||
name = "MINIO_UPDATE"
|
||||
value = "off"
|
||||
}
|
||||
|
||||
port {
|
||||
container_port = 9000
|
||||
}
|
||||
port {
|
||||
container_port = 9001
|
||||
}
|
||||
|
||||
volume_mount {
|
||||
name = "store"
|
||||
mount_path = "/data"
|
||||
}
|
||||
}
|
||||
volume {
|
||||
name = "store"
|
||||
persistent_volume_claim {
|
||||
claim_name = kubernetes_persistent_volume_claim.minio.metadata[0].name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "kubernetes_service" "minio" {
|
||||
metadata {
|
||||
name = "minio"
|
||||
namespace = kubernetes_namespace.ground.metadata[0].name
|
||||
}
|
||||
spec {
|
||||
type = "NodePort"
|
||||
selector = { app = "minio" }
|
||||
port {
|
||||
name = "api"
|
||||
port = 9000
|
||||
target_port = 9000
|
||||
}
|
||||
port {
|
||||
name = "console"
|
||||
port = 9001
|
||||
target_port = 9001
|
||||
node_port = var.node_ports.minio_console
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# --- Post-flight offload: T1 lake -> T3 warehouse ------------------------------
|
||||
# In production this runs on the docking station after a drone lands: only
|
||||
# sealed partitions move, checksums are verified, then the on-board lake is
|
||||
# pruned. The simulation compresses that into a periodic rsync-style copy
|
||||
# plus an object-store mirror.
|
||||
|
||||
resource "kubernetes_cron_job_v1" "offload" {
|
||||
metadata {
|
||||
name = "offload"
|
||||
namespace = kubernetes_namespace.ground.metadata[0].name
|
||||
labels = local.labels
|
||||
}
|
||||
|
||||
spec {
|
||||
schedule = var.offload_schedule
|
||||
concurrency_policy = "Forbid"
|
||||
successful_jobs_history_limit = 3
|
||||
failed_jobs_history_limit = 3
|
||||
|
||||
job_template {
|
||||
metadata {
|
||||
labels = local.labels
|
||||
}
|
||||
spec {
|
||||
backoff_limit = 1
|
||||
template {
|
||||
metadata {
|
||||
labels = local.labels
|
||||
}
|
||||
spec {
|
||||
restart_policy = "Never"
|
||||
|
||||
# Step 1: copy Hive partitions verbatim (T1 layout == T3 layout)
|
||||
init_container {
|
||||
name = "copy"
|
||||
image = "busybox:1.36"
|
||||
command = ["sh", "-c", "cp -ru /lake/flight_id=* /warehouse/ 2>/dev/null; ls /warehouse | wc -l"]
|
||||
volume_mount {
|
||||
name = "lake"
|
||||
mount_path = "/lake"
|
||||
read_only = true
|
||||
}
|
||||
volume_mount {
|
||||
name = "warehouse"
|
||||
mount_path = "/warehouse"
|
||||
}
|
||||
}
|
||||
|
||||
# Step 2: mirror the warehouse into the object store for
|
||||
# downstream consumers (training pipelines, replay tooling)
|
||||
container {
|
||||
name = "mirror"
|
||||
image = "minio/mc:RELEASE.2024-06-12T14-34-03Z"
|
||||
command = ["sh", "-c", "mc alias set store http://minio:9000 \"$MINIO_ROOT_USER\" \"$MINIO_ROOT_PASSWORD\" && mc mb -p store/warehouse && mc mirror --overwrite /warehouse store/warehouse"]
|
||||
env_from {
|
||||
secret_ref {
|
||||
name = kubernetes_secret.minio.metadata[0].name
|
||||
}
|
||||
}
|
||||
volume_mount {
|
||||
name = "warehouse"
|
||||
mount_path = "/warehouse"
|
||||
read_only = true
|
||||
}
|
||||
}
|
||||
|
||||
volume {
|
||||
name = "lake"
|
||||
host_path {
|
||||
path = var.lake_host_path
|
||||
}
|
||||
}
|
||||
volume {
|
||||
name = "warehouse"
|
||||
host_path {
|
||||
path = var.warehouse_host_path
|
||||
type = "DirectoryOrCreate"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# --- Warehouse explorer: historical read-only SQL over all flights -------------
|
||||
|
||||
resource "kubernetes_deployment" "warehouse_explorer" {
|
||||
metadata {
|
||||
name = "warehouse-explorer"
|
||||
namespace = kubernetes_namespace.ground.metadata[0].name
|
||||
labels = local.labels
|
||||
}
|
||||
|
||||
spec {
|
||||
replicas = 1
|
||||
selector {
|
||||
match_labels = { app = "warehouse-explorer" }
|
||||
}
|
||||
template {
|
||||
metadata {
|
||||
labels = merge(local.labels, { app = "warehouse-explorer" })
|
||||
}
|
||||
spec {
|
||||
container {
|
||||
name = "explorer"
|
||||
image = var.simulator_image
|
||||
image_pull_policy = "Never"
|
||||
command = ["python", "explorer/server.py"]
|
||||
env {
|
||||
name = "DATA_DIR"
|
||||
value = "/warehouse"
|
||||
}
|
||||
port {
|
||||
container_port = 8088
|
||||
}
|
||||
volume_mount {
|
||||
name = "warehouse"
|
||||
mount_path = "/warehouse"
|
||||
read_only = true
|
||||
}
|
||||
}
|
||||
volume {
|
||||
name = "warehouse"
|
||||
host_path {
|
||||
path = var.warehouse_host_path
|
||||
type = "DirectoryOrCreate"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "kubernetes_service" "warehouse_explorer" {
|
||||
metadata {
|
||||
name = "warehouse-explorer"
|
||||
namespace = kubernetes_namespace.ground.metadata[0].name
|
||||
}
|
||||
spec {
|
||||
type = "NodePort"
|
||||
selector = { app = "warehouse-explorer" }
|
||||
port {
|
||||
port = 8088
|
||||
target_port = 8088
|
||||
node_port = var.node_ports.warehouse_explorer
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user