feat(ci,iac): Consolidate Forgejo CI workflows and add Talos Terraform IaC
Consolidate three separate Forgejo Actions (argocd-sync, security-scan, validate-k8s) into single cluster-ci workflow for cleaner CI/CD pipeline with proper job sequencing and reduced auth overhead. Add Terraform configuration for Talos cluster machine configs: - Provider setup for Talos - Centralized variables for CP and worker configs - Template-based config generation for controlplane.yaml and worker-*.yaml - Sensitive data separated in terraform.tfvars (gitignored) - Local state tracking for infrastructure
This commit is contained in:
@@ -1,66 +0,0 @@
|
|||||||
name: ArgoCD Sync on Main
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
paths:
|
|
||||||
- 'k8s/**'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
argocd-sync:
|
|
||||||
runs-on: docker
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
run: |
|
|
||||||
REPO_URL="${{ gitea.server_url }}/${{ gitea.repository }}.git"
|
|
||||||
CLONE_URL="https://${{ secrets.CI_RUNNER }}:${{ secrets.CI_RUNNER_SECRET }}@${REPO_URL#https://}"
|
|
||||||
git clone --depth 1 "$CLONE_URL" .
|
|
||||||
cd ${GITHUB_WORKSPACE}
|
|
||||||
git fetch origin main
|
|
||||||
git checkout main
|
|
||||||
|
|
||||||
- name: Install ArgoCD CLI
|
|
||||||
run: |
|
|
||||||
curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
|
|
||||||
chmod +x /usr/local/bin/argocd
|
|
||||||
|
|
||||||
- name: Configure ArgoCD Access
|
|
||||||
env:
|
|
||||||
ARGOCD_SERVER: ${{ secrets.ARGOCD_SERVER }}
|
|
||||||
ARGOCD_AUTH_TOKEN: ${{ secrets.ARGOCD_AUTH_TOKEN }}
|
|
||||||
run: |
|
|
||||||
echo "Configured ArgoCD credentials"
|
|
||||||
|
|
||||||
- name: Sync Root Application
|
|
||||||
env:
|
|
||||||
ARGOCD_SERVER: ${{ secrets.ARGOCD_SERVER }}
|
|
||||||
ARGOCD_AUTH_TOKEN: ${{ secrets.ARGOCD_AUTH_TOKEN }}
|
|
||||||
run: |
|
|
||||||
echo "=== Syncing homelab-root ==="
|
|
||||||
argocd app sync homelab-root --force
|
|
||||||
argocd app wait homelab-root --timeout 5m
|
|
||||||
|
|
||||||
- name: Check Sync Status
|
|
||||||
env:
|
|
||||||
ARGOCD_SERVER: ${{ secrets.ARGOCD_SERVER }}
|
|
||||||
ARGOCD_AUTH_TOKEN: ${{ secrets.ARGOCD_AUTH_TOKEN }}
|
|
||||||
run: |
|
|
||||||
echo "=== ArgoCD Applications Status ==="
|
|
||||||
argocd app list -o table
|
|
||||||
|
|
||||||
# Verify root app is synced
|
|
||||||
STATUS=$(argocd app get homelab-root -o jsonpath='{.status.syncStatus}')
|
|
||||||
if [ "$STATUS" != "Synced" ]; then
|
|
||||||
echo "❌ Root app sync failed: $STATUS"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "✓ Root app synced successfully"
|
|
||||||
|
|
||||||
- name: Health Check
|
|
||||||
env:
|
|
||||||
ARGOCD_SERVER: ${{ secrets.ARGOCD_SERVER }}
|
|
||||||
ARGOCD_AUTH_TOKEN: ${{ secrets.ARGOCD_AUTH_TOKEN }}
|
|
||||||
run: |
|
|
||||||
echo "=== Checking Application Health ==="
|
|
||||||
argocd app get homelab-root -o wide
|
|
||||||
@@ -0,0 +1,247 @@
|
|||||||
|
name: Cluster CI Pipeline
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- develop
|
||||||
|
paths:
|
||||||
|
- 'k8s/**'
|
||||||
|
- '.forgejo/workflows/cluster-ci.yaml'
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'k8s/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ci:
|
||||||
|
runs-on: docker
|
||||||
|
steps:
|
||||||
|
# === Checkout ===
|
||||||
|
- name: Checkout
|
||||||
|
run: |
|
||||||
|
REPO_URL="${{ gitea.server_url }}/${{ gitea.repository }}.git"
|
||||||
|
CLONE_URL="https://${{ secrets.CI_RUNNER }}:${{ secrets.CI_RUNNER_SECRET }}@${REPO_URL#https://}"
|
||||||
|
git clone --depth 1 "$CLONE_URL" .
|
||||||
|
git fetch origin main
|
||||||
|
git checkout main
|
||||||
|
|
||||||
|
# === Install Tools ===
|
||||||
|
- name: Install Tools
|
||||||
|
run: |
|
||||||
|
unset GITHUB_TOKEN
|
||||||
|
apt-get update && apt-get install -y \
|
||||||
|
yamllint \
|
||||||
|
python3-pip \
|
||||||
|
curl \
|
||||||
|
jq
|
||||||
|
|
||||||
|
# kubeval
|
||||||
|
curl -L https://github.com/instrumenta/kubeval/releases/latest/download/kubeval-linux-amd64.tar.gz | tar xz
|
||||||
|
mv -f kubeval /usr/local/bin/
|
||||||
|
|
||||||
|
# kustomize
|
||||||
|
rm -f kustomize
|
||||||
|
curl -s https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh | bash
|
||||||
|
mv -f kustomize /usr/local/bin/
|
||||||
|
|
||||||
|
# argocd
|
||||||
|
curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
|
||||||
|
chmod +x /usr/local/bin/argocd
|
||||||
|
|
||||||
|
# trivy
|
||||||
|
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
|
||||||
|
|
||||||
|
# polaris
|
||||||
|
curl -L https://github.com/FairwindsOps/polaris/releases/latest/download/polaris-linux-amd64 -o /usr/local/bin/polaris
|
||||||
|
chmod +x /usr/local/bin/polaris
|
||||||
|
|
||||||
|
# === YAML Lint ===
|
||||||
|
- name: YAML Lint
|
||||||
|
run: |
|
||||||
|
echo "=== Linting YAML files ==="
|
||||||
|
yamllint k8s/ -c .yamllint.yaml || true
|
||||||
|
|
||||||
|
# === Kubeval - Validate K8s Syntax ===
|
||||||
|
- name: Kubeval - Validate K8s Syntax
|
||||||
|
run: |
|
||||||
|
echo "=== Validating Kubernetes manifests ==="
|
||||||
|
find k8s -name "*.yaml" -o -name "*.yml" | grep -v "\.archive" | while read file; do
|
||||||
|
echo "Validating $file..."
|
||||||
|
kubeval "$file" -d 2>/dev/null || true
|
||||||
|
done
|
||||||
|
|
||||||
|
# === Kustomize Build - All overlays ===
|
||||||
|
- name: Kustomize Build - Infrastructure
|
||||||
|
run: |
|
||||||
|
echo "=== Building k8s/infrastructure/ ==="
|
||||||
|
kustomize build k8s/infrastructure > /tmp/infrastructure.yaml
|
||||||
|
echo "✓ Infrastructure built successfully"
|
||||||
|
echo "Resources: $(grep -c 'kind:' /tmp/infrastructure.yaml)"
|
||||||
|
|
||||||
|
- name: Kustomize Build - Bootstrap
|
||||||
|
run: |
|
||||||
|
echo "=== Building k8s/bootstrap/ ==="
|
||||||
|
kustomize build k8s/bootstrap > /tmp/bootstrap.yaml
|
||||||
|
echo "✓ Bootstrap built successfully"
|
||||||
|
echo "Resources: $(grep -c 'kind:' /tmp/bootstrap.yaml || echo 0)"
|
||||||
|
|
||||||
|
- name: Kustomize Build - Platform
|
||||||
|
run: |
|
||||||
|
echo "=== Building k8s/platform/ ==="
|
||||||
|
kustomize build k8s/platform > /tmp/platform.yaml
|
||||||
|
echo "✓ Platform built successfully"
|
||||||
|
echo "Resources: $(grep -c 'kind:' /tmp/platform.yaml || echo 0)"
|
||||||
|
|
||||||
|
- name: Kustomize Build - Security
|
||||||
|
run: |
|
||||||
|
echo "=== Building k8s/security/ ==="
|
||||||
|
kustomize build k8s/security > /tmp/security.yaml
|
||||||
|
echo "✓ Security built successfully"
|
||||||
|
echo "Resources: $(grep -c 'kind:' /tmp/security.yaml || echo 0)"
|
||||||
|
|
||||||
|
- name: Kustomize Build - Applications
|
||||||
|
run: |
|
||||||
|
echo "=== Building k8s/applications/ ==="
|
||||||
|
kustomize build k8s/applications > /tmp/applications.yaml
|
||||||
|
echo "✓ Applications built successfully"
|
||||||
|
echo "Resources: $(grep -c 'kind:' /tmp/applications.yaml || echo 0)"
|
||||||
|
|
||||||
|
- name: Kustomize Build - Data
|
||||||
|
run: |
|
||||||
|
echo "=== Building k8s/data/ ==="
|
||||||
|
kustomize build k8s/data > /tmp/data.yaml
|
||||||
|
echo "✓ Data built successfully"
|
||||||
|
echo "Resources: $(grep -c 'kind:' /tmp/data.yaml || echo 0)"
|
||||||
|
|
||||||
|
- name: Validate ArgoCD Applications
|
||||||
|
run: |
|
||||||
|
echo "=== Validating ArgoCD Applications ==="
|
||||||
|
kubeval k8s/argocd/apps/*.yaml
|
||||||
|
|
||||||
|
# === Trivy - Scan Dockerfile ===
|
||||||
|
- name: Trivy - Scan Dockerfile
|
||||||
|
run: |
|
||||||
|
if find . -name "Dockerfile" 2>/dev/null | grep -v node_modules | head -1 | grep -q .; then
|
||||||
|
echo "=== Scanning Dockerfiles with Trivy ==="
|
||||||
|
find . -name "Dockerfile" -not -path "*/node_modules/*" -exec trivy config {} \;
|
||||||
|
else
|
||||||
|
echo "No Dockerfiles found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# === Trivy - Scan Helm Charts ===
|
||||||
|
- name: Trivy - Scan Helm Charts
|
||||||
|
run: |
|
||||||
|
if find k8s -name "Chart.yaml" 2>/dev/null | head -1 | grep -q .; then
|
||||||
|
echo "=== Scanning Helm charts with Trivy ==="
|
||||||
|
find k8s -name "Chart.yaml" -exec dirname {} \; | while read chart; do
|
||||||
|
echo "Scanning $chart..."
|
||||||
|
trivy config "$chart" || true
|
||||||
|
done
|
||||||
|
else
|
||||||
|
echo "No Helm charts found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# === Polaris - K8s Security Audit ===
|
||||||
|
- name: Polaris - K8s Security Audit
|
||||||
|
run: |
|
||||||
|
echo "=== Running Polaris K8s security audit ==="
|
||||||
|
polaris audit --audit-path /tmp/polaris-audit.json k8s/ || true
|
||||||
|
|
||||||
|
if [ -f /tmp/polaris-audit.json ]; then
|
||||||
|
echo "Security issues found:"
|
||||||
|
jq '.results[] | select(.pass == false)' /tmp/polaris-audit.json || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# === Check for Secrets in Code ===
|
||||||
|
- name: Check for Secrets in Code
|
||||||
|
run: |
|
||||||
|
echo "=== Scanning for hardcoded secrets ==="
|
||||||
|
SECRETS_FOUND=0
|
||||||
|
|
||||||
|
for pattern in "password:" "secret:" "token:" "api_key:" "apikey:" "private_key:" "privatekey:"; do
|
||||||
|
if grep -r "$pattern" k8s/ --include="*.yaml" --include="*.yml" | grep -v "^Binary"; then
|
||||||
|
echo "⚠️ Found potential secret pattern: $pattern"
|
||||||
|
SECRETS_FOUND=$((SECRETS_FOUND + 1))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ $SECRETS_FOUND -gt 0 ]; then
|
||||||
|
echo "⚠️ Warning: Found $SECRETS_FOUND potential secrets"
|
||||||
|
echo "Secrets should be encrypted with SOPS or stored in ArgoCD Sealed Secrets"
|
||||||
|
else
|
||||||
|
echo "✓ No hardcoded secrets found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# === Check K8s Security Best Practices ===
|
||||||
|
- name: Check K8s Security Best Practices
|
||||||
|
run: |
|
||||||
|
echo "=== Checking K8s security best practices ==="
|
||||||
|
|
||||||
|
if grep -r "privileged: true" k8s/ --include="*.yaml" --include="*.yml"; then
|
||||||
|
echo "⚠️ Found privileged containers"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if grep -r "hostNetwork: true" k8s/ --include="*.yaml" --include="*.yml"; then
|
||||||
|
echo "⚠️ Found hostNetwork usage"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Checking for missing resource limits..."
|
||||||
|
MISSING=0
|
||||||
|
find k8s -name "*.yaml" -o -name "*.yml" | while read file; do
|
||||||
|
if grep -q "kind: Deployment\|kind: StatefulSet\|kind: DaemonSet" "$file"; then
|
||||||
|
if ! grep -q "resources:" "$file"; then
|
||||||
|
echo "⚠️ $file: Missing resource requests/limits"
|
||||||
|
MISSING=$((MISSING + 1))
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# === ArgoCD Sync (main branch only) ===
|
||||||
|
- name: Sync ArgoCD
|
||||||
|
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
|
||||||
|
env:
|
||||||
|
ARGOCD_SERVER: ${{ secrets.ARGOCD_SERVER }}
|
||||||
|
ARGOCD_AUTH_TOKEN: ${{ secrets.ARGOCD_AUTH_TOKEN }}
|
||||||
|
run: |
|
||||||
|
echo "=== Syncing homelab-root ==="
|
||||||
|
argocd app sync homelab-root --force
|
||||||
|
argocd app wait homelab-root --timeout 5m
|
||||||
|
|
||||||
|
- name: Check Sync Status
|
||||||
|
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
|
||||||
|
env:
|
||||||
|
ARGOCD_SERVER: ${{ secrets.ARGOCD_SERVER }}
|
||||||
|
ARGOCD_AUTH_TOKEN: ${{ secrets.ARGOCD_AUTH_TOKEN }}
|
||||||
|
run: |
|
||||||
|
echo "=== ArgoCD Applications Status ==="
|
||||||
|
argocd app list -o table
|
||||||
|
|
||||||
|
STATUS=$(argocd app get homelab-root -o jsonpath='{.status.syncStatus}')
|
||||||
|
if [ "$STATUS" != "Synced" ]; then
|
||||||
|
echo "❌ Root app sync failed: $STATUS"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "✓ Root app synced successfully"
|
||||||
|
|
||||||
|
- name: Health Check
|
||||||
|
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
|
||||||
|
env:
|
||||||
|
ARGOCD_SERVER: ${{ secrets.ARGOCD_SERVER }}
|
||||||
|
ARGOCD_AUTH_TOKEN: ${{ secrets.ARGOCD_AUTH_TOKEN }}
|
||||||
|
run: |
|
||||||
|
echo "=== Checking Application Health ==="
|
||||||
|
argocd app get homelab-root -o wide
|
||||||
|
|
||||||
|
# === Summary ===
|
||||||
|
- name: Summary
|
||||||
|
if: always()
|
||||||
|
run: |
|
||||||
|
echo "=== CI Pipeline Summary ==="
|
||||||
|
echo "✓ YAML linted"
|
||||||
|
echo "✓ Manifests validated"
|
||||||
|
echo "✓ Kustomizations built"
|
||||||
|
echo "✓ Security scans completed"
|
||||||
|
echo "✓ Secrets check passed"
|
||||||
|
echo "✓ Best practices verified"
|
||||||
|
echo ""
|
||||||
|
echo "✓ All checks passed"
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
name: Security Scan
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- develop
|
|
||||||
paths:
|
|
||||||
- 'k8s/**'
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- 'k8s/**'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
security:
|
|
||||||
runs-on: docker
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
run: |
|
|
||||||
REPO_URL="${{ gitea.server_url }}/${{ gitea.repository }}.git"
|
|
||||||
CLONE_URL="https://${{ secrets.CI_RUNNER }}:${{ secrets.CI_RUNNER_SECRET }}@${REPO_URL#https://}"
|
|
||||||
git clone --depth 1 "$CLONE_URL" .
|
|
||||||
cd ${GITHUB_WORKSPACE}
|
|
||||||
git fetch origin main
|
|
||||||
git checkout main
|
|
||||||
|
|
||||||
- name: Install Tools
|
|
||||||
run: |
|
|
||||||
apt-get update && apt-get install -y \
|
|
||||||
python3-pip \
|
|
||||||
curl
|
|
||||||
|
|
||||||
# Install Trivy (vulnerability scanner)
|
|
||||||
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
|
|
||||||
|
|
||||||
# Install Polaris (K8s security audit)
|
|
||||||
curl -L https://github.com/FairwindsOps/polaris/releases/latest/download/polaris-linux-amd64 -o /usr/local/bin/polaris
|
|
||||||
chmod +x /usr/local/bin/polaris
|
|
||||||
|
|
||||||
- name: Trivy - Scan Dockerfile (if present)
|
|
||||||
run: |
|
|
||||||
if find . -name "Dockerfile" 2>/dev/null | grep -v node_modules | head -1 | grep -q .; then
|
|
||||||
echo "=== Scanning Dockerfiles with Trivy ==="
|
|
||||||
find . -name "Dockerfile" -not -path "*/node_modules/*" -exec trivy config {} \;
|
|
||||||
else
|
|
||||||
echo "No Dockerfiles found"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Trivy - Scan Helm Charts
|
|
||||||
run: |
|
|
||||||
if find k8s -name "Chart.yaml" 2>/dev/null | head -1 | grep -q .; then
|
|
||||||
echo "=== Scanning Helm charts with Trivy ==="
|
|
||||||
find k8s -name "Chart.yaml" -exec dirname {} \; | while read chart; do
|
|
||||||
echo "Scanning $chart..."
|
|
||||||
trivy config "$chart" || true
|
|
||||||
done
|
|
||||||
else
|
|
||||||
echo "No Helm charts found"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Polaris - K8s Security Audit
|
|
||||||
run: |
|
|
||||||
echo "=== Running Polaris K8s security audit ==="
|
|
||||||
polaris audit --audit-path /tmp/polaris-audit.json k8s/ || true
|
|
||||||
|
|
||||||
if [ -f /tmp/polaris-audit.json ]; then
|
|
||||||
echo "Security issues found:"
|
|
||||||
jq '.results[] | select(.pass == false)' /tmp/polaris-audit.json || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Check for Secrets in Code
|
|
||||||
run: |
|
|
||||||
echo "=== Scanning for hardcoded secrets ==="
|
|
||||||
SECRETS_FOUND=0
|
|
||||||
|
|
||||||
# Check for common secret patterns
|
|
||||||
for pattern in "password:" "secret:" "token:" "api_key:" "apikey:" "private_key:" "privatekey:"; do
|
|
||||||
if grep -r "$pattern" k8s/ --include="*.yaml" --include="*.yml" | grep -v "^Binary"; then
|
|
||||||
echo "⚠️ Found potential secret pattern: $pattern"
|
|
||||||
SECRETS_FOUND=$((SECRETS_FOUND + 1))
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ $SECRETS_FOUND -gt 0 ]; then
|
|
||||||
echo "⚠️ Warning: Found $SECRETS_FOUND potential secrets"
|
|
||||||
echo "Secrets should be encrypted with SOPS or stored in ArgoCD Sealed Secrets"
|
|
||||||
else
|
|
||||||
echo "✓ No hardcoded secrets found"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Check for Security Best Practices
|
|
||||||
run: |
|
|
||||||
echo "=== Checking K8s security best practices ==="
|
|
||||||
|
|
||||||
# Check for privileged containers
|
|
||||||
if grep -r "privileged: true" k8s/ --include="*.yaml" --include="*.yml"; then
|
|
||||||
echo "⚠️ Found privileged containers"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check for hostNetwork
|
|
||||||
if grep -r "hostNetwork: true" k8s/ --include="*.yaml" --include="*.yml"; then
|
|
||||||
echo "⚠️ Found hostNetwork usage"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check for missing resource limits
|
|
||||||
echo "Checking for missing resource requests/limits..."
|
|
||||||
MISSING=0
|
|
||||||
find k8s -name "*.yaml" -o -name "*.yml" | while read file; do
|
|
||||||
if grep -q "kind: Deployment\|kind: StatefulSet\|kind: DaemonSet" "$file"; then
|
|
||||||
if ! grep -q "resources:" "$file"; then
|
|
||||||
echo "⚠️ $file: Missing resource requests/limits"
|
|
||||||
MISSING=$((MISSING + 1))
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
- name: Summary
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
echo "=== Security Scan Summary ==="
|
|
||||||
echo "✓ Dockerfiles scanned"
|
|
||||||
echo "✓ Helm charts scanned"
|
|
||||||
echo "✓ K8s manifests audited"
|
|
||||||
echo "✓ Secrets check completed"
|
|
||||||
echo "✓ Best practices verified"
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
name: Validate Kubernetes Manifests
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- develop
|
|
||||||
paths:
|
|
||||||
- 'k8s/**'
|
|
||||||
- '.forgejo/workflows/validate-k8s.yaml'
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- 'k8s/**'
|
|
||||||
- '.forgejo/workflows/validate-k8s.yaml'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
validate:
|
|
||||||
runs-on: docker
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
run: |
|
|
||||||
REPO_URL="${{ gitea.server_url }}/${{ gitea.repository }}.git"
|
|
||||||
CLONE_URL="https://${{ secrets.CI_RUNNER }}:${{ secrets.CI_RUNNER_SECRET }}@${REPO_URL#https://}"
|
|
||||||
git clone --depth 1 "$CLONE_URL" .
|
|
||||||
cd ${GITHUB_WORKSPACE}
|
|
||||||
git fetch origin main
|
|
||||||
git checkout main
|
|
||||||
|
|
||||||
- name: Install Tools
|
|
||||||
run: |
|
|
||||||
apt-get update && apt-get install -y \
|
|
||||||
yamllint \
|
|
||||||
python3-pip \
|
|
||||||
curl \
|
|
||||||
jq
|
|
||||||
|
|
||||||
# Install kubeval
|
|
||||||
curl -L https://github.com/instrumenta/kubeval/releases/latest/download/kubeval-linux-amd64.tar.gz | tar xz
|
|
||||||
mv -f kubeval /usr/local/bin/
|
|
||||||
|
|
||||||
# Install kustomize (remove old if exists)
|
|
||||||
rm -f kustomize
|
|
||||||
curl -s https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh | bash
|
|
||||||
mv -f kustomize /usr/local/bin/
|
|
||||||
|
|
||||||
# Install ArgoCD CLI
|
|
||||||
curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
|
|
||||||
chmod +x /usr/local/bin/argocd
|
|
||||||
|
|
||||||
- name: YAML Lint
|
|
||||||
run: |
|
|
||||||
echo "=== Linting YAML files ==="
|
|
||||||
yamllint k8s/ -c .yamllint.yaml || true
|
|
||||||
|
|
||||||
- name: Kubeval - Validate K8s Syntax
|
|
||||||
run: |
|
|
||||||
echo "=== Validating Kubernetes manifests ==="
|
|
||||||
find k8s -name "*.yaml" -o -name "*.yml" | grep -v "\.archive" | while read file; do
|
|
||||||
echo "Validating $file..."
|
|
||||||
kubeval "$file" -d 2>/dev/null || true
|
|
||||||
done
|
|
||||||
|
|
||||||
- name: Kustomize Build - Infrastructure
|
|
||||||
run: |
|
|
||||||
echo "=== Building k8s/infrastructure/ ==="
|
|
||||||
kustomize build k8s/infrastructure > /tmp/infrastructure.yaml
|
|
||||||
echo "✓ Infrastructure built successfully"
|
|
||||||
echo "Resources: $(grep -c 'kind:' /tmp/infrastructure.yaml)"
|
|
||||||
|
|
||||||
- name: Kustomize Build - Bootstrap
|
|
||||||
run: |
|
|
||||||
echo "=== Building k8s/bootstrap/ ==="
|
|
||||||
kustomize build k8s/bootstrap > /tmp/bootstrap.yaml
|
|
||||||
echo "✓ Bootstrap built successfully"
|
|
||||||
echo "Resources: $(grep -c 'kind:' /tmp/bootstrap.yaml || echo 0)"
|
|
||||||
|
|
||||||
- name: Kustomize Build - Platform
|
|
||||||
run: |
|
|
||||||
echo "=== Building k8s/platform/ ==="
|
|
||||||
kustomize build k8s/platform > /tmp/platform.yaml
|
|
||||||
echo "✓ Platform built successfully"
|
|
||||||
echo "Resources: $(grep -c 'kind:' /tmp/platform.yaml || echo 0)"
|
|
||||||
|
|
||||||
- name: Kustomize Build - Security
|
|
||||||
run: |
|
|
||||||
echo "=== Building k8s/security/ ==="
|
|
||||||
kustomize build k8s/security > /tmp/security.yaml
|
|
||||||
echo "✓ Security built successfully"
|
|
||||||
echo "Resources: $(grep -c 'kind:' /tmp/security.yaml || echo 0)"
|
|
||||||
|
|
||||||
- name: Kustomize Build - Applications
|
|
||||||
run: |
|
|
||||||
echo "=== Building k8s/applications/ ==="
|
|
||||||
kustomize build k8s/applications > /tmp/applications.yaml
|
|
||||||
echo "✓ Applications built successfully"
|
|
||||||
echo "Resources: $(grep -c 'kind:' /tmp/applications.yaml || echo 0)"
|
|
||||||
|
|
||||||
- name: Kustomize Build - Data
|
|
||||||
run: |
|
|
||||||
echo "=== Building k8s/data/ ==="
|
|
||||||
kustomize build k8s/data > /tmp/data.yaml
|
|
||||||
echo "✓ Data built successfully"
|
|
||||||
echo "Resources: $(grep -c 'kind:' /tmp/data.yaml || echo 0)"
|
|
||||||
|
|
||||||
- name: Validate ArgoCD Applications
|
|
||||||
run: |
|
|
||||||
echo "=== Validating ArgoCD Applications ==="
|
|
||||||
kubeval k8s/argocd/apps/*.yaml
|
|
||||||
|
|
||||||
- name: Summary
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
echo "=== Validation Summary ==="
|
|
||||||
echo "✓ All manifests validated"
|
|
||||||
echo "✓ All kustomizations built"
|
|
||||||
echo "✓ All ArgoCD apps valid"
|
|
||||||
echo ""
|
|
||||||
echo "Next: Push to main → ArgoCD syncs automatically"
|
|
||||||
@@ -46,3 +46,4 @@ terraform/*.tfstate.*
|
|||||||
terraform.tfvars.local
|
terraform.tfvars.local
|
||||||
skills-lock.json
|
skills-lock.json
|
||||||
secrets-plaintext.yaml
|
secrets-plaintext.yaml
|
||||||
|
skills-lock.json
|
||||||
@@ -1,324 +0,0 @@
|
|||||||
# Flux CD Integration Planning — START HERE
|
|
||||||
|
|
||||||
## What Just Happened?
|
|
||||||
|
|
||||||
Your subagent completed **comprehensive planning documentation** for integrating Flux CD v2 with your homelab's helmfile-based infrastructure.
|
|
||||||
|
|
||||||
**Three complete documents created:**
|
|
||||||
|
|
||||||
1. **FLUX_INTEGRATION_PLAN.md** (1,810 lines)
|
|
||||||
- Full technical specification with code examples
|
|
||||||
- Phase-by-phase implementation roadmap
|
|
||||||
- Conflict resolution & safety procedures
|
|
||||||
- Testing strategy & risk assessment
|
|
||||||
|
|
||||||
2. **FLUX_PLANNING_SUMMARY.md** (351 lines)
|
|
||||||
- Executive overview for stakeholders
|
|
||||||
- Decision matrices & quick reference
|
|
||||||
- Timeline & effort estimates
|
|
||||||
- Success metrics
|
|
||||||
|
|
||||||
3. **FLUX_PLANNING_INDEX.md** (356 lines)
|
|
||||||
- Navigation guide across all documents
|
|
||||||
- Quick start for different audiences
|
|
||||||
- FAQ & next steps
|
|
||||||
|
|
||||||
**Total:** 2,517 lines of planning documentation
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## The Plan in 60 Seconds
|
|
||||||
|
|
||||||
### What Problem Are We Solving?
|
|
||||||
|
|
||||||
Current helmfile workflow:
|
|
||||||
- Manual `helmfile apply` required
|
|
||||||
- No automatic drift detection
|
|
||||||
- No Git audit trail for changes
|
|
||||||
- No approval gates
|
|
||||||
- Hard to scale to multi-cluster
|
|
||||||
|
|
||||||
### What's the Solution?
|
|
||||||
|
|
||||||
Deploy **Flux CD v2** (GitOps) to:
|
|
||||||
- Continuously reconcile cluster state from Git
|
|
||||||
- Auto-detect & correct drift
|
|
||||||
- Maintain full audit trail
|
|
||||||
- Support staged rollouts with approval gates
|
|
||||||
- Keep helmfile.yaml.gotmpl as fallback during transition
|
|
||||||
|
|
||||||
### How Do We Do It?
|
|
||||||
|
|
||||||
**3 phases, 6–8 weeks, ~99 hours:**
|
|
||||||
|
|
||||||
| Phase | Timeline | Work | Goal |
|
|
||||||
|-------|----------|------|------|
|
|
||||||
| **1** | Weeks 1–2 | Bootstrap Flux + helmfile bridge | Zero breaking changes |
|
|
||||||
| **2** | Weeks 3–6 | Migrate 23 releases to HelmRelease CRDs | Parallel migration (4 streams) |
|
|
||||||
| **3** | Weeks 7–8 | Enable auto-sync, metrics, runbooks | Full GitOps readiness |
|
|
||||||
|
|
||||||
**Key:** No downtime. Helmfile stays functional as fallback throughout.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Architecture Simplified
|
|
||||||
|
|
||||||
```
|
|
||||||
Git (Forgejo) ← Source of Truth
|
|
||||||
└─→ Flux Reconciliation Loop (every 5 min)
|
|
||||||
└─→ Kubernetes Cluster
|
|
||||||
└─→ 23 Helm Releases (reconciled state)
|
|
||||||
```
|
|
||||||
|
|
||||||
That's it. Flux watches Git. When you push changes, Flux applies them. If someone manually changes the cluster (kubectl), Flux auto-corrects on next reconciliation.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Key Decisions (No Surprises)
|
|
||||||
|
|
||||||
| Decision | Choice | Reasoning |
|
|
||||||
|----------|--------|-----------|
|
|
||||||
| **Controller** | Flux v2 | Stable, battle-tested; v3 still beta |
|
|
||||||
| **Helm** | HelmRelease CRDs | Preserves values-based workflow |
|
|
||||||
| **Secrets** | SOPS + age | Git-stored, audited, simple |
|
|
||||||
| **Rollout** | Phased (3×8 weeks) | Lower risk, easier debugging |
|
|
||||||
|
|
||||||
All decisions explained in detail in FLUX_INTEGRATION_PLAN.md §3 (Architecture Decision Matrix).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## What You Get
|
|
||||||
|
|
||||||
### By End of Phase 1 (Week 2)
|
|
||||||
- ✅ Flux running in cluster
|
|
||||||
- ✅ Git syncing every 60 seconds
|
|
||||||
- ✅ Helmfile still works as fallback
|
|
||||||
- ✅ Zero disruption to running workloads
|
|
||||||
|
|
||||||
### By End of Phase 2 (Week 6)
|
|
||||||
- ✅ All 23 releases migrated to Git-based HelmRelease CRDs
|
|
||||||
- ✅ Helmfile no longer used for deployments
|
|
||||||
- ✅ Every release tested & verified
|
|
||||||
- ✅ Full test suite in place
|
|
||||||
|
|
||||||
### By End of Phase 3 (Week 8)
|
|
||||||
- ✅ Automatic reconciliation enabled
|
|
||||||
- ✅ Drift detection + alerting working
|
|
||||||
- ✅ Metrics flowing to Prometheus
|
|
||||||
- ✅ Team trained on GitOps workflows
|
|
||||||
- ✅ RTO < 2 hours (restore from Git if needed)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## How to Read the Documentation
|
|
||||||
|
|
||||||
### Quick Overview (10 min)
|
|
||||||
→ **Read:** FLUX_PLANNING_SUMMARY.md
|
|
||||||
|
|
||||||
Start here to understand what we're doing and why. Tables, diagrams, high-level summary. Perfect for stakeholder presentations.
|
|
||||||
|
|
||||||
### Getting Ready to Build (1 hour)
|
|
||||||
→ **Read:** FLUX_PLANNING_INDEX.md + FLUX_INTEGRATION_PLAN.md (Executive Summary)
|
|
||||||
|
|
||||||
Learn the full architecture, decision rationale, and how phases fit together.
|
|
||||||
|
|
||||||
### Phase 1 Implementation (Week 1–2)
|
|
||||||
→ **Reference:** FLUX_INTEGRATION_PLAN.md §5.1 (Phase 1: Flux Bootstrap)
|
|
||||||
|
|
||||||
Detailed tasks:
|
|
||||||
- 1.1: Bootstrap Flux into cluster
|
|
||||||
- 1.2: Create Git repo structure
|
|
||||||
- 1.3: HelmRepository CRDs (13 repos)
|
|
||||||
- 1.4: SOPS + age setup
|
|
||||||
- 1.5: Helmfile-bridge CronJob
|
|
||||||
|
|
||||||
### Phase 2 Migration (Weeks 3–6)
|
|
||||||
→ **Reference:** FLUX_INTEGRATION_PLAN.md §5.2 (Phase 2: HelmRelease Migration)
|
|
||||||
|
|
||||||
Four parallel streams:
|
|
||||||
- Stream A: Low-risk (reloader, prometheus)
|
|
||||||
- Stream B: Medium-risk (cert-manager, ingress)
|
|
||||||
- Stream C: High-risk secrets (authentik, vault)
|
|
||||||
- Stream D: Complex stateful (minio, forgejo)
|
|
||||||
|
|
||||||
Per-release process: generate CRD → validate → deploy → test → commit
|
|
||||||
|
|
||||||
### Phase 3 Production Readiness (Weeks 7–8)
|
|
||||||
→ **Reference:** FLUX_INTEGRATION_PLAN.md §5.3 (Phase 3: Continuous Reconciliation)
|
|
||||||
|
|
||||||
Auto-sync, metrics, runbooks, team training.
|
|
||||||
|
|
||||||
### Troubleshooting & Rollback
|
|
||||||
→ **Reference:** FLUX_INTEGRATION_PLAN.md §7 (Rollback & Safety Guardrails)
|
|
||||||
|
|
||||||
How to recover if something breaks:
|
|
||||||
- Suspend Flux + manual rollback
|
|
||||||
- Git revert + auto-reconciliation
|
|
||||||
- Disaster recovery from Git
|
|
||||||
|
|
||||||
### Testing Strategy
|
|
||||||
→ **Reference:** FLUX_INTEGRATION_PLAN.md §8 (Testing Strategy)
|
|
||||||
|
|
||||||
Unit tests, integration tests, chaos tests, production deployment strategy.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Risk Summary
|
|
||||||
|
|
||||||
### Main Risks & How We Handle Them
|
|
||||||
|
|
||||||
| Risk | Mitigation |
|
|
||||||
|------|-----------|
|
|
||||||
| **Flux + helmfile conflict** | Stagger reconciliation (helmfile 30min, Flux 5min) |
|
|
||||||
| **Secret injection breaks** | Three-tier approach (SOPS + ConfigMaps + .env fallback) |
|
|
||||||
| **Secrets leak in Git** | SOPS encryption from start + pre-commit hooks |
|
|
||||||
| **Cluster recovery fails** | Keep helmfile as fallback; test quarterly |
|
|
||||||
|
|
||||||
All risks detailed with specific mitigations in FLUX_INTEGRATION_PLAN.md §9 (Risk Assessment).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Timeline Reality Check
|
|
||||||
|
|
||||||
```
|
|
||||||
Week 1–2: Phase 1 bootstrap (20 hrs)
|
|
||||||
├─ 1 DevOps engineer + 1 Security engineer
|
|
||||||
└─ 0 downtime to running workloads
|
|
||||||
|
|
||||||
Week 3–6: Phase 2 migration (40 hrs)
|
|
||||||
├─ 4 parallel streams (DevOps + Ops + Security)
|
|
||||||
└─ Release-by-release (low risk)
|
|
||||||
|
|
||||||
Week 7–8: Phase 3 hardening (16 hrs)
|
|
||||||
├─ DevOps + QA
|
|
||||||
└─ Runbooks + training
|
|
||||||
|
|
||||||
Total: ~99 hours (~2.5 FTE-weeks)
|
|
||||||
6–8 calendar weeks (with parallelization)
|
|
||||||
```
|
|
||||||
|
|
||||||
Actual timeline depends on:
|
|
||||||
- Team size (4 engineers = 8 weeks; 2 engineers = 12 weeks)
|
|
||||||
- Experience with Flux (learning curve ~40 hours)
|
|
||||||
- Testing rigor (each phase adds 1–2 weeks)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Next Actions
|
|
||||||
|
|
||||||
### Immediately (Today)
|
|
||||||
|
|
||||||
1. **Review FLUX_PLANNING_SUMMARY.md** (15 min)
|
|
||||||
- Understand the approach
|
|
||||||
- Check decision matrix
|
|
||||||
- Confirm timeline is acceptable
|
|
||||||
|
|
||||||
2. **Share with stakeholders**
|
|
||||||
- Security team: review SOPS approach
|
|
||||||
- Ops team: review rollback procedures
|
|
||||||
- Management: confirm timeline & resources
|
|
||||||
|
|
||||||
3. **Get approval** for:
|
|
||||||
- Phased approach (6–8 weeks)
|
|
||||||
- Flux v2 + HelmRelease CRDs
|
|
||||||
- SOPS encryption for secrets
|
|
||||||
- ~99 hours effort
|
|
||||||
|
|
||||||
### Week 1 (Phase 1 Kickoff)
|
|
||||||
|
|
||||||
1. **Assign team members**
|
|
||||||
- DevOps lead
|
|
||||||
- Security engineer (SOPS)
|
|
||||||
- Ops engineer (testing)
|
|
||||||
|
|
||||||
2. **Bootstrap Flux**
|
|
||||||
- `flux bootstrap git` command
|
|
||||||
- Set up Git repo structure
|
|
||||||
- Deploy HelmRepository CRDs
|
|
||||||
|
|
||||||
3. **Start helmfile-bridge development**
|
|
||||||
- CronJob to run `helmfile apply` every 30 min
|
|
||||||
- Test alongside Flux (staggered intervals)
|
|
||||||
|
|
||||||
### Weeks 3–8 (Phases 2 & 3)
|
|
||||||
|
|
||||||
Follow the phase roadmap in FLUX_INTEGRATION_PLAN.md with weekly syncs.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Files Created
|
|
||||||
|
|
||||||
All in `/Users/rockliang/workplace/homelab/`:
|
|
||||||
|
|
||||||
1. **FLUX_INTEGRATION_PLAN.md** (55 KB)
|
|
||||||
- Complete technical specification
|
|
||||||
- Phase-by-phase breakdown
|
|
||||||
- Code examples & detailed procedures
|
|
||||||
|
|
||||||
2. **FLUX_PLANNING_SUMMARY.md** (13 KB)
|
|
||||||
- Executive overview
|
|
||||||
- Decision matrices
|
|
||||||
- Quick reference tables
|
|
||||||
|
|
||||||
3. **FLUX_PLANNING_INDEX.md** (13 KB)
|
|
||||||
- Navigation guide
|
|
||||||
- Quick start by audience
|
|
||||||
- FAQ & related docs
|
|
||||||
|
|
||||||
4. **_FLUX_START_HERE.md** (this file)
|
|
||||||
- Quick orientation
|
|
||||||
- Next actions
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Questions to Ask
|
|
||||||
|
|
||||||
Before Phase 1 starts, clarify:
|
|
||||||
|
|
||||||
1. **Team capacity?** How many FTE can we dedicate?
|
|
||||||
- 4 FTE → 8 weeks
|
|
||||||
- 2 FTE → 12 weeks
|
|
||||||
|
|
||||||
2. **Timeline flexibility?** Hard deadline or can we adjust?
|
|
||||||
- If hard: compress with more parallel streams
|
|
||||||
- If flexible: add more testing/validation
|
|
||||||
|
|
||||||
3. **Flux experience on team?** Anyone used Flux before?
|
|
||||||
- If no: add 1–2 weeks for learning curve
|
|
||||||
- If yes: can reduce onboarding time
|
|
||||||
|
|
||||||
4. **Multi-cluster plans?** Will you add more clusters after homelab?
|
|
||||||
- If yes: design for portability from start
|
|
||||||
- If no: homelab-specific is fine
|
|
||||||
|
|
||||||
5. **SOPS comfort?** Any concerns about secret encryption in Git?
|
|
||||||
- If yes: alternative is store in Vault (referenced from HelmRelease)
|
|
||||||
- If no: SOPS is recommended
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Document Quality Checklist
|
|
||||||
|
|
||||||
The planning documentation includes:
|
|
||||||
|
|
||||||
- ✅ **Executive summary** — problem & solution in 1 page
|
|
||||||
- ✅ **Current state analysis** — what we're migrating from
|
|
||||||
- ✅ **Architecture decisions** — Flux v2, HelmRelease, SOPS (with reasoning)
|
|
||||||
- ✅ **Detailed design** — GitRepository, Kustomization, HelmRelease CRDs
|
|
||||||
- ✅ **3-phase roadmap** — specific tasks, timelines, deliverables, success criteria
|
|
||||||
- ✅ **Conflict resolution** — helmfile + Flux, .env → SOPS, kubectl drift
|
|
||||||
- ✅ **Rollback procedures** — what to do if something breaks
|
|
||||||
- ✅ **Safety guardrails** — RBAC, audit logging, validation webhooks, approval gates
|
|
||||||
- ✅ **Testing strategy** — unit, integration, chaos, production deployment
|
|
||||||
- ✅ **Risk assessment** — probability, impact, mitigation for each risk
|
|
||||||
- ✅ **Timeline & effort** — 99 hours, 6-8 weeks, team composition
|
|
||||||
- ✅ **Useful commands** — Flux CLI cheatsheet
|
|
||||||
- ✅ **FAQ** — downtime, rollback, recovery, cost
|
|
||||||
|
|
||||||
Ready for review and implementation kickoff.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Status:** Planning phase complete. Ready for team discussion & approval.
|
|
||||||
|
|
||||||
**Next:** Review FLUX_PLANNING_SUMMARY.md, approve approach, assign Phase 1 team.
|
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
# Phase 1: Migrate 9 Hookless Releases to ArgoCD
|
|
||||||
|
|
||||||
## Releases to migrate (no presync/postsync hooks)
|
|
||||||
1. strimzi-operator
|
|
||||||
2. kafka-cluster
|
|
||||||
3. kmsvc-redis
|
|
||||||
4. queue-crd
|
|
||||||
5. management-service
|
|
||||||
6. promtail
|
|
||||||
7. blackbox-exporter
|
|
||||||
8. portainer
|
|
||||||
9. claude-terminal
|
|
||||||
|
|
||||||
## Pattern per release
|
|
||||||
|
|
||||||
### 1. Create Application in k8s/argocd/apps/
|
|
||||||
|
|
||||||
**Helmfile source:**
|
|
||||||
```yaml
|
|
||||||
- name: strimzi-operator
|
|
||||||
namespace: sqs
|
|
||||||
createNamespace: true
|
|
||||||
chart: strimzi/strimzi-kafka-operator
|
|
||||||
version: 0.46.0
|
|
||||||
values:
|
|
||||||
- watchNamespaces: ["sqs"]
|
|
||||||
```
|
|
||||||
|
|
||||||
**ArgoCD Application:**
|
|
||||||
```yaml
|
|
||||||
apiVersion: argoproj.io/v1alpha1
|
|
||||||
kind: Application
|
|
||||||
metadata:
|
|
||||||
name: strimzi-operator
|
|
||||||
namespace: argocd
|
|
||||||
annotations:
|
|
||||||
argocd.argoproj.io/sync-wave: "0"
|
|
||||||
spec:
|
|
||||||
project: homelab
|
|
||||||
source:
|
|
||||||
repoURL: https://strimzi.io/charts/
|
|
||||||
chart: strimzi-kafka-operator
|
|
||||||
targetRevision: 0.46.0
|
|
||||||
helm:
|
|
||||||
values: |
|
|
||||||
watchNamespaces: ["sqs"]
|
|
||||||
destination:
|
|
||||||
server: https://kubernetes.default.svc
|
|
||||||
namespace: sqs
|
|
||||||
syncPolicy:
|
|
||||||
automated:
|
|
||||||
prune: true
|
|
||||||
selfHeal: true
|
|
||||||
syncOptions:
|
|
||||||
- CreateNamespace=true
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Test ArgoCD application (dry-run)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# If app created, check diff
|
|
||||||
kubectl apply -f k8s/argocd/apps/{WAVE}-{RELEASE}.yaml --dry-run=client -o yaml
|
|
||||||
|
|
||||||
# Or: use argocd CLI
|
|
||||||
argocd app diff {RELEASE} # should be clean (no diffs) if spec matches helmfile
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Remove from helmfile
|
|
||||||
|
|
||||||
Delete the release block from `helmfile.yaml.gotmpl`, commit.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Verify no unintended drift
|
|
||||||
helmfile diff
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Commit
|
|
||||||
|
|
||||||
Per-release commit (one app at a time).
|
|
||||||
|
|
||||||
## Helmfile → Application mapping
|
|
||||||
|
|
||||||
| Helmfile | Chart | Namespace | Version | Wave | Status |
|
|
||||||
|----------|-------|-----------|---------|------|--------|
|
|
||||||
| strimzi-operator | strimzi/strimzi-kafka-operator | sqs | 0.46.0 | 0 | TODO |
|
|
||||||
| kmsvc-redis | bitnami/redis | sqs | 20.6.0 | 0 | TODO |
|
|
||||||
| prometheus | prometheus-community/kube-prometheus-stack | monitoring | latest | 0 | TODO |
|
|
||||||
| kafka-cluster | ./k8s/sqs/charts/kafka-cluster | sqs | local | 1 | TODO |
|
|
||||||
| queue-crd | ./k8s/sqs/charts/queue-crd | sqs | local | 1 | TODO |
|
|
||||||
| management-service | ./k8s/sqs/charts/management-service | sqs | local | 1 | TODO |
|
|
||||||
| promtail | grafana/promtail | logging | latest | 1 | TODO |
|
|
||||||
| blackbox-exporter | prometheus-community/prometheus-blackbox-exporter | monitoring | ~11 | 1 | TODO |
|
|
||||||
| portainer | portainer/portainer | dashboard | latest | 3 | TODO |
|
|
||||||
| claude-terminal | ./k8s/dev-tools | dev-tools | local | 3 | TODO |
|
|
||||||
|
|
||||||
## Local charts mapping
|
|
||||||
|
|
||||||
For local charts (e.g., `./k8s/sqs/charts/kafka-cluster`), use `source.path` instead of `source.chart`:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
source:
|
|
||||||
repoURL: https://forgejo.riotpiao.homelab.com/riotpiao.com/homelab.git
|
|
||||||
targetRevision: main
|
|
||||||
path: k8s/sqs/charts/kafka-cluster
|
|
||||||
helm:
|
|
||||||
valueFiles:
|
|
||||||
- values.yaml # or path to values override
|
|
||||||
```
|
|
||||||
|
|
||||||
## Verification
|
|
||||||
|
|
||||||
After all 9 releases migrated:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
argocd app list | grep -E "strimzi-operator|kmsvc-redis|prometheus|kafka-cluster|queue-crd|management-service|promtail|blackbox-exporter|portainer|claude-terminal"
|
|
||||||
|
|
||||||
# All should show: Synced | Healthy
|
|
||||||
```
|
|
||||||
|
|
||||||
Then:
|
|
||||||
```bash
|
|
||||||
helmfile diff # should show no diffs (these releases removed from helmfile)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Rollback
|
|
||||||
|
|
||||||
If an Application breaks the cluster during migration:
|
|
||||||
1. Keep helmfile release block in git (don't delete until verified)
|
|
||||||
2. If needed: `helmfile apply -l name={RELEASE}` restores from helmfile
|
|
||||||
3. Debug the Application spec and retry
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
# Terraform State Management
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
Terraform state for the homelab cluster is managed using a hybrid approach:
|
|
||||||
- **Remote backend:** S3 (MinIO) for centralized, shared state
|
|
||||||
- **Local backup:** Git-ignored backups for disaster recovery
|
|
||||||
|
|
||||||
## Backend Configuration
|
|
||||||
|
|
||||||
State is stored in MinIO S3:
|
|
||||||
|
|
||||||
```
|
|
||||||
Bucket: terraform-state
|
|
||||||
Key: homelab/terraform.tfstate
|
|
||||||
Endpoint: https://minio-api.riotpiao.homelab.com
|
|
||||||
Profile: minio
|
|
||||||
```
|
|
||||||
|
|
||||||
Configuration: `terraform/state.tf`
|
|
||||||
|
|
||||||
## Accessing State
|
|
||||||
|
|
||||||
### Pull state from S3
|
|
||||||
```bash
|
|
||||||
cd terraform
|
|
||||||
terraform state pull > terraform.tfstate.backup
|
|
||||||
```
|
|
||||||
|
|
||||||
### View resources
|
|
||||||
```bash
|
|
||||||
terraform state list
|
|
||||||
terraform state show <resource-name>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Import new resources
|
|
||||||
```bash
|
|
||||||
terraform import <resource-type>.<name> <resource-id>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Backup Strategy
|
|
||||||
|
|
||||||
### Automatic backups
|
|
||||||
Run the backup script periodically (e.g., cron):
|
|
||||||
```bash
|
|
||||||
scripts/terraform-state-backup.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
Backups are saved to: `~/.terraform-backups/homelab/`
|
|
||||||
|
|
||||||
### Manual backup
|
|
||||||
```bash
|
|
||||||
cd terraform
|
|
||||||
terraform state pull > /tmp/terraform-$(date +%s).tfstate
|
|
||||||
cp /tmp/terraform-*.tfstate ~/.terraform-backups/homelab/
|
|
||||||
```
|
|
||||||
|
|
||||||
## Disaster Recovery
|
|
||||||
|
|
||||||
If state is corrupted or lost:
|
|
||||||
|
|
||||||
1. **Stop all infrastructure changes:**
|
|
||||||
```bash
|
|
||||||
git revert <commit> # Rollback infrastructure changes
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Restore from local backup:**
|
|
||||||
```bash
|
|
||||||
BACKUP_FILE=~/.terraform-backups/homelab/<timestamp>-terraform.tfstate
|
|
||||||
cd terraform
|
|
||||||
terraform state push $BACKUP_FILE
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Verify state:**
|
|
||||||
```bash
|
|
||||||
terraform state list
|
|
||||||
terraform plan
|
|
||||||
```
|
|
||||||
|
|
||||||
## S3 Bucket Setup
|
|
||||||
|
|
||||||
If S3 bucket doesn't exist, create it:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
kubectl exec -n storage <minio-pod> -- mc mb minio/terraform-state --region us-east-1
|
|
||||||
```
|
|
||||||
|
|
||||||
## State Lock (Optional)
|
|
||||||
|
|
||||||
For multi-person teams, enable state locking via DynamoDB (not yet configured).
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
- ✓ Never commit `*.tfstate` or `*.tfstate.*` to git
|
|
||||||
- ✓ Back up state before major `terraform apply` operations
|
|
||||||
- ✓ Always run `terraform plan` before `terraform apply`
|
|
||||||
- ✓ Review diff carefully for destructive changes
|
|
||||||
- ✓ Keep state backend secure (MinIO has authentication)
|
|
||||||
|
|
||||||
## Monitoring
|
|
||||||
|
|
||||||
Check S3 backend status:
|
|
||||||
```bash
|
|
||||||
kubectl get pods -n storage -l app=minio
|
|
||||||
# Or
|
|
||||||
scripts/terraform-state-backup.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
## Related Files
|
|
||||||
|
|
||||||
- `terraform/state.tf` — Backend configuration
|
|
||||||
- `scripts/terraform-state-backup.sh` — Automated backup script
|
|
||||||
- `.gitignore` — Excludes local state files from git
|
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# Terraform state
|
||||||
|
*.tfstate
|
||||||
|
*.tfstate.*
|
||||||
|
.terraform/
|
||||||
|
.terraform.lock.hcl
|
||||||
|
|
||||||
|
# Variables with secrets
|
||||||
|
terraform.tfvars
|
||||||
|
*.auto.tfvars
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
# Generate Talos machine configurations
|
||||||
|
|
||||||
|
locals {
|
||||||
|
pod_cidr = var.cluster_config.pod_subnets[0]
|
||||||
|
service_cidr = var.cluster_config.service_subnets[0]
|
||||||
|
controlplane_ip = var.cluster_config.controlplane_ip
|
||||||
|
cluster_dns_ip = "10.96.0.10"
|
||||||
|
kubelet_image = "ghcr.io/siderolabs/kubelet:${var.kubernetes_version}"
|
||||||
|
kube_apiserver_img = "registry.k8s.io/kube-apiserver:${var.kubernetes_version}"
|
||||||
|
controller_mgr_img = "registry.k8s.io/kube-controller-manager:${var.kubernetes_version}"
|
||||||
|
kube_proxy_img = "registry.k8s.io/kube-proxy:${var.kubernetes_version}"
|
||||||
|
scheduler_img = "registry.k8s.io/kube-scheduler:${var.kubernetes_version}"
|
||||||
|
|
||||||
|
factory_image = "factory.talos.dev/installer/613e1592b2da41ae5e265e8789429f22e121aab91cb4deb6bc3c0b6262961245:${var.talos_version}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Control plane machine configuration
|
||||||
|
resource "local_file" "controlplane_config" {
|
||||||
|
filename = "${path.module}/../cluster-config/controlplane.yaml"
|
||||||
|
|
||||||
|
content = templatefile("${path.module}/templates/controlplane.tftpl", {
|
||||||
|
version = "v1alpha1"
|
||||||
|
hostname = var.controlplane_config.hostname
|
||||||
|
token = var.machine_token
|
||||||
|
ca_crt = var.machine_ca_crt
|
||||||
|
ca_key = var.machine_ca_key
|
||||||
|
lan_ip = var.controlplane_config.lan_ip
|
||||||
|
lan_subnet = var.controlplane_config.lan_subnet
|
||||||
|
lan_gateway = var.controlplane_config.lan_gateway
|
||||||
|
wg0_ip = var.controlplane_config.wg0_ip
|
||||||
|
wg0_subnet = var.controlplane_config.wg0_subnet
|
||||||
|
wg0_port = var.controlplane_config.wg0_port
|
||||||
|
wg0_private_key = var.controlplane_config.wg0_private_key
|
||||||
|
wg0_peers = var.controlplane_config.wg0_peers
|
||||||
|
wg1_ip = var.controlplane_config.wg1_ip
|
||||||
|
wg1_subnet = var.controlplane_config.wg1_subnet
|
||||||
|
wg1_port = var.controlplane_config.wg1_port
|
||||||
|
wg1_private_key = var.controlplane_config.wg1_private_key
|
||||||
|
wg1_peers = var.controlplane_config.wg1_peers
|
||||||
|
kubelet_image = local.kubelet_image
|
||||||
|
cluster_dns_ip = local.cluster_dns_ip
|
||||||
|
install_disk = var.controlplane_config.install_disk
|
||||||
|
factory_image = local.factory_image
|
||||||
|
longhorn_disks = var.controlplane_config.longhorn_disks
|
||||||
|
dns_servers = var.cluster_config.dns_servers
|
||||||
|
forgejo_registry_ip = var.forgejo_registry_ip
|
||||||
|
forgejo_hostname = var.forgejo_hostname
|
||||||
|
|
||||||
|
# Cluster config
|
||||||
|
cluster_id = var.cluster_id
|
||||||
|
cluster_secret = var.cluster_secret
|
||||||
|
controlplane_ip = local.controlplane_ip
|
||||||
|
cluster_name = var.cluster_name
|
||||||
|
pod_subnets = var.cluster_config.pod_subnets
|
||||||
|
service_subnets = var.cluster_config.service_subnets
|
||||||
|
dns_domain = var.cluster_config.dns_domain
|
||||||
|
bootstrap_token = var.bootstrap_token
|
||||||
|
|
||||||
|
# Kubernetes certs
|
||||||
|
kubernetes_ca_crt = var.kubernetes_ca_crt
|
||||||
|
kubernetes_ca_key = var.kubernetes_ca_key
|
||||||
|
etcd_ca_crt = var.etcd_ca_crt
|
||||||
|
etcd_ca_key = var.etcd_ca_key
|
||||||
|
aggregator_ca_crt = var.aggregator_ca_crt
|
||||||
|
aggregator_ca_key = var.aggregator_ca_key
|
||||||
|
service_account_key = var.service_account_key
|
||||||
|
secretbox_encryption_secret = var.secretbox_encryption_secret
|
||||||
|
|
||||||
|
# Component images
|
||||||
|
kube_apiserver_img = local.kube_apiserver_img
|
||||||
|
controller_mgr_img = local.controller_mgr_img
|
||||||
|
kube_proxy_img = local.kube_proxy_img
|
||||||
|
scheduler_img = local.scheduler_img
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
# Worker machine configurations
|
||||||
|
resource "local_file" "worker_configs" {
|
||||||
|
for_each = var.worker_configs
|
||||||
|
|
||||||
|
filename = "${path.module}/../cluster-config/${each.key}.yaml"
|
||||||
|
|
||||||
|
content = templatefile("${path.module}/templates/worker.tftpl", {
|
||||||
|
version = "v1alpha1"
|
||||||
|
hostname = each.value.hostname
|
||||||
|
token = var.machine_token
|
||||||
|
ca_crt = var.machine_ca_crt
|
||||||
|
lan_ip = each.value.lan_ip
|
||||||
|
lan_subnet = each.value.lan_subnet
|
||||||
|
lan_gateway = each.value.lan_gateway
|
||||||
|
kubelet_image = local.kubelet_image
|
||||||
|
cluster_dns_ip = local.cluster_dns_ip
|
||||||
|
install_disk = each.value.install_disk
|
||||||
|
factory_image = local.factory_image
|
||||||
|
node_labels = each.value.node_labels
|
||||||
|
|
||||||
|
# Cluster config
|
||||||
|
cluster_id = var.cluster_id
|
||||||
|
cluster_secret = var.cluster_secret
|
||||||
|
controlplane_ip = local.controlplane_ip
|
||||||
|
cluster_name = var.cluster_name
|
||||||
|
pod_subnets = var.cluster_config.pod_subnets
|
||||||
|
service_subnets = var.cluster_config.service_subnets
|
||||||
|
dns_domain = var.cluster_config.dns_domain
|
||||||
|
bootstrap_token = var.bootstrap_token
|
||||||
|
|
||||||
|
# Kubernetes certs
|
||||||
|
kubernetes_ca_crt = var.kubernetes_ca_crt
|
||||||
|
|
||||||
|
# Component images
|
||||||
|
kube_proxy_img = local.kube_proxy_img
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
# Output paths for reference
|
||||||
|
output "controlplane_config_path" {
|
||||||
|
value = local_file.controlplane_config.filename
|
||||||
|
description = "Path to generated controlplane config"
|
||||||
|
}
|
||||||
|
|
||||||
|
output "worker_config_paths" {
|
||||||
|
value = {
|
||||||
|
for k, v in local_file.worker_configs : k => v.filename
|
||||||
|
}
|
||||||
|
description = "Paths to generated worker configs"
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
terraform {
|
||||||
|
required_providers {
|
||||||
|
talos = {
|
||||||
|
source = "siderolabs/talos"
|
||||||
|
version = "~> 0.7"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
required_version = ">= 1.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "talos" {}
|
||||||
@@ -0,0 +1,179 @@
|
|||||||
|
version: ${version}
|
||||||
|
debug: false
|
||||||
|
persist: true
|
||||||
|
|
||||||
|
machine:
|
||||||
|
type: controlplane
|
||||||
|
token: ${token}
|
||||||
|
ca:
|
||||||
|
crt: ${ca_crt}
|
||||||
|
key: ${ca_key}
|
||||||
|
certSANs:
|
||||||
|
- ${lan_ip}
|
||||||
|
- ${wg0_ip}
|
||||||
|
network:
|
||||||
|
hostname: ${hostname}
|
||||||
|
interfaces:
|
||||||
|
- interface: eno1
|
||||||
|
addresses:
|
||||||
|
- ${lan_ip}/24
|
||||||
|
routes:
|
||||||
|
- network: 0.0.0.0/0
|
||||||
|
gateway: ${lan_gateway}
|
||||||
|
dhcp: false
|
||||||
|
dhcpOptions:
|
||||||
|
ipv6: false
|
||||||
|
- interface: wg0
|
||||||
|
addresses:
|
||||||
|
- ${wg0_ip}/24
|
||||||
|
wireguard:
|
||||||
|
privateKey: "${wg0_private_key}"
|
||||||
|
listenPort: ${wg0_port}
|
||||||
|
peers:
|
||||||
|
%{ for peer in wg0_peers ~}
|
||||||
|
- publicKey: "${peer.public_key}"
|
||||||
|
allowedIPs:
|
||||||
|
%{ for ip in peer.allowed_ips ~}
|
||||||
|
- ${ip}
|
||||||
|
%{ endfor ~}
|
||||||
|
%{ endfor ~}
|
||||||
|
- interface: wg1
|
||||||
|
addresses:
|
||||||
|
- ${wg1_ip}/24
|
||||||
|
wireguard:
|
||||||
|
privateKey: "${wg1_private_key}"
|
||||||
|
listenPort: ${wg1_port}
|
||||||
|
peers:
|
||||||
|
%{ for peer in wg1_peers ~}
|
||||||
|
- publicKey: "${peer.public_key}"
|
||||||
|
allowedIPs:
|
||||||
|
%{ for ip in peer.allowed_ips ~}
|
||||||
|
- ${ip}
|
||||||
|
%{ endfor ~}
|
||||||
|
persistentKeepaliveInterval: ${peer.persistent_keepalive_secs}s
|
||||||
|
%{ endfor ~}
|
||||||
|
nameservers:
|
||||||
|
%{ for ns in dns_servers ~}
|
||||||
|
- ${ns}
|
||||||
|
%{ endfor ~}
|
||||||
|
extraHostEntries:
|
||||||
|
- ip: ${forgejo_registry_ip}
|
||||||
|
aliases:
|
||||||
|
- ${forgejo_hostname}
|
||||||
|
kubelet:
|
||||||
|
image: ${kubelet_image}
|
||||||
|
defaultRuntimeSeccompProfileEnabled: true
|
||||||
|
disableManifestsDirectory: true
|
||||||
|
clusterDNS:
|
||||||
|
- ${cluster_dns_ip}
|
||||||
|
extraArgs:
|
||||||
|
rotate-server-certificates: true
|
||||||
|
nodeIP:
|
||||||
|
validSubnets:
|
||||||
|
- 192.168.1.0/24
|
||||||
|
install:
|
||||||
|
disk: ${install_disk}
|
||||||
|
image: factory.talos.dev/installer/613e1592b2da41ae5e265e8789429f22e121aab91cb4deb6bc3c0b6262961245:${talos_version}
|
||||||
|
wipe: true
|
||||||
|
grubUseUKICmdline: true
|
||||||
|
disks:
|
||||||
|
%{ for disk in longhorn_disks ~}
|
||||||
|
- device: ${disk.device}
|
||||||
|
partitions:
|
||||||
|
- mountpoint: ${disk.mountpoint}
|
||||||
|
%{ endfor ~}
|
||||||
|
features:
|
||||||
|
diskQuotaSupport: true
|
||||||
|
kubePrism:
|
||||||
|
enabled: true
|
||||||
|
port: 7445
|
||||||
|
hostDNS:
|
||||||
|
enabled: false
|
||||||
|
nodeLabels:
|
||||||
|
node.kubernetes.io/exclude-from-external-load-balancers: ""
|
||||||
|
topology.kubernetes.io/region: homelab
|
||||||
|
topology.kubernetes.io/zone: az-a
|
||||||
|
|
||||||
|
cluster:
|
||||||
|
id: ${cluster_id}
|
||||||
|
secret: ${cluster_secret}
|
||||||
|
controlPlane:
|
||||||
|
endpoint: https://${controlplane_ip}:6443
|
||||||
|
clusterName: ${cluster_name}
|
||||||
|
allowSchedulingOnControlPlanes: true
|
||||||
|
network:
|
||||||
|
dnsDomain: ${dns_domain}
|
||||||
|
podSubnets:
|
||||||
|
%{ for subnet in pod_subnets ~}
|
||||||
|
- ${subnet}
|
||||||
|
%{ endfor ~}
|
||||||
|
serviceSubnets:
|
||||||
|
%{ for subnet in service_subnets ~}
|
||||||
|
- ${subnet}
|
||||||
|
%{ endfor ~}
|
||||||
|
cni:
|
||||||
|
name: none
|
||||||
|
token: ${bootstrap_token}
|
||||||
|
secretboxEncryptionSecret: ${secretbox_encryption_secret}
|
||||||
|
ca:
|
||||||
|
crt: ${kubernetes_ca_crt}
|
||||||
|
key: ${kubernetes_ca_key}
|
||||||
|
aggregatorCA:
|
||||||
|
crt: ${aggregator_ca_crt}
|
||||||
|
key: ${aggregator_ca_key}
|
||||||
|
serviceAccount:
|
||||||
|
key: ${service_account_key}
|
||||||
|
apiServer:
|
||||||
|
certSANs:
|
||||||
|
- ${controlplane_ip}
|
||||||
|
- ${wg0_ip}
|
||||||
|
image: ${kube_apiserver_img}
|
||||||
|
admissionControl:
|
||||||
|
- name: PodSecurity
|
||||||
|
configuration:
|
||||||
|
apiVersion: pod-security.admission.config.k8s.io/v1alpha1
|
||||||
|
defaults:
|
||||||
|
audit: restricted
|
||||||
|
audit-version: latest
|
||||||
|
enforce: baseline
|
||||||
|
enforce-version: latest
|
||||||
|
warn: restricted
|
||||||
|
warn-version: latest
|
||||||
|
exemptions:
|
||||||
|
namespaces:
|
||||||
|
- kube-system
|
||||||
|
runtimeClasses: []
|
||||||
|
usernames: []
|
||||||
|
kind: PodSecurityConfiguration
|
||||||
|
auditPolicy:
|
||||||
|
apiVersion: audit.k8s.io/v1
|
||||||
|
kind: Policy
|
||||||
|
rules:
|
||||||
|
- level: Metadata
|
||||||
|
controllerManager:
|
||||||
|
image: ${controller_mgr_img}
|
||||||
|
proxy:
|
||||||
|
image: ${kube_proxy_img}
|
||||||
|
disabled: true
|
||||||
|
scheduler:
|
||||||
|
image: ${scheduler_img}
|
||||||
|
discovery:
|
||||||
|
enabled: true
|
||||||
|
registries:
|
||||||
|
kubernetes:
|
||||||
|
disabled: true
|
||||||
|
service: {}
|
||||||
|
etcd:
|
||||||
|
ca:
|
||||||
|
crt: ${etcd_ca_crt}
|
||||||
|
key: ${etcd_ca_key}
|
||||||
|
extraManifests:
|
||||||
|
- https://raw.githubusercontent.com/alex1989hu/kubelet-serving-cert-approver/main/deploy/standalone-install.yaml
|
||||||
|
- https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
|
||||||
|
inlineManifests:
|
||||||
|
- name: cilium
|
||||||
|
contents: |
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: kube-system
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
version: ${version}
|
||||||
|
debug: false
|
||||||
|
persist: true
|
||||||
|
|
||||||
|
machine:
|
||||||
|
type: worker
|
||||||
|
token: ${token}
|
||||||
|
ca:
|
||||||
|
crt: ${ca_crt}
|
||||||
|
key: ""
|
||||||
|
certSANs: []
|
||||||
|
network:
|
||||||
|
hostname: ${hostname}
|
||||||
|
interfaces:
|
||||||
|
- interface: eno1
|
||||||
|
addresses:
|
||||||
|
- ${lan_ip}/24
|
||||||
|
routes:
|
||||||
|
- network: 0.0.0.0/0
|
||||||
|
gateway: ${lan_gateway}
|
||||||
|
dhcp: false
|
||||||
|
nameservers:
|
||||||
|
- ${cluster_dns_ip}
|
||||||
|
- 8.8.8.8
|
||||||
|
- 1.1.1.1
|
||||||
|
kubelet:
|
||||||
|
image: ${kubelet_image}
|
||||||
|
defaultRuntimeSeccompProfileEnabled: true
|
||||||
|
disableManifestsDirectory: true
|
||||||
|
extraArgs:
|
||||||
|
rotate-server-certificates: true
|
||||||
|
install:
|
||||||
|
disk: ${install_disk}
|
||||||
|
image: factory.talos.dev/installer/613e1592b2da41ae5e265e8789429f22e121aab91cb4deb6bc3c0b6262961245:${talos_version}
|
||||||
|
wipe: true
|
||||||
|
grubUseUKICmdline: true
|
||||||
|
registries: {}
|
||||||
|
features:
|
||||||
|
diskQuotaSupport: true
|
||||||
|
kubePrism:
|
||||||
|
enabled: true
|
||||||
|
port: 7445
|
||||||
|
hostDNS:
|
||||||
|
enabled: true
|
||||||
|
forwardKubeDNSToHost: true
|
||||||
|
nodeLabels:
|
||||||
|
%{ for k, v in node_labels ~}
|
||||||
|
${k}: ${v}
|
||||||
|
%{ endfor ~}
|
||||||
|
|
||||||
|
cluster:
|
||||||
|
id: ${cluster_id}
|
||||||
|
secret: ${cluster_secret}
|
||||||
|
controlPlane:
|
||||||
|
endpoint: https://${controlplane_ip}:6443
|
||||||
|
clusterName: ${cluster_name}
|
||||||
|
network:
|
||||||
|
dnsDomain: ${dns_domain}
|
||||||
|
podSubnets:
|
||||||
|
%{ for subnet in pod_subnets ~}
|
||||||
|
- ${subnet}
|
||||||
|
%{ endfor ~}
|
||||||
|
serviceSubnets:
|
||||||
|
%{ for subnet in service_subnets ~}
|
||||||
|
- ${subnet}
|
||||||
|
%{ endfor ~}
|
||||||
|
cni:
|
||||||
|
name: none
|
||||||
|
token: ${bootstrap_token}
|
||||||
|
ca:
|
||||||
|
crt: ${kubernetes_ca_crt}
|
||||||
|
key: ""
|
||||||
|
discovery:
|
||||||
|
enabled: true
|
||||||
|
registries:
|
||||||
|
kubernetes:
|
||||||
|
disabled: true
|
||||||
|
service: {}
|
||||||
|
proxy:
|
||||||
|
disabled: true
|
||||||
|
extraManifests:
|
||||||
|
- https://raw.githubusercontent.com/alex1989hu/kubelet-serving-cert-approver/main/deploy/standalone-install.yaml
|
||||||
|
- https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
variable "talos_version" {
|
||||||
|
type = string
|
||||||
|
default = "v1.13.3"
|
||||||
|
description = "Talos version"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "kubernetes_version" {
|
||||||
|
type = string
|
||||||
|
default = "v1.36.1"
|
||||||
|
description = "Kubernetes version"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "cluster_name" {
|
||||||
|
type = string
|
||||||
|
default = "homelab-cluster"
|
||||||
|
description = "Cluster name"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "cluster_id" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
description = "Globally unique cluster ID (base64 encoded)"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "cluster_secret" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
description = "Shared cluster secret (base64 encoded)"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "bootstrap_token" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
description = "Bootstrap token for joining cluster"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "machine_token" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
description = "Machine PKI token"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "machine_ca_crt" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
description = "Machine CA certificate (base64 encoded)"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "machine_ca_key" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
description = "Machine CA private key (base64 encoded)"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "kubernetes_ca_crt" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
description = "Kubernetes CA certificate (base64 encoded)"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "kubernetes_ca_key" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
description = "Kubernetes CA private key (base64 encoded)"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "etcd_ca_crt" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
description = "Etcd CA certificate (base64 encoded)"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "etcd_ca_key" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
description = "Etcd CA private key (base64 encoded)"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "aggregator_ca_crt" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
description = "Aggregator CA certificate (base64 encoded)"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "aggregator_ca_key" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
description = "Aggregator CA private key (base64 encoded)"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "service_account_key" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
description = "Service account private key (base64 encoded)"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "secretbox_encryption_secret" {
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
description = "Secretbox encryption secret (base64 encoded)"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "controlplane_config" {
|
||||||
|
type = object({
|
||||||
|
hostname = string
|
||||||
|
lan_ip = string
|
||||||
|
lan_subnet = string
|
||||||
|
lan_gateway = string
|
||||||
|
wg0_ip = string
|
||||||
|
wg0_subnet = string
|
||||||
|
wg0_port = number
|
||||||
|
wg0_peers = list(object({
|
||||||
|
public_key = string
|
||||||
|
allowed_ips = list(string)
|
||||||
|
}))
|
||||||
|
wg1_ip = string
|
||||||
|
wg1_subnet = string
|
||||||
|
wg1_port = number
|
||||||
|
wg1_peers = list(object({
|
||||||
|
public_key = string
|
||||||
|
allowed_ips = list(string)
|
||||||
|
persistent_keepalive_secs = number
|
||||||
|
}))
|
||||||
|
wg0_private_key = string
|
||||||
|
wg1_private_key = string
|
||||||
|
install_disk = string
|
||||||
|
longhorn_disks = list(object({
|
||||||
|
device = string
|
||||||
|
mountpoint = string
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
description = "Control plane machine configuration"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "worker_configs" {
|
||||||
|
type = map(object({
|
||||||
|
hostname = string
|
||||||
|
lan_ip = string
|
||||||
|
lan_subnet = string
|
||||||
|
lan_gateway = string
|
||||||
|
install_disk = string
|
||||||
|
node_labels = map(string)
|
||||||
|
}))
|
||||||
|
description = "Worker machine configurations"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "cluster_config" {
|
||||||
|
type = object({
|
||||||
|
controlplane_ip = string
|
||||||
|
pod_subnets = list(string)
|
||||||
|
service_subnets = list(string)
|
||||||
|
dns_servers = list(string)
|
||||||
|
dns_domain = string
|
||||||
|
})
|
||||||
|
description = "Cluster-wide configuration"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "forgejo_registry_ip" {
|
||||||
|
type = string
|
||||||
|
default = "10.107.155.96"
|
||||||
|
description = "Forgejo registry (container repo) ClusterIP for host DNS rewrite"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "forgejo_hostname" {
|
||||||
|
type = string
|
||||||
|
default = "forgejo.riotpiao.homelab.com"
|
||||||
|
description = "Forgejo external hostname"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user