Use the real hive column name (`drone`) in the prototype's live query and pin the Grafana datasource uid to `swarm-prom` so provisioned dashboards resolve their Prometheus source in k3d.
444 lines
10 KiB
Terraform
444 lines
10 KiB
Terraform
# Simulated fleet (T4 dev environment, docs/06) on the k3d cluster created
|
|
# by infra/ansible/sim-cluster.yml. Drones are StatefulSet replicas writing
|
|
# to a shared hostPath lake — the same /data contract as the on-board NVMe.
|
|
|
|
locals {
|
|
labels = { "app.kubernetes.io/part-of" = "swarm-sim" }
|
|
}
|
|
|
|
resource "kubernetes_namespace" "swarm" {
|
|
metadata {
|
|
name = var.namespace
|
|
labels = local.labels
|
|
}
|
|
}
|
|
|
|
# --- Fleet ------------------------------------------------------------------
|
|
|
|
resource "kubernetes_stateful_set" "drone" {
|
|
metadata {
|
|
name = "drone"
|
|
namespace = kubernetes_namespace.swarm.metadata[0].name
|
|
labels = local.labels
|
|
}
|
|
|
|
spec {
|
|
service_name = "drone"
|
|
replicas = var.drone_count
|
|
# A landed drone powers off; the next start is the next flight
|
|
pod_management_policy = "Parallel"
|
|
|
|
selector {
|
|
match_labels = { app = "drone" }
|
|
}
|
|
|
|
template {
|
|
metadata {
|
|
labels = merge(local.labels, { app = "drone" })
|
|
}
|
|
|
|
spec {
|
|
container {
|
|
name = "drone"
|
|
image = var.simulator_image
|
|
image_pull_policy = "Never" # imported via k3d image import
|
|
|
|
env {
|
|
name = "DRONE_ID"
|
|
value_from {
|
|
field_ref {
|
|
field_path = "metadata.name"
|
|
}
|
|
}
|
|
}
|
|
env {
|
|
name = "DURATION_S"
|
|
value = tostring(var.flight_duration_s)
|
|
}
|
|
env {
|
|
name = "SPEEDUP"
|
|
value = tostring(var.speedup)
|
|
}
|
|
env {
|
|
name = "DATA_DIR"
|
|
value = "/data"
|
|
}
|
|
|
|
volume_mount {
|
|
name = "lake"
|
|
mount_path = "/data"
|
|
}
|
|
|
|
resources {
|
|
requests = { cpu = "50m", memory = "128Mi" }
|
|
limits = { cpu = "500m", memory = "512Mi" }
|
|
}
|
|
}
|
|
|
|
volume {
|
|
name = "lake"
|
|
host_path {
|
|
path = var.data_host_path
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# --- Exporter + Prometheus + Grafana (observability, docs/07) ----------------
|
|
|
|
resource "kubernetes_deployment" "exporter" {
|
|
metadata {
|
|
name = "exporter"
|
|
namespace = kubernetes_namespace.swarm.metadata[0].name
|
|
labels = local.labels
|
|
}
|
|
|
|
spec {
|
|
replicas = 1
|
|
selector {
|
|
match_labels = { app = "exporter" }
|
|
}
|
|
template {
|
|
metadata {
|
|
labels = merge(local.labels, { app = "exporter" })
|
|
}
|
|
spec {
|
|
container {
|
|
name = "exporter"
|
|
image = var.simulator_image
|
|
image_pull_policy = "Never"
|
|
command = ["python", "monitoring/exporter.py"]
|
|
env {
|
|
name = "DATA_DIR"
|
|
value = "/data"
|
|
}
|
|
env {
|
|
name = "EXPORTER_PORT"
|
|
value = "9105"
|
|
}
|
|
port {
|
|
container_port = 9105
|
|
}
|
|
volume_mount {
|
|
name = "lake"
|
|
mount_path = "/data"
|
|
read_only = true
|
|
}
|
|
}
|
|
volume {
|
|
name = "lake"
|
|
host_path {
|
|
path = var.data_host_path
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
resource "kubernetes_service" "exporter" {
|
|
metadata {
|
|
name = "exporter"
|
|
namespace = kubernetes_namespace.swarm.metadata[0].name
|
|
}
|
|
spec {
|
|
selector = { app = "exporter" }
|
|
port {
|
|
port = 9105
|
|
target_port = 9105
|
|
}
|
|
}
|
|
}
|
|
|
|
resource "kubernetes_deployment" "explorer" {
|
|
metadata {
|
|
name = "explorer"
|
|
namespace = kubernetes_namespace.swarm.metadata[0].name
|
|
labels = local.labels
|
|
}
|
|
|
|
spec {
|
|
replicas = 1
|
|
selector {
|
|
match_labels = { app = "explorer" }
|
|
}
|
|
template {
|
|
metadata {
|
|
labels = merge(local.labels, { app = "explorer" })
|
|
}
|
|
spec {
|
|
container {
|
|
name = "explorer"
|
|
image = var.simulator_image
|
|
image_pull_policy = "Never"
|
|
command = ["python", "explorer/server.py"]
|
|
env {
|
|
name = "DATA_DIR"
|
|
value = "/data"
|
|
}
|
|
env {
|
|
name = "EXPLORER_PORT"
|
|
value = "8088"
|
|
}
|
|
port {
|
|
container_port = 8088
|
|
}
|
|
volume_mount {
|
|
name = "lake"
|
|
mount_path = "/data"
|
|
read_only = true
|
|
}
|
|
}
|
|
volume {
|
|
name = "lake"
|
|
host_path {
|
|
path = var.data_host_path
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
resource "kubernetes_service" "explorer" {
|
|
metadata {
|
|
name = "explorer"
|
|
namespace = kubernetes_namespace.swarm.metadata[0].name
|
|
}
|
|
spec {
|
|
type = "NodePort"
|
|
selector = { app = "explorer" }
|
|
port {
|
|
port = 8088
|
|
target_port = 8088
|
|
node_port = var.node_ports.explorer
|
|
}
|
|
}
|
|
}
|
|
|
|
resource "kubernetes_config_map" "prometheus" {
|
|
metadata {
|
|
name = "prometheus-config"
|
|
namespace = kubernetes_namespace.swarm.metadata[0].name
|
|
}
|
|
data = {
|
|
"prometheus.yml" = <<-EOT
|
|
global:
|
|
scrape_interval: 5s
|
|
scrape_configs:
|
|
- job_name: swarm
|
|
static_configs:
|
|
- targets: ["exporter:9105"]
|
|
EOT
|
|
}
|
|
}
|
|
|
|
resource "kubernetes_deployment" "prometheus" {
|
|
metadata {
|
|
name = "prometheus"
|
|
namespace = kubernetes_namespace.swarm.metadata[0].name
|
|
labels = local.labels
|
|
}
|
|
|
|
spec {
|
|
replicas = 1
|
|
selector {
|
|
match_labels = { app = "prometheus" }
|
|
}
|
|
template {
|
|
metadata {
|
|
labels = merge(local.labels, { app = "prometheus" })
|
|
}
|
|
spec {
|
|
container {
|
|
name = "prometheus"
|
|
image = "prom/prometheus:v2.53.0"
|
|
port {
|
|
container_port = 9090
|
|
}
|
|
volume_mount {
|
|
name = "config"
|
|
mount_path = "/etc/prometheus"
|
|
}
|
|
}
|
|
volume {
|
|
name = "config"
|
|
config_map {
|
|
name = kubernetes_config_map.prometheus.metadata[0].name
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
resource "kubernetes_service" "prometheus" {
|
|
metadata {
|
|
name = "prometheus"
|
|
namespace = kubernetes_namespace.swarm.metadata[0].name
|
|
}
|
|
spec {
|
|
type = "NodePort"
|
|
selector = { app = "prometheus" }
|
|
port {
|
|
port = 9090
|
|
target_port = 9090
|
|
node_port = var.node_ports.prometheus
|
|
}
|
|
}
|
|
}
|
|
|
|
resource "kubernetes_config_map" "grafana_provisioning" {
|
|
metadata {
|
|
name = "grafana-provisioning"
|
|
namespace = kubernetes_namespace.swarm.metadata[0].name
|
|
}
|
|
data = {
|
|
"datasource.yml" = <<-EOT
|
|
apiVersion: 1
|
|
deleteDatasources:
|
|
- name: Prometheus
|
|
orgId: 1
|
|
datasources:
|
|
- name: Prometheus
|
|
uid: swarm-prom
|
|
type: prometheus
|
|
access: proxy
|
|
url: http://prometheus:9090
|
|
isDefault: true
|
|
EOT
|
|
"dashboards.yml" = <<-EOT
|
|
apiVersion: 1
|
|
providers:
|
|
- name: swarm
|
|
folder: ""
|
|
type: file
|
|
options:
|
|
path: /var/lib/grafana/dashboards
|
|
EOT
|
|
}
|
|
}
|
|
|
|
resource "kubernetes_config_map" "grafana_dashboard" {
|
|
metadata {
|
|
name = "grafana-dashboard"
|
|
namespace = kubernetes_namespace.swarm.metadata[0].name
|
|
}
|
|
data = {
|
|
"swarm.json" = file("${path.module}/../../../simulator/monitoring/grafana/dashboards/swarm.json")
|
|
}
|
|
}
|
|
|
|
resource "kubernetes_deployment" "grafana" {
|
|
metadata {
|
|
name = "grafana"
|
|
namespace = kubernetes_namespace.swarm.metadata[0].name
|
|
labels = local.labels
|
|
}
|
|
|
|
spec {
|
|
replicas = 1
|
|
selector {
|
|
match_labels = { app = "grafana" }
|
|
}
|
|
template {
|
|
metadata {
|
|
labels = merge(local.labels, { app = "grafana" })
|
|
}
|
|
spec {
|
|
container {
|
|
name = "grafana"
|
|
image = "grafana/grafana:11.1.0"
|
|
port {
|
|
container_port = 3000
|
|
}
|
|
|
|
env {
|
|
name = "GF_AUTH_ANONYMOUS_ENABLED"
|
|
value = "true"
|
|
}
|
|
env {
|
|
name = "GF_AUTH_ANONYMOUS_ORG_ROLE"
|
|
value = "Admin"
|
|
}
|
|
env {
|
|
name = "GF_AUTH_DISABLE_LOGIN_FORM"
|
|
value = "true"
|
|
}
|
|
# Air-gap hygiene — same flags as the Compose profile
|
|
env {
|
|
name = "GF_ANALYTICS_REPORTING_ENABLED"
|
|
value = "false"
|
|
}
|
|
env {
|
|
name = "GF_ANALYTICS_CHECK_FOR_UPDATES"
|
|
value = "false"
|
|
}
|
|
env {
|
|
name = "GF_ANALYTICS_CHECK_FOR_PLUGIN_UPDATES"
|
|
value = "false"
|
|
}
|
|
|
|
volume_mount {
|
|
name = "provisioning-datasources"
|
|
mount_path = "/etc/grafana/provisioning/datasources"
|
|
}
|
|
volume_mount {
|
|
name = "provisioning-dashboards"
|
|
mount_path = "/etc/grafana/provisioning/dashboards"
|
|
}
|
|
volume_mount {
|
|
name = "dashboards"
|
|
mount_path = "/var/lib/grafana/dashboards"
|
|
}
|
|
}
|
|
|
|
volume {
|
|
name = "provisioning-datasources"
|
|
config_map {
|
|
name = kubernetes_config_map.grafana_provisioning.metadata[0].name
|
|
items {
|
|
key = "datasource.yml"
|
|
path = "datasource.yml"
|
|
}
|
|
}
|
|
}
|
|
volume {
|
|
name = "provisioning-dashboards"
|
|
config_map {
|
|
name = kubernetes_config_map.grafana_provisioning.metadata[0].name
|
|
items {
|
|
key = "dashboards.yml"
|
|
path = "dashboards.yml"
|
|
}
|
|
}
|
|
}
|
|
volume {
|
|
name = "dashboards"
|
|
config_map {
|
|
name = kubernetes_config_map.grafana_dashboard.metadata[0].name
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
resource "kubernetes_service" "grafana" {
|
|
metadata {
|
|
name = "grafana"
|
|
namespace = kubernetes_namespace.swarm.metadata[0].name
|
|
}
|
|
spec {
|
|
type = "NodePort"
|
|
selector = { app = "grafana" }
|
|
port {
|
|
port = 3000
|
|
target_port = 3000
|
|
node_port = var.node_ports.grafana
|
|
}
|
|
}
|
|
}
|