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