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