Skip to content
This repository was archived by the owner on Mar 3, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
49 changes: 49 additions & 0 deletions .github/Gthulhu - multi-node management.md

Large diffs are not rendered by default.

65 changes: 65 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Helm Charts for Gthulhu deployment


## Usage

The Gthulhu requires prometheus and grafana to be installed in your kubernetes cluster.

```bash
$ helm install kube-prometheus-stack kube-prometheus-stack
```

To deploy Gthulhu using Helm charts, follow these steps:
```bash
$ helm install gthulhu gthulhu -f ./gthulhu/values-production.yaml
```

To uninstall Gthulhu, run the following command:
```bash
$ helm uninstall gthulhu
```

## Testing

To access the Gthulhu API, you can set up port forwarding using kubectl:
```bash
$ kubectl port-forward svc/gthulhu-manager 8080:8080
```

After deploying Gthulhu, you can test the API by sending a login request using curl:
```bash
$ curl -X POST http://localhost:8080/api/v1/auth/login -H "Content-Type: application/json" -d '{
"username": "admin@example.com",
"password": "your-password-here"
}'
{"success":true,"data":{"token":"<TOKEN>"},"timestamp":"2025-12-30T13:09:10Z"}
```

Then, you can create a new strategy by sending another curl request with the obtained token:

```bash
$ curl -X POST http://localhost:8080/api/v1/strategies \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <TOKEN>" \
-d '{
"strategyNamespace": "default",
"labelSelectors": [
{"key": "app.kubernetes.io/name", "value": "prometheus"}
],
"k8sNamespace": ["default"],
"priority": 10,
"executionTime": 20000000
}'
```

You can also retrieve your own strategies using the following curl command:

```bash
$ curl http://localhost:8080/api/v1/strategies/self \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <TOKEN>"
```

## Licence

This project is licensed under the Apache 2.0 License - see the [LICENSE](LICENSE) file for details.
74 changes: 48 additions & 26 deletions gthulhu/templates/NOTES.txt
Original file line number Diff line number Diff line change
@@ -1,48 +1,69 @@
1. Gthulhu has been deployed to your Kubernetes cluster!

{{- if .Values.pod.enabled }}
{{- if .Values.scheduler.enabled }}

** Gthulhu Scheduler **
The BPF scheduler is running as a DaemonSet on all nodes.
You can check the scheduler status with:

kubectl get daemonset {{ include "gthulhu.fullname" . }} -n {{ .Release.Namespace }}
kubectl logs -l app.kubernetes.io/name={{ include "gthulhu.name" . }} -c gthulhu-scheduler -n {{ .Release.Namespace }}
kubectl get daemonset {{ include "gthulhu.fullname" . }}-scheduler -n {{ .Release.Namespace }}
kubectl logs -l app.kubernetes.io/component=scheduler -n {{ .Release.Namespace }}

{{- end }}

{{- if .Values.pod.api.enabled }}
{{- if .Values.manager.enabled }}

** BSS Metrics API Server **
{{- if .Values.ingress.enabled }}
{{- range $host := .Values.ingress.hosts }}
** Gthulhu Manager **
The Manager is running as a Deployment (replicas=1).
Manager provides frontend and user-related APIs, accepts user intents.

You can check the manager status with:
kubectl get deployment {{ include "gthulhu.fullname" . }}-manager -n {{ .Release.Namespace }}
kubectl logs -l app.kubernetes.io/component=manager -n {{ .Release.Namespace }}

{{- if .Values.manager.ingress.enabled }}
Manager API is accessible at:
{{- range $host := .Values.manager.ingress.hosts }}
{{- range .paths }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
http{{ if $.Values.manager.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
{{- end }}
{{- end }}
{{- else if and .Values.service.enabled (contains "NodePort" .Values.service.type) }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "gthulhu.fullname" . }}-api)
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if and .Values.service.enabled (contains "LoadBalancer" .Values.service.type) }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "gthulhu.fullname" . }}-api'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "gthulhu.fullname" . }}-api --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if and .Values.service.enabled (contains "ClusterIP" .Values.service.type) }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "gthulhu.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[1].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
{{- else if contains "ClusterIP" .Values.manager.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/component=manager,app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:8080 to access Manager API"
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:{{ .Values.manager.port }}
{{- end }}

{{- end }}

{{- if and .Values.scheduler.enabled .Values.scheduler.sidecar.enabled }}

** Gthulhu Scheduler Sidecar **
The Scheduler Sidecar is running alongside the scheduler in the DaemonSet.
Sidecar shares PID namespace with the host and finds matching PIDs based on intent from Manager.

You can check the sidecar logs with:
kubectl logs -l app.kubernetes.io/component=scheduler -c scheduler-sidecar -n {{ .Release.Namespace }}

{{- end }}

API Endpoints:
POST /api/v1/metrics - Submit BSS metrics data
GET /health - Health check
GET / - API information
{{- if .Values.mongodb.enabled }}

** MongoDB **
MongoDB is running as a StatefulSet for data persistence.

You can check the MongoDB status with:
kubectl get statefulset {{ include "gthulhu.fullname" . }}-mongodb -n {{ .Release.Namespace }}
kubectl logs -l app.kubernetes.io/component=mongodb -n {{ .Release.Namespace }}

{{- end }}

** API Endpoints (Manager) **
POST /api/v1/intents - Submit scheduling intents
GET /api/v1/nodes - Get node load distribution
GET /health - Health check
GET / - API information

** Monitoring **
{{- if .Values.monitoring.enabled }}
Prometheus ServiceMonitor has been created to scrape metrics.
Expand All @@ -54,5 +75,6 @@ To enable monitoring, set monitoring.enabled=true in your values.yaml
- The scheduler requires Linux kernel 6.12+ with sched_ext support
- Nodes must have the necessary BPF capabilities
- The scheduler runs with privileged access for BPF operations
- Decision Maker requires hostPID access to discover process PIDs

For more information, visit: https://github.com/Gthulhu/Gthulhu
103 changes: 4 additions & 99 deletions gthulhu/templates/configmap.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{{- if .Values.pod.enabled }}
{{- if .Values.scheduler.enabled }}
apiVersion: v1
kind: ConfigMap
metadata:
Expand Down Expand Up @@ -29,111 +29,16 @@ data:
scheduler:
# Default time slice in nanoseconds (default: 5000000 = 5ms)
slice_ns_default: 5000000

# Minimum time slice in nanoseconds (default: 500000 = 0.5ms)
slice_ns_min: 500000
api:
enabled: true
url: http://localhost:8080
auth_enabled: true
url: http://localhost:{{ .Values.scheduler.sidecar.port }}
interval: 5
public_key_path: /etc/gthulhu/jwt_public_key.pem
debug: true
early_processing: false
builtin_idle: false
{{- end }}
---
{{- if .Values.pod.api.enabled }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "gthulhu.fullname" . }}-api-config
labels:
{{- include "gthulhu.labels" . | nindent 4 }}
app.kubernetes.io/component: api
data:
jwt_private_key.key: |
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

config.json: |
{
"server": {
"port": ":{{ .Values.pod.api.port }}",
"read_timeout": 15,
"write_timeout": 15,
"idle_timeout": 60
},
"logging": {
"level": "info",
"format": "text"
},
"jwt": {
"private_key_path": "/etc/gthulhu/jwt_private_key.key",
"token_duration": 24
},
"strategies": {
"default": [
{
"priority": true,
"execution_time": 20000,
"selectors": [
{
"key": "app",
"value": "ueransim-macvlan"
}
],
"command_regex": "nr-gnb|nr-ue|ping"
}
]
}
}
{{- end }}
Loading