Skip to content

Commit 81e9797

Browse files
added ec2 spot fleet example
1 parent ec208b5 commit 81e9797

File tree

2 files changed

+388
-0
lines changed

2 files changed

+388
-0
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
---
2+
display_name: AWS EC2 (Linux)
3+
description: Provision AWS EC2 VMs as Coder workspaces
4+
icon: ../../../site/static/icon/aws.svg
5+
maintainer_github: coder
6+
verified: true
7+
tags: [vm, linux, aws, persistent-vm]
8+
---
9+
10+
# Remote Development on AWS EC2 VMs (Linux)
11+
12+
Provision AWS EC2 VMs as [Coder workspaces](https://coder.com/docs/workspaces) with this example template.
13+
14+
<!-- TODO: Add screenshot -->
15+
16+
## Prerequisites
17+
18+
### Authentication
19+
20+
By default, this template authenticates to AWS using the provider's default [authentication methods](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#authentication-and-configuration).
21+
22+
The simplest way (without making changes to the template) is via environment variables (e.g. `AWS_ACCESS_KEY_ID`) or a [credentials file](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html#cli-configure-files-format). If you are running Coder on a VM, this file must be in `/home/coder/aws/credentials`.
23+
24+
To use another [authentication method](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#authentication), edit the template.
25+
26+
## Required permissions / policy
27+
28+
The following sample policy allows Coder to create EC2 instances and modify
29+
instances provisioned by Coder:
30+
31+
```json
32+
{
33+
"Version": "2012-10-17",
34+
"Statement": [
35+
{
36+
"Sid": "VisualEditor0",
37+
"Effect": "Allow",
38+
"Action": [
39+
"ec2:GetDefaultCreditSpecification",
40+
"ec2:DescribeIamInstanceProfileAssociations",
41+
"ec2:DescribeTags",
42+
"ec2:DescribeInstances",
43+
"ec2:DescribeInstanceTypes",
44+
"ec2:CreateTags",
45+
"ec2:RunInstances",
46+
"ec2:DescribeInstanceCreditSpecifications",
47+
"ec2:DescribeImages",
48+
"ec2:ModifyDefaultCreditSpecification",
49+
"ec2:DescribeVolumes"
50+
],
51+
"Resource": "*"
52+
},
53+
{
54+
"Sid": "CoderResources",
55+
"Effect": "Allow",
56+
"Action": [
57+
"ec2:DescribeInstanceAttribute",
58+
"ec2:UnmonitorInstances",
59+
"ec2:TerminateInstances",
60+
"ec2:StartInstances",
61+
"ec2:StopInstances",
62+
"ec2:DeleteTags",
63+
"ec2:MonitorInstances",
64+
"ec2:CreateTags",
65+
"ec2:RunInstances",
66+
"ec2:ModifyInstanceAttribute",
67+
"ec2:ModifyInstanceCreditSpecification"
68+
],
69+
"Resource": "arn:aws:ec2:*:*:instance/*",
70+
"Condition": {
71+
"StringEquals": {
72+
"aws:ResourceTag/Coder_Provisioned": "true"
73+
}
74+
}
75+
}
76+
]
77+
}
78+
```
79+
80+
## Architecture
81+
82+
This template provisions the following resources:
83+
84+
- AWS Instance
85+
86+
Coder uses `aws_ec2_instance_state` to start and stop the VM. This example template is fully persistent, meaning the full filesystem is preserved when the workspace restarts. See this [community example](https://github.com/bpmct/coder-templates/tree/main/aws-linux-ephemeral) of an ephemeral AWS instance.
87+
88+
> **Note**
89+
> This template is designed to be a starting point! Edit the Terraform to extend the template to support your use case.
90+
91+
## code-server
92+
93+
`code-server` is installed via the `startup_script` argument in the `coder_agent`
94+
resource block. The `coder_app` resource is defined to access `code-server` through
95+
the dashboard UI over `localhost:13337`.

templates/aws-linux-spotfleet/main.tf

Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
terraform {
2+
required_providers {
3+
coder = {
4+
source = "coder/coder"
5+
}
6+
aws = {
7+
source = "hashicorp/aws"
8+
}
9+
}
10+
}
11+
12+
# Change
13+
# Last updated 2023-03-14
14+
# aws ec2 describe-regions | jq -r '[.Regions[].RegionName] | sort'
15+
data "coder_parameter" "region" {
16+
name = "region"
17+
display_name = "Region"
18+
description = "The region to deploy the workspace in."
19+
default = "us-east-1"
20+
mutable = false
21+
option {
22+
name = "Asia Pacific (Tokyo)"
23+
value = "ap-northeast-1"
24+
icon = "/emojis/1f1ef-1f1f5.png"
25+
}
26+
option {
27+
name = "Asia Pacific (Seoul)"
28+
value = "ap-northeast-2"
29+
icon = "/emojis/1f1f0-1f1f7.png"
30+
}
31+
option {
32+
name = "Asia Pacific (Osaka)"
33+
value = "ap-northeast-3"
34+
icon = "/emojis/1f1ef-1f1f5.png"
35+
}
36+
option {
37+
name = "Asia Pacific (Mumbai)"
38+
value = "ap-south-1"
39+
icon = "/emojis/1f1ee-1f1f3.png"
40+
}
41+
option {
42+
name = "Asia Pacific (Singapore)"
43+
value = "ap-southeast-1"
44+
icon = "/emojis/1f1f8-1f1ec.png"
45+
}
46+
option {
47+
name = "Asia Pacific (Sydney)"
48+
value = "ap-southeast-2"
49+
icon = "/emojis/1f1e6-1f1fa.png"
50+
}
51+
option {
52+
name = "Canada (Central)"
53+
value = "ca-central-1"
54+
icon = "/emojis/1f1e8-1f1e6.png"
55+
}
56+
option {
57+
name = "EU (Frankfurt)"
58+
value = "eu-central-1"
59+
icon = "/emojis/1f1ea-1f1fa.png"
60+
}
61+
option {
62+
name = "EU (Stockholm)"
63+
value = "eu-north-1"
64+
icon = "/emojis/1f1ea-1f1fa.png"
65+
}
66+
option {
67+
name = "EU (Ireland)"
68+
value = "eu-west-1"
69+
icon = "/emojis/1f1ea-1f1fa.png"
70+
}
71+
option {
72+
name = "EU (London)"
73+
value = "eu-west-2"
74+
icon = "/emojis/1f1ea-1f1fa.png"
75+
}
76+
option {
77+
name = "EU (Paris)"
78+
value = "eu-west-3"
79+
icon = "/emojis/1f1ea-1f1fa.png"
80+
}
81+
option {
82+
name = "South America (São Paulo)"
83+
value = "sa-east-1"
84+
icon = "/emojis/1f1e7-1f1f7.png"
85+
}
86+
option {
87+
name = "US East (N. Virginia)"
88+
value = "us-east-1"
89+
icon = "/emojis/1f1fa-1f1f8.png"
90+
}
91+
option {
92+
name = "US East (Ohio)"
93+
value = "us-east-2"
94+
icon = "/emojis/1f1fa-1f1f8.png"
95+
}
96+
option {
97+
name = "US West (N. California)"
98+
value = "us-west-1"
99+
icon = "/emojis/1f1fa-1f1f8.png"
100+
}
101+
option {
102+
name = "US West (Oregon)"
103+
value = "us-west-2"
104+
icon = "/emojis/1f1fa-1f1f8.png"
105+
}
106+
}
107+
108+
data "coder_parameter" "instance_type" {
109+
name = "instance_type"
110+
display_name = "Instance type"
111+
description = "What instance type should your workspace use?"
112+
default = "t3.micro"
113+
mutable = false
114+
option {
115+
name = "2 vCPU, 1 GiB RAM"
116+
value = "t3.micro"
117+
}
118+
option {
119+
name = "2 vCPU, 2 GiB RAM"
120+
value = "t3.small"
121+
}
122+
option {
123+
name = "2 vCPU, 4 GiB RAM"
124+
value = "t3.medium"
125+
}
126+
option {
127+
name = "2 vCPU, 8 GiB RAM"
128+
value = "t3.large"
129+
}
130+
option {
131+
name = "4 vCPU, 16 GiB RAM"
132+
value = "t3.xlarge"
133+
}
134+
option {
135+
name = "8 vCPU, 32 GiB RAM"
136+
value = "t3.2xlarge"
137+
}
138+
}
139+
140+
provider "aws" {
141+
region = data.coder_parameter.region.value
142+
}
143+
144+
data "coder_workspace" "me" {
145+
}
146+
data "coder_workspace_owner" "me" {}
147+
148+
data "aws_ami" "ubuntu" {
149+
most_recent = true
150+
filter {
151+
name = "name"
152+
values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
153+
}
154+
filter {
155+
name = "virtualization-type"
156+
values = ["hvm"]
157+
}
158+
owners = ["099720109477"] # Canonical
159+
}
160+
161+
resource "coder_agent" "dev" {
162+
count = data.coder_workspace.me.start_count
163+
arch = "amd64"
164+
auth = "aws-instance-identity"
165+
os = "linux"
166+
startup_script = <<-EOT
167+
set -e
168+
169+
# Install the latest code-server.
170+
# Append "--version x.x.x" to install a specific version of code-server.
171+
curl -fsSL https://code-server.dev/install.sh | sh -s -- --method=standalone --prefix=/tmp/code-server
172+
173+
# Start code-server in the background.
174+
/tmp/code-server/bin/code-server --auth none --port 13337 >/tmp/code-server.log 2>&1 &
175+
EOT
176+
177+
metadata {
178+
key = "cpu"
179+
display_name = "CPU Usage"
180+
interval = 5
181+
timeout = 5
182+
script = "coder stat cpu"
183+
}
184+
metadata {
185+
key = "memory"
186+
display_name = "Memory Usage"
187+
interval = 5
188+
timeout = 5
189+
script = "coder stat mem"
190+
}
191+
metadata {
192+
key = "disk"
193+
display_name = "Disk Usage"
194+
interval = 600 # every 10 minutes
195+
timeout = 30 # df can take a while on large filesystems
196+
script = "coder stat disk --path $HOME"
197+
}
198+
}
199+
200+
resource "coder_app" "code-server" {
201+
count = data.coder_workspace.me.start_count
202+
agent_id = coder_agent.dev[0].id
203+
slug = "code-server"
204+
display_name = "code-server"
205+
url = "http://localhost:13337/?folder=/home/coder"
206+
icon = "/icon/code.svg"
207+
subdomain = false
208+
share = "owner"
209+
210+
healthcheck {
211+
url = "http://localhost:13337/healthz"
212+
interval = 3
213+
threshold = 10
214+
}
215+
}
216+
217+
locals {
218+
linux_user = "coder"
219+
user_data = <<-EOT
220+
Content-Type: multipart/mixed; boundary="//"
221+
MIME-Version: 1.0
222+
223+
--//
224+
Content-Type: text/cloud-config; charset="us-ascii"
225+
MIME-Version: 1.0
226+
Content-Transfer-Encoding: 7bit
227+
Content-Disposition: attachment; filename="cloud-config.txt"
228+
229+
#cloud-config
230+
cloud_final_modules:
231+
- [scripts-user, always]
232+
hostname: ${lower(data.coder_workspace.me.name)}
233+
users:
234+
- name: ${local.linux_user}
235+
sudo: ALL=(ALL) NOPASSWD:ALL
236+
shell: /bin/bash
237+
238+
--//
239+
Content-Type: text/x-shellscript; charset="us-ascii"
240+
MIME-Version: 1.0
241+
Content-Transfer-Encoding: 7bit
242+
Content-Disposition: attachment; filename="userdata.txt"
243+
244+
#!/bin/bash
245+
sudo -u ${local.linux_user} sh -c '${try(coder_agent.dev[0].init_script, "")}'
246+
--//--
247+
EOT
248+
}
249+
250+
resource "aws_instance" "dev" {
251+
ami = data.aws_ami.ubuntu.id
252+
instance_market_options {
253+
market_type = "spot"
254+
spot_options {
255+
instance_interruption_behavior = "stop"
256+
spot_instance_type = "persistent"
257+
}
258+
}
259+
260+
availability_zone = "${data.coder_parameter.region.value}a"
261+
instance_type = data.coder_parameter.instance_type.value
262+
263+
user_data = local.user_data
264+
tags = {
265+
Name = "coder-${data.coder_workspace_owner.me.name}-${data.coder_workspace.me.name}"
266+
# Required if you are using our example policy, see template README
267+
Coder_Provisioned = "true"
268+
}
269+
lifecycle {
270+
ignore_changes = [ami]
271+
}
272+
}
273+
274+
resource "coder_metadata" "workspace_info" {
275+
resource_id = aws_instance.dev.id
276+
item {
277+
key = "region"
278+
value = data.coder_parameter.region.value
279+
}
280+
item {
281+
key = "instance type"
282+
value = aws_instance.dev.instance_type
283+
}
284+
item {
285+
key = "disk"
286+
value = "${aws_instance.dev.root_block_device[0].volume_size} GiB"
287+
}
288+
}
289+
290+
resource "aws_ec2_instance_state" "dev" {
291+
instance_id = aws_instance.dev.id
292+
state = data.coder_workspace.me.transition == "start" ? "running" : "stopped"
293+
}

0 commit comments

Comments
 (0)