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" {}
104data "coder_workspace_owner" "me" {}
105
106resource "coder_agent" "main" {
107  os             = "linux"
108  arch           = "amd64"
109  startup_script = <<-EOT
110    set -e
111
112    # Install the latest code-server.
113    # Append "--version x.x.x" to install a specific version of code-server.
114    curl -fsSL https://code-server.dev/install.sh | sh -s -- --method=standalone --prefix=/tmp/code-server
115
116    # Start code-server in the background.
117    /tmp/code-server/bin/code-server --auth none --port 13337 >/tmp/code-server.log 2>&1 &
118  EOT
119
120  # The following metadata blocks are optional. They are used to display
121  # information about your workspace in the dashboard. You can remove them
122  # if you don't want to display any information.
123  # For basic resources, you can use the `coder stat` command.
124  # If you need more control, you can write your own script.
125  metadata {
126    display_name = "CPU Usage"
127    key          = "0_cpu_usage"
128    script       = "coder stat cpu"
129    interval     = 10
130    timeout      = 1
131  }
132
133  metadata {
134    display_name = "RAM Usage"
135    key          = "1_ram_usage"
136    script       = "coder stat mem"
137    interval     = 10
138    timeout      = 1
139  }
140
141  metadata {
142    display_name = "Home Disk"
143    key          = "3_home_disk"
144    script       = "coder stat disk --path $${HOME}"
145    interval     = 60
146    timeout      = 1
147  }
148
149  metadata {
150    display_name = "CPU Usage (Host)"
151    key          = "4_cpu_usage_host"
152    script       = "coder stat cpu --host"
153    interval     = 10
154    timeout      = 1
155  }
156
157  metadata {
158    display_name = "Memory Usage (Host)"
159    key          = "5_mem_usage_host"
160    script       = "coder stat mem --host"
161    interval     = 10
162    timeout      = 1
163  }
164
165  metadata {
166    display_name = "Load Average (Host)"
167    key          = "6_load_host"
168    # get load avg scaled by number of cores
169    script   = <<EOT
170      echo "`cat /proc/loadavg | awk '{ print $1 }'` `nproc`" | awk '{ printf "%0.2f", $1/$2 }'
171    EOT
172    interval = 60
173    timeout  = 1
174  }
175}
176
177# code-server
178resource "coder_app" "code-server" {
179  agent_id     = coder_agent.main.id
180  slug         = "code-server"
181  display_name = "code-server"
182  icon         = "/icon/code.svg"
183  url          = "http://localhost:13337?folder=/home/coder"
184  subdomain    = false
185  share        = "owner"
186
187  healthcheck {
188    url       = "http://localhost:13337/healthz"
189    interval  = 3
190    threshold = 10
191  }
192}
193
194resource "kubernetes_persistent_volume_claim" "home" {
195  metadata {
196    name      = "coder-${data.coder_workspace.me.id}-home"
197    namespace = var.namespace
198    labels = {
199      "app.kubernetes.io/name"     = "coder-pvc"
200      "app.kubernetes.io/instance" = "coder-pvc-${data.coder_workspace.me.id}"
201      "app.kubernetes.io/part-of"  = "coder"
202      //Coder-specific labels.
203      "com.coder.resource"       = "true"
204      "com.coder.workspace.id"   = data.coder_workspace.me.id
205      "com.coder.workspace.name" = data.coder_workspace.me.name
206      "com.coder.user.id"        = data.coder_workspace_owner.me.id
207      "com.coder.user.username"  = data.coder_workspace_owner.me.name
208    }
209    annotations = {
210      "com.coder.user.email" = data.coder_workspace_owner.me.email
211    }
212  }
213  wait_until_bound = false
214  spec {
215    access_modes = ["ReadWriteOnce"]
216    resources {
217      requests = {
218        storage = "${data.coder_parameter.home_disk_size.value}Gi"
219      }
220    }
221  }
222}
223
224resource "kubernetes_deployment" "main" {
225  count = data.coder_workspace.me.start_count
226  depends_on = [
227    kubernetes_persistent_volume_claim.home
228  ]
229  wait_for_rollout = false
230  metadata {
231    name      = "coder-${data.coder_workspace.me.id}"
232    namespace = var.namespace
233    labels = {
234      "app.kubernetes.io/name"     = "coder-workspace"
235      "app.kubernetes.io/instance" = "coder-workspace-${data.coder_workspace.me.id}"
236      "app.kubernetes.io/part-of"  = "coder"
237      "com.coder.resource"         = "true"
238      "com.coder.workspace.id"     = data.coder_workspace.me.id
239      "com.coder.workspace.name"   = data.coder_workspace.me.name
240      "com.coder.user.id"          = data.coder_workspace_owner.me.id
241      "com.coder.user.username"    = data.coder_workspace_owner.me.name
242    }
243    annotations = {
244      "com.coder.user.email" = data.coder_workspace_owner.me.email
245    }
246  }
247
248  spec {
249    replicas = 1
250    selector {
251      match_labels = {
252        "app.kubernetes.io/name"     = "coder-workspace"
253        "app.kubernetes.io/instance" = "coder-workspace-${data.coder_workspace.me.id}"
254        "app.kubernetes.io/part-of"  = "coder"
255        "com.coder.resource"         = "true"
256        "com.coder.workspace.id"     = data.coder_workspace.me.id
257        "com.coder.workspace.name"   = data.coder_workspace.me.name
258        "com.coder.user.id"          = data.coder_workspace_owner.me.id
259        "com.coder.user.username"    = data.coder_workspace_owner.me.name
260      }
261    }
262    strategy {
263      type = "Recreate"
264    }
265
266    template {
267      metadata {
268        labels = {
269          "app.kubernetes.io/name"     = "coder-workspace"
270          "app.kubernetes.io/instance" = "coder-workspace-${data.coder_workspace.me.id}"
271          "app.kubernetes.io/part-of"  = "coder"
272          "com.coder.resource"         = "true"
273          "com.coder.workspace.id"     = data.coder_workspace.me.id
274          "com.coder.workspace.name"   = data.coder_workspace.me.name
275          "com.coder.user.id"          = data.coder_workspace_owner.me.id
276          "com.coder.user.username"    = data.coder_workspace_owner.me.name
277        }
278      }
279      spec {
280        security_context {
281          run_as_user = 1000
282          fs_group    = 1000
283        }
284
285        container {
286          name              = "dev"
287          image             = "codercom/enterprise-base:ubuntu"
288          image_pull_policy = "Always"
289          command           = ["sh", "-c", coder_agent.main.init_script]
290          security_context {
291            run_as_user = "1000"
292          }
293          env {
294            name  = "CODER_AGENT_TOKEN"
295            value = coder_agent.main.token
296          }
297          resources {
298            requests = {
299              "cpu"    = "250m"
300              "memory" = "512Mi"
301            }
302            limits = {
303              "cpu"    = "${data.coder_parameter.cpu.value}"
304              "memory" = "${data.coder_parameter.memory.value}Gi"
305            }
306          }
307          volume_mount {
308            mount_path = "/home/coder"
309            name       = "home"
310            read_only  = false
311          }
312        }
313
314        volume {
315          name = "home"
316          persistent_volume_claim {
317            claim_name = kubernetes_persistent_volume_claim.home.metadata.0.name
318            read_only  = false
319          }
320        }
321
322        affinity {
323          // This affinity attempts to spread out all workspace pods evenly across
324          // nodes.
325          pod_anti_affinity {
326            preferred_during_scheduling_ignored_during_execution {
327              weight = 1
328              pod_affinity_term {
329                topology_key = "kubernetes.io/hostname"
330                label_selector {
331                  match_expressions {
332                    key      = "app.kubernetes.io/name"
333                    operator = "In"
334                    values   = ["coder-workspace"]
335                  }
336                }
337              }
338            }
339          }
340        }
341      }
342    }
343  }
344}
345