refactor(k8s): Reorganize into 5-layer structure with production kustomizations
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
# k8s/cert-manager/cert-manager-values.yaml
|
||||
# cert-manager issues every per-hostname cert off homelab-ca and renews the
|
||||
# wildcard cert nginx serves — certmanager_certificate_expiration_timestamp_seconds
|
||||
# is the early-warning signal before any service's TLS breaks.
|
||||
|
||||
prometheus:
|
||||
enabled: true
|
||||
servicemonitor:
|
||||
enabled: true
|
||||
interval: 60s
|
||||
@@ -0,0 +1,5 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namespace: cert-manager
|
||||
resources: []
|
||||
# cert-manager deployed via ArgoCD Helm source (see layer-2-bootstrap app)
|
||||
@@ -0,0 +1,5 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namespace: kube-system
|
||||
resources: []
|
||||
# Cilium deployed via Helm chart
|
||||
@@ -0,0 +1,39 @@
|
||||
# k8s/cilium/l2-announcement-policy.yaml
|
||||
# CiliumL2AnnouncementPolicy — without this, LB-IPAM (lb-ipam-pool.yaml)
|
||||
# assigns IPs to LoadBalancer Services but nothing ARPs for them on the LAN,
|
||||
# so they're unreachable from outside the cluster even though `kubectl get
|
||||
# svc` shows a real EXTERNAL-IP. Confirmed both forgejo's .165 and
|
||||
# shadowsocks' .166 were 100% packet loss / incomplete ARP before this.
|
||||
#
|
||||
# loadBalancerIPs: true makes Cilium announce every Service's LB-IPAM IP via
|
||||
# ARP from whichever node currently holds the lease for it (one node per IP,
|
||||
# decided by leaderElection — not all nodes simultaneously, which would
|
||||
# otherwise cause ARP flapping/duplicate-IP confusion on the LAN).
|
||||
#
|
||||
# externalIPs/loadBalancerIPs split exists because Cilium also supports
|
||||
# announcing Service externalIPs (a different field, unused in this repo);
|
||||
# we only need loadBalancerIPs since every exposed Service here is type
|
||||
# LoadBalancer via lb-ipam-pool.yaml.
|
||||
#
|
||||
# requires kube-proxy replacement (already the case — see
|
||||
# k8s/talos-iam or helmfile.yaml.gotmpl kubeProxyReplacement=true) and a
|
||||
# Cilium build with L2 announcements enabled (default since v1.14).
|
||||
#
|
||||
# Apply once after cluster bootstrap, alongside lb-ipam-pool.yaml:
|
||||
# kubectl apply -f k8s/cilium/l2-announcement-policy.yaml
|
||||
#
|
||||
# Verify:
|
||||
# kubectl get ciliuml2announcementpolicy
|
||||
# ping 192.168.1.165 && ping 192.168.1.166 # both should now respond
|
||||
# arp -a | grep 192.168.1.16 # should resolve to a real MAC
|
||||
apiVersion: cilium.io/v2alpha1
|
||||
kind: CiliumL2AnnouncementPolicy
|
||||
metadata:
|
||||
name: homelab-l2-announce
|
||||
spec:
|
||||
loadBalancerIPs: true
|
||||
interfaces:
|
||||
- eno1
|
||||
# No nodeSelector restriction — all 3 nodes already run workloads
|
||||
# (allowSchedulingOnControlPlanes: true in controlplane.yaml), and with
|
||||
# 3 zone-labeled nodes, redundancy for per-IP leader election is maintained.
|
||||
@@ -0,0 +1,36 @@
|
||||
# k8s/cilium/lb-ipam-pool.yaml
|
||||
# CiliumLoadBalancerIPPool — tells Cilium LB-IPAM which IPs it can assign
|
||||
# to LoadBalancer services in this cluster.
|
||||
#
|
||||
# CIDR 192.168.1.160/28 covers .160–.175 on the LAN:
|
||||
# .160 talos-cp-1 (node — not assignable to services)
|
||||
# .161 reserved
|
||||
# .162 talos-worker-1 (node — not assignable to services)
|
||||
# .163–.175 free for LoadBalancer services
|
||||
#
|
||||
# Current service IP assignments (via io.cilium/lb-ipam-ips annotation):
|
||||
# 192.168.1.165 forgejo-gitea-http (cicd)
|
||||
# 192.168.1.165 forgejo-gitea-ssh (cicd) — same IP, different ports
|
||||
# 192.168.1.166 shadowsocks (vpn)
|
||||
#
|
||||
# Apply once after cluster bootstrap:
|
||||
# kubectl apply -f k8s/cilium/lb-ipam-pool.yaml
|
||||
#
|
||||
# Verify assignment:
|
||||
# kubectl get svc -n cicd forgejo-gitea-http forgejo-gitea-ssh
|
||||
# # EXTERNAL-IP should change from <pending> to 192.168.1.165
|
||||
|
||||
apiVersion: "cilium.io/v2alpha1"
|
||||
kind: CiliumLoadBalancerIPPool
|
||||
metadata:
|
||||
name: homelab-pool
|
||||
spec:
|
||||
blocks:
|
||||
- cidr: "192.168.1.160/28"
|
||||
# DO NOT add any 10.6.0.0/24 block here. That is the WireGuard subnet
|
||||
# (10.6.0.1 = talos-cp-1 tunnel IP, 10.6.0.2 = DNS — see
|
||||
# cluster-config/controlplane.yaml). A 10.6.0.x block let Cilium LB-IPAM
|
||||
# auto-assign the CP's own tunnel IP to a Service, which broke the
|
||||
# WireGuard tunnel and locked out the default kubectl context. It also
|
||||
# can't work over WireGuard anyway — L2 announcements only ARP on eno1
|
||||
# (the LAN interface), not wg0. Keep this pool LAN-only.
|
||||
@@ -0,0 +1,72 @@
|
||||
# k8s/coredns/coredns-configmap.yaml
|
||||
# Patches the CoreDNS Corefile to rewrite homelab hostnames to internal services.
|
||||
#
|
||||
# Why this is needed:
|
||||
# Grafana v10+ does OIDC auto-discovery by fetching
|
||||
# /.well-known/openid-configuration from Authentik. When Grafana reaches
|
||||
# Authentik via the external hostname (authentik.riotpiao.homelab.com), the
|
||||
# HTTP Host header is preserved and Authentik returns external URLs in the
|
||||
# discovery response. Without this rewrite, the hostname doesn't resolve
|
||||
# inside the cluster and Grafana falls back to the internal service DNS,
|
||||
# causing all OAuth redirects to go to authentik-server.iam.svc.cluster.local.
|
||||
#
|
||||
# Applied by helmfile presync hook on the ingress-nginx release.
|
||||
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: coredns
|
||||
namespace: kube-system
|
||||
data:
|
||||
Corefile: |
|
||||
.:53 {
|
||||
errors
|
||||
health {
|
||||
lameduck 5s
|
||||
}
|
||||
ready
|
||||
log . {
|
||||
class error
|
||||
}
|
||||
prometheus :9153
|
||||
|
||||
# Forgejo: route through nginx ingress like every other host below. nginx
|
||||
# terminates TLS (wildcard-tls) on :443 and routes both /v2/ (container
|
||||
# registry) and web/git to forgejo-gitea-http:3000.
|
||||
# Do NOT point this at forgejo-gitea-http directly: that service only serves
|
||||
# port 3000, so containerd image pulls (which use https/:443) get
|
||||
# `dial tcp <clusterIP>:443: i/o timeout`. SSH stays on its own LB service.
|
||||
rewrite name forgejo.riotpiao.homelab.com ingress-nginx-controller.ingress-nginx.svc.cluster.local
|
||||
|
||||
# Rewrite homelab hostnames to the nginx ingress controller so in-cluster pods
|
||||
# hit nginx TLS termination (cert-manager cert) and preserve the Host header.
|
||||
# Routing through nginx — not directly to the backend service — is critical:
|
||||
# direct rewrites to the backend bypass nginx TLS and expose each app's own
|
||||
# self-signed cert, which nothing in the cluster trusts.
|
||||
rewrite name authentik.riotpiao.homelab.com ingress-nginx-controller.ingress-nginx.svc.cluster.local
|
||||
rewrite name grafana.riotpiao.homelab.com ingress-nginx-controller.ingress-nginx.svc.cluster.local
|
||||
rewrite name minio.riotpiao.homelab.com ingress-nginx-controller.ingress-nginx.svc.cluster.local
|
||||
rewrite name minio-api.riotpiao.homelab.com ingress-nginx-controller.ingress-nginx.svc.cluster.local
|
||||
rewrite name argocd.riotpiao.homelab.com ingress-nginx-controller.ingress-nginx.svc.cluster.local
|
||||
rewrite name vault.riotpiao.homelab.com ingress-nginx-controller.ingress-nginx.svc.cluster.local
|
||||
rewrite name loki.riotpiao.homelab.com ingress-nginx-controller.ingress-nginx.svc.cluster.local
|
||||
rewrite name prometheus.riotpiao.homelab.com ingress-nginx-controller.ingress-nginx.svc.cluster.local
|
||||
rewrite name portainer.riotpiao.homelab.com ingress-nginx-controller.ingress-nginx.svc.cluster.local
|
||||
rewrite name longhorn.riotpiao.homelab.com ingress-nginx-controller.ingress-nginx.svc.cluster.local
|
||||
|
||||
kubernetes cluster.local in-addr.arpa ip6.arpa {
|
||||
pods insecure
|
||||
fallthrough in-addr.arpa ip6.arpa
|
||||
ttl 30
|
||||
}
|
||||
forward . 8.8.8.8 1.1.1.1 {
|
||||
max_concurrent 1000
|
||||
}
|
||||
cache 30 {
|
||||
disable success cluster.local
|
||||
disable denial cluster.local
|
||||
}
|
||||
loop
|
||||
reload
|
||||
loadbalance
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
# CoreDNS is deployed by Talos's bootstrap manifests (not a helm release we
|
||||
# own). This tracks the full Deployment spec as the single source of truth
|
||||
# for any changes we apply on top of the Talos default — currently just
|
||||
# topologySpreadConstraints so the 2 replicas don't land on the same node.
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: coredns
|
||||
namespace: kube-system
|
||||
labels:
|
||||
k8s-app: kube-dns
|
||||
kubernetes.io/name: CoreDNS
|
||||
spec:
|
||||
replicas: 2
|
||||
revisionHistoryLimit: 10
|
||||
selector:
|
||||
matchLabels:
|
||||
k8s-app: kube-dns
|
||||
strategy:
|
||||
rollingUpdate:
|
||||
maxSurge: 25%
|
||||
maxUnavailable: 1
|
||||
type: RollingUpdate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
k8s-app: kube-dns
|
||||
spec:
|
||||
affinity:
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchExpressions:
|
||||
- key: k8s-app
|
||||
operator: In
|
||||
values:
|
||||
- kube-dns
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 100
|
||||
containers:
|
||||
- args:
|
||||
- -conf
|
||||
- /etc/coredns/Corefile
|
||||
env:
|
||||
- name: GOMEMLIMIT
|
||||
value: 161MiB
|
||||
image: registry.k8s.io/coredns/coredns:v1.14.2
|
||||
imagePullPolicy: IfNotPresent
|
||||
livenessProbe:
|
||||
failureThreshold: 5
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 8080
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 60
|
||||
periodSeconds: 10
|
||||
successThreshold: 1
|
||||
timeoutSeconds: 5
|
||||
name: coredns
|
||||
ports:
|
||||
- containerPort: 53
|
||||
name: dns
|
||||
protocol: UDP
|
||||
- containerPort: 53
|
||||
name: dns-tcp
|
||||
protocol: TCP
|
||||
- containerPort: 9153
|
||||
name: metrics
|
||||
protocol: TCP
|
||||
readinessProbe:
|
||||
failureThreshold: 3
|
||||
httpGet:
|
||||
path: /ready
|
||||
port: 8181
|
||||
scheme: HTTP
|
||||
periodSeconds: 10
|
||||
successThreshold: 1
|
||||
timeoutSeconds: 1
|
||||
resources:
|
||||
limits:
|
||||
memory: 170Mi
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 70Mi
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
add:
|
||||
- NET_BIND_SERVICE
|
||||
drop:
|
||||
- ALL
|
||||
readOnlyRootFilesystem: true
|
||||
terminationMessagePath: /dev/termination-log
|
||||
terminationMessagePolicy: File
|
||||
volumeMounts:
|
||||
- mountPath: /etc/coredns
|
||||
name: config-volume
|
||||
readOnly: true
|
||||
dnsPolicy: Default
|
||||
nodeSelector:
|
||||
kubernetes.io/os: linux
|
||||
priorityClassName: system-cluster-critical
|
||||
restartPolicy: Always
|
||||
schedulerName: default-scheduler
|
||||
serviceAccount: coredns
|
||||
serviceAccountName: coredns
|
||||
terminationGracePeriodSeconds: 30
|
||||
tolerations:
|
||||
- effect: NoSchedule
|
||||
key: node-role.kubernetes.io/control-plane
|
||||
operator: Exists
|
||||
- effect: NoSchedule
|
||||
key: node.cloudprovider.kubernetes.io/uninitialized
|
||||
operator: Exists
|
||||
topologySpreadConstraints:
|
||||
- labelSelector:
|
||||
matchLabels:
|
||||
k8s-app: kube-dns
|
||||
maxSkew: 1
|
||||
topologyKey: kubernetes.io/hostname
|
||||
whenUnsatisfiable: ScheduleAnyway
|
||||
volumes:
|
||||
- configMap:
|
||||
defaultMode: 420
|
||||
items:
|
||||
- key: Corefile
|
||||
path: Corefile
|
||||
name: coredns
|
||||
name: config-volume
|
||||
@@ -0,0 +1,5 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namespace: kube-system
|
||||
resources: []
|
||||
# CoreDNS deployed via Helm chart
|
||||
@@ -0,0 +1,319 @@
|
||||
# k8s/ingress/ingress.yaml
|
||||
# Ingress rules for all homelab services.
|
||||
# TLS is handled centrally: nginx serves the wildcard-tls cert (*.riotpiao.homelab.com)
|
||||
# as its default-ssl-certificate. No per-rule tls: blocks or cert-manager annotations
|
||||
# are needed — cert-manager manages one cert, nginx uses it for all hosts.
|
||||
#
|
||||
# DNS: *.riotpiao.homelab.com must resolve to 10.6.0.1 (WireGuard) or 192.168.1.160 (LAN).
|
||||
|
||||
# ── Grafana ───────────────────────────────────────────────────────────────────
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: grafana
|
||||
namespace: logging
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: grafana.riotpiao.homelab.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: grafana
|
||||
port:
|
||||
number: 80
|
||||
|
||||
---
|
||||
# ── Loki (API access for external tools) ─────────────────────────────────────
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: loki
|
||||
namespace: logging
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: loki.riotpiao.homelab.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: loki
|
||||
port:
|
||||
number: 3100
|
||||
|
||||
---
|
||||
# ── Authentik ─────────────────────────────────────────────────────────────────
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: authentik
|
||||
namespace: iam
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/proxy-buffer-size: "16k"
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: authentik.riotpiao.homelab.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: authentik-server
|
||||
port:
|
||||
number: 80
|
||||
|
||||
---
|
||||
# ── Vault ─────────────────────────────────────────────────────────────────────
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: vault
|
||||
namespace: iam
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: vault.riotpiao.homelab.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: vault
|
||||
port:
|
||||
number: 8200
|
||||
|
||||
---
|
||||
# ── MinIO console (storage namespace) ────────────────────────────────────────
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: minio
|
||||
namespace: storage
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: "0"
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
|
||||
nginx.ingress.kubernetes.io/affinity: "cookie"
|
||||
nginx.ingress.kubernetes.io/session-cookie-name: "minio-console-affinity"
|
||||
nginx.ingress.kubernetes.io/session-cookie-max-age: "3600"
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: minio.riotpiao.homelab.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: minio-console
|
||||
port:
|
||||
number: 9001
|
||||
|
||||
---
|
||||
# ── MinIO S3 API ──────────────────────────────────────────────────────────────
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: minio-api
|
||||
namespace: storage
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: "0"
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: minio-api.riotpiao.homelab.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: minio
|
||||
port:
|
||||
number: 9000
|
||||
|
||||
---
|
||||
# ── Prometheus ────────────────────────────────────────────────────────────────
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: prometheus
|
||||
namespace: monitoring
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: prometheus.riotpiao.homelab.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: prometheus-kube-prometheus-prometheus
|
||||
port:
|
||||
number: 9090
|
||||
|
||||
---
|
||||
# ── Portainer ─────────────────────────────────────────────────────────────────
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: portainer
|
||||
namespace: dashboard
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: portainer.riotpiao.homelab.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: portainer
|
||||
port:
|
||||
number: 9000
|
||||
|
||||
---
|
||||
# ── Forgejo (git forge + OCI registry UI) ────────────────────────────────────
|
||||
# nginx terminates TLS using the wildcard cert, then proxies plain HTTP to
|
||||
# Forgejo on port 3000. ROOT_URL stays https:// so Forgejo generates correct URLs.
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: forgejo
|
||||
namespace: cicd
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: "0"
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: forgejo.riotpiao.homelab.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: forgejo-gitea-http
|
||||
port:
|
||||
number: 3000
|
||||
|
||||
---
|
||||
# ── Argo CD (cicd namespace) ──────────────────────────────────────────────────
|
||||
# argocd-server runs HTTPS internally — nginx proxies via backend-protocol: HTTPS.
|
||||
# proxy-ssl-verify: off because argocd-server's pod cert is self-signed (not homelab-ca).
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: argocd
|
||||
namespace: cicd
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
|
||||
nginx.ingress.kubernetes.io/proxy-ssl-verify: "off"
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: argocd.riotpiao.homelab.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: argocd-server
|
||||
port:
|
||||
number: 443
|
||||
|
||||
---
|
||||
# ── Longhorn UI ───────────────────────────────────────────────────────────────
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: longhorn
|
||||
namespace: longhorn-system
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: longhorn.riotpiao.homelab.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: longhorn-frontend
|
||||
port:
|
||||
number: 80
|
||||
|
||||
---
|
||||
# ── Temporal Web UI ────────────────────────────────────────────────────────────
|
||||
# Temporal workflow orchestration Web UI
|
||||
# TLS: wildcard cert managed by cert-manager, served by nginx default-ssl-certificate
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: temporal
|
||||
namespace: temporal
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: temporal.riotpiao.homelab.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: temporal-web
|
||||
port:
|
||||
number: 8080
|
||||
|
||||
---
|
||||
# ── kmsvc (SQS/Kafka REST API) ────────────────────────────────────────────
|
||||
# SQS-compatible messaging API via kmsvc management service
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: kmsvc
|
||||
namespace: sqs
|
||||
spec:
|
||||
ingressClassName: nginx
|
||||
rules:
|
||||
- host: kmsvc.riotpiao.homelab.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: management-service
|
||||
port:
|
||||
number: 8080
|
||||
@@ -0,0 +1,5 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namespace: ingress-nginx
|
||||
resources: []
|
||||
# Ingress controller deployed via Helm chart
|
||||
@@ -0,0 +1,78 @@
|
||||
# k8s/ingress/nginx-values.yaml
|
||||
# Nginx Ingress Controller — bare-metal homelab config.
|
||||
# LoadBalancer service with Cilium LB-IPAM assigns fixed IP (192.168.1.160).
|
||||
# Access services at https://grafana.riotpiao.homelab.com (80/443 via LoadBalancer).
|
||||
|
||||
controller:
|
||||
kind: DaemonSet
|
||||
|
||||
# Single wildcard cert served for every *.riotpiao.homelab.com host.
|
||||
# Applied by the ingress-nginx presync hook (wildcard-cert.yaml) before nginx starts.
|
||||
# nginx hot-reloads when cert-manager renews homelab-tls — no restart needed.
|
||||
extraArgs:
|
||||
default-ssl-certificate: "ingress-nginx/homelab-tls"
|
||||
|
||||
hostPort:
|
||||
enabled: true
|
||||
ports:
|
||||
http: 80
|
||||
https: 443
|
||||
|
||||
# TCP proxy: forward port 2222 on every node → Forgejo SSH service.
|
||||
# This lets `git clone [email protected]:repo` work via the
|
||||
# same hostname as HTTPS without a separate LoadBalancer IP for SSH.
|
||||
tcp:
|
||||
2222: "cicd/forgejo-gitea-ssh:2222"
|
||||
|
||||
# Service as LoadBalancer — Cilium LB-IPAM assigns fixed IP.
|
||||
service:
|
||||
type: LoadBalancer
|
||||
annotations:
|
||||
io.cilium/lb-ipam-ips: "192.168.1.160"
|
||||
|
||||
# Allow the controller to land on the control-plane node.
|
||||
tolerations:
|
||||
- key: node-role.kubernetes.io/control-plane
|
||||
operator: Exists
|
||||
effect: NoSchedule
|
||||
|
||||
# Use the ingress-nginx IngressClass by default.
|
||||
ingressClassResource:
|
||||
default: true
|
||||
|
||||
# Required when using hostPort so DNS resolves correctly inside the pod.
|
||||
dnsPolicy: ClusterFirstWithHostNet
|
||||
|
||||
# Reduce noise in a single-admin homelab.
|
||||
admissionWebhooks:
|
||||
enabled: false
|
||||
|
||||
# ── Connection timeouts to upstreams ──────────────────────────────────────────
|
||||
# Increased to tolerate 5+ second pod-to-pod network latency spikes.
|
||||
# Default: 60s for all — acceptable but explicitly set for clarity.
|
||||
config:
|
||||
upstream-connect-timeout: "60"
|
||||
upstream-send-timeout: "60"
|
||||
upstream-read-timeout: "60"
|
||||
keepalive-timeout: "65"
|
||||
keepalive-requests: "100"
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 64Mi
|
||||
limits:
|
||||
cpu: 200m
|
||||
memory: 128Mi
|
||||
|
||||
# RED metrics (rate/errors/duration) for every host fronted by this controller —
|
||||
# every exposed service in the cluster goes through here, so this single block
|
||||
# is the cluster-wide "latency and availability" signal. Prometheus auto-discovers
|
||||
# the ServiceMonitor (serviceMonitorSelectorNilUsesHelmValues: false in prometheus-values.yaml).
|
||||
metrics:
|
||||
enabled: true
|
||||
serviceMonitor:
|
||||
enabled: true
|
||||
namespace: ingress-nginx
|
||||
interval: 30s
|
||||
scrapeTimeout: 60s
|
||||
@@ -0,0 +1,22 @@
|
||||
# k8s/ingress/wildcard-cert.yaml
|
||||
# Single wildcard TLS certificate for all *.riotpiao.homelab.com services.
|
||||
# Lives in the ingress-nginx namespace and is set as nginx's default-ssl-certificate,
|
||||
# so every ingress host gets it automatically — no per-service TLS blocks needed.
|
||||
#
|
||||
# Renewal: cert-manager auto-renews 30 days before expiry (renewBefore: 720h).
|
||||
# nginx detects the secret update via its K8s watch and hot-reloads — no pod restart.
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: homelab-tls
|
||||
namespace: ingress-nginx
|
||||
spec:
|
||||
secretName: homelab-tls
|
||||
dnsNames:
|
||||
- "*.riotpiao.homelab.com"
|
||||
- "riotpiao.homelab.com"
|
||||
issuerRef:
|
||||
name: homelab-ca
|
||||
kind: ClusterIssuer
|
||||
duration: 8760h # 1 year
|
||||
renewBefore: 720h # renew 30 days before expiry
|
||||
@@ -0,0 +1,14 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: bootstrap
|
||||
|
||||
# Layer 2: Bootstrap — networking, certificates, DNS
|
||||
# Dependencies: infrastructure (namespaces, storage from layer 1)
|
||||
# Order: Applied second
|
||||
|
||||
resources:
|
||||
- cert-manager/
|
||||
- cilium/
|
||||
- coredns/
|
||||
- ingress/
|
||||
Reference in New Issue
Block a user