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