diff --git a/AGENTS.md b/AGENTS.md index 8a80cc7..79463b2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -102,8 +102,8 @@ Fetch only the files relevant to the task. A typical example contains A collection of STACKIT Application Load Balancer (ALB) showcases with different TLS strategies — from self-signed to Let's Encrypt, from a single VM to Kubernetes - **`cdn-s3-static-website`** `[cdn, s3, object-storage, static-website, waf]` A reference implementation showing how to deploy a static website using [STACKIT CDN](https://stackit.com/en/products/network/stackit-cdn) with [STACKIT Object Storage](http://stackit.com/en/products/storage/stackit-object-storage) as the origin -- **`dbaas-otel-collect-metrics`** `[dbaas, postgresql, mongodb, otel, observability, metrics, monitoring]` - Collect metrics from STACKIT PostgreSQL Flex and MongoDB instances using OpenTelemetry (OTel) and export them to STACKIT Observability +- **`dbaas-otel-collect-metrics`** `[dbaas, postgresql, otel, observability, metrics, monitoring]` + Collect metrics from STACKIT PostgreSQL Flex instances using OpenTelemetry (OTel) and export them to STACKIT Observability - **`dbaas-postgresql-kms-encryption`** `[dbaas, postgresql, kms, encryption, backup, key-management]` Deploys a STACKIT PostgreSQL Flex instance whose volume **and backup storage** are encrypted with a customer-managed key from [STACKIT KMS](https://docs.stackit.cloud/products/security/kms/), together with the keyring, the key and the service account the database service uses to unwrap that key - **`iaas-cross-az-layer4-loadbalancer`** `[iaas, load-balancer, layer4, ha, networking, cross-az]` diff --git a/examples/dbaas-otel-collect-metrics/.terraform.lock.hcl b/examples/dbaas-otel-collect-metrics/.terraform.lock.hcl index 810732d..2e94968 100644 --- a/examples/dbaas-otel-collect-metrics/.terraform.lock.hcl +++ b/examples/dbaas-otel-collect-metrics/.terraform.lock.hcl @@ -42,27 +42,6 @@ provider "registry.terraform.io/hashicorp/kubernetes" { ] } -provider "registry.terraform.io/hashicorp/random" { - version = "3.8.1" - constraints = ">= 3.6.3" - hashes = [ - "h1:Eexl06+6J+s75uD46+WnZtpJZYRVUMB0AiuPBifK6Jc=", - "h1:u8AKlWVDTH5r9YLSeswoVEjiY72Rt4/ch7U+61ZDkiQ=", - "zh:08dd03b918c7b55713026037c5400c48af5b9f468f483463321bd18e17b907b4", - "zh:0eee654a5542dc1d41920bbf2419032d6f0d5625b03bd81339e5b33394a3e0ae", - "zh:229665ddf060aa0ed315597908483eee5b818a17d09b6417a0f52fd9405c4f57", - "zh:2469d2e48f28076254a2a3fc327f184914566d9e40c5780b8d96ebf7205f8bc0", - "zh:37d7eb334d9561f335e748280f5535a384a88675af9a9eac439d4cfd663bcb66", - "zh:741101426a2f2c52dee37122f0f4a2f2d6af6d852cb1db634480a86398fa3511", - "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", - "zh:a902473f08ef8df62cfe6116bd6c157070a93f66622384300de235a533e9d4a9", - "zh:b85c511a23e57a2147355932b3b6dce2a11e856b941165793a0c3d7578d94d05", - "zh:c5172226d18eaac95b1daac80172287b69d4ce32750c82ad77fa0768be4ea4b8", - "zh:dab4434dba34aad569b0bc243c2d3f3ff86dd7740def373f2a49816bd2ff819b", - "zh:f49fd62aa8c5525a5c17abd51e27ca5e213881d58882fd42fec4a545b53c9699", - ] -} - provider "registry.terraform.io/hashicorp/time" { version = "0.13.1" hashes = [ @@ -84,24 +63,24 @@ provider "registry.terraform.io/hashicorp/time" { } provider "registry.terraform.io/stackitcloud/stackit" { - version = "0.92.0" - constraints = ">= 0.87.0" + version = "0.117.0" + constraints = ">= 0.117.0" hashes = [ - "h1:dE5sdzUaHkzVL8AW3+GXD2EEWX2PlS+sHT7F25SXcZ0=", - "h1:j26ncxqlAp4q0/NHFoiATuVdIg7KH0zZhWoSAd+4Yj0=", + "h1:3UMzDKpnhaLxtnsfmBO5d1DJxxMvxXmssosPhPZQVUc=", + "h1:GPtyX5yigbBFjYQtOUIFJ6t/O6QJxpRbWuDDEE98u4M=", "zh:0dde99e7b343fa01f8eefc378171fb8621bedb20f59157d6cc8e3d46c738105f", - "zh:5eaa713f68a004ec33697f510ca4c7722940e2bab8080c025822ca547325ef98", - "zh:60ed4496492b9781f7cc581e346222a6356538a527e4ac67dce6815a64fc5c66", - "zh:6834a7819429e3482a5fdd547c442cc032d7047c3fb0dee30e8babb2438598e1", - "zh:6de632db0cbb42b429a9e752078df37716b0f335e5c39e883be5c55f7f1da553", - "zh:ac8b1bc8212236aaab789cef1dce718e6b8394bcf4b5f6c6f8dabf8c8a213573", - "zh:af4b1e805d6082a3ec94d2f5b68e8a62f04205af3f75a4a7d1b167e0f027d9ec", - "zh:b709258a4cd3acd0a9426809c1d7c1ed25859010b566c1b29481b132a7e2af13", - "zh:c7e8c5e8f2ca8c14c1bf5c92716a761b67792b38046b99653bdbf9ca423fc675", - "zh:c7f47c6b7e33d1f28bdc8d1aa5fda2734d74d6b1b0c6ef8b258489d9405af231", - "zh:d57dc6ad6b3a2879aa47012faf82f597a2ca1c3de1561bb96c6191e65072ea95", - "zh:d5b18390104164477913ced864e7a1cd5a678490f9412be887e5d8e3961d242e", - "zh:ead616306ab18c30a4c1110ad7fa8aee7d8a99e4410ceecbe5875beac5724f8a", - "zh:f73ad70183a35e5d04e4b48c44654c76fec48a8f4c913dd31a5befc2a1c2e4dc", + "zh:1778fef5cb73508e14ac021ea905e840ade6b5ba093b86743eca295b1a101980", + "zh:1eca69d4054d1027248d0fa9f928d31a6649691a3d9e09611ad4f60b67c0a6af", + "zh:2ccb74a622c7f98af87a0889a281a4eb4cb82a9af210dea54508bf8a092f0765", + "zh:31aa1576e6b3b86a3a08e6293cdd1f29555f77c78550ea10ec5b13b6907b4218", + "zh:3e54805af3e78ed59b7f9331f6928d01707b9ee547b892111eb681900eecc3cd", + "zh:41693875f5354df3976fe576cbdb90779a84480931cc69dc98a8fbefa73157f9", + "zh:42f8dfb1b6f21d0a85d3f23d7b56c16f7ef3bc8987983ced62252f9ba674d044", + "zh:4c841a04769a401ca4ef96e52e4908915eb3bb18a5ecceef61cdbe2bf385b650", + "zh:5750987df198dbfe01a838ea019fbb4a4ef9943a39b2d90631223e6c3a0b5185", + "zh:8c7b2e3f488dc9f577dd01b9a279dae867135347d7336b9170e2da8fe7a2d3ad", + "zh:bc15bc4ef82f625f167589399adad532769c033eae12cc707ddf53236863e327", + "zh:d7dcc857ba301a477970cce8342eee95499bd4ce0f37df642b042d008214d7c8", + "zh:f9cd95acc1f40577f9bb345820925787de840104ec6ee4dede6ff1da224ae82c", ] } diff --git a/examples/dbaas-otel-collect-metrics/010-provider.tf b/examples/dbaas-otel-collect-metrics/010-provider.tf index 8bf8bc1..438832e 100644 --- a/examples/dbaas-otel-collect-metrics/010-provider.tf +++ b/examples/dbaas-otel-collect-metrics/010-provider.tf @@ -15,41 +15,45 @@ # This file defines the required Terraform providers and their configurations. # It sets up the STACKIT, Kubernetes, and Helm providers to manage resources in the project and the SKE cluster. terraform { - required_version = ">= 0.14.0" + required_version = ">= 1.10.0" required_providers { stackit = { source = "stackitcloud/stackit" - version = ">= 0.87.0" - } - random = { - source = "hashicorp/random" - version = ">= 3.6.3" + version = ">= 0.117.0" } kubernetes = { source = "hashicorp/kubernetes" version = ">=2.14.0" } + helm = { + source = "hashicorp/helm" + version = ">= 3.1.1" + } + time = { + source = "hashicorp/time" + version = ">= 0.13.1" + } } } provider "stackit" { default_region = var.stackit_region service_account_key_path = var.stackit_service_account_key_path - experiments = ["iam"] + experiments = ["iam", "ske"] } provider "kubernetes" { - host = yamldecode(stackit_ske_kubeconfig.this.kube_config).clusters.0.cluster.server - client_certificate = base64decode(yamldecode(stackit_ske_kubeconfig.this.kube_config).users.0.user.client-certificate-data) - client_key = base64decode(yamldecode(stackit_ske_kubeconfig.this.kube_config).users.0.user.client-key-data) - cluster_ca_certificate = base64decode(yamldecode(stackit_ske_kubeconfig.this.kube_config).clusters.0.cluster.certificate-authority-data) + host = yamldecode(ephemeral.stackit_ske_kubeconfig.this.kube_config).clusters.0.cluster.server + client_certificate = base64decode(yamldecode(ephemeral.stackit_ske_kubeconfig.this.kube_config).users.0.user.client-certificate-data) + client_key = base64decode(yamldecode(ephemeral.stackit_ske_kubeconfig.this.kube_config).users.0.user.client-key-data) + cluster_ca_certificate = base64decode(yamldecode(ephemeral.stackit_ske_kubeconfig.this.kube_config).clusters.0.cluster.certificate-authority-data) } provider "helm" { kubernetes = { - host = yamldecode(stackit_ske_kubeconfig.this.kube_config).clusters.0.cluster.server - client_certificate = base64decode(yamldecode(stackit_ske_kubeconfig.this.kube_config).users.0.user.client-certificate-data) - client_key = base64decode(yamldecode(stackit_ske_kubeconfig.this.kube_config).users.0.user.client-key-data) - cluster_ca_certificate = base64decode(yamldecode(stackit_ske_kubeconfig.this.kube_config).clusters.0.cluster.certificate-authority-data) + host = yamldecode(ephemeral.stackit_ske_kubeconfig.this.kube_config).clusters.0.cluster.server + client_certificate = base64decode(yamldecode(ephemeral.stackit_ske_kubeconfig.this.kube_config).users.0.user.client-certificate-data) + client_key = base64decode(yamldecode(ephemeral.stackit_ske_kubeconfig.this.kube_config).users.0.user.client-key-data) + cluster_ca_certificate = base64decode(yamldecode(ephemeral.stackit_ske_kubeconfig.this.kube_config).clusters.0.cluster.certificate-authority-data) } } diff --git a/examples/dbaas-otel-collect-metrics/020-variables.tf b/examples/dbaas-otel-collect-metrics/020-variables.tf index 35ec61c..650a26e 100644 --- a/examples/dbaas-otel-collect-metrics/020-variables.tf +++ b/examples/dbaas-otel-collect-metrics/020-variables.tf @@ -12,22 +12,36 @@ # See the License for the specific language governing permissions and # limitations under the License. -variable "stackit_project_id" { - type = string - default = "d75e6aab-b616-4b42-ae3b-aaf161ad626d" +variable "stackit_parent_container_id" { + type = string + description = "Container ID of the organization or folder the project is created in" +} + +variable "stackit_admin_email" { + type = string + description = "Email address that becomes the owner of the created project" +} + +variable "project_name" { + type = string + description = "Name of the project that holds the cluster, the database and the Observability instance" + default = "dbaas-otel-metrics" } variable "stackit_region" { - type = string - default = "eu01" + type = string + description = "STACKIT region" + default = "eu01" } -variable "stackit_service_account_key_path" { - type = string - default = "../../keys/stackit-sa.json" +variable "observability_plan_name" { + type = string + description = "Plan of the Observability instance, for example Observability-Starter-EU01 for a smaller one" + default = "Observability-Large-EU01" } -resource "stackit_key_pair" "admin_keypair" { - name = "admin-keypair-12345" - public_key = chomp(file("~/.ssh/id_rsa.pub")) +variable "stackit_service_account_key_path" { + type = string + description = "Path to the service account key file. Unset falls back to the STACKIT_SERVICE_ACCOUNT_KEY_PATH environment variable, then to that entry in the credentials file ($HOME/.stackit/credentials.json, or STACKIT_CREDENTIALS_PATH)." + default = null } diff --git a/examples/dbaas-otel-collect-metrics/025-project.tf b/examples/dbaas-otel-collect-metrics/025-project.tf new file mode 100644 index 0000000..2807b13 --- /dev/null +++ b/examples/dbaas-otel-collect-metrics/025-project.tf @@ -0,0 +1,29 @@ +# Copyright 2026 Schwarz Digits Cloud GmbH & Co. KG +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +resource "stackit_resourcemanager_project" "this" { + parent_container_id = var.stackit_parent_container_id + name = var.project_name + owner_email = var.stackit_admin_email + + # A new project needs either this label or a networkArea label. PUBLIC keeps + # the project outside a network area, so SKE creates the cluster network. + labels = { + scope = "PUBLIC" + } +} + +locals { + project_id = stackit_resourcemanager_project.this.project_id +} diff --git a/examples/dbaas-otel-collect-metrics/030-ske.tf b/examples/dbaas-otel-collect-metrics/030-ske.tf index 0e78184..bd228d9 100644 --- a/examples/dbaas-otel-collect-metrics/030-ske.tf +++ b/examples/dbaas-otel-collect-metrics/030-ske.tf @@ -12,36 +12,17 @@ # See the License for the specific language governing permissions and # limitations under the License. -resource "stackit_ske_kubeconfig" "this" { - project_id = var.stackit_project_id - cluster_name = stackit_ske_cluster.this.name - refresh = true - - depends_on = [stackit_ske_cluster.this] -} - -data "stackit_ske_kubernetes_versions" "this" { - version_state = "SUPPORTED" -} - -data "stackit_ske_machine_image_versions" "this" { - version_state = "SUPPORTED" -} - -locals { - flatcar_supported_version = one(flatten([ - for mi in data.stackit_ske_machine_image_versions.this.machine_images : [ - for v in mi.versions : - v.version - if mi.name == "flatcar" - ] - ])) +# The cluster ID condition defers the kubeconfig request until the cluster exists. +ephemeral "stackit_ske_kubeconfig" "this" { + project_id = local.project_id + cluster_name = stackit_ske_cluster.this.id != "" ? stackit_ske_cluster.this.name : "" + # Two hours cover a first apply in which the database finishes long after the cluster. + expiration = 7200 } resource "stackit_ske_cluster" "this" { - project_id = var.stackit_project_id - name = "dbaas-otel" - kubernetes_version_min = data.stackit_ske_kubernetes_versions.this.kubernetes_versions.0.version + project_id = local.project_id + name = "dbaas-otel" node_pools = [ { @@ -50,8 +31,7 @@ resource "stackit_ske_cluster" "this" { minimum = "3" maximum = "9" max_surge = "3" - availability_zones = ["eu01-1", "eu01-2", "eu01-3"] - os_version_min = local.flatcar_supported_version + availability_zones = [for z in [1, 2, 3] : "${var.stackit_region}-${z}"] os_name = "flatcar" volume_size = 150 volume_type = "storage_premium_perf6" diff --git a/examples/dbaas-otel-collect-metrics/040-observability.tf b/examples/dbaas-otel-collect-metrics/040-observability.tf index 916d7db..36e37ae 100644 --- a/examples/dbaas-otel-collect-metrics/040-observability.tf +++ b/examples/dbaas-otel-collect-metrics/040-observability.tf @@ -13,8 +13,8 @@ # limitations under the License. resource "stackit_observability_instance" "example" { - project_id = var.stackit_project_id + project_id = local.project_id name = "example-obs" - plan_name = "Observability-Large-EU01" + plan_name = var.observability_plan_name alert_config = null } diff --git a/examples/dbaas-otel-collect-metrics/050-postgres.tf b/examples/dbaas-otel-collect-metrics/050-postgres.tf index 6247083..77ed922 100644 --- a/examples/dbaas-otel-collect-metrics/050-postgres.tf +++ b/examples/dbaas-otel-collect-metrics/050-postgres.tf @@ -12,32 +12,35 @@ # See the License for the specific language governing permissions and # limitations under the License. +data "stackit_postgresflex_flavors" "this" { + project_id = local.project_id +} + resource "stackit_postgresflex_instance" "this" { - project_id = var.stackit_project_id + project_id = local.project_id name = "example-instance" - backup_schedule = "00 00 * * *" - flavor = { - cpu = 2 - ram = 4 - } - replicas = 3 + backup_schedule = "0 0 * * *" + retention_days = 32 + flavor_id = one([for f in data.stackit_postgresflex_flavors.this.flavors : f.id if f.cpu == 2 && f.memory == 4 && f.node_type == "Replica"]) storage = { class = "premium-perf2-stackit" size = 15 } version = 15 - acl = ["0.0.0.0/0"] + network = { + acl = ["0.0.0.0/0"] + } } resource "stackit_postgresflex_user" "this" { - project_id = var.stackit_project_id + project_id = local.project_id instance_id = stackit_postgresflex_instance.this.instance_id username = "test" roles = ["createdb", "login"] } resource "stackit_postgresflex_database" "this" { - project_id = var.stackit_project_id + project_id = local.project_id instance_id = stackit_postgresflex_instance.this.instance_id name = "test" owner = stackit_postgresflex_user.this.username diff --git a/examples/dbaas-otel-collect-metrics/051-service-account.tf b/examples/dbaas-otel-collect-metrics/051-service-account.tf index eb25d4c..17b4a0f 100644 --- a/examples/dbaas-otel-collect-metrics/051-service-account.tf +++ b/examples/dbaas-otel-collect-metrics/051-service-account.tf @@ -14,25 +14,32 @@ resource "stackit_service_account" "this" { name = "prom-proxy" - project_id = var.stackit_project_id + project_id = local.project_id } resource "time_rotating" "rotate" { rotation_days = 150 } +# The service account API generates the private key, and Terraform stores it in +# plain text in the state. This is only an example and should not be used this +# way in production. resource "stackit_service_account_key" "this" { - project_id = var.stackit_project_id + project_id = local.project_id service_account_email = stackit_service_account.this.email ttl_days = 180 rotate_when_changed = { rotation = time_rotating.rotate.id } + + lifecycle { + create_before_destroy = true + } } resource "stackit_authorization_project_role_assignment" "this" { - resource_id = var.stackit_project_id + resource_id = local.project_id role = "prometheus-proxy.reader" subject = stackit_service_account.this.email } diff --git a/examples/dbaas-otel-collect-metrics/060-otel-helm.tf b/examples/dbaas-otel-collect-metrics/060-otel-helm.tf index acc8ee6..49d2f78 100644 --- a/examples/dbaas-otel-collect-metrics/060-otel-helm.tf +++ b/examples/dbaas-otel-collect-metrics/060-otel-helm.tf @@ -15,39 +15,40 @@ locals { sa_json = jsondecode(stackit_service_account_key.this.json) otel_helm_values = templatefile("${path.module}/helm-values/otel-collector-values.tftpl", { - stackit_project_id = var.stackit_project_id + stackit_project_id = local.project_id stackit_region = var.stackit_region stackit_postgres_instance_id = stackit_postgresflex_instance.this.instance_id observability_metrics_endpoint = stackit_observability_instance.example.metrics_push_url - secret_name = kubernetes_secret.otel_secret.metadata[0].name + secret_name = kubernetes_secret_v1.otel_secret.metadata[0].name sa_client_id = local.sa_json.credentials.sub sa_issuer = local.sa_json.credentials.iss sa_key_id = local.sa_json.credentials.kid + sa_audience = local.sa_json.credentials.aud + sa_token_url = try(local.sa_json.credentials.tokenEndpoint, "https://service-account.api.stackit.cloud/token") }) } resource "stackit_observability_credential" "otel" { - project_id = var.stackit_project_id + project_id = local.project_id instance_id = stackit_observability_instance.example.instance_id } -resource "kubernetes_namespace" "monitoring" { +resource "kubernetes_namespace_v1" "monitoring" { metadata { name = "monitoring" } } -resource "kubernetes_secret" "otel_secret" { +resource "kubernetes_secret_v1" "otel_secret" { metadata { name = "otel-secrets" - namespace = kubernetes_namespace.monitoring.metadata[0].name + namespace = kubernetes_namespace_v1.monitoring.metadata[0].name } data = { OBSERVABILITY_AUTHORIZATION_HEADER = "Basic ${base64encode("${stackit_observability_credential.otel.username}:${stackit_observability_credential.otel.password}")}" - JSON = stackit_service_account_key.this.json - PRIVATE_KEY = jsondecode(stackit_service_account_key.this.json).credentials.privateKey + PRIVATE_KEY = local.sa_json.credentials.privateKey } } @@ -56,10 +57,11 @@ resource "helm_release" "opentelemetry_collector" { repository = "https://open-telemetry.github.io/opentelemetry-helm-charts" chart = "opentelemetry-collector" version = "0.152.0" - namespace = kubernetes_namespace.monitoring.metadata[0].name - timeout = 30 + namespace = kubernetes_namespace_v1.monitoring.metadata[0].name values = [ local.otel_helm_values ] + + depends_on = [stackit_authorization_project_role_assignment.this] } diff --git a/examples/dbaas-otel-collect-metrics/070-outputs.tf b/examples/dbaas-otel-collect-metrics/070-outputs.tf new file mode 100644 index 0000000..e9adaf1 --- /dev/null +++ b/examples/dbaas-otel-collect-metrics/070-outputs.tf @@ -0,0 +1,33 @@ +# Copyright 2026 Schwarz Digits Cloud GmbH & Co. KG +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +output "project_id" { + description = "ID of the created project" + value = local.project_id +} + +output "ske_cluster_name" { + description = "Name of the SKE cluster" + value = stackit_ske_cluster.this.name +} + +output "kubeconfig_command" { + description = "Fetch a kubeconfig for kubectl" + value = "stackit ske kubeconfig create ${stackit_ske_cluster.this.name} --project-id ${local.project_id} --region ${stackit_ske_cluster.this.region} --expiration 8h" +} + +output "grafana_url" { + description = "Grafana of the Observability instance" + value = stackit_observability_instance.example.grafana_url +} diff --git a/examples/dbaas-otel-collect-metrics/README.md b/examples/dbaas-otel-collect-metrics/README.md index 0b11334..1685204 100644 --- a/examples/dbaas-otel-collect-metrics/README.md +++ b/examples/dbaas-otel-collect-metrics/README.md @@ -1,8 +1,8 @@ - + # DBaaS OpenTelemetry Metrics Collection -Collect metrics from STACKIT PostgreSQL Flex and MongoDB instances using OpenTelemetry (OTel) and export them to STACKIT Observability. +Collect metrics from STACKIT PostgreSQL Flex instances using OpenTelemetry (OTel) and export them to STACKIT Observability. ## Architecture @@ -29,7 +29,7 @@ sequenceDiagram API->>PG: fetch Prometheus metrics PG-->>API: metrics data API-->>OT: metrics (prometheus format) - OT->>OBS: push metrics (prometheus exporter) + OT->>OBS: push metrics (Prometheus remote write) ``` ```mermaid @@ -61,36 +61,58 @@ flowchart LR ## Prerequisites -- STACKIT Project ID and Service Account key. -- Terraform, `kubectl`, and `helm` installed. +- A service account key for Terraform, see `stackit_service_account_key_path` in `020-variables.tf`. The service account needs a role on the parent container that includes `resource-manager.project.create` and applies to the new project, for example `owner`: the example creates the project, and in it a service account and a role assignment. +- Terraform 1.10 or later and STACKIT provider 0.117.0 or later. +- An authenticated `stackit` CLI (`stackit auth login` or `stackit auth activate-service-account`) and `kubectl` for debugging. + +The provider block enables the experiments `iam` for the role assignment and `ske` for the ephemeral kubeconfig. ## Usage -1. **Configure**: Update `stackit_project_id` and `stackit_service_account_key_path` in `01-variables.tf`. +1. **Configure**: `cp terraform.tfvars.example terraform.tfvars` and set `stackit_parent_container_id` (organization or folder) and `stackit_admin_email`. 2. **Deploy**: ```bash terraform init terraform apply ``` +> [!WARNING] +> The service account API generates the private key, and Terraform stores it in plain text in the state and in the Kubernetes Secret `otel-secrets`. Anyone who can read the state can read the key. This setup is only an example and should not be used this way in production. + +The key is valid for 180 days. `time_rotating` replaces it on the first `terraform apply` after day 150, so run `terraform apply` between day 150 and 180, or the collector stops scraping. + +## Verify + +Open the Grafana URL, select the data source `Thanos` in Explore and query `pg_up`. A series there shows that the collector scrapes the prom-proxy and pushes to Observability; the value `1` means the exporter reaches the database. + +```bash +terraform output -raw grafana_url +``` + ## Scrape Configuration The OTel Collector scrapes metrics from: - **PostgreSQL**: `https://postgres-prom-proxy.api.stackit.cloud/v2/...` -- **MongoDB**: `https://mongodb-prom-proxy.api.stackit.cloud/v2/...` - -_Note: MSSQL is not supported._ ## Debugging -View live scrape data in the collector logs: +View live scrape data in the collector logs. `--timestamps` prefixes every line with the time the container wrote it: ```bash -kubectl logs -l app.kubernetes.io/name=otel-collector -n monitoring -f +eval "$(terraform output -raw kubeconfig_command)" +kubectl config use-context "$(terraform output -raw ske_cluster_name)" +kubectl logs deploy/otel-collector -n monitoring -f --timestamps ``` +## Clean up + +```bash +terraform destroy +``` + +`terraform destroy` removes everything the example created, including the project. + ## Documentation - [PostgreSQL Flex Metrics](https://docs.stackit.cloud/products/databases/postgresql-flex/reference/observability-metrics-in-postgresql-flex/) -- [MongoDB Flex Metrics](https://docs.stackit.cloud/products/databases/mongodb-flex/reference/observability-metrics/) diff --git a/examples/dbaas-otel-collect-metrics/helm-values/otel-collector-values.tftpl b/examples/dbaas-otel-collect-metrics/helm-values/otel-collector-values.tftpl index 2f34dd0..6c52b84 100644 --- a/examples/dbaas-otel-collect-metrics/helm-values/otel-collector-values.tftpl +++ b/examples/dbaas-otel-collect-metrics/helm-values/otel-collector-values.tftpl @@ -22,7 +22,7 @@ config: grant_type: urn:ietf:params:oauth:grant-type:jwt-bearer iss: $${SA_TOKEN_REQUEST_ISSUER} signature_algorithm: RS512 - token_url: https://service-account.api.stackit.cloud/token + token_url: "${sa_token_url}" scheme: https scrape_interval: 1m static_configs: @@ -63,7 +63,7 @@ extraEnvs: - name: SA_TOKEN_REQUEST_CLIENT_CERTIFICATE_KEY_ID value: "${sa_key_id}" - name: SA_TOKEN_REQUEST_AUDIENCE - value: "https://service-account.api.stackit.cloud/token" + value: "${sa_audience}" extraVolumes: - name: otel-secrets diff --git a/examples/dbaas-otel-collect-metrics/terraform.tfvars.example b/examples/dbaas-otel-collect-metrics/terraform.tfvars.example new file mode 100644 index 0000000..b7f95b7 --- /dev/null +++ b/examples/dbaas-otel-collect-metrics/terraform.tfvars.example @@ -0,0 +1,2 @@ +stackit_parent_container_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" +stackit_admin_email = "admin@example.com"