Skip to main content

Deploy with OpenTofu

The aperturedata/aperturedb-terraform package is an OCI module for OpenTofu. It creates native Kubernetes resources for distributed ApertureDB in an existing cluster. The default topology has three database pods, three ZooKeeper pods, two Lenz gateways, one checkpointer, a Web UI, and telemetry.

Prerequisites​

  • OpenTofu 1.10 or later for OCI module sources. Use tofu for the OCI source shown below, even though the package name contains terraform.

  • Kubernetes 1.23 or later and a kubeconfig for the intended cluster.

  • An object storage bucket and credentials; see Deploying ApertureDB.

  • Persistent volume provisioning for ZooKeeper and a 100Gi monitoring volume when telemetry is enabled.

  • Sufficient cluster capacity: each database pod and the checkpointer requests 8Gi of memory by default, and each Lenz pod requests 1Gi, in addition to the other components.

  • An ingress controller and LoadBalancer support for the example below.

The Kubernetes and Helm providers must both be configured. The Helm provider is used by the optional bundled ingress controller. Use a separate namespace per deployment because application resource names are fixed within a namespace.

Configure the module​

In a new directory, create main.tf. Replace <package-version> with the published package tag you want, and replace the bucket name and hostname. This example references secrets you create separately in the namespace:

terraform {
required_version = ">= 1.10.0"
required_providers {
kubernetes = {
source = "hashicorp/kubernetes"
version = "~> 2.30"
}
helm = {
source = "hashicorp/helm"
version = "~> 3.0"
}
}
}

provider "kubernetes" {
config_path = "~/.kube/config"
}

provider "helm" {
kubernetes = {
config_path = "~/.kube/config"
}
}

module "aperturedb" {
source = "oci://registry-1.docker.io/aperturedata/aperturedb-terraform?tag=<package-version>"

namespace = "aperturedb"
create_namespace = false
master_key = { existing_secret = "adb-master-key" }

storage = {
type = "s3"
bucket = "<your-bucket-name>"
existing_secret = "adb-storage"
}

service = { type = "LoadBalancer" }
ingress = {
enabled = true
class_name = "nginx"
host = "aperturedb.example.com"
tls = true
}

telemetry = {
enabled = true
auth = { existing_secret = "adb-monitoring" }
grafana = { existing_secret = "adb-grafana" }
}
}

output "database_load_balancer" {
value = module.aperturedb.lenz_external_load_balancer
}

The example expects an existing ingress-nginx controller with class nginx. To deploy the bundled controller, add ingress_nginx = { enabled = true } inside the module and omit ingress.class_name to use its class. In a shared cluster, set a unique ingress_nginx.controller.ingress_class_name for each bundled controller.

Create the namespace and secrets​

Create the namespace if it does not already exist:

kubectl create namespace aperturedb

Create four local files: master-password.txt, monitoring-password.txt, grafana-password.txt, and storage-credentials.json. Each password file must contain a different strong password without a trailing newline. The JSON file contains the S3 credentials described in Deploying ApertureDB. Then run:

chmod 600 master-password.txt monitoring-password.txt grafana-password.txt storage-credentials.json
kubectl -n aperturedb create secret generic adb-master-key \
--from-file=password=master-password.txt
kubectl -n aperturedb create secret generic adb-monitoring \
--from-file=password=monitoring-password.txt
kubectl -n aperturedb create secret generic adb-grafana \
--from-file=password=grafana-password.txt
kubectl -n aperturedb create secret generic adb-storage \
--from-file=ADB_STORAGE_CREDENTIALS=storage-credentials.json

For Google Cloud Storage, set storage.type = "gs" and put the service account JSON in storage-credentials.json. The OpenTofu storage secret key is ADB_STORAGE_CREDENTIALS; the Helm chart uses a different key.

Alternatively, the module can create its namespace with create_namespace = true and create secrets from inline values: master_key.password, storage.credentials (a map containing ADB_STORAGE_CREDENTIALS), telemetry.auth.password, and telemetry.grafana.admin_password. Inline credentials are stored in OpenTofu state. Protect state, plan files, and local credential files, and keep them out of source control.

For private workload images, create a registry secret in the namespace and set image_pull_secrets = ["my-registry-secret"] in the module.

Configure TLS​

The module uses an existing TLS secret, inline certificate and key, cert-manager, or a generated self-signed certificate, in that order. To use a certificate covering your web and TCP hostnames:

kubectl -n aperturedb create secret tls adb-tls \
--cert=cert.pem --key=key.pem

Add tls = { existing_secret = "adb-tls" } to the module. Other options are tls.cert and tls.key, or tls.cert_manager with a configured issuer. Without these settings, the self-signed fallback is for evaluation; clients must explicitly trust it or use an evaluation-only certificate verification override. The generated private key is stored in OpenTofu state.

Plan, apply, and connect​

Initialize the module and providers, review the plan, then apply it:

tofu init
tofu validate
tofu plan -out=aperturedb.tfplan
tofu apply aperturedb.tfplan
kubectl -n aperturedb get pods,svc,ingress,pvc
tofu output database_load_balancer

Point the DNS record for ingress.host at your ingress controller’s address. When using the bundled controller, its service name is available as the module output ingress_nginx_controller_service. The database TCP load balancer is a separate endpoint; use its own DNS name covered by your certificate.

  • Web UI: https://aperturedb.example.com/.

  • REST API: https://aperturedb.example.com/api/.

  • Native clients: the lenz-external service’s external address on port 55555, with TLS enabled. In-cluster clients can use lenz on that port.

  • Grafana: https://aperturedb.example.com/grafana/ with Web UI and telemetry enabled. Log in as admin using the Grafana secret’s password.

Log in to ApertureDB as admin with the master key and run GetStatus. Check pod events, PVC status, and container logs if startup does not complete. A pending external address requires checking LoadBalancer support; HTTP routing also depends on DNS and the ingress class.

Customize, upgrade, and remove​

Common module settings include aperturedb.replicas, lenz.replicas, global.storage_class, and telemetry.persistence.storage_class. Telemetry is enabled by default with a fixed 100Gi monitoring volume; set telemetry = { enabled = false } to disable it. inactive = true scales application workloads to zero while retaining their resources.

The package pins all ApertureData image versions, including the database and Web UI. Select the module package tag in source to use the matching images.

Before upgrading, back up your data and state and review the target release’s upgrade requirements. Change ?tag= in source to the desired package version and run:

tofu init -upgrade
tofu plan -out=aperturedb.tfplan
tofu apply aperturedb.tfplan

Review changes to the provider lock file too: init -upgrade can select new provider versions within your constraints.

To remove the resources managed by this configuration, run tofu destroy and review its proposed deletions. It removes the active monitoring PVC, whose underlying data follows the storage reclaim policy. ZooKeeper PVCs created by StatefulSet claim templates remain when their namespace remains. The module does not delete the object storage bucket. In this example the namespace and separately created secrets also remain; if you instead let the module manage the namespace, destroying it deletes the namespace and its remaining resources, including retained claims.