Initial commit: imcu.ro HA k3s cluster manifests

- 3-node HA cluster (k3s v1.36.3+k3s1, embedded etcd) on Hetzner
- Netbird VPN mesh (wt0) for all cluster traffic
- Longhorn distributed storage (2 replicas, /var/lib/longhorn/)
- Traefik ingress (3 replicas, LoadBalancer on all node IPs)
- cert-manager with Let's Encrypt (auto-renewing TLS)
- Gitea (git + container registry) with SQLite on Longhorn
- 4 migrated workloads: wolsey, school-games (3 variants), randomly
- All HelmCharts and workload manifests as YAML
- deploy.sh for one-shot cluster setup
This commit is contained in:
Mihai Cimpoesu
2026-08-16 21:34:36 +03:00
commit 703c229dd2
16 changed files with 1439 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
# Sensitive files - never commit
*.env
.env
secrets/
*.key
*.pem
# OS
.DS_Store
+326
View File
@@ -0,0 +1,326 @@
# Agents & Services
Operational reference for all workloads and system services in the imcu.ro cluster.
## System Services
### Netbird VPN
| Property | Value |
|---|---|
| Purpose | WireGuard mesh connecting all k3s nodes |
| Control plane | 167.233.140.254 (netbird.imcu.ro) |
| Interface | wt0 |
| Client version | v0.77.0 |
| Setup key | `E5496A84-6B50-4B13-943A-102B36D5D190` (reusable) |
| Management URL | https://netbird.imcu.ro:443 |
| Local auth | Embedded Dex IdP, password auth |
| TLS | Let's Encrypt |
**Per-node commands:**
```bash
netbird status # show connection state
netbird up --management-url https://netbird.imcu.ro:443 --hostname <hostname> --disable-client-routes --disable-server-routes --disable-dns
netbird down
```
**VPN IPs:**
- k3s-fi-01: 100.121.136.204
- k3s-de-01: 100.121.191.170
- k3s-de-02: 100.121.49.137
### k3s
| Property | Value |
|---|---|
| Version | v1.36.3+k3s1 |
| Mode | HA (embedded etcd, 3 control-plane nodes) |
| Data dir | /var/lib/rancher/k3s |
| Kubeconfig | /etc/rancher/k3s/k3s.yaml |
| Binaries | k3s, kubectl (symlink), crictl (symlink), ctr (symlink) |
| Service | systemd `k3s.service` |
**Useful commands:**
```bash
k3s kubectl get nodes
k3s kubectl get pods -A
systemctl status k3s
journalctl -u k3s -f
```
### Longhorn
| Property | Value |
|---|---|
| Version | v1.12.1 |
| Namespace | longhorn-system |
| Storage class | longhorn (default) |
| Default replicas | 2 |
| Disk path | /var/lib/longhorn/ |
| UI | ClusterIP 10.43.255.96:80 (SSH tunnel only) |
| Managed via | k3s HelmChart in kube-system |
**HelmChart values:**
```yaml
defaultSettings:
defaultDataPath: /var/lib/longhorn/
defaultReplicaCount: 2
persistence:
defaultClass: true
defaultClassReplicaCount: 2
```
**Accessing the UI:**
```bash
ssh -L 8080:10.43.255.96:80 root@89.167.46.12
# Open http://localhost:8080
```
**Adding a disk to a node:**
1. Attach new volume in Hetzner Cloud
2. Mount it: `mount /dev/disk/by-id/scsi-<id> /mnt/longhorn-disk2`
3. Add to `/etc/fstab` for persistence
4. In Longhorn UI: Node > Edit > Add Disk (path `/mnt/longhorn-disk2`)
### Traefik
| Property | Value |
|---|---|
| Namespace | kube-system |
| Replicas | 3 (one per node) |
| Service type | LoadBalancer (external IPs: all 3 node public IPs) |
| IngressClass | traefik (default) |
| Managed via | k3s HelmChart in kube-system |
**Ports:**
- 80/TCP (HTTP, redirects to HTTPS)
- 443/TCP (HTTPS)
### cert-manager
| Property | Value |
|---|---|
| Version | v1.18.2 |
| Namespace | cert-manager |
| ClusterIssuer | letsencrypt-prod |
| ACME server | https://acme-v02.api.letsencrypt.org/directory |
| Email | admin@imcu.ro |
| Challenge | HTTP-01 via Traefik ingress |
| Managed via | k3s HelmChart in kube-system |
**All ingresses must have:**
```yaml
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
```
**Check cert status:**
```bash
k3s kubectl get certificate -A
k3s kubectl describe certificate <name> -n <namespace>
```
## Application Workloads
### Gitea
| Property | Value |
|---|---|
| Namespace | gitea |
| Image | gitea/gitea:1.27.2-rootless |
| URL | https://gitea.imcu.ro |
| SSH | gitea.imcu.ro:30022 (NodePort) |
| Database | SQLite (on Longhorn PVC) |
| Storage | 10 Gi Longhorn PVC `gitea-data` |
| Admin user | mcimpoesu (mihai@imcu.ro) |
| Registry | Built-in (enabled) |
| Packages | Enabled |
**Container registry:**
```bash
docker login gitea.imcu.ro
# Username: mcimpoesu
# Password: <Gitea access token>
docker push gitea.imcu.ro/homeschool/<image>:<tag>
```
**Image repositories in Gitea:**
- homeschool/games (tags: 2a4f03df, 7d3a57d)
- homeschool/school (tag: e76d244)
- homeschool/school-games (tag: 20260112T132336)
- homeschool/randomly (tag: latest)
**Create access token:**
```bash
k3s kubectl exec -n gitea deploy/gitea -- gitea admin user generate-access-token \
--username mcimpoesu --token-name "<name>" --scopes "read:repository,write:package,read:package"
```
**Gitea config location:** `/etc/gitea/app.ini` inside the container (mounted from PVC `config` subPath).
### Wolsey
| Property | Value |
|---|---|
| Namespace | default |
| Image | gitea.imcu.ro/homeschool/school:e76d244 |
| URL | https://wolsey.imcu.ro |
| Storage | 10 Gi Longhorn PVC `wolsey-content` (mounted at /content, readOnly) |
| Content size | 6.5 GB (markdown-based educational content) |
| Port | 3000 |
**Environment:**
- PORT=3000
- HOSTNAME=0.0.0.0
- NODE_ENV=production
- CONTENT_ROOT=/content
### School Games (production)
| Property | Value |
|---|---|
| Namespace | games |
| Image | gitea.imcu.ro/homeschool/school-games:20260112T132336 |
| URL | https://school.imcu.ro |
| Port | 3000 |
### School Games (develop)
| Property | Value |
|---|---|
| Namespace | default |
| Image | gitea.imcu.ro/homeschool/games:7d3a57d |
| URL | https://develop.imcu.ro |
| Port | 3000 |
### School Games (default)
| Property | Value |
|---|---|
| Namespace | default |
| Image | gitea.imcu.ro/homeschool/games:2a4f03df |
| URL | none (internal service only) |
| Port | 3000 |
### Randomly
| Property | Value |
|---|---|
| Namespace | randomly |
| Image | gitea.imcu.ro/homeschool/randomly:latest |
| URL | https://randomly.imcu.ro |
| Port | 3000 |
**Environment:**
- PORT=3000
- HOSTNAME=0.0.0.0
- NODE_ENV=production
## Image Pull Secrets
All namespaces pulling from Gitea registry use secret `gitea-registry`:
```bash
k3s kubectl create secret docker-registry gitea-registry \
--namespace=<ns> \
--docker-server=gitea.imcu.ro \
--docker-username=mcimpoesu \
--docker-password=<token> \
--docker-email=mihai@imcu.ro
```
Present in namespaces: `default`, `games`, `randomly`.
## School (decommissioned)
| Property | Value |
|---|---|
| IP | 157.90.228.70 |
| Hostname | school |
| k3s | v1.36.3+k3s1 (still running, system pods only) |
| Status | All workloads migrated, data preserved as backup |
**Backup data on school:**
- `/opt/gitea/` — 255 MB (Gitea data)
- `/opt/registry-data/` — 411 MB (old standalone registry)
- `/mnt/HC_Volume_106055137/school-content/` — 6.5 GB (Wolsey content)
School is reachable via SSH from all k3s nodes (public IP) but is not on the VPN.
## Cloudflare DNS
| Property | Value |
|---|---|
| Zone | imcu.ro |
| Zone ID | 0fca28c6b103c6a730f5c34de3543503 |
| Nameservers | neil.ns.cloudflare.com, may.ns.cloudflare.com |
| API token | Stored in environment as CF_API_TOKEN (Zone:DNS:Edit permission) |
**Manage DNS via API:**
```bash
export CF_API_TOKEN="<token>"
# List records
curl -s -X GET "https://api.cloudflare.com/client/v4/zones/0fca28c6b103c6a730f5c34de3543503/dns_records?per_page=100" \
-H "Authorization: Bearer $CF_API_TOKEN" | python3 -m json.tool
```
## Common Operations
### Deploy a new service
1. Create namespace if needed: `k3s kubectl create ns <name>`
2. Create image pull secret if pulling from Gitea registry
3. Apply deployment + service manifest
4. Apply ingress with cert-manager annotation:
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: traefik
tls:
- hosts: [<domain>]
secretName: <domain>-tls
```
5. Add DNS A records (3x, one per node IP) via Cloudflare API
### Add a new node to the cluster
1. Provision Hetzner server with Ubuntu
2. Install Netbird: `curl -fsSL https://pkgs.netbird.io/install.sh | sh`
3. Enroll: `netbird login --management-url https://netbird.imcu.ro:443 --setup-key E5496A84-6B50-4B13-943A-102B36D5D190 --hostname <name>`
4. Bring up tunnel: `netbird up --management-url https://netbird.imcu.ro:443 --hostname <name> --disable-client-routes --disable-server-routes --disable-dns`
5. Mount Hetzner volume at `/var/lib/longhorn`, update fstab
6. Install k3s:
```bash
curl -sfL https://get.k3s.io | sh -s - server \
--server https://100.121.136.204:6443 \
--token <token-from-/var/lib/rancher/k3s/server/node-token> \
--node-ip <vpn-ip> \
--node-external-ip <public-ip> \
--flannel-iface wt0 \
--disable traefik
```
7. Distribute SSH keys
### Back up Gitea
```bash
# Dump Gitea data
k3s kubectl exec -n gitea deploy/gitea -- gitea dump -c /etc/gitea/app.ini
# Copy dump out
k3s kubectl cp gitea/<pod>:/data/gitea-dump-*.zip ./gitea-backup.zip
```
### Update a deployment image
```bash
# Push new image to Gitea registry
docker push gitea.imcu.ro/homeschool/<image>:<new-tag>
# Update deployment
k3s kubectl set image deploy/<name> <container>=gitea.imcu.ro/homeschool/<image>:<new-tag> -n <namespace>
```
+248
View File
@@ -0,0 +1,248 @@
# imcu.ro HA Kubernetes Cluster
3-node highly available Kubernetes cluster running on Hetzner Cloud, connected via Netbird VPN mesh, with distributed storage via Longhorn.
## Cluster Nodes
| Node | Public IP | VPN IP (wt0) | Location | Volume |
|---|---|---|---|---|
| k3s-fi-01 | 89.167.46.12 | 100.121.136.204 | Helsinki, FI | scsi-0HC_Volume_106631386 |
| k3s-de-01 | 91.99.15.157 | 100.121.191.170 | Falkenstein, DE | scsi-0HC_Volume_106631392 |
| k3s-de-02 | 2.28.55.101 | 100.121.49.137 | Falkenstein, DE | scsi-0HC_Volume_106631403 |
All nodes: Ubuntu 26.04 LTS, 2 vCPU, 3.7 Gi RAM, 100 GB Hetzner volume (mounted at `/var/lib/longhorn`).
## Network Topology
```
Internet
|
Cloudflare DNS
(imcu.ro zone)
|
+---------------+---------------+
| | |
89.167.46.12 91.99.15.157 2.28.55.101
k3s-fi-01 k3s-de-01 k3s-de-02
| | |
+------- wt0 (Netbird VPN) -----+
100.121.x.x mesh
|
k3s control plane
(embedded etcd, HA)
```
- **All k3s traffic** (API server, etcd, flannel, node communication) runs over the Netbird VPN (`wt0` interface).
- **Public IPs** are only used for: ingress traffic (Traefik LoadBalancer), Gitea SSH (NodePort 30022), and SSH admin access.
- **Netbird control plane** runs on `167.233.140.254` at `netbird.imcu.ro`.
## Core Components
| Component | Version | Purpose |
|---|---|---|
| k3s | v1.36.3+k3s1 | Kubernetes distribution with embedded etcd (HA) |
| Longhorn | v1.12.1 | Distributed block storage (2 replicas, `/var/lib/longhorn/`) |
| Traefik | 3.x | Ingress controller (3 replicas, LoadBalancer on all node IPs) |
| cert-manager | v1.18.2 | TLS certificate management (Let's Encrypt, auto-renewing) |
All three are installed as k3s-native `HelmChart` resources in `kube-system` namespace.
## Storage
- **Longhorn** is the default StorageClass (`longhorn`).
- Each node contributes its 100 GB Hetzner volume as a Longhorn disk at `/var/lib/longhorn/`.
- Volumes are replicated across **2 of 3 nodes** (configurable per-PVC).
- To add more disks: mount a new volume at `/mnt/longhorn-disk2`, add it via Longhorn UI.
### Persistent Volumes
| PVC | Namespace | Size | Content |
|---|---|---|---|
| gitea-data | gitea | 10 Gi | Gitea SQLite DB, git repos, packages |
| wolsey-content | default | 10 Gi | Wolsey static content (6.5 GB) |
## Workloads
| Service | URL | Namespace | Image | Storage |
|---|---|---|---|---|
| Gitea | https://gitea.imcu.ro | gitea | gitea/gitea:1.27.2-rootless | 10 GB Longhorn |
| Gitea SSH | gitea.imcu.ro:30022 | gitea | (same pod) | NodePort 30022 |
| Gitea Registry | gitea.imcu.ro/v2 | gitea | (built-in) | — |
| Wolsey | https://wolsey.imcu.ro | default | homeschool/school:e76d244 | 10 GB Longhorn |
| School Games (prod) | https://school.imcu.ro | games | homeschool/school-games:20260112T132336 | — |
| School Games (dev) | https://develop.imcu.ro | default | homeschool/games:7d3a57d | — |
| School Games (default) | — | default | homeschool/games:2a4f03df | — |
| Randomly | https://randomly.imcu.ro | randomly | homeschool/randomly:latest | — |
## DNS (Cloudflare)
All A records point to all 3 node public IPs (round-robin):
| Record | Type | Value |
|---|---|---|
| gitea.imcu.ro | A x3 | 89.167.46.12, 91.99.15.157, 2.28.55.101 |
| school.imcu.ro | A x3 | 89.167.46.12, 91.99.15.157, 2.28.55.101 |
| wolsey.imcu.ro | A x3 | 89.167.46.12, 91.99.15.157, 2.28.55.101 |
| develop.imcu.ro | A x3 | 89.167.46.12, 91.99.15.157, 2.28.55.101 |
| randomly.imcu.ro | A x3 | 89.167.46.12, 91.99.15.157, 2.28.55.101 |
| *.imcu.ro | A x3 | 89.167.46.12, 91.99.15.157, 2.28.55.101 |
| netbird.imcu.ro | A | 167.233.140.254 |
## TLS Certificates
All managed by cert-manager via `letsencrypt-prod` ClusterIssuer (HTTP-01 challenge via Traefik). Auto-renewed 30 days before expiry.
| Domain | Secret | Namespace |
|---|---|---|
| gitea.imcu.ro | gitea-tls | gitea |
| wolsey.imcu.ro | wolsey-imcu-ro-tls | default |
| school.imcu.ro | school-imcu-ro-tls | games |
| develop.imcu.ro | develop-imcu-ro-tls | default |
| randomly.imcu.ro | randomly-imcu-ro-tls | randomly |
## Admin Access
### SSH
All k3s nodes have SSH keys distributed. From any node you can SSH to any other using VPN IPs:
```
ssh k3s-fi-01 # 100.121.136.204
ssh k3s-de-01 # 100.121.191.170
ssh k3s-de-02 # 100.121.49.137
ssh school # 157.90.228.70 (old server, public IP)
```
### kubectl
```bash
ssh root@89.167.46.12
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
k3s kubectl get nodes
```
### Longhorn UI
SSH tunnel (UI is not publicly exposed):
```bash
ssh -L 8080:10.43.255.96:80 root@89.167.46.12
# Open http://localhost:8080
```
### Gitea
- Web: https://gitea.imcu.ro
- SSH: `git clone ssh://git@gitea.imcu.ro:30022/homeschool/repo.git`
- Registry: `docker login gitea.imcu.ro` (use Gitea access token as password)
## Old Server (school)
`157.90.228.70` (hostname `school`) is the previous single-node k3s cluster. All workloads have been migrated to the HA cluster. It still runs only system pods (kube-system, cert-manager) and preserves original data as backup:
| Path | Size | Content |
|---|---|---|
| /opt/gitea/ | 255 MB | Gitea data backup |
| /opt/registry-data/ | 411 MB | Old standalone registry |
| /mnt/HC_Volume_106055137/school-content/ | 6.5 GB | Wolsey content backup |
Can be decommissioned or repurposed once the migration is confirmed stable.
## Bootstrap (reference)
The cluster was built in this order:
1. Netbird client installed on all 3 nodes, enrolled with setup key, tunnels up on `wt0`
2. Hetzner volumes mounted at `/var/lib/longhorn` on all 3 nodes (fstab updated)
3. k3s installed with `--cluster-init` on k3s-fi-01, then joined k3s-de-01 and k3s-de-02
4. Longhorn installed via k3s HelmChart (2 replicas, default data path `/var/lib/longhorn/`)
5. Traefik installed via k3s HelmChart (3 replicas, LoadBalancer)
6. cert-manager installed via k3s HelmChart, ClusterIssuer created
7. Gitea deployed, data migrated from school via tar over SSH
8. All other workloads migrated from school, images pushed to Gitea registry
### k3s install flags
**Node 1 (cluster-init):**
```bash
curl -sfL https://get.k3s.io | sh -s - server \
--cluster-init \
--node-ip 100.121.136.204 \
--node-external-ip 89.167.46.12 \
--flannel-iface wt0 \
--advertise-address 100.121.136.204 \
--tls-san netbird.imcu.ro \
--tls-san 100.121.136.204 \
--tls-san 100.121.191.170 \
--tls-san 100.121.49.137 \
--disable traefik
```
**Nodes 2 & 3 (join):**
```bash
curl -sfL https://get.k3s.io | sh -s - server \
--server https://100.121.136.204:6443 \
--token <node-token> \
--node-ip <vpn-ip> \
--node-external-ip <public-ip> \
--flannel-iface wt0 \
--disable traefik
```
## SSH Key Mesh
All k3s nodes have ed25519 SSH keys distributed to each other and to school. Config at `/root/.ssh/config` on each node uses VPN IPs for k3s nodes and public IP for school.
```
Host k3s-fi-01 HostName 100.121.136.204
Host k3s-de-01 HostName 100.121.191.170
Host k3s-de-02 HostName 100.121.49.137
Host school HostName 157.90.228.70
```
## Repository Structure (GitOps)
This repo lives at `gitea.imcu.ro/cluster/k3s-cluster`. It contains all manifests needed to recreate the cluster from scratch.
```
k3s-cluster/
├── README.md # This file (architecture overview)
├── AGENTS.md # Operational reference (per-service details)
├── deploy.sh # Apply all manifests to the cluster
├── .gitignore
├── helmcharts/ # k3s HelmChart resources (system components)
│ ├── traefik.yaml # Ingress controller (3 replicas, LoadBalancer)
│ ├── cert-manager.yaml # TLS certificate manager (Let's Encrypt)
│ └── longhorn.yaml # Distributed storage (2 replicas, /var/lib/longhorn/)
├── cluster-config/ # Cluster-wide configuration
│ ├── namespaces.yaml # Application namespaces (gitea, games, randomly)
│ ├── clusterissuer.yaml # Let's Encrypt ClusterIssuer
│ └── image-pull-secret-template.txt # How to create gitea-registry secrets
└── workloads/ # Application manifests (deploy + svc + ingress + pvc)
├── gitea/gitea.yaml
├── wolsey/wolsey.yaml
├── school-games-prod/school-games-prod.yaml
├── school-games-dev/school-games-dev.yaml
├── school-games-default/school-games-default.yaml
└── randomly/randomly.yaml
```
### How to update a service
1. Edit the manifest in `workloads/<service>/<service>.yaml`
2. Apply: `k3s kubectl apply -f workloads/<service>/<service>.yaml`
3. Commit and push to keep the repo in sync
### How to update a HelmChart (system component)
1. Edit the manifest in `helmcharts/<component>.yaml` (e.g. bump `version:` for cert-manager)
2. Apply: `k3s kubectl apply -f helmcharts/<component>.yaml`
3. k3s HelmChart controller will automatically reconcile the change
4. Commit and push
### How to deploy from scratch
```bash
# From any k3s node:
KUBECONFIG=/etc/rancher/k3s/k3s.yaml ./deploy.sh
```
+14
View File
@@ -0,0 +1,14 @@
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
email: admin@imcu.ro
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: traefik
@@ -0,0 +1,17 @@
# Image Pull Secret Template
#
# Create this secret in each namespace that pulls from the Gitea registry.
# Replace <TOKEN> with a valid Gitea access token (scopes: read:repository, read:package, write:package).
#
# kubectl create secret docker-registry gitea-registry \
# --namespace=<namespace> \
# --docker-server=gitea.imcu.ro \
# --docker-username=mcimpoesu \
# --docker-password=<TOKEN> \
# --docker-email=mihai@imcu.ro
#
# Present in namespaces: default, games, randomly, gitea (if needed)
#
# To generate a new Gitea token:
# kubectl exec -n gitea deploy/gitea -- gitea admin user generate-access-token \
# --username mcimpoesu --token-name "<name>" --scopes "read:repository,write:package,read:package"
+14
View File
@@ -0,0 +1,14 @@
apiVersion: v1
kind: Namespace
metadata:
name: gitea
---
apiVersion: v1
kind: Namespace
metadata:
name: games
---
apiVersion: v1
kind: Namespace
metadata:
name: randomly
Executable
+37
View File
@@ -0,0 +1,37 @@
#!/bin/bash
# Deploy script - applies all manifests to the cluster
# Usage: SSH to k3s-fi-01 (or any node), then run:
# KUBECONFIG=/etc/rancher/k3s/k3s.yaml ./deploy.sh
#
# Or from local machine with kubectl configured:
# ./deploy.sh
set -e
KUBECTL="${KUBECTL:-k3s kubectl}"
if ! command -v k3s &>/dev/null; then
KUBECTL="kubectl"
fi
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
echo "=== Applying namespaces ==="
$KUBECTL apply -f "$DIR/cluster-config/namespaces.yaml"
echo "=== Applying cluster config ==="
$KUBECTL apply -f "$DIR/cluster-config/clusterissuer.yaml"
echo "=== Applying HelmCharts ==="
$KUBECTL apply -f "$DIR/helmcharts/"
echo "=== Applying workloads ==="
for f in "$DIR"/workloads/*/*.yaml; do
echo " -> $(basename "$f")"
$KUBECTL apply -f "$f"
done
echo ""
echo "=== Done. Check status with: ==="
echo " $KUBECTL get pods -A"
echo " $KUBECTL get ingress -A"
echo " $KUBECTL get certificate -A"
+14
View File
@@ -0,0 +1,14 @@
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
name: cert-manager
namespace: kube-system
spec:
repo: https://charts.jetstack.io
chart: cert-manager
version: v1.18.2
targetNamespace: cert-manager
createNamespace: true
valuesContent: |-
crds:
enabled: true
+17
View File
@@ -0,0 +1,17 @@
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
name: longhorn
namespace: kube-system
spec:
repo: https://charts.longhorn.io
chart: longhorn
targetNamespace: longhorn-system
createNamespace: true
valuesContent: |-
defaultSettings:
defaultDataPath: /var/lib/longhorn/
defaultReplicaCount: 2
persistence:
defaultClass: true
defaultClassReplicaCount: 2
+17
View File
@@ -0,0 +1,17 @@
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
name: traefik
namespace: kube-system
spec:
repo: https://traefik.github.io/charts
chart: traefik
targetNamespace: kube-system
valuesContent: |-
deployment:
replicas: 3
service:
type: LoadBalancer
ingressClass:
enabled: true
isDefaultClass: true
+203
View File
@@ -0,0 +1,203 @@
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: gitea
name: gitea
namespace: gitea
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
app: gitea
strategy:
type: Recreate
template:
metadata:
labels:
app: gitea
spec:
containers:
- env:
- name: GITEA__server__DOMAIN
value: gitea.imcu.ro
- name: GITEA__server__SSH_DOMAIN
value: gitea.imcu.ro
- name: GITEA__server__ROOT_URL
value: https://gitea.imcu.ro/
- name: GITEA__server__SSH_PORT
value: '30022'
- name: GITEA__server__SSH_LISTEN_PORT
value: '2222'
- name: GITEA__database__DB_TYPE
value: sqlite3
- name: GITEA__packages__ENABLED
value: 'true'
- name: GITEA__actions__ENABLED
value: 'true'
- name: GITEA__security__INSTALL_LOCK
value: 'true'
- name: GITEA__service__DISABLE_REGISTRATION
value: 'true'
- name: GITEA__service__REQUIRE_SIGNIN_VIEW
value: 'false'
- name: GITEA__service__DEFAULT_ALLOW_CREATE_ORGANIZATION
value: 'true'
- name: GITEA__service__SHOW_REGISTRATION_BUTTON
value: 'false'
- name: GITEA__service__ENABLE_CAPTCHA
value: 'true'
- name: GITEA__service__EMAIL_DOMAIN_ALLOWLIST
value: imcu.ro,danubian.io,gmail.com
- name: GITEA__admin__DEFAULT_EMAIL_NOTIFICATIONS
value: disabled
- name: GITEA__oauth2__ENABLED
value: 'true'
image: gitea/gitea:1.27.2-rootless
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 3
httpGet:
path: /api/healthz
port: 3000
scheme: HTTP
initialDelaySeconds: 60
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
name: gitea
ports:
- containerPort: 3000
name: http
protocol: TCP
- containerPort: 2222
name: ssh
protocol: TCP
readinessProbe:
failureThreshold: 3
httpGet:
path: /api/healthz
port: 3000
scheme: HTTP
initialDelaySeconds: 30
periodSeconds: 5
successThreshold: 1
timeoutSeconds: 1
resources:
limits:
cpu: '1'
memory: 1Gi
requests:
cpu: 100m
memory: 256Mi
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/lib/gitea
name: gitea-data
- mountPath: /etc/gitea
name: gitea-data
subPath: config
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
volumes:
- name: gitea-data
persistentVolumeClaim:
claimName: gitea-data
---
apiVersion: v1
kind: Service
metadata:
name: gitea
namespace: gitea
spec:
clusterIP: 10.43.169.153
clusterIPs:
- 10.43.169.153
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- name: http
port: 3000
protocol: TCP
targetPort: 3000
- name: ssh
port: 2222
protocol: TCP
targetPort: 2222
selector:
app: gitea
sessionAffinity: None
type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
name: gitea-ssh
namespace: gitea
spec:
clusterIP: 10.43.220.233
clusterIPs:
- 10.43.220.233
externalTrafficPolicy: Cluster
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- name: ssh
nodePort: 30022
port: 2222
protocol: TCP
targetPort: 2222
selector:
app: gitea
sessionAffinity: None
type: NodePort
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
finalizers:
- kubernetes.io/pvc-protection
name: gitea-data
namespace: gitea
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: longhorn
volumeMode: Filesystem
volumeName: pvc-6949d930-c76f-4bd8-9bd1-13fbad019d37
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gitea
namespace: gitea
spec:
ingressClassName: traefik
rules:
- host: gitea.imcu.ro
http:
paths:
- backend:
service:
name: gitea
port:
number: 3000
path: /
pathType: Prefix
tls:
- hosts:
- gitea.imcu.ro
secretName: gitea-tls
+119
View File
@@ -0,0 +1,119 @@
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: randomly
name: randomly
namespace: randomly
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
app: randomly
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
labels:
app: randomly
spec:
containers:
- env:
- name: PORT
value: '3000'
- name: HOSTNAME
value: 0.0.0.0
- name: NODE_ENV
value: production
image: gitea.imcu.ro/homeschool/randomly:latest
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 3
httpGet:
path: /
port: 3000
scheme: HTTP
initialDelaySeconds: 15
periodSeconds: 20
successThreshold: 1
timeoutSeconds: 1
name: randomly
ports:
- containerPort: 3000
protocol: TCP
readinessProbe:
failureThreshold: 3
httpGet:
path: /
port: 3000
scheme: HTTP
initialDelaySeconds: 5
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 50m
memory: 128Mi
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
imagePullSecrets:
- name: gitea-registry
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
---
apiVersion: v1
kind: Service
metadata:
name: randomly-service
namespace: randomly
spec:
clusterIP: 10.43.57.214
clusterIPs:
- 10.43.57.214
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- port: 80
protocol: TCP
targetPort: 3000
selector:
app: randomly
sessionAffinity: None
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: randomly-ingress
namespace: randomly
spec:
ingressClassName: traefik
rules:
- host: randomly.imcu.ro
http:
paths:
- backend:
service:
name: randomly-service
port:
number: 80
path: /
pathType: Prefix
tls:
- hosts:
- randomly.imcu.ro
secretName: randomly-imcu-ro-tls
@@ -0,0 +1,66 @@
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: school-games
name: school-games
namespace: default
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
app: school-games
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
labels:
app: school-games
spec:
containers:
- env:
- name: PORT
value: '3000'
image: gitea.imcu.ro/homeschool/games:2a4f03df7e2e43af116a033e4cca55bbb3b30d89
imagePullPolicy: IfNotPresent
name: school-games
ports:
- containerPort: 3000
protocol: TCP
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
imagePullSecrets:
- name: gitea-registry
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
---
apiVersion: v1
kind: Service
metadata:
name: school-games-service
namespace: default
spec:
clusterIP: 10.43.59.103
clusterIPs:
- 10.43.59.103
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- port: 80
protocol: TCP
targetPort: 3000
selector:
app: school-games
sessionAffinity: None
type: ClusterIP
@@ -0,0 +1,96 @@
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: school-games-develop
name: school-games-develop
namespace: default
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
app: school-games-develop
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
labels:
app: school-games-develop
spec:
containers:
- env:
- name: PORT
value: '3000'
image: gitea.imcu.ro/homeschool/games:7d3a57d7fbc4939763b05dc37e6e146ab6e14a26
imagePullPolicy: IfNotPresent
name: school-games
ports:
- containerPort: 3000
protocol: TCP
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
imagePullSecrets:
- name: gitea-registry
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
---
apiVersion: v1
kind: Service
metadata:
name: school-games-develop-service
namespace: default
spec:
clusterIP: 10.43.92.195
clusterIPs:
- 10.43.92.195
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- port: 80
protocol: TCP
targetPort: 3000
selector:
app: school-games-develop
sessionAffinity: None
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: school-games-develop-ingress
namespace: default
spec:
ingressClassName: traefik
rules:
- host: develop.imcu.ro
http:
paths:
- backend:
service:
name: school-games-develop-service
port:
number: 80
path: /games
pathType: Prefix
- backend:
service:
name: school-games-develop-service
port:
number: 80
path: /
pathType: Prefix
tls:
- hosts:
- develop.imcu.ro
secretName: develop-imcu-ro-tls
@@ -0,0 +1,96 @@
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: school-games
name: school-games
namespace: games
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
app: school-games
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
labels:
app: school-games
spec:
containers:
- env:
- name: PORT
value: '3000'
image: gitea.imcu.ro/homeschool/school-games:20260112T132336
imagePullPolicy: IfNotPresent
name: school-games
ports:
- containerPort: 3000
protocol: TCP
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
imagePullSecrets:
- name: gitea-registry
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
---
apiVersion: v1
kind: Service
metadata:
name: school-games-service
namespace: games
spec:
clusterIP: 10.43.115.23
clusterIPs:
- 10.43.115.23
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- port: 80
protocol: TCP
targetPort: 3000
selector:
app: school-games
sessionAffinity: None
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: school-games-ingress
namespace: games
spec:
ingressClassName: traefik
rules:
- host: school.imcu.ro
http:
paths:
- backend:
service:
name: school-games-service
port:
number: 80
path: /games
pathType: Prefix
- backend:
service:
name: school-games-service
port:
number: 80
path: /
pathType: Exact
tls:
- hosts:
- school.imcu.ro
secretName: school-imcu-ro-tls
+146
View File
@@ -0,0 +1,146 @@
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: wolsey
name: wolsey
namespace: default
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
app: wolsey
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
labels:
app: wolsey
spec:
containers:
- env:
- name: PORT
value: '3000'
- name: HOSTNAME
value: 0.0.0.0
- name: NODE_ENV
value: production
- name: CONTENT_ROOT
value: /content
image: gitea.imcu.ro/homeschool/school:e76d244
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 3
httpGet:
path: /
port: 3000
scheme: HTTP
initialDelaySeconds: 15
periodSeconds: 20
successThreshold: 1
timeoutSeconds: 1
name: wolsey
ports:
- containerPort: 3000
protocol: TCP
readinessProbe:
failureThreshold: 3
httpGet:
path: /
port: 3000
scheme: HTTP
initialDelaySeconds: 5
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
resources:
limits:
cpu: '1'
memory: 768Mi
requests:
cpu: 50m
memory: 128Mi
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /content
name: content
readOnly: true
dnsPolicy: ClusterFirst
imagePullSecrets:
- name: gitea-registry
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
volumes:
- name: content
persistentVolumeClaim:
claimName: wolsey-content
---
apiVersion: v1
kind: Service
metadata:
name: wolsey-service
namespace: default
spec:
clusterIP: 10.43.31.159
clusterIPs:
- 10.43.31.159
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- port: 80
protocol: TCP
targetPort: 3000
selector:
app: wolsey
sessionAffinity: None
type: ClusterIP
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
finalizers:
- kubernetes.io/pvc-protection
name: wolsey-content
namespace: default
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: longhorn
volumeMode: Filesystem
volumeName: pvc-06d36938-7042-4bcf-aa28-a12e695e0779
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: wolsey-ingress
namespace: default
spec:
ingressClassName: traefik
rules:
- host: wolsey.imcu.ro
http:
paths:
- backend:
service:
name: wolsey-service
port:
number: 80
path: /
pathType: Prefix
tls:
- hosts:
- wolsey.imcu.ro
secretName: wolsey-imcu-ro-tls