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