Home

Awesome

Workbench Helm chart

NDS Labs Workbench is an open-source platform for hosting and launching pre-packages apps for educational or workshop environments.

TL;DR

$ git clone https://github.com/nds-org/workbench-helm-chart && cd workbench-helm-chart/ 
$ helm dep up
$ helm upgrade --install workbench -n workbench --create-namespace .

Introduction

This chart bootstraps a Workbench webui and apiserver on a Kubernetes cluster using the Helm package manager.

Prerequisites

Installing the Chart

Download dependency subcharts by running the following:

$ helm dep up

Then install a new release with Helm:

$ helm upgrade --install workbench -n workbench --create-namespace .

Uninstalling the Chart

To shut down all Workbench dependencies, webui, and apiserver:

$ helm uninstall workbench -n workbench

NOTE: this does not shutdown or affect UserApps

Configuration

The following table lists the configurable parameters of the Workbench chart and their default values.

Controller

These options affect the Deployment resource created by this chart.

PathTypeDescriptionDefault
controller.kindstringKind to use for application manifestDeployment
controller.images.webuistringImage to use for webui containerndslabs/webui:2.0.0
controller.images.apiserverstringImage to use for apiserver containerndslabs/apiserver:2.0.0
controller.serviceAccount.namestring(optional) Specify the serviceAccountName for this controller<release-name>
controller.serviceAccount.createboolIf true, create a ServiceAccount for this releasetrue
controller.extraInitContainersarray[map]Specify initContainers for main application[]
controller.extraLabelsmapExtra labels to apply to the controller/service{}
controller.extraEnv.webuiarray[map]Additional env to set for webui container[]
controller.extraEnv.apiserverarray[map]Additional env variables to set for apiserver container[]
controller.extraVolumeMounts.webuiarray[map]Additional volumeMounts to set for webui container[]
controller.extraVolumeMounts.apiserverarray[map]Additional volumeMounts to set for apiserver container[]
controller.extraVolumesarray[map]Additional volumes to attach to the main application[]

Ingress

These options affect the Ingress resources created by this chart.

PathTypeDescriptionDefault
ingress.classstringClass name for Ingress resources""
ingress.hoststringHost name to use for Ingress resources""
ingress.tlsarray[map]TLS config to set for Ingress resources[]
ingress.tls.hostsarray[string]Host names to set for TLS on Ingress resource[]
ingress.api.annotationsmapAnnotations to set for api Ingress resource{}
ingress.api.extraRulesarrayList of additional ingress rules for api Ingress resource[]
ingress.webui.annotationsmapAnnotations to set for webui Ingress resource{}
ingress.webui.extraRulesarrayLisst of additional ingress rules for webui Ingress resource[]

Workbench Options

These options affect the internals of Workbench and the customization of the WebUI.

Frontend: Domain + Auth + UI Customizations

PathTypeDescriptionDefault
config.frontend.domainstringDomain name (used by backend for self-reference)https://changeme.ndslabs.org
config.frontend.live_reloadboolIf true, change to use dev image ports (instead of port 80) when running dev imagefalse
config.frontend.signin_urlstringURL to route frontend requests to "Log In"https://changeme.ndslabs.org/oauth2/start?rd=https%3A%2F%2Fkubernetes.docker.internal%2Fmy-apps
config.frontend.signout_urlstringURL to route frontend requests to "Log Out"https://changeme.ndslabs.org/oauth2/sign_out?rd=https%3A%2F%2Fkubernetes.docker.internal%2F
config.frontend.customization.product_namestringHuman-friendly name to use for this product in the navbarWorkbench
config.frontend.customization.landing_htmlstringHTML string to use as the splash text on the Landing Pageexisting HTML
config.frontend.customization.favicon_pathstringImage to use as the favicon/favicon.svg
config.frontend.customization.brand_logo_pathstringImage to use as the brand log (top-left of navbar)/favicon.svg
config.frontend.customization.landing_header_1stringHeader to display on the landing page (section 01)Find the tools you need
config.frontend.customization.landing_section_1stringSection body to display on the landing page (section 01)Search our catalog of web-based research and software tools. We offer over 30 different software tools that fit many common scenarios encountered in research software development. Find a set of tools to help you build out a new software product or extend an existing one.
config.frontend.customization.landing_header_2stringHeader to display on the landing page (section 02)Run the tools on our cloud service
config.frontend.customization.landing_section_2stringSection body to display on the landing page (section 02)Once you've narrowed down your choices, launch your desired tool set on our cloud resources. Access your running applications using our web interface, and start integrating the tools and shaping your software product.
config.frontend.customization.learn_more_urlstring(currently unused) URL to use for the "Learn More" button on the Landing Pagehttp://www.nationaldataservice.org/platform/workbench.html
config.frontend.customization.help_linksarrayList of links to use in the navbar "Help" sectionexisting URLs

