working internal traefik
This commit is contained in:
parent
f537e5c9cb
commit
1ec6e3b383
@ -4,3 +4,8 @@ mac-worker Ready <none> 3h13m v1.34.6+k3s1 192.168.139.12 <n
|
|||||||
|
|
||||||
|
|
||||||
The mac-worker is running inside orbstack linux VM if that matters.
|
The mac-worker is running inside orbstack linux VM if that matters.
|
||||||
|
|
||||||
|
|
||||||
|
I have a DNS rewrite pointing *.internal to 10.0.1.250 which is traefik-internal.
|
||||||
|
|
||||||
|
/dogstore/ is a NFS path that's available to all nodes
|
||||||
|
|||||||
127
README.md
127
README.md
@ -1,41 +1,107 @@
|
|||||||
# Homelab — k3s Cluster
|
# Homelab — k3s Cluster
|
||||||
|
|
||||||
2-node k3s cluster (1 manager, 1 worker) running a self-hosted homelab stack.
|
2-node k3s cluster (1 manager, 1 worker) running a self-hosted homelab stack on `ratboo.me`.
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
|
### Nodes
|
||||||
|
|
||||||
|
| Node | Role | OS | IP | Runtime |
|
||||||
|
|------|------|----|----|---------|
|
||||||
|
| **dogbox** | control-plane | Fedora 40 Server | `10.0.1.2` | k3s server + containerd |
|
||||||
|
| **mac-worker** | worker | Ubuntu 25.10 (OrbStack VM) | `192.168.139.12` | k3s agent + containerd |
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
|
||||||
```
|
```
|
||||||
Manager Node Worker Node
|
Internet
|
||||||
┌──────────────┐ ┌──────────────┐
|
│
|
||||||
│ k3s server │────────────│ k3s agent │
|
Cloudflare DNS
|
||||||
│ Traefik │ │ │
|
*.ratboo.me
|
||||||
│ Longhorn │ │ Longhorn │
|
│
|
||||||
└──────┬───────┘ └──────┬───────┘
|
┌──────────────────────────┼──────────────────────────┐
|
||||||
|
│ dogbox (manager) │
|
||||||
|
│ Fedora 40 · 10.0.1.2 │
|
||||||
│ │
|
│ │
|
||||||
└───────── NFS ─────────────┘
|
│ ┌─────────────────┐ ┌──────────────────────┐ │
|
||||||
/dogstore
|
│ │ k3s server │ │ Traefik (k3s) │ │
|
||||||
|
│ │ control-plane │ │ :443 websecure │ │
|
||||||
|
│ └─────────────────┘ │ Let's Encrypt + CF │ │
|
||||||
|
│ └──────────┬───────────┘ │
|
||||||
|
│ ┌─────────────────┐ │ │
|
||||||
|
│ │ traefik-internal │ Routes to pods across │
|
||||||
|
│ │ :80 LB 10.0.1.250│ both nodes via CNI │
|
||||||
|
│ │ (MetalLB L2) │ │ │
|
||||||
|
│ └─────────────────┘ │ │
|
||||||
|
│ Longhorn │ │
|
||||||
|
└──────────────┬─────────────────────┼─────────────────┘
|
||||||
|
│ │
|
||||||
|
NFS /dogstore k3s cluster
|
||||||
|
│ │
|
||||||
|
┌──────────────┴─────────────────────┼─────────────────┐
|
||||||
|
│ mac-worker (worker) │
|
||||||
|
│ Ubuntu 25.10 · OrbStack VM │
|
||||||
|
│ 192.168.139.12 │
|
||||||
|
│ │
|
||||||
|
│ Longhorn · workload pods │
|
||||||
|
└──────────────────────────────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
**Storage strategy:**
|
### Networking
|
||||||
|
|
||||||
- `/dogstore` (NFS) — mounted on both nodes, used via hostPath for large media/data volumes
|
**Public ingress** — k3s bundles Traefik, configured via `HelmChartConfig` in `traefik-config`. TLS terminates at Traefik using Let's Encrypt with Cloudflare DNS-01 challenge. HTTP automatically redirects to HTTPS.
|
||||||
- Longhorn — replicated block storage for small config/state volumes
|
|
||||||
|
|
||||||
**Ingress:** k3s built-in Traefik with Let's Encrypt via Cloudflare DNS challenge.
|
| Public hostname | Service |
|
||||||
|
|-----------------|---------|
|
||||||
|
| `plex.ratboo.me` | Plex |
|
||||||
|
| `sonarr.ratboo.me` | Sonarr |
|
||||||
|
| `radarr.ratboo.me` | Radarr |
|
||||||
|
| `paperless.ratboo.me` | Paperless-ngx |
|
||||||
|
| `mealie.ratboo.me` | Mealie |
|
||||||
|
| `watch.ratboo.me` | Seerr |
|
||||||
|
|
||||||
**Secrets:** SOPS + age encryption. Secrets live in `secrets/secrets.enc.yaml`, encrypted at rest.
|
**Internal ingress** — A separate Traefik instance (`traefik-internal`) listens on `10.0.1.250:80`, served by MetalLB L2. A DNS rewrite points `*.internal` to that IP. Internal services use Traefik `IngressRoute` CRDs with `ingressClass: traefik-internal`.
|
||||||
|
|
||||||
|
| Internal hostname | Service |
|
||||||
|
|-------------------|---------|
|
||||||
|
| `homepage.rat` | Homepage |
|
||||||
|
| `glance.rat` | Glance |
|
||||||
|
|
||||||
|
**Cluster-only (no ingress):** Prowlarr, Bazarr, qBittorrent, Zerobyte.
|
||||||
|
|
||||||
|
### Storage
|
||||||
|
|
||||||
|
| Mechanism | Use |
|
||||||
|
|-----------|-----|
|
||||||
|
| **Longhorn** (`storageClass: longhorn`, replica count 2) | Small config/state PVCs — Traefik ACME (128Mi), app configs (1–20Gi), Paperless Postgres/Redis, Mealie data, Seerr, Zerobyte |
|
||||||
|
| **NFS via hostPath `/dogstore`** | Large/shared data — Plex media + transcode, Sonarr/Radarr/qBittorrent/unpackerr data trees, Paperless documents, Homepage/Glance configs |
|
||||||
|
|
||||||
|
### Secrets
|
||||||
|
|
||||||
|
SOPS + age encryption. All secrets live in `secrets/secrets.enc.yaml`, encrypted at rest. The age key lives at `/etc/sops/age/keys.txt` on each node. Referenced secrets include Cloudflare API tokens, database passwords, Plex claim tokens, and application API keys.
|
||||||
|
|
||||||
|
## Namespaces
|
||||||
|
|
||||||
|
| Namespace | Contents |
|
||||||
|
|-----------|----------|
|
||||||
|
| `kube-system` | k3s Traefik, `traefik-config` (HelmChartConfig + redirect middleware) |
|
||||||
|
| `longhorn-system` | Longhorn storage |
|
||||||
|
| `media` | Plex, Sonarr, Radarr, Bazarr, Prowlarr, qBittorrent, unpackerr |
|
||||||
|
| `paperless` | Paperless-ngx, Redis, PostgreSQL |
|
||||||
|
| `apps` | Mealie, Homepage, Glance, Seerr, Zerobyte |
|
||||||
|
|
||||||
## Services
|
## Services
|
||||||
|
|
||||||
|
| Chart | Namespace | Services | Notes |
|
||||||
| Chart | Namespace | Services |
|
|-------|-----------|----------|-------|
|
||||||
| -------------- | ----------- | -------------------------------------------------------------- |
|
| traefik-config | kube-system | Traefik HelmChartConfig overlay | Cloudflare DNS-01, ACME on Longhorn |
|
||||||
| traefik-config | kube-system | Traefik HelmChartConfig overlay |
|
| traefik-internal | — | Internal Traefik instance | LB via MetalLB at `10.0.1.250` |
|
||||||
| media | media | Plex, Sonarr, Radarr, Bazarr, Prowlarr, qBittorrent, unpackerr |
|
| metallb | — | MetalLB L2 pool | Single-IP pool for internal LB |
|
||||||
| paperless | paperless | Paperless-ngx, Redis, PostgreSQL |
|
| media | media | Plex, Sonarr, Radarr, Bazarr, Prowlarr, qBittorrent, unpackerr | Media stack with `/dogstore` data paths |
|
||||||
| mealie | apps | Mealie |
|
| paperless | paperless | Paperless-ngx, Redis, PostgreSQL | Postgres 15, Redis 7 |
|
||||||
| dashboards | apps | Homepage, Glance |
|
| mealie | apps | Mealie (v3.14.0) | Gemini API integration for recipes |
|
||||||
| utils | apps | Zerobyte, Seerr |
|
| dashboards | apps | Homepage, Glance | Internal-only via `traefik-internal` |
|
||||||
|
| utils | apps | Seerr, Zerobyte | Seerr public, Zerobyte cluster-only |
|
||||||
|
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
@ -136,16 +202,19 @@ curl -I https://mealie.ratboo.me
|
|||||||
```
|
```
|
||||||
homelab/
|
homelab/
|
||||||
├── README.md
|
├── README.md
|
||||||
|
├── AGENTS.md
|
||||||
├── .sops.yaml
|
├── .sops.yaml
|
||||||
├── scripts/
|
├── scripts/
|
||||||
│ └── bootstrap.sh
|
│ └── bootstrap.sh
|
||||||
├── charts/
|
├── charts/
|
||||||
│ ├── traefik-config/
|
│ ├── traefik-config/ # k3s Traefik overrides (HelmChartConfig)
|
||||||
│ ├── media/
|
│ ├── traefik-internal/ # Separate internal Traefik instance
|
||||||
│ ├── paperless/
|
│ ├── metallb/ # MetalLB L2 for internal LB IP
|
||||||
│ ├── mealie/
|
│ ├── media/ # Plex, *arr stack, qBittorrent, unpackerr
|
||||||
│ ├── dashboards/
|
│ ├── paperless/ # Paperless-ngx + Postgres + Redis
|
||||||
│ └── utils/
|
│ ├── mealie/ # Mealie recipe manager
|
||||||
|
│ ├── dashboards/ # Homepage + Glance (internal only)
|
||||||
|
│ └── utils/ # Seerr + Zerobyte
|
||||||
└── secrets/
|
└── secrets/
|
||||||
└── secrets.enc.yaml
|
└── secrets.enc.yaml
|
||||||
```
|
```
|
||||||
|
|||||||
@ -8,7 +8,7 @@ spec:
|
|||||||
entryPoints:
|
entryPoints:
|
||||||
- web
|
- web
|
||||||
routes:
|
routes:
|
||||||
- match: Host(`glance.internal`)
|
- match: Host(`glance.{{ .Values.internalDomain }}`)
|
||||||
kind: Rule
|
kind: Rule
|
||||||
services:
|
services:
|
||||||
- name: glance
|
- name: glance
|
||||||
|
|||||||
@ -17,10 +17,7 @@ spec:
|
|||||||
app: glance
|
app: glance
|
||||||
spec:
|
spec:
|
||||||
nodeSelector:
|
nodeSelector:
|
||||||
node-role.kubernetes.io/control-plane: "true"
|
homelab/node-role: worker
|
||||||
tolerations:
|
|
||||||
- key: node-role.kubernetes.io/control-plane
|
|
||||||
effect: NoSchedule
|
|
||||||
containers:
|
containers:
|
||||||
- name: glance
|
- name: glance
|
||||||
image: {{ .Values.glance.image }}
|
image: {{ .Values.glance.image }}
|
||||||
|
|||||||
@ -8,7 +8,7 @@ spec:
|
|||||||
entryPoints:
|
entryPoints:
|
||||||
- web
|
- web
|
||||||
routes:
|
routes:
|
||||||
- match: Host(`homepage.internal`)
|
- match: Host(`homepage.{{ .Values.internalDomain }}`)
|
||||||
kind: Rule
|
kind: Rule
|
||||||
services:
|
services:
|
||||||
- name: homepage
|
- name: homepage
|
||||||
|
|||||||
@ -17,10 +17,7 @@ spec:
|
|||||||
app: homepage
|
app: homepage
|
||||||
spec:
|
spec:
|
||||||
nodeSelector:
|
nodeSelector:
|
||||||
node-role.kubernetes.io/control-plane: "true"
|
homelab/node-role: worker
|
||||||
tolerations:
|
|
||||||
- key: node-role.kubernetes.io/control-plane
|
|
||||||
effect: NoSchedule
|
|
||||||
containers:
|
containers:
|
||||||
- name: homepage
|
- name: homepage
|
||||||
image: {{ .Values.homepage.image }}
|
image: {{ .Values.homepage.image }}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
domain: ratboo.me
|
domain: ratboo.me
|
||||||
|
internalDomain: dog
|
||||||
hostIp: "10.0.1.2"
|
hostIp: "10.0.1.2"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user