Skip to content

Integrate selkies-gstreamer to Jupyter and Coder (just like noVNC) #64

Open
@ehfd

Description

@ehfd

Update:

We currently have this Coder configuration working self-hosted:

entrypoint.sh for Coder:

set -e
supervisord
Open main.tf
terraform {
  required_providers {
    coder = {
      source  = "coder/coder"
      version = "~> 0.6.17"
    }
    kubernetes = {
      source  = "hashicorp/kubernetes"
      version = "~> 2.18"
    }
  }
}

provider "coder" {
  feature_use_managed_variables = true
}

provider "kubernetes" {
  config_path = var.use_kubeconfig == true ? "~/.kube/config" : null
}

data "coder_workspace" "me" {}

data "coder_parameter" "cpu" {
  name    = "CPU (cores)"
  default = "2"
  icon    = "/icon/memory.svg"
  mutable = true
  option {
    name  = "2 Cores"
    value = "2"
  }
  option {
    name  = "4 Cores"
    value = "4"
  }
  option {
    name  = "6 Cores"
    value = "6"
  }
  option {
    name  = "8 Cores"
    value = "8"
  }
  option {
    name  = "16 Cores"
    value = "16"
  }
}

data "coder_parameter" "memory" {
  name    = "Memory (GB)"
  default = "2"
  icon    = "/icon/memory.svg"
  mutable = true
  option {
    name  = "2 GB"
    value = "2"
  }
  option {
    name  = "4 GB"
    value = "4"
  }
  option {
    name  = "6 GB"
    value = "6"
  }
  option {
    name  = "8 GB"
    value = "8"
  }
    option {
    name  = "16 GB"
    value = "16"
  }
  option {
    name  = "32 GB"
    value = "32"
  }
  option {
    name  = "64 GB"
    value = "64"
  }
}

data "coder_parameter" "password" {
  name         = "password"
  display_name = "Selkies Password"
  description  = "The Selkies password for authentication. User is ubuntu."
  icon         = "/emojis/1f511.png"
  mutable      = false
  option {
    name  = "mypasswd"
    value = "mypasswd"
  }
}

data "coder_parameter" "home_disk_size" {
  name         = "home_disk_size"
  display_name = "Home disk size"
  description  = "The size of the home disk in GB"
  default      = "100"
  type         = "number"
  icon         = "/emojis/1f4be.png"
  mutable      = false
  validation {
    min = 1
    max = 99999
  }
}



variable "use_kubeconfig" {
  type        = bool
  description = "Use host kubeconfig? (true/false)"
  default     = false
}

variable "namespace" {
  type        = string
  description = "The Kubernetes namespace to create workspaces in (must exist prior to creating workspaces)"
  default     = "coder"
}


resource "kubernetes_pod" "main" {
  count = data.coder_workspace.me.start_count
  metadata {
    name      = "coder-${lower(data.coder_workspace.me.owner)}-${lower(data.coder_workspace.me.name)}"
    namespace = var.namespace
    labels = {
      "app.kubernetes.io/name"     = "coder-workspace"
      "app.kubernetes.io/instance" = "coder-workspace-${lower(data.coder_workspace.me.owner)}-${lower(data.coder_workspace.me.name)}"
      "app.kubernetes.io/part-of"  = "coder"
      // Coder specific labels.
      "com.coder.resource"       = "true"
      "com.coder.workspace.id"   = data.coder_workspace.me.id
      "com.coder.workspace.name" = data.coder_workspace.me.name
      "com.coder.user.id"        = data.coder_workspace.me.owner_id
      "com.coder.user.username"  = data.coder_workspace.me.owner
    }
    annotations = {
      "com.coder.user.email" = data.coder_workspace.me.owner_email
    }
  }

  spec {

    container {
      name  = "dev"
      image = "ghcr.io/selkies-project/nvidia-glx-desktop:latest"
      image_pull_policy = "Always"
      command           = ["sh", "-c", coder_agent.main.init_script]
      # security_context {
      #   run_as_user = "1000"
      #   privileged = true
      # }

      env {
        name  = "TZ"
        value = "UTC"
      }
      env {
        name  = "DISPLAY_SIZEW"
        value = "1920"
      }
      env {
        name  = "DISPLAY_SIZEH"
        value = "1080"
      }
      env {
        name  = "DISPLAY_REFRESH"
        value = "60"
      }
      env {
        name  = "DISPLAY_DPI"
        value = "96"
      }
      env {
        name  = "DISPLAY_CDEPTH"
        value = "24"
      }
      env {
        name  = "VIDEO_PORT"
        value = "DFP"
      }
      env {
        name  = "PASSWD"
        value = "${data.coder_parameter.password.value}"
      }
      env {
        name  = "SELKIES_ENCODER"
        value = "nvh264enc"
      }
      env {
        name  = "SELKIES_ENABLE_RESIZE"
        value = "false"
      }
      env {
        name  = "SELKIES_VIDEO_BITRATE"
        value = "8000"
      }
      env {
        name  = "SELKIES_FRAMERATE"
        value = "60"
      }
      env {
        name  = "SELKIES_AUDIO_BITRATE"
        value = "128000"
      }
      env {
        name  = "SELKIES_ENABLE_BASIC_AUTH"
        value = "true"
      }
      env {
        name  = "SELKIES_BASIC_AUTH_PASSWORD"
        value = "${data.coder_parameter.password.value}"
      }
      env {
        name  = "SELKIES_TURN_REST_URI"
        value = "http://turn-rest.nrp-nautilus.io"
      }
      env {
        name  = "SELKIES_TURN_PROTOCOL"
        value = "tcp"
      }
      env {
        name  = "SELKIES_TURN_TLS"
        value = "false"
      }
      env {
        name  = "CODER_AGENT_TOKEN"
        value = coder_agent.main.token
      }

      stdin = true
      tty   = true

      port {
        name           = "http"
        container_port = 8080
        protocol       = "TCP"
      }

      resources {
        limits = {
          "cpu"    = "${data.coder_parameter.cpu.value}"
          "memory" = "${data.coder_parameter.memory.value}Gi"
          "nvidia.com/gpu" = 1
        }
        requests = {
          "cpu"    = "${data.coder_parameter.cpu.value}"
          "memory" = "${data.coder_parameter.memory.value}Gi"
          "nvidia.com/gpu" = 1
        }
      }

      volume_mount {
        mount_path = "/home/ubuntu/persistent"
        name       = "home"
        read_only  = false
        sub_path   = "home"
      }

      volume_mount {
        mount_path = "/dev/shm"
        name       = "dshm"
      }
    }

    dns_policy = "None"
    dns_config {
      nameservers = ["8.8.8.8", "8.8.4.4"]
    }

    volume {
      name = "dshm"
      empty_dir {}
    }

    volume {
      name = "home"
      persistent_volume_claim {
        claim_name = kubernetes_persistent_volume_claim.home.metadata.0.name
        read_only  = false
      }
    }

    affinity {
      node_affinity {
        required_during_scheduling_ignored_during_execution {
          node_selector_term {
            match_expressions {
              key      = "topology.kubernetes.io/zone"
              operator = "NotIn"
              values   = ["myzone"]
            }
          }
        }
      }
    }
  }
}

