Skip to content

Australian-Imaging-Service/ais-devstack

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

37 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AIS-XNAT Deployment for k3s

XNAT deployment on k3s with NFS-backed storage for the Australian Imaging Service.

Prerequisites

  • Ubuntu 20.04+ or similar Linux distribution
  • Minimum 4GB RAM, 2 CPU cores
  • 600GB+ storage for NFS server
  • Helm 3.x installed

Directory Structure

ais-devstack/
├── README.md                    # This file
├── manifests/
│   ├── pv.yaml                  # Persistent Volumes (NFS-backed)
│   ├── pvc.yaml                 # Persistent Volume Claims
│   ├── configmap.yaml           # XNAT init script ConfigMap
│   ├── kustomization.yaml       # Kustomize patches for StatefulSet
│   ├── kustomize.sh             # Helm post-renderer script
│   └── values.yaml              # XNAT Helm chart values (domain config)
├── nfs-server/
│   └── values.yaml              # NFS server Helm chart values
├── plugins/
│   └── container-service-*.jar  # XNAT plugins (auto-copied during install)
├── jupyterhub/                  # JupyterHub integration (git subtree)
│   ├── INSTALL.sh               # JupyterHub orchestrator
│   ├── 5-jupyterhub-values.yaml.template  # JupyterHub config template
│   └── ...                      # See jupyterhub/README.md
└── scripts/
    ├── install.sh               # XNAT install (prompts for JupyterHub)
    ├── install-jupyterhub.sh    # JupyterHub installation
    ├── uninstall.sh             # XNAT uninstallation
    └── uninstall-jupyterhub.sh  # JupyterHub uninstallation

Quick Start

Install

