feat: add linode vm template (#367)
Co-authored-by: Atif Ali <atif@coder.com>
This commit is contained in:
parent
c4c484089f
commit
77328656ff
1
.github/typos.toml
vendored
1
.github/typos.toml
vendored
@ -1,5 +1,6 @@
|
||||
[default.extend-words]
|
||||
muc = "muc" # For Munich location code
|
||||
tyo = "tyo" # For Tokyo location code
|
||||
Hashi = "Hashi"
|
||||
HashiCorp = "HashiCorp"
|
||||
mavrickrishi = "mavrickrishi" # Username
|
||||
|
||||
4
.icons/akamai.svg
Normal file
4
.icons/akamai.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<title>Akamai</title>
|
||||
<path d="M13.0548 0C6.384 0 .961 5.3802.961 12.0078.961 18.6354 6.3698 24 13.0548 24c.6168 0 .6454-.3572.0859-.5293-4.9349-1.5063-8.5352-6.069-8.5352-11.4629 0-5.4656 3.6725-10.0706 8.6934-11.5195C13.8153.3448 13.6716 0 13.0548 0Zm2.3242 1.8223c-5.2648 0-9.5254 4.2606-9.5254 9.5254 0 1.2193.2285 2.3818.6445 3.4433.1722.459.4454.4584.4024.0137-.0287-.3156-.0567-.6447-.0567-.9746 0-5.2648 4.2606-9.5254 9.5254-9.5254 4.9779 0 6.4698 2.2235 6.6563 2.08.2008-.1577-1.808-4.5624-7.6465-4.5624zm.4687 4.0703c-1.8622.0592-3.651.7168-5.1035 1.8554-.2582.2009-.1567.3284.1445.1993 2.4675-1.076 5.5812-1.1046 8.6368-.043 2.0514.7173 3.2413 1.7364 3.3418 1.6934.1578-.0718-1.1915-2.2226-3.6446-3.1407-1.1135-.4196-2.2576-.6-3.375-.5644z" fill="#0096D6"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 852 B |
47
registry/umair/templates/linode-vm/README.md
Normal file
47
registry/umair/templates/linode-vm/README.md
Normal file
@ -0,0 +1,47 @@
|
||||
---
|
||||
display_name: Linode Instance (Linux)
|
||||
description: Provision Linode instances as Coder workspaces
|
||||
icon: ../../../../.icons/akamai.svg
|
||||
verified: false
|
||||
tags: [vm, linux, linode]
|
||||
---
|
||||
|
||||
# Remote Development on Linode Instances
|
||||
|
||||
Provision Linode instances as [Coder workspaces](https://coder.com/docs/workspaces) with this example template.
|
||||
|
||||
<!-- TODO: Add screenshot -->
|
||||
|
||||
## Prerequisites
|
||||
|
||||
To deploy workspaces as Linode instances, you'll need:
|
||||
|
||||
- Linode [personal access token (PAT)](https://www.linode.com/docs/products/tools/api/guides/manage-api-tokens/)
|
||||
|
||||
### Authentication
|
||||
|
||||
This template assumes that the Coder Provisioner is run in an environment that is authenticated with Linode.
|
||||
|
||||
Obtain a [Linode Personal Access Token](https://cloud.linode.com/profile/tokens) and set the `linode_token` variable when deploying the template.
|
||||
For other ways to authenticate [consult the Terraform provider's docs](https://registry.terraform.io/providers/linode/linode/latest/docs).
|
||||
|
||||
## Features
|
||||
|
||||
- **Multiple Instance Types**: From Nanode 1GB to 32GB configurations
|
||||
- **Comprehensive OS Support**: Ubuntu, Debian, CentOS, Fedora, AlmaLinux, Rocky Linux
|
||||
- **Global Regions**: 32 Linode regions across North America, Europe, Asia-Pacific, South America, and Australia
|
||||
- **Persistent Storage**: Configurable volumes (10GB-1TB) that persist `$HOME` across workspace restarts
|
||||
- **Development Tools**: Pre-configured with VS Code Server
|
||||
- **Monitoring**: Built-in CPU, memory, and disk usage monitoring
|
||||
|
||||
## Architecture
|
||||
|
||||
This template provisions the following resources:
|
||||
|
||||
- Linode instance (ephemeral, deleted on stop)
|
||||
- Linode volume (persistent, mounted to `/home/coder`)
|
||||
|
||||
This means, when the workspace restarts, any tools or files outside of the home directory are not persisted. To pre-bake tools into the workspace (e.g. `python3`), modify the VM image, or use a [startup script](https://registry.terraform.io/providers/coder/coder/latest/docs/resources/script).
|
||||
|
||||
> [!NOTE]
|
||||
> This template is designed to be a starting point! Edit the Terraform to extend the template to support your use case.
|
||||
@ -0,0 +1,56 @@
|
||||
#cloud-config
|
||||
hostname: ${hostname}
|
||||
users:
|
||||
- name: ${username}
|
||||
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
|
||||
groups: sudo
|
||||
shell: /bin/bash
|
||||
packages:
|
||||
- git
|
||||
- curl
|
||||
- wget
|
||||
- unzip
|
||||
disk_setup:
|
||||
/dev/sdb:
|
||||
table_type: 'gpt'
|
||||
layout: true
|
||||
overwrite: false
|
||||
fs_setup:
|
||||
- label: ${home_volume_label}
|
||||
filesystem: ext4
|
||||
device: /dev/sdb
|
||||
partition: auto
|
||||
mounts:
|
||||
- ["/dev/sdb", "/home/${username}", "ext4", "defaults", "0", "2"]
|
||||
write_files:
|
||||
- path: /opt/coder/init
|
||||
permissions: "0755"
|
||||
encoding: b64
|
||||
content: ${init_script}
|
||||
- path: /etc/systemd/system/coder-agent.service
|
||||
permissions: "0644"
|
||||
content: |
|
||||
[Unit]
|
||||
Description=Coder Agent
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
User=${username}
|
||||
ExecStart=/opt/coder/init
|
||||
Environment=CODER_AGENT_TOKEN=${coder_agent_token}
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
TimeoutStopSec=90
|
||||
KillMode=process
|
||||
|
||||
OOMScoreAdjust=-1000
|
||||
SyslogIdentifier=coder-agent
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
runcmd:
|
||||
- mkdir -p /home/${username}
|
||||
- chown ${username}:${username} /home/${username}
|
||||
- systemctl enable coder-agent
|
||||
- systemctl start coder-agent
|
||||
397
registry/umair/templates/linode-vm/main.tf
Normal file
397
registry/umair/templates/linode-vm/main.tf
Normal file
@ -0,0 +1,397 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
coder = {
|
||||
source = "coder/coder"
|
||||
}
|
||||
linode = {
|
||||
source = "linode/linode"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "coder" {}
|
||||
|
||||
# Variable for Linode API token
|
||||
variable "linode_token" {
|
||||
description = "Linode API token for authentication"
|
||||
type = string
|
||||
sensitive = true
|
||||
default = ""
|
||||
}
|
||||
|
||||
# Configure the Linode Provider
|
||||
provider "linode" {
|
||||
token = var.linode_token != "" ? var.linode_token : null
|
||||
}
|
||||
|
||||
data "coder_workspace" "me" {}
|
||||
data "coder_workspace_owner" "me" {}
|
||||
|
||||
resource "coder_agent" "main" {
|
||||
os = "linux"
|
||||
arch = "amd64"
|
||||
|
||||
metadata {
|
||||
key = "cpu"
|
||||
display_name = "CPU Usage"
|
||||
interval = 5
|
||||
timeout = 5
|
||||
script = "coder stat cpu"
|
||||
}
|
||||
metadata {
|
||||
key = "memory"
|
||||
display_name = "Memory Usage"
|
||||
interval = 5
|
||||
timeout = 5
|
||||
script = "coder stat mem"
|
||||
}
|
||||
metadata {
|
||||
key = "home"
|
||||
display_name = "Home Usage"
|
||||
interval = 600 # every 10 minutes
|
||||
timeout = 30 # df can take a while on large filesystems
|
||||
script = "coder stat disk --path /home/${lower(data.coder_workspace_owner.me.name)}"
|
||||
}
|
||||
}
|
||||
|
||||
locals {
|
||||
vm_name = "coder-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}"
|
||||
root_disk_label = substr("${local.vm_name}-root", 0, 32)
|
||||
home_volume_label = substr("${local.vm_name}-home", 0, 32)
|
||||
}
|
||||
|
||||
# See https://registry.coder.com/modules/coder/code-server
|
||||
module "code-server" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/code-server/coder"
|
||||
version = "~> 1.0"
|
||||
|
||||
agent_id = coder_agent.main.id
|
||||
order = 1
|
||||
}
|
||||
|
||||
data "coder_parameter" "region" {
|
||||
name = "region"
|
||||
display_name = "Region"
|
||||
description = "This is the region where your workspace will be created."
|
||||
icon = "/emojis/1f30e.png"
|
||||
type = "string"
|
||||
default = "us-east"
|
||||
mutable = false
|
||||
|
||||
option {
|
||||
name = "Newark, NJ (US East)"
|
||||
value = "us-east"
|
||||
icon = "/emojis/1f1fa-1f1f8.png"
|
||||
}
|
||||
option {
|
||||
name = "Washington, DC (US East)"
|
||||
value = "us-iad"
|
||||
icon = "/emojis/1f1fa-1f1f8.png"
|
||||
}
|
||||
option {
|
||||
name = "Fremont, CA (US West)"
|
||||
value = "us-west"
|
||||
icon = "/emojis/1f1fa-1f1f8.png"
|
||||
}
|
||||
option {
|
||||
name = "Los Angeles, CA (US West)"
|
||||
value = "us-lax"
|
||||
icon = "/emojis/1f1fa-1f1f8.png"
|
||||
}
|
||||
option {
|
||||
name = "Dallas, TX (US Central)"
|
||||
value = "us-central"
|
||||
icon = "/emojis/1f1fa-1f1f8.png"
|
||||
}
|
||||
option {
|
||||
name = "Chicago, IL (US Central)"
|
||||
value = "us-ord"
|
||||
icon = "/emojis/1f1fa-1f1f8.png"
|
||||
}
|
||||
option {
|
||||
name = "Atlanta, GA (US Southeast)"
|
||||
value = "us-southeast"
|
||||
icon = "/emojis/1f1fa-1f1f8.png"
|
||||
}
|
||||
option {
|
||||
name = "Miami, FL (US Southeast)"
|
||||
value = "us-mia"
|
||||
icon = "/emojis/1f1fa-1f1f8.png"
|
||||
}
|
||||
option {
|
||||
name = "Seattle, WA (US West)"
|
||||
value = "us-sea"
|
||||
icon = "/emojis/1f1fa-1f1f8.png"
|
||||
}
|
||||
option {
|
||||
name = "Toronto, CA"
|
||||
value = "ca-central"
|
||||
icon = "/emojis/1f1e8-1f1e6.png"
|
||||
}
|
||||
option {
|
||||
name = "London, UK"
|
||||
value = "eu-west"
|
||||
icon = "/emojis/1f1ec-1f1e7.png"
|
||||
}
|
||||
option {
|
||||
name = "London 2, UK"
|
||||
value = "gb-lon"
|
||||
icon = "/emojis/1f1ec-1f1e7.png"
|
||||
}
|
||||
option {
|
||||
name = "Frankfurt, DE"
|
||||
value = "eu-central"
|
||||
icon = "/emojis/1f1e9-1f1ea.png"
|
||||
}
|
||||
option {
|
||||
name = "Frankfurt 2, DE"
|
||||
value = "de-fra-2"
|
||||
icon = "/emojis/1f1e9-1f1ea.png"
|
||||
}
|
||||
option {
|
||||
name = "Paris, FR"
|
||||
value = "fr-par"
|
||||
icon = "/emojis/1f1eb-1f1f7.png"
|
||||
}
|
||||
option {
|
||||
name = "Amsterdam, NL"
|
||||
value = "nl-ams"
|
||||
icon = "/emojis/1f1f3-1f1f1.png"
|
||||
}
|
||||
option {
|
||||
name = "Stockholm, SE"
|
||||
value = "se-sto"
|
||||
icon = "/emojis/1f1f8-1f1ea.png"
|
||||
}
|
||||
option {
|
||||
name = "Madrid, ES"
|
||||
value = "es-mad"
|
||||
icon = "/emojis/1f1ea-1f1f8.png"
|
||||
}
|
||||
option {
|
||||
name = "Milan, IT"
|
||||
value = "it-mil"
|
||||
icon = "/emojis/1f1ee-1f1f9.png"
|
||||
}
|
||||
option {
|
||||
name = "Singapore, SG"
|
||||
value = "ap-south"
|
||||
icon = "/emojis/1f1f8-1f1ec.png"
|
||||
}
|
||||
option {
|
||||
name = "Singapore 2, SG"
|
||||
value = "sg-sin-2"
|
||||
icon = "/emojis/1f1f8-1f1ec.png"
|
||||
}
|
||||
option {
|
||||
name = "Tokyo 2, JP"
|
||||
value = "ap-northeast"
|
||||
icon = "/emojis/1f1ef-1f1f5.png"
|
||||
}
|
||||
option {
|
||||
name = "Tokyo 3, JP"
|
||||
value = "jp-tyo-3"
|
||||
icon = "/emojis/1f1ef-1f1f5.png"
|
||||
}
|
||||
option {
|
||||
name = "Osaka, JP"
|
||||
value = "jp-osa"
|
||||
icon = "/emojis/1f1ef-1f1f5.png"
|
||||
}
|
||||
option {
|
||||
name = "Sydney, AU"
|
||||
value = "ap-southeast"
|
||||
icon = "/emojis/1f1e6-1f1fa.png"
|
||||
}
|
||||
option {
|
||||
name = "Melbourne, AU"
|
||||
value = "au-mel"
|
||||
icon = "/emojis/1f1e6-1f1fa.png"
|
||||
}
|
||||
option {
|
||||
name = "Mumbai, IN"
|
||||
value = "ap-west"
|
||||
icon = "/emojis/1f1ee-1f1f3.png"
|
||||
}
|
||||
option {
|
||||
name = "Mumbai 2, IN"
|
||||
value = "in-bom-2"
|
||||
icon = "/emojis/1f1ee-1f1f3.png"
|
||||
}
|
||||
option {
|
||||
name = "Chennai, IN"
|
||||
value = "in-maa"
|
||||
icon = "/emojis/1f1ee-1f1f3.png"
|
||||
}
|
||||
option {
|
||||
name = "Jakarta, ID"
|
||||
value = "id-cgk"
|
||||
icon = "/emojis/1f1ee-1f1e9.png"
|
||||
}
|
||||
option {
|
||||
name = "Sao Paulo, BR"
|
||||
value = "br-gru"
|
||||
icon = "/emojis/1f1e7-1f1f7.png"
|
||||
}
|
||||
}
|
||||
|
||||
data "coder_parameter" "instance_type" {
|
||||
name = "instance_type"
|
||||
display_name = "Instance Type"
|
||||
description = "Which Linode instance type would you like to use?"
|
||||
default = "g6-nanode-1"
|
||||
type = "string"
|
||||
icon = "/icon/memory.svg"
|
||||
mutable = false
|
||||
|
||||
option {
|
||||
name = "Nanode 1GB (1 vCPU, 1 GB RAM)"
|
||||
value = "g6-nanode-1"
|
||||
}
|
||||
option {
|
||||
name = "Linode 2GB (1 vCPU, 2 GB RAM)"
|
||||
value = "g6-standard-1"
|
||||
}
|
||||
option {
|
||||
name = "Linode 4GB (2 vCPU, 4 GB RAM)"
|
||||
value = "g6-standard-2"
|
||||
}
|
||||
option {
|
||||
name = "Linode 8GB (4 vCPU, 8 GB RAM)"
|
||||
value = "g6-standard-4"
|
||||
}
|
||||
option {
|
||||
name = "Linode 16GB (6 vCPU, 16 GB RAM)"
|
||||
value = "g6-standard-6"
|
||||
}
|
||||
option {
|
||||
name = "Linode 32GB (8 vCPU, 32 GB RAM)"
|
||||
value = "g6-standard-8"
|
||||
}
|
||||
}
|
||||
|
||||
data "coder_parameter" "instance_image" {
|
||||
name = "instance_image"
|
||||
display_name = "Instance Image"
|
||||
description = "Which Linode image would you like to use?"
|
||||
default = "linode/ubuntu24.04"
|
||||
type = "string"
|
||||
mutable = false
|
||||
|
||||
option {
|
||||
name = "Ubuntu 24.04 LTS"
|
||||
value = "linode/ubuntu24.04"
|
||||
icon = "/icon/ubuntu.svg"
|
||||
}
|
||||
option {
|
||||
name = "Debian 13"
|
||||
value = "linode/debian13"
|
||||
icon = "/icon/debian.svg"
|
||||
}
|
||||
option {
|
||||
name = "Fedora 42"
|
||||
value = "linode/fedora42"
|
||||
icon = "/icon/fedora.svg"
|
||||
}
|
||||
option {
|
||||
name = "AlmaLinux 9"
|
||||
value = "linode/almalinux9"
|
||||
icon = "/icon/almalinux.svg"
|
||||
}
|
||||
option {
|
||||
name = "Rocky Linux 9"
|
||||
value = "linode/rocky9"
|
||||
icon = "/icon/rockylinux.svg"
|
||||
}
|
||||
}
|
||||
|
||||
data "coder_parameter" "home_volume_size" {
|
||||
name = "home_volume_size"
|
||||
display_name = "Home Volume Size (GB)"
|
||||
description = "How large would you like your home volume to be (in GB)?"
|
||||
type = "number"
|
||||
default = 20
|
||||
mutable = true
|
||||
|
||||
validation {
|
||||
min = 10
|
||||
max = 1024
|
||||
monotonic = "increasing"
|
||||
}
|
||||
}
|
||||
|
||||
resource "linode_volume" "home_volume" {
|
||||
label = local.home_volume_label
|
||||
size = data.coder_parameter.home_volume_size.value
|
||||
region = data.coder_parameter.region.value
|
||||
|
||||
# Protect the volume from being deleted due to changes in attributes.
|
||||
lifecycle {
|
||||
ignore_changes = all
|
||||
}
|
||||
}
|
||||
|
||||
resource "linode_instance" "workspace" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
label = local.vm_name
|
||||
region = data.coder_parameter.region.value
|
||||
type = data.coder_parameter.instance_type.value
|
||||
|
||||
private_ip = true
|
||||
|
||||
metadata {
|
||||
user_data = base64encode(templatefile("cloud-init/cloud-config.yaml.tftpl", {
|
||||
hostname = local.vm_name
|
||||
username = lower(data.coder_workspace_owner.me.name)
|
||||
home_volume_label = linode_volume.home_volume.label
|
||||
init_script = base64encode(coder_agent.main.init_script)
|
||||
coder_agent_token = coder_agent.main.token
|
||||
}))
|
||||
}
|
||||
tags = ["coder", "workspace", lower(data.coder_workspace_owner.me.name), lower(data.coder_workspace.me.name)]
|
||||
}
|
||||
|
||||
# Create root disk
|
||||
resource "linode_instance_disk" "root" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
label = "boot"
|
||||
linode_id = linode_instance.workspace[0].id
|
||||
size = 25000 # 25GB boot disk
|
||||
image = data.coder_parameter.instance_image.value
|
||||
}
|
||||
|
||||
# Create instance configuration with volume attached
|
||||
resource "linode_instance_config" "workspace" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
label = "${local.vm_name}-config"
|
||||
linode_id = linode_instance.workspace[0].id
|
||||
|
||||
device {
|
||||
device_name = "sda"
|
||||
disk_id = linode_instance_disk.root[0].id
|
||||
}
|
||||
|
||||
device {
|
||||
device_name = "sdb"
|
||||
volume_id = linode_volume.home_volume.id
|
||||
}
|
||||
|
||||
root_device = "/dev/sda"
|
||||
kernel = "linode/latest-64bit"
|
||||
booted = true
|
||||
}
|
||||
|
||||
resource "coder_metadata" "workspace-info" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
resource_id = linode_instance.workspace[0].id
|
||||
|
||||
item {
|
||||
key = "region"
|
||||
value = linode_instance.workspace[0].region
|
||||
}
|
||||
item {
|
||||
key = "type"
|
||||
value = linode_instance.workspace[0].type
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user