starting homelab RKE2 cluster
This commit is contained in:
@@ -0,0 +1,19 @@
|
|||||||
|
frr version 9.1
|
||||||
|
frr defaults traditional
|
||||||
|
hostname UniFi-Gateway
|
||||||
|
log syslog informational
|
||||||
|
service integrated-vtysh-config
|
||||||
|
!
|
||||||
|
router bgp 65100
|
||||||
|
bgp router-id 192.168.1.1
|
||||||
|
no bgp ebgp-requires-policy
|
||||||
|
neighbor RKE2 peer-group
|
||||||
|
neighbor RKE2 remote-as 65200
|
||||||
|
neighbor 192.168.1.238 peer-group RKE2
|
||||||
|
address-family ipv4 unicast
|
||||||
|
neighbor RKE2 activate
|
||||||
|
neighbor RKE2 next-hop-self
|
||||||
|
redistribute connected
|
||||||
|
exit-address-family
|
||||||
|
!
|
||||||
|
end
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
direkt auf dem Server ausführen
|
||||||
|
|
||||||
|
# RKE2 installieren
|
||||||
|
curl -sfL https://get.rke2.io | sudo INSTALL_RKE2_TYPE="server" sh
|
||||||
|
|
||||||
|
# Verzeichnis für die Konfiguration anlegen
|
||||||
|
sudo mkdir -p /etc/rancher/rke2
|
||||||
|
|
||||||
|
# Konfigurationsdatei erstellen
|
||||||
|
cat <<EOF | sudo tee /etc/rancher/rke2/config.yaml
|
||||||
|
cni: none
|
||||||
|
disable-kube-proxy: true
|
||||||
|
disable:
|
||||||
|
- rke2-canal
|
||||||
|
- rke2-ingress-nginx # (Da wir Envoy nutzen wollen)
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# RKE2-Service enablen und starten
|
||||||
|
sudo systemctl enable --now rke2-server.service
|
||||||
|
|
||||||
|
# K8S kube-config Datei auf Workstation kopieren
|
||||||
|
sudo cat /etc/rancher/rke2/rke2.yaml
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# notwendige Tools installieren
|
||||||
|
brew install cilium-cli kubernetes-cli helm egctl switcher cmctl
|
||||||
|
|
||||||
|
# !!! zum richtigen cluster wechseln !!! Ich nutze dafür switcher
|
||||||
|
|
||||||
|
# cilium installieren
|
||||||
|
cilium install \
|
||||||
|
--version 1.18.5 \
|
||||||
|
--set bgpControlPlane.enabled=true \
|
||||||
|
--set bgpControlPlane.defaultInstance.type=cluster \
|
||||||
|
--set kubeProxyReplacement=true \
|
||||||
|
--set operator.replicas=1 \
|
||||||
|
--set gatewayAPI.enabled=true
|
||||||
|
|
||||||
|
# Cilium überprüfen
|
||||||
|
cilium status
|
||||||
|
|
||||||
|
# envoy installieren
|
||||||
|
helm install eg oci://docker.io/envoyproxy/gateway-helm --version v1.6.1 -n envoy-gateway-system --create-namespace
|
||||||
|
|
||||||
|
# Envoy Gateway Dashboard
|
||||||
|
egctl experimental dashboard envoy-gateway
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
# 1. Definiert, WAS angekündigt wird (LoadBalancer IPs)
|
||||||
|
apiVersion: cilium.io/v2
|
||||||
|
kind: CiliumBGPAdvertisement
|
||||||
|
metadata:
|
||||||
|
name: bgp-advertisement
|
||||||
|
labels:
|
||||||
|
bgp.cilium.io/advertise: loadbalancer-services
|
||||||
|
spec:
|
||||||
|
advertisements:
|
||||||
|
- advertisementType: "Service"
|
||||||
|
service:
|
||||||
|
addresses:
|
||||||
|
- LoadBalancerIP
|
||||||
|
selector:
|
||||||
|
matchLabels: {}
|
||||||
|
---
|
||||||
|
|
||||||
|
# 2. Definiert, an WEN wir senden (Dein UniFi Router)
|
||||||
|
apiVersion: cilium.io/v2
|
||||||
|
kind: CiliumBGPPeerConfig
|
||||||
|
metadata:
|
||||||
|
name: unifi-peer-config
|
||||||
|
spec:
|
||||||
|
families:
|
||||||
|
- afi: ipv4
|
||||||
|
safi: unicast
|
||||||
|
advertisements:
|
||||||
|
matchLabels:
|
||||||
|
bgp.cilium.io/advertise: loadbalancer-services
|
||||||
|
gracefulRestart:
|
||||||
|
enabled: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# 3. Verknüpft alles mit deinem Node
|
||||||
|
apiVersion: cilium.io/v2
|
||||||
|
kind: CiliumBGPClusterConfig
|
||||||
|
metadata:
|
||||||
|
name: bpg-cluster-config
|
||||||
|
spec:
|
||||||
|
nodeSelector:
|
||||||
|
matchLabels:
|
||||||
|
kubernetes.io/os: linux
|
||||||
|
bgpInstances:
|
||||||
|
- name: "asus-pn51-e1"
|
||||||
|
localASN: 65200
|
||||||
|
peers:
|
||||||
|
- name: "unifi-router"
|
||||||
|
peerAddress: 192.168.1.1
|
||||||
|
peerASN: 65100 # Hier gehört die Remote-ASN jetzt hin!
|
||||||
|
peerConfigRef:
|
||||||
|
name: unifi-peer-config
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: "cilium.io/v2alpha1"
|
||||||
|
kind: CiliumLoadBalancerIPPool
|
||||||
|
metadata:
|
||||||
|
name: "envoy-gateway-pool"
|
||||||
|
spec:
|
||||||
|
blocks:
|
||||||
|
- cidr: "192.168.200.240/28"
|
||||||
|
serviceSelector:
|
||||||
|
matchLabels: {}
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: gateway.networking.k8s.io/v1
|
||||||
|
kind: GatewayClass
|
||||||
|
metadata:
|
||||||
|
name: envoy-gateway-class
|
||||||
|
spec:
|
||||||
|
controllerName: gateway.envoyproxy.io/gatewayclass-controller
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: gateway.networking.k8s.io/v1
|
||||||
|
kind: Gateway
|
||||||
|
metadata:
|
||||||
|
name: external-gateway
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
gatewayClassName: envoy-gateway-class
|
||||||
|
listeners:
|
||||||
|
- name: http
|
||||||
|
protocol: HTTP
|
||||||
|
port: 80
|
||||||
|
allowedRoutes:
|
||||||
|
namespaces:
|
||||||
|
from: All
|
||||||
|
- name: https
|
||||||
|
protocol: HTTPS
|
||||||
|
port: 443
|
||||||
|
allowedRoutes:
|
||||||
|
namespaces:
|
||||||
|
from: All
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
fullnameOverride: external-dns-unifi
|
||||||
|
|
||||||
|
# Konfiguration des Webhook-Providers (der "Übersetzer" für UniFi)
|
||||||
|
provider:
|
||||||
|
name: webhook
|
||||||
|
webhook:
|
||||||
|
image:
|
||||||
|
repository: ghcr.io/kashalls/external-dns-unifi-webhook
|
||||||
|
tag: main
|
||||||
|
env:
|
||||||
|
- name: UNIFI_HOST
|
||||||
|
value: https://192.168.1.1 # Deine Gateway IP
|
||||||
|
- name: UNIFI_API_KEY
|
||||||
|
value: "J5VRZY-rGrtdGTf-fj1mTdZeUpLGzDBH"
|
||||||
|
- name: UNIFI_EXTERNAL_CONTROLLER
|
||||||
|
value: "false" # false, da der Controller auf dem Gateway selbst läuft
|
||||||
|
- name: LOG_LEVEL
|
||||||
|
value: info
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: http-webhook
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /readyz
|
||||||
|
port: http-webhook
|
||||||
|
|
||||||
|
# Allgemeine External-DNS Einstellungen
|
||||||
|
policy: sync # "sync" löscht auch Einträge, die nicht mehr in K8s sind. "upsert-only" ist sicherer.
|
||||||
|
sources:
|
||||||
|
- service
|
||||||
|
- ingress
|
||||||
|
- gateway-httproute
|
||||||
|
|
||||||
|
# WICHTIG: Nur diese Domain verwalten
|
||||||
|
domainFilters:
|
||||||
|
- "k8s.hnrx.net" # <--- ÄNDERE DIES auf deine Domain (muss im UniFi als Domain konfiguriert sein?)
|
||||||
|
- "hnrx.net"
|
||||||
|
# Registry (verhindert, dass external-dns fremde Einträge überschreibt)
|
||||||
|
txtOwnerId: "k8s-cluster"
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# Basic requirements
|
||||||
|
|
||||||
|
In diesem Schritt installieren wir
|
||||||
|
- phase-secrets-operator
|
||||||
|
- cert-manager
|
||||||
|
- external-DNS mit Webhook Provider für Unifi
|
||||||
|
|
||||||
|
## Phase-Secrets-Operator
|
||||||
|
|
||||||
|
helm repo add phase https://helm.phase.dev && helm repo update
|
||||||
|
|
||||||
|
helm install phase-secrets-operator phase/phase-kubernetes-operator --set image.tag=v1.3.0
|
||||||
|
|
||||||
|
kubectl create secret generic phase-service-token \
|
||||||
|
--from-literal=token=pss_service:v2:XXXXXXXXXXXXXXXXXXXXX \
|
||||||
|
--type=Opaque \
|
||||||
|
--namespace=default
|
||||||
|
|
||||||
|
## Cert-Manager und Cluster-Issuer
|
||||||
|
|
||||||
|
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.19.2/cert-manager.yaml
|
||||||
|
|
||||||
|
k apply -f manifests
|
||||||
|
|
||||||
|
## External-DNS
|
||||||
|
|
||||||
|
helm repo add external-dns https://kubernetes-sigs.github.io/external-dns/
|
||||||
|
|
||||||
|
kubectl create ns external-dns
|
||||||
|
|
||||||
|
helm upgrade --install external-dns external-dns/external-dns --namespace external-dns --version 1.19.0 -f external-dns-values.yaml
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
apiVersion: secrets.phase.dev/v1alpha1
|
||||||
|
kind: PhaseSecret
|
||||||
|
metadata:
|
||||||
|
name: cloudflare-api-key-phase-secret
|
||||||
|
namespace: cert-manager
|
||||||
|
spec:
|
||||||
|
phaseApp: 'cert-manager' # The name of your Phase application
|
||||||
|
phaseAppEnv: 'production' # OPTIONAL - The Phase App Environment to fetch secrets from
|
||||||
|
phaseAppEnvPath: '/' # OPTIONAL Path within the Phase application environment to fetch secrets from
|
||||||
|
phaseHost: 'https://phase.hnrx.net' # OPTIONAL - URL of a Phase Console instance
|
||||||
|
authentication:
|
||||||
|
serviceToken:
|
||||||
|
serviceTokenSecretReference:
|
||||||
|
secretName: 'phase-service-token' # Name of the Phase Service Token with access to your application
|
||||||
|
secretNamespace: 'default'
|
||||||
|
managedSecretReferences:
|
||||||
|
- secretName: 'cloudflare-api-key' # Name of the Kubernetes managed secret that Phase will sync
|
||||||
|
secretNamespace: 'cert-manager'
|
||||||
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
apiVersion: cert-manager.io/v1
|
||||||
|
kind: ClusterIssuer
|
||||||
|
metadata:
|
||||||
|
name: cloudflare-cluster-issuer
|
||||||
|
spec:
|
||||||
|
acme:
|
||||||
|
email: matthias.hinrichs@me.com
|
||||||
|
server: https://acme-v02.api.letsencrypt.org/directory
|
||||||
|
privateKeySecretRef:
|
||||||
|
name: cluster-issuer-account-key
|
||||||
|
solvers:
|
||||||
|
- dns01:
|
||||||
|
cloudflare:
|
||||||
|
apiTokenSecretRef:
|
||||||
|
name: cloudflare-api-key
|
||||||
|
key: CLOUDFLARE_API_KEY
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: argocd
|
||||||
|
---
|
||||||
|
apiVersion: gateway.networking.k8s.io/v1
|
||||||
|
kind: Gateway
|
||||||
|
metadata:
|
||||||
|
name: argocd-gateway
|
||||||
|
namespace: argocd
|
||||||
|
labels:
|
||||||
|
bgp.cilium.io/ip-pool: default # Damit bekommt das Gateway eine IP aus deinem Pool
|
||||||
|
annotations:
|
||||||
|
# Damit external-dns diesen Gateway findet und einen DNS-Eintrag erstellt
|
||||||
|
# (falls external-dns Gateway API unterstützt, was es tut)
|
||||||
|
cert-manager.io/cluster-issuer: cloudflare-cluster-issuer
|
||||||
|
spec:
|
||||||
|
gatewayClassName: envoy-gateway-class
|
||||||
|
listeners:
|
||||||
|
- name: https
|
||||||
|
hostname: "argocd.k8s.hnrx.net"
|
||||||
|
protocol: HTTPS
|
||||||
|
port: 443
|
||||||
|
allowedRoutes:
|
||||||
|
namespaces:
|
||||||
|
from: All
|
||||||
|
tls:
|
||||||
|
mode: Terminate
|
||||||
|
certificateRefs:
|
||||||
|
- name: argocd-gateway-tls
|
||||||
|
- name: http
|
||||||
|
hostname: "argocd.k8s.hnrx.net"
|
||||||
|
protocol: HTTP
|
||||||
|
port: 80
|
||||||
|
allowedRoutes:
|
||||||
|
kinds:
|
||||||
|
- kind: HTTPRoute
|
||||||
|
- kind: GRPCRoute
|
||||||
|
namespaces:
|
||||||
|
from: All
|
||||||
|
---
|
||||||
|
apiVersion: cert-manager.io/v1
|
||||||
|
kind: Certificate
|
||||||
|
metadata:
|
||||||
|
name: argocd-gateway-tls
|
||||||
|
namespace: argocd
|
||||||
|
spec:
|
||||||
|
secretName: argocd-gateway-tls
|
||||||
|
dnsNames:
|
||||||
|
- argocd.k8s.hnrx.net
|
||||||
|
issuerRef:
|
||||||
|
name: cloudflare-cluster-issuer
|
||||||
|
kind: ClusterIssuer
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
global:
|
||||||
|
domain: "argocd.k8s.hnrx.net"
|
||||||
|
|
||||||
|
extraObjects:
|
||||||
|
- apiVersion: secrets.phase.dev/v1alpha1
|
||||||
|
kind: PhaseSecret
|
||||||
|
metadata:
|
||||||
|
name: argocd-phase-secret
|
||||||
|
namespace: argocd
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/part-of: argocd
|
||||||
|
spec:
|
||||||
|
phaseApp: 'argocd' # The name of your Phase application
|
||||||
|
phaseAppEnv: 'production' # OPTIONAL - The Phase App Environment to fetch secrets from
|
||||||
|
phaseAppEnvPath: '/' # OPTIONAL Path within the Phase application environment to fetch secrets from
|
||||||
|
phaseHost: 'https://phase.hnrx.net' # OPTIONAL - URL of a Phase Console instance
|
||||||
|
authentication:
|
||||||
|
serviceToken:
|
||||||
|
serviceTokenSecretReference:
|
||||||
|
secretName: 'phase-service-token' # Name of the Phase Service Token with access to your application
|
||||||
|
secretNamespace: 'default'
|
||||||
|
managedSecretReferences:
|
||||||
|
- secretName: 'argocd-authentik-client-secret' # Name of the Kubernetes managed secret that Phase will sync
|
||||||
|
secretNamespace: 'argocd'
|
||||||
|
|
||||||
|
configs:
|
||||||
|
cm:
|
||||||
|
url: https://argocd.k8s.hnrx.net
|
||||||
|
dex.config: |
|
||||||
|
connectors:
|
||||||
|
- config:
|
||||||
|
issuer: ${AUTHENTIK_ISSUER_URL}
|
||||||
|
clientID: ${AUTHENTIK_CLIENT_ID}
|
||||||
|
clientSecret: ${AUTHENTIK_CLIENT_SECRET}
|
||||||
|
insecureEnableGroups: true
|
||||||
|
scopes:
|
||||||
|
- openid
|
||||||
|
- profile
|
||||||
|
- email
|
||||||
|
name: authentik
|
||||||
|
type: oidc
|
||||||
|
id: authentik
|
||||||
|
|
||||||
|
params:
|
||||||
|
server.insecure: true
|
||||||
|
rbac:
|
||||||
|
policy.csv: |
|
||||||
|
g, ArgoCD Admins, role:admin
|
||||||
|
g, ArgoCD Viewers, role:readonly
|
||||||
|
secret:
|
||||||
|
extra:
|
||||||
|
dex.authentik.clientSecret: "${AUTHENTIK_CLIENT_SECRET}"
|
||||||
|
cmp:
|
||||||
|
credentialTemplates:
|
||||||
|
https-creds:
|
||||||
|
url: https://git.hnrx.net
|
||||||
|
username: ${GIT_USER}
|
||||||
|
password: ${GIT_PASSWORD}
|
||||||
|
|
||||||
|
|
||||||
|
dex:
|
||||||
|
envFrom:
|
||||||
|
- secretRef:
|
||||||
|
name: argocd-authentik-client-secret
|
||||||
|
|
||||||
|
server:
|
||||||
|
httproute:
|
||||||
|
enabled: true
|
||||||
|
parentRefs:
|
||||||
|
- name: argocd-gateway
|
||||||
|
namespace: argocd
|
||||||
|
sectionName: https
|
||||||
|
hostnames:
|
||||||
|
- "argocd.k8s.hnrx.net"
|
||||||
|
rules:
|
||||||
|
- matches:
|
||||||
|
- path:
|
||||||
|
type: PathPrefix
|
||||||
|
value: /
|
||||||
|
backendRefs:
|
||||||
|
- name: argocd-server
|
||||||
|
port: 80
|
||||||
|
|
||||||
|
grpcroute:
|
||||||
|
enabled: true
|
||||||
|
parentRefs:
|
||||||
|
- name: argocd-gateway
|
||||||
|
namespace: argocd
|
||||||
|
sectionName: http
|
||||||
|
hostnames:
|
||||||
|
- "argocd.k8s.hnrx.net"
|
||||||
|
rules:
|
||||||
|
- matches:
|
||||||
|
- method:
|
||||||
|
type: Exact
|
||||||
|
service: "cluster.argoproj.v1alpha1.repositorieservice"
|
||||||
|
method: "List"
|
||||||
|
backendRefs:
|
||||||
|
- name: argocd-server
|
||||||
|
port: 443
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Argo-CD Installation mit SSO über Authentik
|
||||||
|
|
||||||
|
helm repo add argo https://argoproj.github.io/argo-helm
|
||||||
|
|
||||||
|
helm upgrade --install argocd argo/argo-cd --namespace argocd -f argo-values.yaml
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# Zwischenstand
|
||||||
|
Der Cluster hat bereits folgendes installiert:
|
||||||
|
- Cilium Netzwerk mit BGP
|
||||||
|
- Envoy Gateway API
|
||||||
|
- Cert-Manager
|
||||||
|
- External DNS für Einträge im Unifi-DNS-Server
|
||||||
|
|
||||||
|
## Weitere Infrastuktur-Bestandteile installieren
|
||||||
|
|
||||||
|
Diese werden mit ArgoCD gemanagt.
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: ApplicationSet
|
||||||
|
metadata:
|
||||||
|
name: cluster-infra
|
||||||
|
namespace: argocd
|
||||||
|
spec:
|
||||||
|
generators:
|
||||||
|
- git:
|
||||||
|
repoURL: https://github.com/dein-user/git-repo.git
|
||||||
|
revision: HEAD
|
||||||
|
directories:
|
||||||
|
- path: 08_cluster_infrastructure/*
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
name: '{{path.basename}}'
|
||||||
|
spec:
|
||||||
|
project: cluster-infra
|
||||||
|
source:
|
||||||
|
repoURL: https://github.com/dein-user/git-repo.git
|
||||||
|
path: '{{path}}'
|
||||||
|
helm:
|
||||||
|
values: values-{{cluster.name}}.yaml # Env-spezifisch
|
||||||
|
destination:
|
||||||
|
server: https://kubernetes.default.svc
|
||||||
|
namespace: '{{path.basename}}'
|
||||||
|
syncPolicy:
|
||||||
|
automated: {prune: true, selfHeal: true}
|
||||||
Reference in New Issue
Block a user