resource "coder_agent" "main" {
  os                     = "linux"
  arch                   = "amd64"
  login_before_ready     = false
  startup_script_timeout = 180
  startup_script         = <<-EOT
    set -e

    # install and start code-server
    curl -fsSL https://code-server.dev/install.sh | sh -s -- --method=standalone --prefix=/tmp/code-server --version 4.8.3
    /tmp/code-server/bin/code-server --auth none --port 13337 >/tmp/code-server.log 2>&1 &

    echo "Initializing Supervisor..."
    nohup supervisord
  EOT
}

resource "coder_app" "code-server" {
  agent_id     = coder_agent.main.id
  slug         = "code-server"
  display_name = "code-server"
  icon         = "/icon/code.svg"
  url          = "http://localhost:13337?folder=/home/coder"
  subdomain    = false
  share        = "owner"

  healthcheck {
    url       = "http://localhost:13337/healthz"
    interval  = 3
    threshold = 10
  }
}

resource "kubernetes_persistent_volume_claim" "home" {
  metadata {
    name      = "coder-${lower(data.coder_workspace.me.owner)}-${lower(data.coder_workspace.me.name)}-home"
    namespace = var.namespace
    labels = {
      "app.kubernetes.io/name"     = "coder-pvc"
      "app.kubernetes.io/instance" = "coder-pvc-${lower(data.coder_workspace.me.owner)}-${lower(data.coder_workspace.me.name)}"
      "app.kubernetes.io/part-of"  = "coder"
      // Coder specific labels.
      "com.coder.resource"       = "true"
      "com.coder.workspace.id"   = data.coder_workspace.me.id
      "com.coder.workspace.name" = data.coder_workspace.me.name
      "com.coder.user.id"        = data.coder_workspace.me.owner_id
      "com.coder.user.username"  = data.coder_workspace.me.owner
    }
    annotations = {
      "com.coder.user.email" = data.coder_workspace.me.owner_email
    }
  }
  wait_until_bound = false
  spec {
    access_modes = ["ReadWriteOnce"]
    storage_class_name = "rook-ceph-block"
    resources {
      requests = {
        storage = "${data.coder_parameter.home_disk_size.value}Gi"
      }
    }
  }
}

resource "coder_app" "selkies" {
  agent_id      = coder_agent.main.id
  slug          = "selkies"  
  display_name  = "Selkies"
  icon          = "/emojis/1f3ae.png"
  url           = "http://localhost:8080"
  subdomain     = true
  share         = "owner"
}

Self-explanatory. Just like VS Code Server and noVNC, a button click in Jupyter should lead to a window with Selkies.
This will help greatly in robotics, simulations, and other kinds of research.

External contribution dearly expected.

It can be a separate project, a PR, or any other form of contribution.
Integration with Jupyter Docker containers should also be possible.

Creating a template for Coder would also be of interest.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestgood first issueGood for newcomershelp wantedExternal contribution is requiredwebWeb components including gst-web

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions