feat: huge beszel hub and agent helm chart update (#1582)

This commit is contained in:
Daniel Nikoloski
2026-08-17 00:52:26 +02:00
committed by GitHub
parent ca5497324c
commit ec4ec01a39
32 changed files with 1859 additions and 5 deletions

View File

@@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/

View File

@@ -0,0 +1,15 @@
apiVersion: v1
description: Installs beszel-hub in kubernetes
home: https://github.com/henrygd/beszel/tree/main/supplemental/helm/beszel-hub
name: beszel-hub
appVersion: "0.17.0"
# Do not touch will be updated during release
version: 0.1.0
sources:
- https://github.com/henrygd/beszel/tree/main/supplemental/helm/beszel-hub
- https://www.beszel.dev/
- https://github.com/henrygd/beszel
icon: https://repository-images.githubusercontent.com/825470378/2710c6db-f934-4a8b-a2c4-7a0abbcd2ad6
maintainers:
- name: cloudwithdan
email: nikoloskid@pm.me

View File

@@ -0,0 +1,346 @@
# Beszel Hub Helm Chart
A Kubernetes Helm chart for deploying [Beszel Hub](https://www.beszel.dev/) - a monitoring and alerting solution for systems, containers, and services.
## Overview
This Helm chart simplifies the deployment of Beszel Hub in Kubernetes environments. Beszel Hub is a centralized monitoring hub that collects and aggregates system metrics from multiple agents deployed across your infrastructure.
## Prerequisites
- Kubernetes 1.19+
- Helm 3.0+
- At least 500Mi of persistent storage (configurable)
## Quick Start
### 1. Add the Repository
```bash
helm repo add beszel https://henrygd.github.io/beszel
helm repo update
```
### 2. Install the Chart
```bash
helm install beszel-hub ./beszel-hub
```
Or with a custom values file:
```bash
helm install beszel-hub ./beszel-hub -f custom-values.yaml
```
### 3. Access Beszel Hub
By default, Beszel Hub is accessible at `http://beszel-hub:8090` within the cluster.
```bash
# Port forward to access locally
kubectl port-forward svc/beszel-hub 8090:8090
```
Then visit: `http://localhost:8090`
## Configuration
### Basic Configuration
Key configuration options in `values.yaml`:
| Parameter | Default | Description |
|-----------|---------|-------------|
| `replicaCount` | `1` | Number of Beszel Hub replicas |
| `image.repository` | `henrygd/beszel` | Container image repository |
| `image.tag` | Chart AppVersion (0.17.0) | Container image tag |
| `image.pullPolicy` | `IfNotPresent` | Image pull policy |
| `service.port` | `8090` | Service port |
| `persistentVolumeClaim.enabled` | `true` | Enable persistent volume |
| `persistentVolumeClaim.size` | `500Mi` | PVC size |
### Installation with Custom Values
```bash
helm install beszel-hub ./beszel-hub \
--set replicaCount=2 \
--set persistentVolumeClaim.size=1Gi \
--set service.type=LoadBalancer
```
Or create a custom values file:
```yaml
# custom-values.yaml
replicaCount: 2
service:
type: LoadBalancer
persistentVolumeClaim:
size: 1Gi
```
Then install:
```bash
helm install beszel-hub ./beszel-hub -f custom-values.yaml
```
## Advanced Configuration
### Ingress Configuration
Enable and configure Ingress for external access:
```yaml
ingress:
enabled: true
className: nginx # or your ingress class
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
hosts:
- host: beszel.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: beszel-tls
hosts:
- beszel.example.com
```
### Persistent Volume Configuration
To use an existing PersistentVolumeClaim:
```yaml
persistentVolumeClaim:
enabled: true
existingClaim: "my-existing-pvc"
```
Or to use a specific storage class:
```yaml
persistentVolumeClaim:
enabled: true
storageClass: "fast-ssd"
size: 1Gi
```
### Resource Limits
Set CPU and memory limits:
```yaml
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
```
### Autoscaling
Enable Horizontal Pod Autoscaler:
```yaml
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 80
```
### Node Selection
Schedule pods on specific nodes:
```yaml
nodeSelector:
node-type: monitoring
tolerations:
- key: "monitoring"
operator: "Equal"
value: "true"
effect: "NoSchedule"
```
## Deployment Examples
### Production Setup
```yaml
replicaCount: 3
image:
tag: "0.17.0"
service:
type: LoadBalancer
ingress:
enabled: true
className: nginx
hosts:
- host: beszel.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: beszel-tls
hosts:
- beszel.example.com
persistentVolumeClaim:
enabled: true
storageClass: "fast-ssd"
size: 2Gi
resources:
limits:
cpu: 1000m
memory: 1Gi
requests:
cpu: 500m
memory: 512Mi
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 75
```
### Development/Test Setup
```yaml
replicaCount: 1
service:
type: ClusterIP
persistentVolumeClaim:
enabled: true
size: 500Mi
resources:
limits:
cpu: 200m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi
```
## Managing Beszel Hub
### Upgrade
```bash
helm upgrade beszel-hub ./beszel-hub
```
### Check Status
```bash
# Get deployment status
kubectl get deployment beszel-hub
kubectl get pods -l app.kubernetes.io/name=beszel
# Get service info
kubectl get svc beszel-hub
```
### View Logs
```bash
kubectl logs -l app.kubernetes.io/name=beszel -f
```
### Access Pod Shell
```bash
kubectl exec -it <pod-name> -- sh
```
### Uninstall
```bash
helm uninstall beszel-hub
```
## Connecting Beszel Agents
After deploying Beszel Hub, you can connect Beszel agents running on:
- Kubernetes nodes
- VM instances
- Bare metal servers
- Docker containers
Agents communicate with the Hub on port `8090`. Configure the agent with the Hub's address:
```
HUB_URL=http://beszel-hub.default.svc.cluster.local:8090
```
Or for external access, use the LoadBalancer IP/DNS or Ingress hostname.
## Troubleshooting
### Pod won't start
```bash
# Check pod status and events
kubectl describe pod <pod-name>
kubectl logs <pod-name>
```
### Persistent volume issues
```bash
# Check PVC status
kubectl get pvc
kubectl describe pvc beszel-hub
```
### Connection issues with agents
- Verify the service is accessible: `kubectl get svc beszel-hub`
- Check network policies aren't blocking traffic
- Ensure agents can resolve the Hub's DNS name
- Verify port `8090` is open on the service
### Storage full
Increase PVC size:
```bash
# Update the PVC size in values
helm upgrade beszel-hub ./charts/beszel-hub \
--set persistentVolumeClaim.size=2Gi
```
## Security Considerations
- Use network policies to restrict traffic to Beszel Hub
- Enable RBAC and pod security policies
- Use TLS/HTTPS via Ingress with cert-manager
- Regularly update the image to the latest version
- Consider running with read-only filesystem
- Use private container registries if applicable
## Persistence
By default, Beszel Hub uses a PersistentVolumeClaim for data storage. Ensure your Kubernetes cluster has enough storage capacity and a default storage class configured.
## Support and Documentation
- **Project Homepage**: https://www.beszel.dev/
- **GitHub Repository**: https://github.com/henrygd/beszel
- **Chart Repository**: https://github.com/henrygd/beszel-kubernetes
## Chart Information
- **Chart Version**: 0.1.0
- **App Version**: 0.17.0
- **Kubernetes Version**: 1.19+
- **Maintainer**: cloudwithdan (nikoloskid@pm.me)
## License
Please refer to the main Beszel project repository for license information.

View File

@@ -0,0 +1,22 @@
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range $host := .Values.ingress.hosts }}
{{- range .paths }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
{{- end }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "beszel.fullname" . }}-web)
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 contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch its status by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "beszel.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "beszel.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "beszel.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[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8090 to use your application"
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8090:$CONTAINER_PORT
{{- end }}

View File

@@ -0,0 +1,51 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "beszel.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "beszel.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "beszel.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "beszel.labels" -}}
helm.sh/chart: {{ include "beszel.chart" . }}
{{ include "beszel.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "beszel.selectorLabels" -}}
app.kubernetes.io/name: {{ include "beszel.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

View File

@@ -0,0 +1,99 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "beszel.fullname" . }}
labels:
{{- include "beszel.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
strategy:
type: {{ .Values.strategyType }}
{{- if eq .Values.strategyType "RollingUpdate" }}
rollingUpdate:
maxSurge: {{ .Values.maxSurge }}
maxUnavailable: {{ .Values.maxUnavailable }}
{{- end }}
selector:
matchLabels:
{{- include "beszel.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "beszel.labels" . | nindent 8 }}
{{- with .Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.podSecurityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- end }}
hostname: {{ .Values.hostname }}
hostNetwork: {{ .Values.hostNetwork }}
containers:
- name: {{ .Chart.Name }}
{{- with .Values.securityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
{{- end }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.port }}
protocol: TCP
{{- with .Values.livenessProbe }}
livenessProbe:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.readinessProbe }}
readinessProbe:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if .Values.persistentVolumeClaim.enabled }}
volumeMounts:
- name: data
mountPath: /beszel_data
{{- with .Values.volumeMounts }}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- else if .Values.volumeMounts }}
volumeMounts:
{{- toYaml .Values.volumeMounts | nindent 12 }}
{{- end }}
{{- if .Values.persistentVolumeClaim.enabled }}
volumes:
- name: data
persistentVolumeClaim:
claimName: {{ .Values.persistentVolumeClaim.existingClaim | default (include "beszel.fullname" .) }}
{{- with .Values.volumes }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- else if .Values.volumes }}
volumes:
{{- toYaml .Values.volumes | nindent 8 }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@@ -0,0 +1,38 @@
{{- if .Values.httpRoute.enabled -}}
{{- $fullName := include "beszel.fullname" . -}}
{{- $svcPort := .Values.service.port -}}
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: {{ $fullName }}
labels:
{{- include "beszel.labels" . | nindent 4 }}
{{- with .Values.httpRoute.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
parentRefs:
{{- with .Values.httpRoute.parentRefs }}
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.httpRoute.hostnames }}
hostnames:
{{- toYaml . | nindent 4 }}
{{- end }}
rules:
{{- range .Values.httpRoute.rules }}
{{- with .matches }}
- matches:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .filters }}
filters:
{{- toYaml . | nindent 8 }}
{{- end }}
backendRefs:
- name: {{ $fullName }}
port: {{ $svcPort }}
weight: 1
{{- end }}
{{- end }}

View File

@@ -0,0 +1,43 @@
{{- if .Values.ingress.enabled -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "beszel.fullname" . }}
labels:
{{- include "beszel.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- with .Values.ingress.className }}
ingressClassName: {{ . }}
{{- end }}
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
{{- with .pathType }}
pathType: {{ . }}
{{- end }}
backend:
service:
name: {{ include "beszel.fullname" $ }}
port:
number: {{ $.Values.service.port }}
{{- end }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,22 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "beszel.fullname" . }}
labels:
{{- include "beszel.labels" . | nindent 4 }}
{{- if .Values.service.annotations }}
annotations:
{{ toYaml .Values.service.annotations | indent 4 }}
{{- end }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
{{- if .Values.service.loadBalancerIP }}
loadBalancerIP: {{ .Values.service.loadBalancerIP }}
{{- end }}
selector:
{{- include "beszel.selectorLabels" . | nindent 4 }}

View File

@@ -0,0 +1,14 @@
apiVersion: v1
kind: Pod
metadata:
name: "{{ .Release.Name }}-smoke-test"
annotations:
"helm.sh/hook": test
spec:
containers:
- name: hook1-container
image: curlimages/curl
imagePullPolicy: IfNotPresent
command: ['sh', '-c', 'curl http://{{ template "beszel.fullname" . }}-web:8090/']
restartPolicy: Never
terminationGracePeriodSeconds: 0

View File

@@ -0,0 +1,27 @@
{{- if .Values.persistentVolumeClaim.enabled -}}
{{- if not .Values.persistentVolumeClaim.existingClaim -}}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
{{- if .Values.persistentVolumeClaim.annotations }}
annotations:
{{ toYaml .Values.persistentVolumeClaim.annotations | indent 4 }}
{{- end }}
labels:
{{- include "beszel.labels" . | nindent 4 }}
name: {{ template "beszel.fullname" . }}
spec:
accessModes:
{{ toYaml .Values.persistentVolumeClaim.accessModes | indent 4 }}
{{- if .Values.persistentVolumeClaim.storageClass }}
{{- if (eq "-" .Values.persistentVolumeClaim.storageClass) }}
storageClassName: ""
{{- else }}
storageClassName: {{ .Values.persistentVolumeClaim.storageClass | quote }}
{{- end }}
{{- end }}
resources:
requests:
storage: {{ .Values.persistentVolumeClaim.size | quote }}
{{- end -}}
{{- end -}}

View File

@@ -0,0 +1,152 @@
# Default values for beszel-hub.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
# To add the Helm repository:
# helm repo add beszel https://henrygd.github.io/beszel
# helm repo update
# -- The number of replicas
replicaCount: 1
image:
repository: henrygd/beszel
pullPolicy: IfNotPresent
tag: ""
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
podAnnotations: {}
podLabels: {}
podSecurityContext: {}
securityContext: {}
# capabilities:
# drop:
# - ALL
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000
service:
enabled: true
annotations: {}
type: ClusterIP
loadBalancerIP: ""
port: 8090
ingress:
enabled: false
className: ""
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
hosts:
- host: chart-example.local
paths:
- path: /
pathType: ImplementationSpecific
tls: []
# - secretName: chart-example-tls
# hosts:
# - chart-example.local
# -- Expose the service via gateway-api HTTPRoute
# Requires Gateway API resources and suitable controller installed within the cluster
# (see: https://gateway-api.sigs.k8s.io/guides/)
httpRoute:
# HTTPRoute enabled.
enabled: false
# HTTPRoute annotations.
annotations: {}
# Which Gateways this Route is attached to.
parentRefs:
- name: gateway
sectionName: http
# namespace: default
# Hostnames matching HTTP header.
hostnames:
- chart-example.local
# List of rules and filters applied.
rules:
- matches:
- path:
type: PathPrefix
value: /headers
# filters:
# - type: RequestHeaderModifier
# requestHeaderModifier:
# set:
# - name: My-Overwrite-Header
# value: this-is-the-only-value
# remove:
# - User-Agent
# - matches:
# - path:
# type: PathPrefix
# value: /echo
# headers:
# - name: version
# value: v2
resources: {}
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
livenessProbe:
httpGet:
path: /
port: http
readinessProbe:
httpGet:
path: /
port: http
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 100
targetCPUUtilizationPercentage: 80
# volumes: {}
# volumeMounts: {}
# -- `spec.PersitentVolumeClaim` configuration
persistentVolumeClaim:
# -- set to true to use pvc
enabled: true
# -- specify an existing `PersistentVolumeClaim` to use
# existingClaim: ""
# -- Annotations for the `PersitentVolumeClaim`
annotations: {}
accessModes:
- ReadWriteOnce
storageClass: ""
# -- volume claim size
size: "500Mi"
# -- hostname of pod
hostname: ""
# -- should the container use host network
hostNetwork: "false"
nodeSelector: {}
tolerations: []
affinity: {}