Backend: Domain + Keycloak + MongoDB + UserApp Kubernetes Config

PathTypeDescriptionDefault
config.backend.domainstringURL of the apiserverhttps://changeme.ndslabs.org
config.backend.namespacestringNamespace where workbench should launch its applicationsworkbench
config.backend.oauth.userinfoUrlstringURLhttps://changeme.ndslabs.org/oauth2/userinfo
config.backend.mongo.uristringURI pointing at running MongoDB instancemongodb://workbench-mongodb.workbench.svc.cluster.local:27017/ndslabs
config.backend.mongo.dbstringDatabase name to use in MongoDBndslabs
config.backend.keycloak.hostnamestringURI pointing at running Keycloak instancehttps://keycloak.workbench.ndslabs.org/auth
config.backend.keycloak.realmNamestringRealm name to use in Keycloakchangeme
config.backend.keycloak.clientIdstringOIDC ClientID to use for Keycloak authchangeme
config.backend.keycloak.clientSecretstringOIDC ClientSecret to use for Keycloak auth""
config.backend.insecure_ssl_verifystringIf false, skip checking insecure/invalid TLS certificatestrue
config.backend.swagger_urlstringOverride the URL of the Swagger spec for the running apiserveropenapi/swagger-v1.yml
config.backend.userapps.singlepodstringPVC name to use for mounting shared datafalse
config.backend.userapps.service_account_namestringName of the ServiceAccount to use for each UserAppworkbench
config.backend.userapps.home_storage.enabledboolIf true, mount user home folder to each UserAppfalse
config.backend.userapps.home_storage.storage_classstringStorageClass to use for user Home volumes""
config.backend.userapps.home_storage.claim_suffixstringSuffix to append to names of user Home volumeshome-data
config.backend.userapps.shared_storage.enabledboolIf true, mount a Shared volume to each UserAppfalse
config.backend.userapps.shared_storage.mount_pathstringPath within the container to mount the Shared volume/shared
config.backend.userapps.shared_storage.read_onlyboolIf true, mount the Shared volume as ReadOnlytrue
config.backend.userapps.shared_storage.claim_namestringPVC name to use for mounting shared dataworkbench-shared-data
config.backend.userapps.ingress.annotationsmapAdditional annotations to add to UserApp Ingress rules<auth annotations, etc>
config.backend.userapps.ingress.tlsarray[map]TLS config to set for Ingress resources[]

Misc

Other miscellaneous top-level configuration options.

PathTypeDescriptionDefault
extraDeployarrayList of additional resources to create[]
tolerationsarrayList of tolerations to include[]
resources.apimapResources to apply to api container{}
resources.webuimapResources to apply to webui container{}
nodeSelectormapNode selector(s) to apply to webui container{}
affinitymapAffinity to apply to webui container{}

Currently Unused?

These options are currently present, but may not yet be used.

PathTypeDescriptionDefault
config.backend.domainstringDomain name (used by backend for self-reference)kubernetes.docker.internal
config.backend.timeoutint(currently unused) startup timeout for UserApps30
config.backend.inactivity_timeoutint(currently unused) Shut down inactive services after this many minutes480
config.backend.specs.repostring(currently unused) Git repo from which to pull application specshttps://github.com/nds-org/ndslabs-specs.git
config.backend.specs.branchstring(currently unused) Git branch from which to pull application specsmaster
config.backend.storage.shared.storage_classstring(currently unused) StorageClass used to create the Shared volumenfs
ingress.userapps.annotationsmapAnnotations to set for Ingress resources of created UserApps{}

Subcharts

MongoDB

To run a local mongodb alongside Workbench, you can set mongodb.enabled to true in the values.yaml:

mongodb:
  enabled: true
  # ... include any other config values from the mongodb chart
  auth:
    rootUser: workbench
    rootPassword: workbench

See https://artifacthub.io/packages/helm/bitnami/mongodb for configuration options

For more info about MongoDB, see https://www.mongodb.com/docs/manual/tutorial/getting-started/

Keycloak + PostgreSQL

To run a local keycloak alongside Workbench, you can set keycloak.enabled to true in the values.yaml:

keycloak:
  enabled: true
  # ... include any other config values from the keycloak chart
  httpRelativePath: "/auth/"
  auth:
     adminUser: "admin"
     adminPassword: "workbench"
  proxyAddressForwarding: true

See https://artifacthub.io/packages/helm/bitnami/keycloak for configuration options

For more info about Keycloak, see https://www.keycloak.org/docs/11.0/getting_started/

OAuth2 Proxy [+ Redis]

To run a local OAuth2 Proxy alongside Workbench, you can set oauth2-proxy.enabled to true in the values.yaml:

