Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions k8s/gcp/gke/karpenter.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# # gcloud services for karpenter to manage compute and Kubernetes resources
# resource "google_project_service" "compute_service" {
# project = var.provider_id
# service = "compute.googleapis.com"
# disable_on_destroy = false
# }

# resource "google_project_service" "container_service" {
# project = var.provider_id
# service = "container.googleapis.com"
# disable_on_destroy = false
# }

# gcloud service account for karpenter
resource "google_service_account" "karpenter" {
count = var.karpenter_configs.enable ? 1 : 0
project = var.provider_id
account_id = "${local.cluster_service_account_name}-karpenter"
display_name = "${local.cluster_name} Karpenter Account"
description = "Service Account created for Karpenter in ${local.cluster_name} gke cluster"
}

# compute admin role
resource "google_project_iam_member" "compute_admin" {
count = var.karpenter_configs.enable ? 1 : 0
project = var.provider_id
member = google_service_account.karpenter[0].member
role = "roles/compute.admin"
}

# kubernetes engine admin role
resource "google_project_iam_member" "kubernetes_engine_admin" {
count = var.karpenter_configs.enable ? 1 : 0
project = var.provider_id
member = google_service_account.karpenter[0].member
role = "roles/container.admin"
}

# monitoring admin role
resource "google_project_iam_member" "monitoring_admin" {
count = var.karpenter_configs.enable ? 1 : 0
project = var.provider_id
member = google_service_account.karpenter[0].member
role = "roles/monitoring.admin"
}

# service account user role
resource "google_project_iam_member" "service_account_user" {
count = var.karpenter_configs.enable ? 1 : 0
project = var.provider_id
member = google_service_account.karpenter[0].member
role = "roles/iam.serviceAccountUser"
}

# namespace for karpenter
resource "kubernetes_namespace" "karpenter" {
count = var.karpenter_configs.enable ? 1 : 0
metadata {
name = "karpenter"
}
}

# get service account key
resource "google_service_account_key" "karpenter_key" {
count = var.karpenter_configs.enable ? 1 : 0
service_account_id = google_service_account.karpenter[0].name
}

# secret with service account key for karpenter
resource "kubernetes_secret" "gcp-credentials" {
count = var.karpenter_configs.enable ? 1 : 0
metadata {
name = "karpenter-gcp-credentials"
namespace = "karpenter"
}

data = {
"key.json" = base64decode(google_service_account_key.karpenter_key[0].private_key)
}
}

# helm chart values
data "template_file" "karpenter_template" {
count = var.karpenter_configs.enable ? 1 : 0
template = file("./templates/karpenter-values.yaml")
vars = {
PROJECT_ID = var.provider_id
REGION = var.app_region
CLUSTER_NAME = local.cluster_name
SECRET_NAME = kubernetes_secret.gcp-credentials[0].metadata[0].name
}
}

# helm chart install
resource "helm_release" "karpenter" {
count = var.karpenter_configs.enable ? 1 : 0
name = "karpenter"
repository = "https://helm.zop.dev"
chart = "karpenter-gcp"
namespace = "karpenter"
version = "0.0.2"

values = [data.template_file.karpenter_template[0].rendered]
}

# available zones in region
data "google_compute_zones" "zones" {
count = var.karpenter_configs.enable ? 1 : 0
project = var.provider_id
region = var.app_region
}

# pass values to nodeClass and nodePool manifests
locals {
nodeclass_yaml = var.karpenter_configs.enable ? templatefile("./templates/karpenter-gcp-nodeclass.yaml", {
SERVICE_ACCOUNT = google_service_account.karpenter[0].email
ENVIRONMENT = var.app_env
}) : null

nodepool_yaml = var.karpenter_configs.enable ? templatefile("./templates/karpenter-gcp-nodepool.yaml", {
ZONES = data.google_compute_zones.zones[0].names
INSTANCE_TYPES = length(var.karpenter_configs.machine_types) > 0 ? var.karpenter_configs.machine_types : ["e2-standard-2", "e2-standard-4"]
CAPACITY_TYPES = length(var.karpenter_configs.capacity_types) > 0 ? var.karpenter_configs.capacity_types : ["on-demand"]
}) : null
}

# deploy NodeClass
resource "kubectl_manifest" "node_class" {
count = var.karpenter_configs.enable ? 1 : 0
depends_on = [helm_release.karpenter]
yaml_body = local.nodeclass_yaml
}

# deploy NodePool
resource "kubectl_manifest" "node_pool" {
count = var.karpenter_configs.enable ? 1 : 0
depends_on = [helm_release.karpenter, kubectl_manifest.node_class]
yaml_body = local.nodepool_yaml
}
14 changes: 14 additions & 0 deletions k8s/gcp/gke/templates/karpenter-gcp-nodeclass.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apiVersion: karpenter.k8s.gcp/v1alpha1
kind: GCENodeClass
metadata:
name: karpenter-gcp-nodeclass
spec:
serviceAccount: ${SERVICE_ACCOUNT}
imageSelectorTerms:
- alias: ContainerOptimizedOS@latest
tags:
env: ${ENVIRONMENT}
disks:
- category: pd-balanced
sizeGiB: 60
boot: true
39 changes: 39 additions & 0 deletions k8s/gcp/gke/templates/karpenter-gcp-nodepool.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: karpenter-gcp-nodepool
spec:
weight: 10
template:
spec:
nodeClassRef:
name: karpenter-gcp-nodeclass
kind: GCENodeClass
group: karpenter.k8s.gcp
requirements:
- key: "karpenter.sh/capacity-type"
operator: In
values:
%{~ for v in CAPACITY_TYPES ~}
- ${v}
%{~ endfor ~}


- key: "node.kubernetes.io/instance-type"
operator: In
values:
%{~ for v in INSTANCE_TYPES ~}
- ${v}
%{~ endfor ~}


- key: "kubernetes.io/arch"
operator: In
values: ["amd64"]

- key: "topology.kubernetes.io/zone"
operator: In
values:
%{~ for v in ZONES ~}
- ${v}
%{~ endfor ~}
9 changes: 9 additions & 0 deletions k8s/gcp/gke/templates/karpenter-values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
controller:
settings:
projectID: "${PROJECT_ID}"
region: "${REGION}"
clusterName: "${CLUSTER_NAME}"

credentials:
secretName: "${SECRET_NAME}"
secretKey: "key.json"
23 changes: 23 additions & 0 deletions k8s/gcp/gke/vars.tf
Original file line number Diff line number Diff line change
Expand Up @@ -706,4 +706,27 @@ variable "slack_alerts_configs" {
labels = optional(map(string))
}))
default = []
}

variable "karpenter_configs" {
description = "Inputs for karpenter - enabling flag, GCP machine types, and capacity types ('on-demand' or 'spot')"

type = object({
enable = bool
machine_types = list(string)
capacity_types = list(string)
})
default = {
enable = false
machine_types = []
capacity_types = []
}

validation {
condition = alltrue([
for t in var.karpenter_configs.capacity_types :
contains(["on-demand", "spot"], t)
])
error_message = "Capacity type can only be either 'on-demand' or 'spot'"
}
}