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:
@@ -0,0 +1,9 @@
|
|||||||
|
# Sensitive files - never commit
|
||||||
|
*.env
|
||||||
|
.env
|
||||||
|
secrets/
|
||||||
|
*.key
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
@@ -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>
|
||||||
|
```
|
||||||
@@ -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
|
||||||
|
```
|
||||||
@@ -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"
|
||||||
@@ -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
|
||||||
@@ -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"
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
Reference in New Issue
Block a user