mirror of https://github.com/coder/coder.git
chore(examples): add sample Incus template (#12114)
Adds sample incus template created for FOSDEM 2024; there's enough intricacy involved to make it worth persisting
This commit is contained in:
parent
e1e352d8c1
commit
c939416702
|
@ -0,0 +1,49 @@
|
||||||
|
---
|
||||||
|
name: Incus System Container with Docker
|
||||||
|
description: Develop in an Incus System Container with Docker using incus
|
||||||
|
tags: [local, incus, lxc, lxd]
|
||||||
|
icon: /icon/lxc.svg
|
||||||
|
---
|
||||||
|
|
||||||
|
# Incus System Container with Docker
|
||||||
|
|
||||||
|
Develop in an Incus System Container and run nested Docker containers using Incus on your local infrastructure.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
1. Install [Incus](https://linuxcontainers.org/incus/) on the same machine as Coder.
|
||||||
|
2. Allow Coder to access the Incus socket.
|
||||||
|
|
||||||
|
- If you're running Coder as system service, run `sudo usermod -aG incus coder` and restart the Coder service.
|
||||||
|
- If you're running Coder as a Docker Compose service, get the group ID of the `incus` group by running `getent group incus` and add the following to your `compose.yaml` file:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
coder:
|
||||||
|
volumes:
|
||||||
|
- /var/lib/incus/unix.socket:/var/lib/incus/unix.socket
|
||||||
|
group_add:
|
||||||
|
- 997 # Replace with the group ID of the `incus` group
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Create a storage pool named `coder` and `btrfs` as the driver by running `incus storage create coder btrfs`.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
> **Note:** this template requires using a container image with cloud-init installed such as `ubuntu/jammy/cloud/amd64`.
|
||||||
|
|
||||||
|
1. Run `coder templates init -id incus`
|
||||||
|
1. Select this template
|
||||||
|
1. Follow the on-screen instructions
|
||||||
|
|
||||||
|
## Extending this template
|
||||||
|
|
||||||
|
See the [lxd/incus](https://registry.terraform.io/providers/lxc/incus/latest/docs) Terraform provider documentation to
|
||||||
|
add the following features to your Coder template:
|
||||||
|
|
||||||
|
- HTTPS incus host
|
||||||
|
- Volume mounts
|
||||||
|
- Custom networks
|
||||||
|
- More
|
||||||
|
|
||||||
|
We also welcome contributions!
|
|
@ -0,0 +1,309 @@
|
||||||
|
terraform {
|
||||||
|
required_providers {
|
||||||
|
coder = {
|
||||||
|
source = "coder/coder"
|
||||||
|
}
|
||||||
|
incus = {
|
||||||
|
source = "lxc/incus"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data "coder_provisioner" "me" {}
|
||||||
|
|
||||||
|
provider "incus" {}
|
||||||
|
|
||||||
|
data "coder_workspace" "me" {}
|
||||||
|
|
||||||
|
data "coder_parameter" "image" {
|
||||||
|
name = "image"
|
||||||
|
display_name = "Image"
|
||||||
|
description = "The container image to use. Be sure to use a variant with cloud-init installed!"
|
||||||
|
default = "ubuntu/jammy/cloud/amd64"
|
||||||
|
icon = "/icon/image.svg"
|
||||||
|
mutable = true
|
||||||
|
}
|
||||||
|
|
||||||
|
data "coder_parameter" "cpu" {
|
||||||
|
name = "cpu"
|
||||||
|
display_name = "CPU"
|
||||||
|
description = "The number of CPUs to allocate to the workspace (1-8)"
|
||||||
|
type = "number"
|
||||||
|
default = "1"
|
||||||
|
icon = "https://raw.githubusercontent.com/matifali/logos/main/cpu-3.svg"
|
||||||
|
mutable = true
|
||||||
|
validation {
|
||||||
|
min = 1
|
||||||
|
max = 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data "coder_parameter" "memory" {
|
||||||
|
name = "memory"
|
||||||
|
display_name = "Memory"
|
||||||
|
description = "The amount of memory to allocate to the workspace in GB (up to 16GB)"
|
||||||
|
type = "number"
|
||||||
|
default = "2"
|
||||||
|
icon = "/icon/memory.svg"
|
||||||
|
mutable = true
|
||||||
|
validation {
|
||||||
|
min = 1
|
||||||
|
max = 16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data "coder_parameter" "git_repo" {
|
||||||
|
type = "string"
|
||||||
|
name = "Git repository"
|
||||||
|
default = "https://github.com/coder/coder"
|
||||||
|
description = "Clone a git repo into [base directory]"
|
||||||
|
mutable = true
|
||||||
|
}
|
||||||
|
|
||||||
|
data "coder_parameter" "repo_base_dir" {
|
||||||
|
type = "string"
|
||||||
|
name = "Repository Base Directory"
|
||||||
|
default = "~"
|
||||||
|
description = "The directory specified will be created (if missing) and the specified repo will be cloned into [base directory]/{repo}🪄."
|
||||||
|
mutable = true
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "coder_agent" "main" {
|
||||||
|
count = data.coder_workspace.me.start_count
|
||||||
|
arch = data.coder_provisioner.me.arch
|
||||||
|
os = "linux"
|
||||||
|
dir = "/home/${local.workspace_user}"
|
||||||
|
env = {
|
||||||
|
CODER_WORKSPACE_ID = data.coder_workspace.me.id
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata {
|
||||||
|
display_name = "CPU Usage"
|
||||||
|
key = "0_cpu_usage"
|
||||||
|
script = "coder stat cpu"
|
||||||
|
interval = 10
|
||||||
|
timeout = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata {
|
||||||
|
display_name = "RAM Usage"
|
||||||
|
key = "1_ram_usage"
|
||||||
|
script = "coder stat mem"
|
||||||
|
interval = 10
|
||||||
|
timeout = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata {
|
||||||
|
display_name = "Home Disk"
|
||||||
|
key = "3_home_disk"
|
||||||
|
script = "coder stat disk --path /home/${lower(data.coder_workspace.me.owner)}"
|
||||||
|
interval = 60
|
||||||
|
timeout = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module "git-clone" {
|
||||||
|
source = "registry.coder.com/modules/git-clone/coder"
|
||||||
|
version = "1.0.2"
|
||||||
|
agent_id = local.agent_id
|
||||||
|
url = data.coder_parameter.git_repo.value
|
||||||
|
base_dir = local.repo_base_dir
|
||||||
|
}
|
||||||
|
|
||||||
|
module "code-server" {
|
||||||
|
source = "registry.coder.com/modules/code-server/coder"
|
||||||
|
version = "1.0.2"
|
||||||
|
agent_id = local.agent_id
|
||||||
|
folder = local.repo_base_dir
|
||||||
|
}
|
||||||
|
|
||||||
|
module "filebrowser" {
|
||||||
|
source = "registry.coder.com/modules/filebrowser/coder"
|
||||||
|
version = "1.0.2"
|
||||||
|
agent_id = local.agent_id
|
||||||
|
}
|
||||||
|
|
||||||
|
module "coder-login" {
|
||||||
|
source = "registry.coder.com/modules/coder-login/coder"
|
||||||
|
version = "1.0.2"
|
||||||
|
agent_id = local.agent_id
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "incus_volume" "home" {
|
||||||
|
name = "coder-${data.coder_workspace.me.id}-home"
|
||||||
|
pool = local.pool
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "incus_volume" "docker" {
|
||||||
|
name = "coder-${data.coder_workspace.me.id}-docker"
|
||||||
|
pool = local.pool
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "incus_cached_image" "image" {
|
||||||
|
source_remote = "images"
|
||||||
|
source_image = data.coder_parameter.image.value
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "incus_instance_file" "agent_token" {
|
||||||
|
count = data.coder_workspace.me.start_count
|
||||||
|
instance = incus_instance.dev.name
|
||||||
|
content = <<EOF
|
||||||
|
CODER_AGENT_TOKEN=${local.agent_token}
|
||||||
|
EOF
|
||||||
|
create_directories = true
|
||||||
|
target_path = "/opt/coder/init.env"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "incus_instance" "dev" {
|
||||||
|
running = data.coder_workspace.me.start_count == 1
|
||||||
|
name = "coder-${lower(data.coder_workspace.me.owner)}-${lower(data.coder_workspace.me.name)}"
|
||||||
|
image = incus_cached_image.image.fingerprint
|
||||||
|
|
||||||
|
config = {
|
||||||
|
"security.nesting" = true
|
||||||
|
"security.syscalls.intercept.mknod" = true
|
||||||
|
"security.syscalls.intercept.setxattr" = true
|
||||||
|
"boot.autostart" = true
|
||||||
|
"cloud-init.user-data" = <<EOF
|
||||||
|
#cloud-config
|
||||||
|
hostname: ${lower(data.coder_workspace.me.name)}
|
||||||
|
users:
|
||||||
|
- name: ${local.workspace_user}
|
||||||
|
uid: 1000
|
||||||
|
gid: 1000
|
||||||
|
groups: sudo
|
||||||
|
packages:
|
||||||
|
- curl
|
||||||
|
shell: /bin/bash
|
||||||
|
sudo: ['ALL=(ALL) NOPASSWD:ALL']
|
||||||
|
write_files:
|
||||||
|
- path: /opt/coder/init
|
||||||
|
permissions: "0755"
|
||||||
|
encoding: b64
|
||||||
|
content: ${base64encode(local.agent_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=${local.workspace_user}
|
||||||
|
EnvironmentFile=/opt/coder/init.env
|
||||||
|
ExecStart=/opt/coder/init
|
||||||
|
Restart=always
|
||||||
|
RestartSec=10
|
||||||
|
TimeoutStopSec=90
|
||||||
|
KillMode=process
|
||||||
|
|
||||||
|
OOMScoreAdjust=-900
|
||||||
|
SyslogIdentifier=coder-agent
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
- path: /etc/systemd/system/coder-agent-watcher.service
|
||||||
|
permissions: "0644"
|
||||||
|
content: |
|
||||||
|
[Unit]
|
||||||
|
Description=Coder Agent Watcher
|
||||||
|
After=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart=/usr/bin/systemctl restart coder-agent.service
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
- path: /etc/systemd/system/coder-agent-watcher.path
|
||||||
|
permissions: "0644"
|
||||||
|
content: |
|
||||||
|
[Path]
|
||||||
|
PathModified=/opt/coder/init.env
|
||||||
|
Unit=coder-agent-watcher.service
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
runcmd:
|
||||||
|
- chown -R ${local.workspace_user}:${local.workspace_user} /home/${local.workspace_user}
|
||||||
|
- |
|
||||||
|
#!/bin/bash
|
||||||
|
apt-get update && apt-get install -y curl docker.io
|
||||||
|
usermod -aG docker ${local.workspace_user}
|
||||||
|
newgrp docker
|
||||||
|
- systemctl enable coder-agent.service coder-agent-watcher.service coder-agent-watcher.path
|
||||||
|
- systemctl start coder-agent.service coder-agent-watcher.service coder-agent-watcher.path
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
limits = {
|
||||||
|
cpu = data.coder_parameter.cpu.value
|
||||||
|
memory = "${data.coder_parameter.cpu.value}GiB"
|
||||||
|
}
|
||||||
|
|
||||||
|
device {
|
||||||
|
name = "home"
|
||||||
|
type = "disk"
|
||||||
|
properties = {
|
||||||
|
path = "/home/${local.workspace_user}"
|
||||||
|
pool = local.pool
|
||||||
|
source = incus_volume.home.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
device {
|
||||||
|
name = "docker"
|
||||||
|
type = "disk"
|
||||||
|
properties = {
|
||||||
|
path = "/var/lib/docker"
|
||||||
|
pool = local.pool
|
||||||
|
source = incus_volume.docker.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
device {
|
||||||
|
name = "root"
|
||||||
|
type = "disk"
|
||||||
|
properties = {
|
||||||
|
path = "/"
|
||||||
|
pool = local.pool
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
locals {
|
||||||
|
workspace_user = lower(data.coder_workspace.me.owner)
|
||||||
|
pool = "coder"
|
||||||
|
repo_base_dir = data.coder_parameter.repo_base_dir.value == "~" ? "/home/${local.workspace_user}" : replace(data.coder_parameter.repo_base_dir.value, "/^~\\//", "/home/${local.workspace_user}/")
|
||||||
|
repo_dir = module.git-clone.repo_dir
|
||||||
|
agent_id = data.coder_workspace.me.start_count == 1 ? coder_agent.main[0].id : ""
|
||||||
|
agent_token = data.coder_workspace.me.start_count == 1 ? coder_agent.main[0].token : ""
|
||||||
|
agent_init_script = data.coder_workspace.me.start_count == 1 ? coder_agent.main[0].init_script : ""
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "coder_metadata" "info" {
|
||||||
|
count = data.coder_workspace.me.start_count
|
||||||
|
resource_id = incus_instance.dev.name
|
||||||
|
item {
|
||||||
|
key = "memory"
|
||||||
|
value = incus_instance.dev.limits.memory
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
key = "cpus"
|
||||||
|
value = incus_instance.dev.limits.cpu
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
key = "instance"
|
||||||
|
value = incus_instance.dev.name
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
key = "image"
|
||||||
|
value = "${incus_cached_image.image.source_remote}:${incus_cached_image.image.source_image}"
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
key = "image_fingerprint"
|
||||||
|
value = substr(incus_cached_image.image.fingerprint, 0, 12)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue