first commit

This commit is contained in:
2026-03-11 12:16:18 +01:00
commit eb46d77fe7
11 changed files with 412 additions and 0 deletions

21
.gitignore vendored Normal file
View File

@@ -0,0 +1,21 @@
# Secrets - never commit these
*.secret.yaml
*.secret.yml
secrets/
*.pem
*.key
# Local overrides
values.local.yaml
*.local.yaml
# Editor files
*.swp
*.swo
*~
.idea/
.vscode/
# OS files
.DS_Store
Thumbs.db

58
README.md Normal file
View File

@@ -0,0 +1,58 @@
# Kubernetes Configuration
Cluster configuration for the roamflow k3s cluster.
## Structure
```
kube-config/
├── apps/ # Application deployments
│ ├── headlamp/ # Kubernetes dashboard
│ └── ...
├── infrastructure/ # Cluster infrastructure
│ └── ...
└── scripts/ # Deployment scripts
└── deploy.sh
```
## Usage
### Deploy a specific app
```bash
./scripts/deploy.sh headlamp
```
### Deploy all apps
```bash
./scripts/deploy.sh --all
```
## Cluster Access
```bash
# Start SSH tunnel
ssh -f -N -L 6443:127.0.0.1:6443 roamflow-1
# Use kubectl
KUBECONFIG=~/.kube/config-roamflow kubectl get pods -A
```
## Apps
| App | Namespace | Description | URL |
|-----|-----------|-------------|-----|
| headlamp | headlamp | Kubernetes dashboard | headlamp.duylai.duckdns.org |
| gitea | gitea | Git hosting | gitea.duylai.duckdns.org |
| vikunja | vikunja | Task management | tasks.duylai.duckdns.org |
## Memory Configuration
The following memory limits have been adjusted to prevent OOM kills:
| Service | Limit | Request |
|---------|-------|---------|
| PostgreSQL (per pod) | 768Mi | 512Mi |
| Pgpool | 512Mi | 384Mi |
| Valkey | 192Mi | 128Mi |
These are saved in `apps/gitea/values.yaml` for future upgrades.

24
apps/gitea/ingress.yaml Normal file
View File

@@ -0,0 +1,24 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gitea-ingress
namespace: gitea
annotations:
kubernetes.io/ingress.class: traefik
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
rules:
- host: gitea.duylai.duckdns.org
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: gitea-http
port:
number: 3000
tls:
- hosts:
- gitea.duylai.duckdns.org
secretName: gitea-tls-secret

View File

@@ -0,0 +1,12 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: gitea
resources:
- namespace.yaml
- ingress.yaml
# Helm values are in values.yaml
# Deploy with:
# helm upgrade --install gitea gitea-charts/gitea -f values.yaml -n gitea --create-namespace

View File

@@ -0,0 +1,6 @@
apiVersion: v1
kind: Namespace
metadata:
name: gitea
labels:
name: gitea

83
apps/gitea/values.yaml Normal file
View File

@@ -0,0 +1,83 @@
# Gitea Helm Values
# Reference: https://gitea.com/gitea/helm-chart/
# Gitea server configuration
gitea:
config:
server:
DOMAIN: gitea.duylai.duckdns.org
ROOT_URL: https://gitea.duylai.duckdns.org/
SSH_DOMAIN: gitea.duylai.duckdns.org
SSH_LISTEN_PORT: 22
SSH_PORT: 30222
# Service configuration
service:
http:
type: ClusterIP
clusterIP: ""
ssh:
type: NodePort
nodePort: 30222
# Persistence for Git data
persistence:
enabled: true
create: true
claimName: gitea-shared-storage
size: 10Gi
accessModes:
- ReadWriteOnce
annotations:
helm.sh/resource-policy: keep
# PostgreSQL HA configuration
postgresql-ha:
enabled: true
postgresql:
# Number of PostgreSQL replicas (minimum 3 for HA)
replicaCount: 3
# Resource limits (fixed for OOM issues)
resources:
limits:
cpu: 375m
memory: 768Mi
requests:
cpu: 250m
memory: 512Mi
persistence:
size: 10Gi
pgpool:
enabled: true
# Resource limits (fixed for OOM issues)
resources:
limits:
cpu: 375m
memory: 512Mi
requests:
cpu: 250m
memory: 384Mi
# Valkey (Redis) cluster configuration
valkey-cluster:
cluster:
# Number of nodes (minimum 3 for cluster)
nodes: 3
# Resource limits
resources:
limits:
cpu: 150m
memory: 192Mi
requests:
cpu: 100m
memory: 128Mi
# Disable single PostgreSQL (using HA version)
postgresql:
enabled: false

View File

@@ -0,0 +1,26 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: headlamp-ingress
namespace: headlamp
annotations:
kubernetes.io/ingress.class: traefik
cert-manager.io/cluster-issuer: letsencrypt-prod
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: "true"
spec:
rules:
- host: headlamp.duylai.duckdns.org
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: headlamp
port:
number: 80
tls:
- hosts:
- headlamp.duylai.duckdns.org
secretName: headlamp-tls

View File

@@ -0,0 +1,11 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: headlamp
resources:
- namespace.yaml
- ingress.yaml
# Helm values are in values.yaml
# Use: helm upgrade --install headlamp headlamp/headlamp -f values.yaml -n headlamp

View File

@@ -0,0 +1,6 @@
apiVersion: v1
kind: Namespace
metadata:
name: headlamp
labels:
name: headlamp

43
apps/headlamp/values.yaml Normal file
View File

@@ -0,0 +1,43 @@
# Headlamp Helm Values
# Reference: https://artifacthub.io/packages/helm/headlamp/headlamp
replicaCount: 1
image:
repository: ghcr.io/headlamp-k8s/headlamp
tag: ""
pullPolicy: IfNotPresent
# Service configuration
service:
type: ClusterIP
port: 80
# Ingress managed via kustomize (see ingress.yaml)
ingress:
enabled: false
# Resources
resources:
limits:
cpu: 200m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi
# Persistent volume for plugins
persistentVolumeClaim:
enabled: false
# Service account for cluster access
serviceAccount:
create: true
annotations: {}
# RBAC - give Headlamp full cluster access
rbac:
create: true
clusterRole:
create: true
name: cluster-admin

122
scripts/deploy.sh Executable file
View File

@@ -0,0 +1,122 @@
#!/bin/bash
# Deploy applications to the Kubernetes cluster
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
KUBECONFIG="${KUBECONFIG:-$HOME/.kube/config-roamflow}"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Ensure SSH tunnel is running
ensure_tunnel() {
if ! pgrep -f "ssh.*6443.*roamflow-1" > /dev/null; then
log_info "Starting SSH tunnel..."
ssh -f -N -L 6443:127.0.0.1:6443 roamflow-1
sleep 1
fi
}
# Helm chart mappings: app -> (repo_name, repo_url, chart_name)
declare -A HELM_CHARTS=(
["headlamp"]="headlamp|https://headlamp-k8s.github.io/headlamp/|headlamp"
["gitea"]="gitea-charts|https://dl.gitea.com/charts/|gitea"
)
# Add Helm repositories
add_repos() {
log_info "Adding Helm repositories..."
for app in "${!HELM_CHARTS[@]}"; do
IFS='|' read -r repo_name repo_url chart_name <<< "${HELM_CHARTS[$app]}"
helm repo add "$repo_name" "$repo_url" 2>/dev/null || true
done
helm repo update
}
# Deploy a specific app
deploy_app() {
local app=$1
local app_dir="$PROJECT_ROOT/apps/$app"
if [[ ! -d "$app_dir" ]]; then
log_error "App '$app' not found in $PROJECT_ROOT/apps/"
exit 1
fi
log_info "Deploying $app..."
# Apply namespace and other kustomize resources first
if [[ -f "$app_dir/kustomization.yaml" ]]; then
kubectl apply -k "$app_dir" --kubeconfig "$KUBECONFIG"
fi
# Deploy with Helm if values.yaml exists
if [[ -f "$app_dir/values.yaml" ]]; then
if [[ -n "${HELM_CHARTS[$app]}" ]]; then
IFS='|' read -r repo_name repo_url chart_name <<< "${HELM_CHARTS[$app]}"
helm upgrade --install "$app" "$repo_name/$chart_name" \
-f "$app_dir/values.yaml" \
-n "$app" \
--kubeconfig "$KUBECONFIG" \
--create-namespace
else
log_warn "No Helm chart configured for $app, skipping Helm deployment"
fi
fi
log_info "$app deployed successfully!"
}
# List available apps
list_apps() {
echo "Available apps:"
for app_dir in "$PROJECT_ROOT/apps"/*/; do
if [[ -d "$app_dir" ]]; then
app_name=$(basename "$app_dir")
echo " - $app_name"
fi
done
}
# Main
main() {
ensure_tunnel
add_repos
if [[ "$1" == "--all" ]]; then
log_info "Deploying all apps..."
for app_dir in "$PROJECT_ROOT/apps"/*/; do
if [[ -d "$app_dir" ]]; then
app_name=$(basename "$app_dir")
deploy_app "$app_name"
fi
done
elif [[ "$1" == "--list" ]]; then
list_apps
elif [[ -n "$1" ]]; then
deploy_app "$1"
else
echo "Usage: $0 <app-name|--all|--list>"
list_apps
exit 1
fi
}
main "$@"