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