NaaVRE consists of several components which have their own Helm charts (Keycloak, Argo, Jupyter Hub, NaaVRE-PaaS-frontend, all NaaVRE-*-services and more).
They are all sub-charts of the naavre chart.
However, Helm requires to set values for all sub-charts manually. This is time-consuming, error-prone, and results in a lot of duplicate configurations (e.g. repeated domain names or internal tokens).
To address this issue, we first render sub-chart values through the values chart.
This allows us to render sub-chart values from a single file without duplicate configurations.
The deployment is done in two steps:
- Step 1: render values for the sub-charts, using the
valueschart - Step 2: Deploy the sub-charts, using the
naavrechart and the previously-rendered values.
Deployments are managed with deploy.sh:
./deploy.sh --helpTip
Whenever you run deploy.sh, you can add --dry-run to print the commands without running them:
./deploy.sh --dry-run <action>Add the third-party Helm repos:
./deploy.sh repo-addDeploy the Keycloak operator (documentation):
./deploy.sh --kube-context <k8s context> -n <namespace> install-keycloak-operatorNote: this can be skipped when using an external Keycloak instance (e.g. values-example-external-keycloak.yaml).
Configure Kubernetes access following the internal documentation.
The configuration is successful when you can run kubectl locally with the desired context:
kubectl --kube-context k8s-test-1 get ns new-naavreConfigure SOPS to decode VLIC secrets following the internal documentation.
Don't forget to set the AWS_PROFILE environment variable for the project and to run aws sso login --profile ... before running deploy.sh.
The configuration is successful when you can decrypt SOPS files:
sops decrypt values/values-deploy-k8s-test-1.secrets.yamlInstall helm-secrets:
helm plugin install https://github.com/jkroepke/helm-secretsImportant
For VLIC-managed deployments, run the following commands before deploy.sh:
ssh -TL 127.0.1.<x>:6443:localhost:6443 <context>(internal documentation)aws sso login --profile <profile name>(internal documentation)git checkout main && git pull
To install or upgrade an existing deployment, use:
./deploy.sh --kube-context <k8s context> -n <namespace> [--use-vlic-secrets] -f <root values file> [-f <additional root values file>] upgrade --installFor example, to install or upgrade the minikube deployment (values/values-deploy-minikube.yaml), run:
./deploy.sh --kube-context minikube -n new-naavre -f values/values-deploy-minikube.yaml upgrade --installTo install or upgrade the k8s-test-1 deployment (values/values-deploy-k8s-test-1.public.yaml and values/values-deploy-k8s-test-1.secrets.yaml), run:
./deploy.sh --kube-context k8s-test-1 -n new-naavre --use-vlic-secrets -f values/values-deploy-k8s-test-1.secrets.yaml -f values/values-deploy-k8s-test-1.public.yaml upgrade --install --timeout 30mFor k8s-staging-1 (values/values-deploy-k8s-staging-1.public.yaml and values/values-deploy-k8s-staging-1.secrets.yaml), run:
./deploy.sh --kube-context k8s-staging-1 -n new-naavre --use-vlic-secrets -f values/values-deploy-k8s-staging-1.secrets.yaml -f values/values-deploy-k8s-staging-1.public.yaml upgrade --timeout 30mAdjust the value of --timeout if you get the error message Error: UPGRADE FAILED: pre-upgrade hooks failed: 1 error occurred: * timed out waiting for the condition.
To rollback an existing deployment, use:
./deploy.sh --kube-context <k8s context> -n <namespace> rollback [REVISION]Examples:
./deploy.sh --kube-context minikube -n new-naavre rollback
./deploy.sh --kube-context k8s-test-1 -n new-naavre rollback 1To uninstall an existing deployment, use:
./deploy.sh --kube-context <k8s context> -n <namespace> uninstallExamples:
./deploy.sh --kube-context minikube -n new-naavre uninstall
./deploy.sh --kube-context k8s-test-1 -n new-naavre uninstallCreate a new root values file and fill in your values. This can be done by copying one of the examples:
cp values/values-example-basic.yaml values/values-deploy-my-k8s-context.yaml
vim values/values-deploy-my-k8s-context.yamlCaution
Values files (values/values-deploy-*.yaml) contain secrets. They are ignored by default by Git. Never commit them!
A deployment consists of two files:
values/values-deploy-<context>.public.yaml(clear text)values/values-deploy-<context>.secrets.yaml(SOPS-encrypted)
For VLIC-managed deployments, virtual labs are defined in separate files placed in values/virtual-labs/. Labels are defined in values/virtual-labs/labels.yaml. As for deployments, a virtual lab definition consist of two files:
values/virtual-labs/values-<vl-slug>.public.yamlvalues/virtual-labs/values-<vl-slug>.secrets.yaml
Virtual labs are disabled by default. To activate a virtual lab for a deployment, set jupyterhub.vlabs.<slug>.enabled: true in values/values-deploy-<context>.public.yaml
Clear-text files should only contain public values. Secrets should be stored in the SOPS-encrypted files (typically, everything under global.secrets, as well as sensitive values under global.externalServices and jupyterhub.vlabs.*). Both files can safely commited to Git.
To create or edit a SOPS-encrypted file, run:
helm secrets edit my-file.secrets.yamlOr in Pycharm using the Simple Sops Edit plugin (read our documentation).
To update sub-charts versions, edit naavre/Chart.yaml and run
./deploy.sh dependency-updateto update naavre/Chart.lock.
To add MinIO mounts to the user's containers (Jupyter Lab and Argo workflow components), you can use the .global.common.userPods.extraVolumeMounts option.
This is an example of how to add a MinIO mount to the user's home directory:
values-example-mount-minio-buckets.yaml
This shows how to run a command after starting a user's Jupyter Lab instance in the singleuser pod. This is useful to, e.g., clone a Git repository.
For each virtual lab, you can provide a snippet that will be executed by
sh -c in Kubernetes' postStart hook.
jupyterhub:
vlabs:
openlab:
slug: openlab
...
postStartShSnippet: |
echo "Pulling some data"
gitpuller https://github.com/user/repo.git main folderThis shows how to automatically provision TLS certificates with cert-manager.
-
Create an
Issuerin the target namespace, or create aClusterIssuer(doc, tutorial). We'll assume that the issuer is namedletsencrypt-prod. -
Add the following to the root values file:
global:
ingress:
commonAnnotations:
# if using a namespaced Issuer
cert-manager.io/issuer: "letsencrypt-prod"
# if using a ClusterIssuer
cert-manager.io/cluster-issuer: "letsencrypt-prod"
tls:
enabled: trueTo export prometheus metrics, add the following to the root values file (generate a random token):
jupyterhub:
hub:
extraConfig:
prometheus.py: |
c.JupyterHub.services += [{
'name': 'service-prometheus',
'api_token': '<a random token>',
}]
c.JupyterHub.load_roles += [{
'name': 'service-metrics-role',
'description': 'access metrics',
'scopes': [ 'read:metrics'],
'services': ['service-prometheus'],
}]To customize the Jupyter Hub templates (doc), add the following to the root values:
jupyterhub:
hub:
initContainers:
- name: git-clone-templates
image: alpine/git
args:
- clone
- --single-branch
- --branch=lifeWatch-jh-4
- --depth=1
- --
- https://github.com/QCDIS/k8s-jhub.git
- /etc/jupyterhub/custom
securityContext:
runAsUser: 1000
volumeMounts:
- name: hub-templates
mountPath: /etc/jupyterhub/custom
- name: copy-static
image: busybox:1.28
command: ["sh", "-c", "mv /etc/jupyterhub/custom/static/* /usr/local/share/jupyterhub/static/external"]
securityContext:
runAsUser: 1000
volumeMounts:
- name: hub-templates
mountPath: /etc/jupyterhub/custom
- name: hub-static
mountPath: /usr/local/share/jupyterhub/static/external
extraVolumes:
- name: hub-templates
emptyDir: { }
- name: hub-static
emptyDir: { }
extraVolumeMounts:
- name: hub-templates
mountPath: /etc/jupyterhub/custom
- name: hub-static
mountPath: /usr/local/share/jupyterhub/static/external
extraConfig:
templates.py: |
c.JupyterHub.template_paths = ['/etc/jupyterhub/custom/templates']This shows how to schedule Jupyter user pods on a dedicated set of nodes, and to only pre-pull images on these nodes (see the z2jh documentation for more).
-
Set the
hub.jupyter.org/node-purpose=useron the target nodes. E.g. with kubectl:kubectl label nodes my-node-1 hub.jupyter.org/node-purpose=user kubectl label nodes my-node-2 hub.jupyter.org/node-purpose=user ...
-
Add the following to the root values:
jupyterhub: scheduling: userPods: nodeAffinity: matchNodePurpose: require
When using a private cells repository (template), containerization works out of the box.
However, an additional token is needed to run workflows. This token should have sufficient permissions to pull containers published to the registry:
jupyterhub:
vlabs:
openlab:
slug: openlab
...
registry_token: "<username>:<token>"For ghcr.io, this is a personal access token (classic) with the read:packages permissions. Documentation.
The above only works when Argo workflows is deployed by this chart.
For external Argo instances, you will need to manually create an image pull secret and configure your instance Argo to use it by default (e.g. by adding it to Helm value controller.workflowDefaults.spec.imagePullSecrets).
NaaVRE needs a dedicated bucket in a S3-compatible object storage in order to store files uploaded to the assets catalogue. A lightweight object storage (SeaweedFS) is deployed by default alongside NaaVRE. To use an external service instead, follow these steps:
- Provision the bucket and generate an access- and secret with permission to list, get, put and delete objects. This can be achieved through the following policy (replace "BUCKET_NAME" with the actual value):
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject",
"s3:ListBucket"
],
"Effect": "Allow",
"Resource": [
"arn:aws:s3:::BUCKET_NAME",
"arn:aws:s3:::BUCKET_NAME/*"
]
}
]
}- Write down the S3 API endpoint URL, bucket name, access key and secret key.
- Update your helm values for
global.externalServices.s3andseaweedfs.enabled. The file values/values-example-external-s3.yaml can be used as an example.
UI tests using Playwright are available to test the NaaVRE interface in a Minikube environment.
See tests/README.md for detailed instructions on running UI tests.
Quick start:
cd tests
npm install
npx playwright install
npm testWhen enabling Seaweedfs and redeploying the chart (./deploy ... upgrade ...), the naavre-catalogue-service doesn't start. This is because the helm hook responsible for creating the bucket in the seaweedfs subchart only runs on install, and not on deploy.
Workaround:
- Manually run the bucket creation hook:
$ ./deploy.sh --kube-context <k8s context> -n <namespace> [--use-vlic-secrets] template
...
@@@@@@@@@@@@@@@@@ The files rendered to values/rendered/ and
@@@ IMPORTANT @@@ naavre/rendered/ may contain unencrypted
@@@@@@@@@@@@@@@@@ secrets. Clean them up after use!
$ kubectl --context <k8s context> -n <namespace> apply -f naavre/rendered/naavre/charts/seaweedfs/templates/shared/post-install-bucket-hook.yaml
job.batch/naavre-bucket-hook created
$ rm -r values/rendered naavre/rendered- Make sure that the job
naavre-bucket-hookruns successfully - Restart the
naavre-catalogue-servicepod
- Assumes that all components are served from one domain
- Assumes that everything runs within a single k8s namespace
- Assumes it is the only NaaVRE instance running in said namespace
- Assumes that the Ingress NGINX Controller is deployed on the cluster
- Does not deploy monitoring (see https://github.com/QCDIS/infrastructure/blob/main/doc/monitoring.md)
- Does not configure pod affinities (see https://github.com/QCDIS/infrastructure/blob/main/doc/pod-affinities.md)
- Does support argo workflow artifacts