oauth2-proxy:
  enabled: true
  # ... include any other config values from the oauth2-proxy chart
  extraArgs:
    - --provider=keycloak-oidc

See https://artifacthub.io/packages/helm/bitnami/oauth2-proxy for configuration options

For more info about OAuth2 Proxy, see https://oauth2-proxy.github.io/oauth2-proxy/docs/configuration/overview

For more info about configuring specific providers, see https://oauth2-proxy.github.io/oauth2-proxy/docs/configuration/oauth_provider/

ReadWriteMany Volumes (NFS)

You'll need a StorageClass on your cluster that supports ReadWriteMany.

If you already have a volume provisioner running that supports ReadWriteMany, you can skip this section.

NOTE: You should only need the subdir OR the server, but you do not need to run both.

(Experimental) NFS Subdir External Provisioner: use an existing NFS server to provision RWM volumes

The NFS Subdir External Provisioner is a small piece of code that will talk to an existing NFS server to provision RWM volumes.

To run a local NFS Subdir External Provisioner alongside Workbench, you can set nfs-subdir-external-provisioner.enabled to true in the values.yaml:

nfs-subdir-external-provisioner:
  enabled: true
  nfs:
    # Use in-cluster DNS to resolve
    # server: "workbench-nfs-server-provisioner.workbench.svc.cluster.local"
    
    # Use external hostname
    server: "taiga-nfs.ncsa.illinois.edu"
    path: "/taiga/ncsa/radiant/<PROJECT_ID>"
    mountOptions:
    - tcp
    - nfsvers=3

See https://artifacthub.io/packages/helm/nfs-subdir-external-provisioner/nfs-subdir-external-provisioner for configuration options

NFS Server Provisioner: run your own NFS server to provision RWM volumes

To run a local NFS Server Provisioner alongside Workbench, you can set nfs-client-provisioner.enabled to true in the values.yaml:

nfs-server-provisioner:
  enabled: true
  # ... include any other config values from the nfs-server-provisioner chart

See https://artifacthub.io/packages/helm/kvaps/nfs-server-provisioner for configuration options

Ingress Controller (NGINX)

To run a local NGINX Ingress Controller alongside Workbench, you can set ingress-nginx.enabled to true in the values.yaml:

ingress-nginx:
  enabled: true
  # ... include any other config values from the ingress-nginx chart
  controller:
    kind: Deployment

See https://artifacthub.io/packages/helm/ingress-nginx/ingress-nginx for configuration options

For more info about NGINX Ingress Controller, see https://kubernetes.github.io/ingress-nginx/deploy/

Advanced Configuration (Optional)

Enable TLS with Wildcard DNS certs

  1. Install cert-manager Helm chart: jetstack/cert-manager
  2. Add an Issuer that support DNS-01 (see below):
apiVersion: cert-manager.io/v1
kind: Issuer 
metadata:
  name: letsencrypt-staging
  namespace: workbench
spec:
  acme:
    # The ACME server URL
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    # Email address used for ACME registration
    email: email@example.com
    # Name of a secret used to store the ACME account private key
    privateKeySecretRef:
      name: letsencrypt-staging
    # Enable the DNS-01 challenge provider
    solvers:
    - dns01:
        # ...
  1. Include tls section in the top-level ingress section of values.yaml:
ingress:
  class: "nginx"
  tls:
    - hosts:
      - "local.ndslabs.org"
      - "*.local.ndslabs.org"

WARNING: Do not include the same issuer or cluster-issuer annotation on multiple ingress rules.

WARNING: LetsEncrypt will rate limit you if you request too many certs are requested for the same domain.

To avoid this, use the staging environment (as above) for testing.

When you are ready (after testing) to move from staging to real certs, you can use the production environment.

DNS-01 via ACMEDNS

If your provider does not support DNS-01 requests (e.g. Google Domains), you can use ACMEDNS:

  1. Register with acmedns for a unique set of credentials: curl -XPOST https://auth.acme-dns.io/register
    • This will return a set of credentials as a JSON blob:
{"username":"_username_","password":"_password_","fulldomain":"_id_.auth.acme-dns.io","subdomain":"_id_","allowfrom":[]}
  1. Build up an acmedns.json file using this JSON blob:
    • You'll need to copy and paste this JSON value multiple times to build it up:
{
  "local.ndslabs.org": {"username":"_username_","password":"_password_","fulldomain":"_id_.auth.acme-dns.io","subdomain":"_id_","allowfrom":[]},
  "*.local.ndslabs.org": {"username":"_username_","password":"_password_","fulldomain":"_id_.auth.acme-dns.io","subdomain":"_id_","allowfrom":[]}
}
  1. Create a secret from the acmedns.json file:
$ kubectl create secret -n cert-manager acme-dns --from-file=acmedns.json`
  1. Point an Issuer at the acme-dns secret:
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: letsencrypt-staging
  namespace: workbench
spec:
  acme:
    # The ACME server URL
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    # Email address used for ACME registration
    email: email@example.com
    # Name of a secret used to store the ACME account private key
    privateKeySecretRef:
      name: letsencrypt-staging
    solvers:
    - dns01:
        acmeDNS:
          host: https://auth.acme-dns.io
          accountSecretRef:
            name: acme-dns
            key: acmedns.json 
  1. Add the issuer annotation to your ingress.api.annotations section:
ingress:
  class: "nginx"
  tls:
    # ....
  api:
    annotations:
      cert-manager.io/issuer: "acmedns-staging"
      ingress.kubernetes.io/ssl-redirect: "true"
      ingress.kubernetes.io/force-ssl-redirect: "true"

Keycloak Realm Import

Download and import realm.json for a preconfigured workbench-dev realm:

$ kubectl create configmap keycloak-realm --from-file=realm.json -n workbench

Then add the following to your values.yaml:

  extraEnvVars:
    - name: KEYCLOAK_EXTRA_ARGS
      value: "-Dkeycloak.import=/config/realm.json"
  extraVolumeMounts:
    - name: config
      mountPath: "/config"
      readOnly: true
  extraVolumes:
    - name: config
      configMap:
        name: keycloak-realm
        items:
        - key: "realm.json"
          path: "realm.json"

Developing the Chart (Optional)

Clone the Git repository locally:

$ git clone https://github.com/nds-org/workbench-helm-chart && cd workbench-helm-chart/

You can then use the Makefile helper script:

$ make help         # Print help message
$ make check_all    # verify that dependencies are installed

To install/uninstall the chart:

$ make dep          # Fetch Helm dependencies
$ make template     # Debug the Helm chart template
$ make install      # Install the Helm chart
$ make uninstall    # Uninstall the Helm chart

To debug Pods startup/runtime:

$ make describe
$ make target=api logs
$ make target=webui logs
$ make target=nginx logs
$ make target=proxy logs
$ make target=keycloak logs
$ make target=mongo logs

To build/push local Docker images:

$ make clone    # clone from git repos
$ make pull     # pull from git upstream
$ make build    # build docker images
$ make push     # push to docker hub - note: this also performs a "make build"
$ make restart  # delete the running Pod so it restart with new docker images

Optional: Makefile Configuration

Change the default parameters of the Makefile by editing the included .env file:

$ cat .env
# Success/failure symbols
SUCCESS=[✔]
FAILED=[x]

# Helm chart config
NAMESPACE=workbench
NAME=workbench
CHART_PATH=.

# Docker image config
APISERVER_IMAGE=ndslabs/apiserver:python
WEBUI_IMAGE=ndslabs/webui:react

# Upstream Git repo/branch config
APISERVER_REPO=https://github.com/nds-org/workbench-apiserver-python
WEBUI_REPO=https://github.com/nds-org/workbench-webui
APISERVER_UPSTREAM_BRANCH=main
WEBUI_UPSTREAM_BRANCH=main

# Set this to empty to disable creating keycloak-realm ConfigMap
REALM_IMPORT=realm_import

Optional: Map source into running containers (local dev / hostpath only)

Run make clone and make pull to grab the latest source code.

Use your favorite IDE(s) or local tools use them to import the src/webui source code and run make compile.

This will produce a new folder src/webui/build containing compiled artifacts that can be mounted directly into the running webui container.

Finally, add the following to values.yaml and run make again:

controller:
  extraEnv:
    webui: []
    
    # Enable auto-reload of Python when source changes
    apiserver:
    - name: DEBUG
      value: "true"

  # Mount source code into respective containers
  extraVolumeMounts:
    webui:
    - mountPath: /usr/share/nginx/html/
      name: webuisrc
    apiserver:
    - mountPath: /app/
      name: apisrc

  # Point the extraVolumes at your local machine (hostpath only)
  extraVolumes:
    - name: webuisrc
      hostPath:
        path: /full/path/to/your/workbench-helm-chart/src/webui/build
    - name: apisrc
      hostPath:
        path: /full/path/to/your/workbench-helm-chart/src/apiserver

Now you can modify the webui or apiserver in any way that you see fit, then navigate to https://kubernetes.docker.internal to immediately test your changes.

NOTE: You'll need to re-run make compile after any modifications to the webui.

This will trigger the build step (during which you will get a 500 error) that will refresh the files in src/webui/build.

Cleaning Up

To delete all of the associated cluster volumes:

$ make clean

NOTE: this will delete your Keycloak Realm and/or MongoDB database and all user data

(OPTIONAL) Last step is to delete the namespace that was created by the helm install step:

$ make clean_all

NOTE: if you're using cert-manager, backup any necessary secrets to avoid being ratelimited by the LetsEncrypt API

TODO