Complex Scenarios with Dependencies
For complex scenarios that require additional services (databases, message queues, monitoring tools, etc.), you can use Learning UI as a Helm dependency in your own chart.
Why Use Dependencies?
Using Learning UI as a dependency allows you to:
- Add custom services alongside the learning environment
- Configure ingress for additional services
- Manage everything in a single Helm release
- Customize the shell container and scenario
Creating a Parent Chart
1. Chart Structure
my-data-science-training/├── Chart.yaml # Dependencies declaration├── values.yaml # Configuration└── templates/ ├── deployment-jupyter.yaml # Jupyter deployment ├── service-jupyter.yaml # Jupyter service └── ingress-jupyter.yaml # Ingress for /jupyter/2. Chart.yaml
Declare Learning UI as a dependency:
apiVersion: v2name: my-data-science-trainingdescription: Data science training with Jupyterversion: 1.0.0appVersion: "1.0.0"
dependencies: - name: learning-ui version: "1.0.0" repository: "oci://ghcr.io/aydev-fr/charts" # Or for local development: # repository: "file://../learning-ui/chart"3. values.yaml
Configure both Learning UI and your custom services:
# Learning UI configuration (prefixed with dependency name)learning-ui: scenario: name: "Data Science Workshop" description: "Learn data science with Jupyter and Python" difficulty: intermediate estimatedTime: 60m steps: - name: "01-intro" title: "Introduction" content: | # Welcome to Data Science
In this workshop, you'll learn: - Python data analysis with Pandas - Data visualization with Matplotlib - Machine learning with Scikit-learn
Open the **Jupyter** tab to start coding!
- name: "02-pandas" title: "Pandas Basics" content: | # Working with Pandas
Create a new notebook in Jupyter and run:
```python import pandas as pd import numpy as np
# Create a DataFrame df = pd.DataFrame({ 'name': ['Alice', 'Bob', 'Charlie'], 'age': [25, 30, 35], 'score': [85, 92, 78] })
print(df) ``` check: | #!/bin/bash # Check if any notebook was created if ls /workspace/*.ipynb 2>/dev/null; then echo "Notebook found!" exit 0 fi echo "Create a notebook in Jupyter first" exit 1
# Hide terminal, focus on Jupyter terminal: enabled: false
# Add Jupyter as a custom tab customTabs: - id: "jupyter" name: "Jupyter" icon: "book" url: "/jupyter/"
# Shell with Python data science tools shell: image: repository: python tag: "3.11-slim"
# Ingress configuration ingress: enabled: true
# Your Jupyter configurationjupyter: enabled: true image: repository: jupyter/scipy-notebook tag: latest resources: limits: memory: "2Gi" cpu: "2000m"4. Jupyter Deployment Template
{{- if .Values.jupyter.enabled }}apiVersion: apps/v1kind: Deploymentmetadata: name: {{ .Release.Name }}-jupyter labels: app.kubernetes.io/name: jupyter app.kubernetes.io/instance: {{ .Release.Name }}spec: replicas: 1 selector: matchLabels: app.kubernetes.io/name: jupyter app.kubernetes.io/instance: {{ .Release.Name }} template: metadata: labels: app.kubernetes.io/name: jupyter app.kubernetes.io/instance: {{ .Release.Name }} spec: containers: - name: jupyter image: "{{ .Values.jupyter.image.repository }}:{{ .Values.jupyter.image.tag }}" ports: - name: http containerPort: 8888 env: - name: JUPYTER_TOKEN value: "" - name: JUPYTER_ENABLE_LAB value: "yes" {{- with .Values.jupyter.resources }} resources: {{- toYaml . | nindent 12 }} {{- end }} volumeMounts: - name: workspace mountPath: /home/jovyan/work volumes: - name: workspace persistentVolumeClaim: claimName: {{ .Release.Name }}-learning-ui-shell-workspace-0{{- end }}5. Jupyter Service Template
{{- if .Values.jupyter.enabled }}apiVersion: v1kind: Servicemetadata: name: {{ .Release.Name }}-jupyter labels: app.kubernetes.io/name: jupyter app.kubernetes.io/instance: {{ .Release.Name }}spec: type: ClusterIP ports: - port: 8888 targetPort: http protocol: TCP name: http selector: app.kubernetes.io/name: jupyter app.kubernetes.io/instance: {{ .Release.Name }}{{- end }}6. Jupyter Ingress Template
{{- if .Values.jupyter.enabled }}apiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: {{ .Release.Name }}-jupyter annotations: nginx.ingress.kubernetes.io/rewrite-target: /$2 nginx.ingress.kubernetes.io/proxy-http-version: "1.1"spec: {{- if .Values.learning-ui.ingress.className }} ingressClassName: {{ index .Values "learning-ui" "ingress" "className" }} {{- end }} rules: - host: {{ index .Values "learning-ui" "ingressHost" }} http: paths: - path: /jupyter(/|$)(.*) pathType: ImplementationSpecific backend: service: name: {{ .Release.Name }}-jupyter port: number: 8888{{- end }}Installing the Chart
# Update dependencieshelm dependency update ./my-data-science-training
# Installhelm install ds-workshop ./my-data-science-training \ --set learning-ui.ingressHost=workshop.localhostMore Examples
Kubernetes Training with Monitoring
learning-ui: scenario: name: "Kubernetes Monitoring" steps: - name: "01-intro" content: | # Monitoring Kubernetes
Open **Grafana** to see cluster metrics.
k3s: enabled: true
customTabs: - id: "grafana" name: "Grafana" icon: "chart" url: "/grafana/" - id: "prometheus" name: "Prometheus" icon: "database" url: "/prometheus/"
# Deploy kube-prometheus-stackprometheus: enabled: trueDatabase Training
learning-ui: scenario: name: "PostgreSQL Basics" steps: - name: "01-connect" content: | # Connect to PostgreSQL
```bash psql -h localhost -U postgres ```
customTabs: - id: "pgadmin" name: "pgAdmin" icon: "database" url: "/pgadmin/"
shell: image: repository: postgres tag: "16-alpine"
# Deploy PostgreSQL and pgAdminpostgresql: enabled: truepgadmin: enabled: trueBest Practices
- Share the workspace volume between services when possible
- Use consistent ingress paths (e.g.,
/jupyter/,/grafana/) - Configure appropriate resources for each service
- Test locally with port-forward before deploying with ingress
- Document the scenario clearly for users