Setting Up KEDA with Terraform
Comprehensive guide for deploying and managing KEDA using Terraform on Kubernetes
Setting Up KEDA with Terraform
This guide provides a comprehensive approach to deploying and managing KEDA (Kubernetes Event-driven Autoscaling) using Terraform, including infrastructure setup, scaler configurations, and best practices.
Prerequisites
- Terraform (v1.0+)
- Kubernetes cluster
- kubectl configured
- Helm provider
- Basic understanding of KEDA and Terraform
Project Structure
keda-terraform/
├── main.tf
├── variables.tf
├── outputs.tf
├── providers.tf
├── versions.tf
└── modules/
├── keda/
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
└── scalers/
├── main.tf
├── variables.tf
└── outputs.tf
Step 1: Provider Configuration
providers.tf
terraform {
required_providers {
kubernetes = {
source = "hashicorp/kubernetes"
version = "~> 2.23.0"
}
helm = {
source = "hashicorp/helm"
version = "~> 2.11.0"
}
}
}
provider "kubernetes" {
config_path = var.kubeconfig_path
}
provider "helm" {
kubernetes {
config_path = var.kubeconfig_path
}
}
versions.tf
terraform {
required_version = ">= 1.0.0"
}
Step 2: KEDA Module
modules/keda/main.tf
resource "kubernetes_namespace" "keda" {
metadata {
name = var.namespace
labels = {
"app.kubernetes.io/managed-by" = "terraform"
}
}
}
resource "helm_release" "keda" {
name = "keda"
repository = "https://kedacore.github.io/charts"
chart = "keda"
version = var.keda_version
namespace = kubernetes_namespace.keda.metadata[0].name
values = [
yamlencode({
operator = {
replicaCount = var.operator_replicas
resources = {
requests = {
cpu = var.operator_resources.requests.cpu
memory = var.operator_resources.requests.memory
}
limits = {
cpu = var.operator_resources.limits.cpu
memory = var.operator_resources.limits.memory
}
}
}
metricsServer = {
replicaCount = var.metrics_server_replicas
resources = {
requests = {
cpu = var.metrics_server_resources.requests.cpu
memory = var.metrics_server_resources.requests.memory
}
limits = {
cpu = var.metrics_server_resources.limits.cpu
memory = var.metrics_server_resources.limits.memory
}
}
}
prometheus = {
enabled = var.prometheus_enabled
}
serviceMonitor = {
enabled = var.service_monitor_enabled
}
})
]
}
modules/keda/variables.tf
variable "namespace" {
description = "Namespace for KEDA installation"
type = string
default = "keda"
}
variable "keda_version" {
description = "Version of KEDA Helm chart"
type = string
default = "2.12.0"
}
variable "operator_replicas" {
description = "Number of KEDA operator replicas"
type = number
default = 1
}
variable "operator_resources" {
description = "Resources for KEDA operator"
type = object({
requests = object({
cpu = string
memory = string
})
limits = object({
cpu = string
memory = string
})
})
default = {
requests = {
cpu = "100m"
memory = "128Mi"
}
limits = {
cpu = "1000m"
memory = "512Mi"
}
}
}
variable "metrics_server_replicas" {
description = "Number of metrics server replicas"
type = number
default = 1
}
variable "metrics_server_resources" {
description = "Resources for metrics server"
type = object({
requests = object({
cpu = string
memory = string
})
limits = object({
cpu = string
memory = string
})
})
default = {
requests = {
cpu = "100m"
memory = "128Mi"
}
limits = {
cpu = "1000m"
memory = "512Mi"
}
}
}
variable "prometheus_enabled" {
description = "Enable Prometheus metrics"
type = bool
default = true
}
variable "service_monitor_enabled" {
description = "Enable ServiceMonitor for Prometheus Operator"
type = bool
default = true
}
Step 3: Scalers Module
modules/scalers/main.tf
resource "kubernetes_manifest" "scaled_object" {
for_each = var.scaled_objects
manifest = {
apiVersion = "keda.sh/v1alpha1"
kind = "ScaledObject"
metadata = {
name = each.key
namespace = each.value.namespace
}
spec = {
scaleTargetRef = {
name = each.value.target_name
kind = each.value.target_kind
}
minReplicaCount = each.value.min_replicas
maxReplicaCount = each.value.max_replicas
pollingInterval = each.value.polling_interval
cooldownPeriod = each.value.cooldown_period
triggers = each.value.triggers
}
}
}
resource "kubernetes_manifest" "trigger_authentication" {
for_each = var.trigger_authentications
manifest = {
apiVersion = "keda.sh/v1alpha1"
kind = "TriggerAuthentication"
metadata = {
name = each.key
namespace = each.value.namespace
}
spec = each.value.spec
}
}
modules/scalers/variables.tf
variable "scaled_objects" {
description = "Map of ScaledObjects to create"
type = map(object({
namespace = string
target_name = string
target_kind = string
min_replicas = number
max_replicas = number
polling_interval = number
cooldown_period = number
triggers = list(any)
}))
}
variable "trigger_authentications" {
description = "Map of TriggerAuthentications to create"
type = map(object({
namespace = string
spec = any
}))
}
Step 4: Root Module Configuration
main.tf
module "keda" {
source = "./modules/keda"
namespace = var.keda_namespace
keda_version = var.keda_version
operator_replicas = var.operator_replicas
operator_resources = var.operator_resources
prometheus_enabled = var.prometheus_enabled
service_monitor_enabled = var.service_monitor_enabled
}
module "scalers" {
source = "./modules/scalers"
depends_on = [module.keda]
scaled_objects = var.scaled_objects
trigger_authentications = var.trigger_authentications
}
variables.tf
variable "kubeconfig_path" {
description = "Path to kubeconfig file"
type = string
}
variable "keda_namespace" {
description = "Namespace for KEDA installation"
type = string
default = "keda"
}
variable "keda_version" {
description = "Version of KEDA Helm chart"
type = string
default = "2.12.0"
}
variable "scaled_objects" {
description = "Map of ScaledObjects to create"
type = map(object({
namespace = string
target_name = string
target_kind = string
min_replicas = number
max_replicas = number
polling_interval = number
cooldown_period = number
triggers = list(any)
}))
default = {}
}
variable "trigger_authentications" {
description = "Map of TriggerAuthentications to create"
type = map(object({
namespace = string
spec = any
}))
default = {}
}
Usage Examples
1. RabbitMQ Scaler
scaled_objects = {
"rabbitmq-consumer" = {
namespace = "default"
target_name = "rabbitmq-consumer"
target_kind = "Deployment"
min_replicas = 1
max_replicas = 10
polling_interval = 30
cooldown_period = 300
triggers = [{
type = "rabbitmq"
metadata = {
protocol = "amqp"
queueName = "orders"
host = "amqp://rabbitmq.default.svc:5672"
queueLength = "50"
}
}]
}
}
2. Prometheus Scaler with Authentication
trigger_authentications = {
"prometheus-auth" = {
namespace = "default"
spec = {
secretTargetRef = [{
parameter = "bearerToken"
name = "prometheus-secret"
key = "token"
}]
}
}
}
scaled_objects = {
"prometheus-scaler" = {
namespace = "default"
target_name = "my-app"
target_kind = "Deployment"
min_replicas = 1
max_replicas = 10
polling_interval = 30
cooldown_period = 300
triggers = [{
type = "prometheus"
metadata = {
serverAddress = "http://prometheus.monitoring.svc"
metricName = "http_requests_total"
threshold = "100"
query = "sum(rate(http_requests_total{service=\"my-service\"}[2m]))"
}
authenticationRef = {
name = "prometheus-auth"
}
}]
}
}
Best Practices
- Secret Management
resource "kubernetes_secret" "scaler_secrets" {
metadata {
name = "scaler-secrets"
namespace = var.namespace
}
data = {
connection_string = var.connection_string
}
}
- High Availability
operator_resources = {
requests = {
cpu = "200m"
memory = "256Mi"
}
limits = {
cpu = "1000m"
memory = "1Gi"
}
}
operator_replicas = 2
- Monitoring Configuration
prometheus_enabled = true
service_monitor_enabled = true
Troubleshooting
Common Issues
- Installation Issues
# Check Helm release status
helm list -n keda
helm status keda -n keda
# Check KEDA pods
kubectl get pods -n keda
kubectl describe pod -l app=keda-operator -n keda
- Scaler Issues
# Check ScaledObject status
kubectl get scaledobject
kubectl describe scaledobject <name>
# Check HPA status
kubectl get hpa
kubectl describe hpa <name>