TemplatesModules
Back to Templates
Kubernetes (Deployment) Icon

Kubernetes (Deployment)

By:
Provision Kubernetes Deployments as Coder workspaces
Source
README
Resources (2)
Variables (2)

Copy and paste the following into main.tf and run coder template push:

1terraform {
2  required_providers {
3    coder = {
4      source = "coder/coder"
5    }
6    kubernetes = {
7      source = "hashicorp/kubernetes"
8    }
9  }
10}
11
12provider "coder" {
13}
14
15variable "use_kubeconfig" {
16  type        = bool
17  description = <<-EOF
18  Use host kubeconfig? (true/false)
19
20  Set this to false if the Coder host is itself running as a Pod on the same
21  Kubernetes cluster as you are deploying workspaces to.
22
23  Set this to true if the Coder host is running outside the Kubernetes cluster
24  for workspaces.  A valid "~/.kube/config" must be present on the Coder host.
25  EOF
26  default     = false
27}
28
29variable "namespace" {
30  type        = string
31  description = "The Kubernetes namespace to create workspaces in (must exist prior to creating workspaces). If the Coder host is itself running as a Pod on the same Kubernetes cluster as you are deploying workspaces to, set this to the same namespace."
32}
33
34data "coder_parameter" "cpu" {
35  name         = "cpu"
36  display_name = "CPU"
37  description  = "The number of CPU cores"
38  default      = "2"
39  icon         = "/icon/memory.svg"
40  mutable      = true
41  option {
42    name  = "2 Cores"
43    value = "2"
44  }
45  option {
46    name  = "4 Cores"
47    value = "4"
48  }
49  option {
50    name  = "6 Cores"
51    value = "6"
52  }
53  option {
54    name  = "8 Cores"
55    value = "8"
56  }
57}
58
59data "coder_parameter" "memory" {
60  name         = "memory"
61  display_name = "Memory"
62  description  = "The amount of memory in GB"
63  default      = "2"
64  icon         = "/icon/memory.svg"
65  mutable      = true
66  option {
67    name  = "2 GB"
68    value = "2"
69  }
70  option {
71    name  = "4 GB"
72    value = "4"
73  }
74  option {
75    name  = "6 GB"
76    value = "6"
77  }
78  option {
79    name  = "8 GB"
80    value = "8"
81  }
82}
83
84data "coder_parameter" "home_disk_size" {
85  name         = "home_disk_size"
86  display_name = "Home disk size"
87  description  = "The size of the home disk in GB"
88  default      = "10"
89  type         = "number"
90  icon         = "/emojis/1f4be.png"
91  mutable      = false
92  validation {
93    min = 1
94    max = 99999
95  }
96}
97
98provider "kubernetes" {
99  # Authenticate via ~/.kube/config or a Coder-specific ServiceAccount, depending on admin preferences
100  config_path = var.use_kubeconfig == true ? "~/.kube/config" : null
101}
102
103data "coder_workspace" "me" {}
104
105resource "coder_agent" "main" {
106  os             = "linux"
107  arch           = "amd64"
108  startup_script = <<-EOT
109    set -e
110
111    # install and start code-server
112    curl -fsSL https://code-server.dev/install.sh | sh -s -- --method=standalone --prefix=/tmp/code-server --version 4.11.0
113    /tmp/code-server/bin/code-server --auth none --port 13337 >/tmp/code-server.log 2>&1 &
114  EOT
115
116  # The following metadata blocks are optional. They are used to display
117  # information about your workspace in the dashboard. You can remove them
118  # if you don't want to display any information.
119  # For basic resources, you can use the `coder stat` command.
120  # If you need more control, you can write your own script.
121  metadata {
122    display_name = "CPU Usage"
123    key          = "0_cpu_usage"
124    script       = "coder stat cpu"
125    interval     = 10
126    timeout      = 1
127  }
128
129  metadata {
130    display_name = "RAM Usage"
131    key          = "1_ram_usage"
132    script       = "coder stat mem"
133    interval     = 10
134    timeout      = 1
135  }
136
137  metadata {
138    display_name = "Home Disk"
139    key          = "3_home_disk"
140    script       = "coder stat disk --path $${HOME}"
141    interval     = 60
142    timeout      = 1
143  }
144
145  metadata {
146    display_name = "CPU Usage (Host)"
147    key          = "4_cpu_usage_host"
148    script       = "coder stat cpu --host"
149    interval     = 10
150    timeout      = 1
151  }
152
153  metadata {
154    display_name = "Memory Usage (Host)"
155    key          = "5_mem_usage_host"
156    script       = "coder stat mem --host"
157    interval     = 10
158    timeout      = 1
159  }
160
161  metadata {
162    display_name = "Load Average (Host)"
163    key          = "6_load_host"
164    # get load avg scaled by number of cores
165    script   = <<EOT
166      echo "`cat /proc/loadavg | awk '{ print $1 }'` `nproc`" | awk '{ printf "%0.2f", $1/$2 }'
167    EOT
168    interval = 60
169    timeout  = 1
170  }
171}
172
173# code-server
174resource "coder_app" "code-server" {
175  agent_id     = coder_agent.main.id
176  slug         = "code-server"
177  display_name = "code-server"
178  icon         = "/icon/code.svg"
179  url          = "http://localhost:13337?folder=/home/coder"
180  subdomain    = false
181  share        = "owner"
182
183  healthcheck {
184    url       = "http://localhost:13337/healthz"
185    interval  = 3
186    threshold = 10
187  }
188}
189
190resource "kubernetes_persistent_volume_claim" "home" {
191  metadata {
192    name      = "coder-${lower(data.coder_workspace.me.owner)}-${lower(data.coder_workspace.me.name)}-home"
193    namespace = var.namespace
194    labels = {
195      "app.kubernetes.io/name"     = "coder-pvc"
196      "app.kubernetes.io/instance" = "coder-pvc-${lower(data.coder_workspace.me.owner)}-${lower(data.coder_workspace.me.name)}"
197      "app.kubernetes.io/part-of"  = "coder"
198      //Coder-specific labels.
199      "com.coder.resource"       = "true"
200      "com.coder.workspace.id"   = data.coder_workspace.me.id
201      "com.coder.workspace.name" = data.coder_workspace.me.name
202      "com.coder.user.id"        = data.coder_workspace.me.owner_id
203      "com.coder.user.username"  = data.coder_workspace.me.owner
204    }
205    annotations = {
206      "com.coder.user.email" = data.coder_workspace.me.owner_email
207    }
208  }
209  wait_until_bound = false
210  spec {
211    access_modes = ["ReadWriteOnce"]
212    resources {
213      requests = {
214        storage = "${data.coder_parameter.home_disk_size.value}Gi"
215      }
216    }
217  }
218}
219
220resource "kubernetes_deployment" "main" {
221  count = data.coder_workspace.me.start_count
222  depends_on = [
223    kubernetes_persistent_volume_claim.home
224  ]
225  wait_for_rollout = false
226  metadata {
227    name      = "coder-${lower(data.coder_workspace.me.owner)}-${lower(data.coder_workspace.me.name)}"
228    namespace = var.namespace
229    labels = {
230      "app.kubernetes.io/name"     = "coder-workspace"
231      "app.kubernetes.io/instance" = "coder-workspace-${lower(data.coder_workspace.me.owner)}-${lower(data.coder_workspace.me.name)}"
232      "app.kubernetes.io/part-of"  = "coder"
233      "com.coder.resource"         = "true"
234      "com.coder.workspace.id"     = data.coder_workspace.me.id
235      "com.coder.workspace.name"   = data.coder_workspace.me.name
236      "com.coder.user.id"          = data.coder_workspace.me.owner_id
237      "com.coder.user.username"    = data.coder_workspace.me.owner
238    }
239    annotations = {
240      "com.coder.user.email" = data.coder_workspace.me.owner_email
241    }
242  }
243
244  spec {
245    replicas = 1
246    selector {
247      match_labels = {
248        "app.kubernetes.io/name" = "coder-workspace"
249      }
250    }
251    strategy {
252      type = "Recreate"
253    }
254
255    template {
256      metadata {
257        labels = {
258          "app.kubernetes.io/name" = "coder-workspace"
259        }
260      }
261      spec {
262        security_context {
263          run_as_user = 1000
264          fs_group    = 1000
265        }
266
267        container {
268          name              = "dev"
269          image             = "codercom/enterprise-base:ubuntu"
270          image_pull_policy = "Always"
271          command           = ["sh", "-c", coder_agent.main.init_script]
272          security_context {
273            run_as_user = "1000"
274          }
275          env {
276            name  = "CODER_AGENT_TOKEN"
277            value = coder_agent.main.token
278          }
279          resources {
280            requests = {
281              "cpu"    = "250m"
282              "memory" = "512Mi"
283            }
284            limits = {
285              "cpu"    = "${data.coder_parameter.cpu.value}"
286              "memory" = "${data.coder_parameter.memory.value}Gi"
287            }
288          }
289          volume_mount {
290            mount_path = "/home/coder"
291            name       = "home"
292            read_only  = false
293          }
294        }
295
296        volume {
297          name = "home"
298          persistent_volume_claim {
299            claim_name = kubernetes_persistent_volume_claim.home.metadata.0.name
300            read_only  = false
301          }
302        }
303
304        affinity {
305          // This affinity attempts to spread out all workspace pods evenly across
306          // nodes.
307          pod_anti_affinity {
308            preferred_during_scheduling_ignored_during_execution {
309              weight = 1
310              pod_affinity_term {
311                topology_key = "kubernetes.io/hostname"
312                label_selector {
313                  match_expressions {
314                    key      = "app.kubernetes.io/name"
315                    operator = "In"
316                    values   = ["coder-workspace"]
317                  }
318                }
319              }
320            }
321          }
322        }
323      }
324    }
325  }
326}
327