cd /home/ubuntu/ais-devstack
chmod +x scripts/*.sh manifests/kustomize.sh
./scripts/install.sh

Uninstall

The uninstall script automatically detects whether you're running MicroK8s or k3s:

./scripts/uninstall.sh

It will:

  • Remove XNAT and related resources
  • Optionally remove NFS server and CSI driver
  • Optionally remove the entire Kubernetes distribution (MicroK8s or k3s)

Manual Installation

If you prefer step-by-step installation:

Step 1: Install k3s

curl -sfL https://get.k3s.io | sh -

# Configure kubectl
mkdir -p ~/.kube
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown $USER:$USER ~/.kube/config

Step 2: Install Helm (if not installed)

curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

Step 3: Install NGINX Ingress Controller

k3s comes with Traefik by default, but XNAT requires nginx-specific annotations for large file uploads:

helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

helm install ingress-nginx ingress-nginx/ingress-nginx \
  --namespace ingress-nginx \
  --create-namespace \
  --set controller.publishService.enabled=true

Step 4: Install NFS CSI Driver

helm repo add csi-driver-nfs https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/charts

helm install csi-driver-nfs csi-driver-nfs/csi-driver-nfs \
  --namespace kube-system \
  --set kubeletDir=/var/lib/kubelet

Step 5: Deploy NFS Server

kubectl create namespace storage

# Deploy using the nfs-server helm chart
helm install nfs-server ./nfs-server \
  --namespace storage \
  --values nfs-server/values.yaml

# Wait for pod to be ready
kubectl -n storage get pods -w

# Create required directories
kubectl -n storage exec deploy/nfs-server -- mkdir -p \
  /exports/gpfs /exports/xnat/data/build /exports/xnat/plugins

Step 6: Deploy XNAT

# Create namespace
kubectl create namespace ais-xnat

# Apply storage resources
kubectl apply -f manifests/pv.yaml
kubectl apply -f manifests/pvc.yaml
kubectl apply -f manifests/configmap.yaml

# Add AIS Helm repo
helm repo add ais https://australian-imaging-service.github.io/charts
helm repo update

# Install XNAT with kustomize patches
chmod +x manifests/kustomize.sh
helm install xnat-web ais/xnat \
  --namespace ais-xnat \
  --values manifests/values.yaml \
  --post-renderer ./manifests/kustomize.sh

Step 7: Verify Installation

# Watch pods start up
kubectl -n ais-xnat get pods -w

# Port forward to access locally
kubectl -n ais-xnat port-forward svc/xnat-web 8080:80

Access XNAT at http://localhost:8080 (default: admin/admin)

Key Differences from MicroK8s

Component MicroK8s k3s
Kubelet path /var/snap/microk8s/common/var/lib/kubelet /var/lib/kubelet
Default storage class microk8s-hostpath local-path
Default ingress nginx (addon) Traefik (requires nginx install)
kubectl microk8s kubectl kubectl
helm microk8s helm helm
Config location Snap-based /etc/rancher/k3s/

Configuration

Ingress Host

Edit manifests/values.yaml to change the ingress hostname:

xnat-web:
  ingress:
    hosts:
      - host: your-domain.example.com

OpenID Connect (OIDC) Authentication

Both XNAT and JupyterHub use OIDC for authentication. Choose one provider and configure both services to use it.

Supported Providers:

  • Google OIDC - Recommended for development and testing (easy setup, works worldwide)
  • AAF - Australian Access Federation (for Australian research institutions)

Important: Both XNAT and JupyterHub must use the same OIDC provider. Users need to exist in both systems with matching usernames.


Option A: Google OIDC Setup

Step 1: Create Google OAuth Credentials

  1. Go to Google Cloud Console

  2. Create a new project or select an existing one

  3. Navigate to APIs & Services > Credentials

  4. Click Create Credentials > OAuth client ID

  5. Select Web application

  6. Configure the OAuth client:

    Field Value
    Name XNAT + JupyterHub
    Authorized JavaScript origins https://your-domain.example.com
    Authorized redirect URIs https://your-domain.example.com/openid-login
    https://your-domain.example.com/hub/oauth_callback
  7. Click Create and note down the Client ID and Client Secret

Step 2: Configure XNAT for Google

Update manifests/values.yaml:

xnat-web:
  plugins:
    openid-auth-plugin:
      - name: "Google Authentication"
        provider:
          id: google
        enabled: "google"
        siteUrl: "https://your-domain.example.com"
        openid:
          google:
            clientId: "your-google-client-id.apps.googleusercontent.com"
            clientSecret: "your-google-client-secret"
            # Other settings already configured in template

Step 3: Configure JupyterHub for Google

Update jupyterhub/5-jupyterhub-values.yaml:

hub:
  config:
    GenericOAuthenticator:
      client_id: "your-google-client-id.apps.googleusercontent.com"
      client_secret: "your-google-client-secret"
      oauth_callback_url: "https://your-domain.example.com/hub/oauth_callback"
      # Google URLs already configured in template
  extraEnv:
    OIDC_PROVIDER_PREFIX: "google"  # Ensures username format matches XNAT

Option B: AAF Setup (Australian Access Federation)

Step 1: Register with AAF

  1. Go to AAF Service Manager (production) or Test AAF (testing)

  2. Register two separate services:

    Service Callback URL
    XNAT https://your-domain.example.com/openid-login
    JupyterHub https://your-domain.example.com/hub/oauth_callback
  3. Note down the Client ID and Client Secret for each service

Step 2: Configure XNAT for AAF

Update manifests/values.yaml - comment out Google, uncomment AAF:

xnat-web:
  plugins:
    openid-auth-plugin:
      - name: "AAF Authentication"
        provider:
          id: aaf
        enabled: "aaf"
        siteUrl: "https://your-domain.example.com"
        openid:
          # google: ...  (comment out)
          aaf:
            accessTokenUri: https://central.aaf.edu.au/providers/op/token
            userAuthUri: https://central.aaf.edu.au/providers/op/authorize
            clientId: "your-aaf-xnat-client-id"
            clientSecret: "your-aaf-xnat-client-secret"
            scopes: "openid,profile,email"
            # Other settings already configured in template

Step 3: Configure JupyterHub for AAF

Update jupyterhub/5-jupyterhub-values.yaml - comment out Google, uncomment AAF:

hub:
  config:
    GenericOAuthenticator:
      # Google settings (comment out)
      # client_id: ...

      # AAF settings (uncomment)
      client_id: "your-aaf-jupyterhub-client-id"
      client_secret: "your-aaf-jupyterhub-client-secret"
      oauth_callback_url: "https://your-domain.example.com/hub/oauth_callback"
      authorize_url: "https://central.aaf.edu.au/providers/op/authorize"
      token_url: "https://central.aaf.edu.au/providers/op/token"
      userdata_url: "https://central.aaf.edu.au/providers/op/userinfo"
      login_service: "AAF"
      scope: [openid, profile, email, eduperson_principal_name]
  extraEnv:
    OIDC_PROVIDER_PREFIX: "aaf"  # Ensures username format matches XNAT

Apply Configuration Changes

After updating the configuration files:

# Upgrade XNAT
helm upgrade xnat-web ais/xnat \
  --namespace ais-xnat \
  --values manifests/values.yaml \
  --post-renderer ./manifests/kustomize.sh

# Upgrade JupyterHub
helm upgrade jupyterhub jupyterhub/jupyterhub -n jupyter \
  --values jupyterhub/5-jupyterhub-values.yaml

Storage Size

Edit nfs-server/values.yaml:

persistence:
  size: 600Gi  # Adjust as needed

Troubleshooting

XNAT pod stuck in Init

kubectl -n ais-xnat describe pod xnat-web-0
kubectl -n ais-xnat logs xnat-web-0 -c home-init

NFS connection issues

# Check NFS server is running
kubectl -n storage get pods
kubectl -n storage logs deploy/nfs-server

# Check PV/PVC binding
kubectl get pv
kubectl -n ais-xnat get pvc

Database issues

kubectl -n ais-xnat logs xnat-web-0-postgresql-0

Plugins

Plugins in the plugins/ directory are automatically copied to the NFS server during installation.

Pre-installed plugins:

  • container-service-3.7.2-uq-fat.jar - Container service plugin

To add more plugins:

  1. Place JAR files in plugins/ before running install, OR
  2. Copy manually after installation:
# Copy plugin jar to NFS server
kubectl -n storage cp my-plugin.jar \
  $(kubectl -n storage get pods -l app=nfs-server -o name | cut -d/ -f2):/exports/xnat/plugins/

# Restart XNAT to load new plugins
kubectl -n ais-xnat rollout restart statefulset xnat-web

JupyterHub Integration

JupyterHub provides interactive Jupyter notebooks integrated with XNAT. The jupyterhub/ directory contains the JupyterHub deployment as a git subtree from ais-jupyterhub.

Install JupyterHub

Option 1: During XNAT installation, answer "y" when prompted:

Install JupyterHub? (y/N): y

Option 2: Install separately after XNAT is running:

./scripts/install-jupyterhub.sh

The install script automatically reads the domain from manifests/values.yaml and configures JupyterHub to use the same domain.

Uninstall JupyterHub

./scripts/uninstall-jupyterhub.sh

Update JupyterHub from Upstream

The jupyterhub/ directory is a git subtree. To pull updates from the upstream ais-jupyterhub repository:

git subtree pull --prefix=jupyterhub \
  https://github.com/Australian-Imaging-Service/ais-jupyterhub.git \
  Development_AB --squash

To push local changes back to upstream (if you have write access):

git subtree push --prefix=jupyterhub \
  https://github.com/Australian-Imaging-Service/ais-jupyterhub.git \
  Development_AB

JupyterHub Documentation

See the following files in jupyterhub/ for more details:

  • README.md - Architecture overview
  • XNAT-CONFIGURATION.md - XNAT plugin setup
  • TROUBLESHOOTING.md - Common issues and solutions

About

Local development stack setup and documentation for AIS

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors