mirror of
https://gitlab.kube.huskypup.net/Scooby/Homelabv4.git
synced 2026-08-20 23:16:49 +00:00
Add VPS warm-standby/backup site (Phase 0-1b)
Foundation for a DR/backup path using an always-on VPS as a second ArgoCD-managed cluster, plus DB/backup standardization work that fell out of it: - vps-standby ArgoCD cluster destination + AppProject, MinIO backup receiver, VPS bootstrap script (k3s, Netbird, cert-manager) - Dual-site DNS failover watcher + home-IP DDNS CronJob, Cloudflare token moved out of git into Vault+ExternalSecret - Nextcloud migrated from ad-hoc MariaDB to CNPG + redis-operator (matches n8n/Authentik/GitLab's backup-native pattern) - Authentik's CNPG manifests moved into the actual ArgoCD-synced manifests/ path (were present but never wired into the sync path) - Vault raft-snapshot CronJob, CNPG barmanObjectStore backups (Authentik/n8n/Nextcloud), Nextcloud file-PVC restic sync - all targeting the new VPS MinIO receiver See VPS Warm-Standby plan doc for full design rationale.
This commit is contained in:
Executable
+169
@@ -0,0 +1,169 @@
|
||||
#!/usr/bin/env bash
|
||||
# vps-bootstrap.sh - Phase 0: turn a bare VPS into the vps-standby ArgoCD destination
|
||||
#
|
||||
# Run this ON THE VPS itself (as root, or via sudo), not against the home cluster.
|
||||
# Installs k3s (single node), joins the existing self-hosted Netbird mesh, and
|
||||
# installs cert-manager with the same Cloudflare DNS-01 ClusterIssuer pattern used
|
||||
# at home — so TLS issuance works identically regardless of which site is "live"
|
||||
# (DNS-01 only needs DNS control, not public HTTP reachability).
|
||||
#
|
||||
# This script does NOT register the cluster with ArgoCD — that's a one-time manual
|
||||
# step run from your workstation/home cluster once this script prints the kubeconfig
|
||||
# (ArgoCD can't reach the VPS until it exists, and shouldn't hold cluster-admin creds
|
||||
# for a box it doesn't manage yet).
|
||||
#
|
||||
# Usage:
|
||||
# sudo ./scripts/vps-bootstrap.sh <netbird-setup-key> <cloudflare-dns-edit-token>
|
||||
#
|
||||
# Prerequisites:
|
||||
# - A Netbird setup key (Netbird dashboard → Settings → Setup Keys → create
|
||||
# a reusable, non-ephemeral key)
|
||||
# - A Cloudflare API token scoped to Zone:DNS:Edit for kube.huskypup.net only
|
||||
# (create a NEW token for this — do not reuse the one from
|
||||
# infrastructure/cert-manager/manifests/secret-cf-token.yaml, that one is
|
||||
# being rotated/retired; see Phase 0.5)
|
||||
# - Ubuntu/Debian VPS with a public IP, run as root
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
NETBIRD_SETUP_KEY="${1:?Usage: $0 <netbird-setup-key> <cloudflare-dns-edit-token>}"
|
||||
CLOUDFLARE_TOKEN="${2:?Usage: $0 <netbird-setup-key> <cloudflare-dns-edit-token>}"
|
||||
|
||||
NETBIRD_MGMT_URL="https://netbird.kube.huskypup.net"
|
||||
LETSENCRYPT_EMAIL="garrettstone499@gmail.com"
|
||||
DNS_ZONE="kube.huskypup.net"
|
||||
CERT_MANAGER_VERSION="v1.13.2" # matches infrastructure/cert-manager chart version at home
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "ERROR: run as root (sudo $0 ...)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "================================================="
|
||||
echo "VPS Standby Bootstrap - Phase 0"
|
||||
echo "================================================="
|
||||
echo ""
|
||||
|
||||
# --- k3s -----------------------------------------------------------------
|
||||
if command -v k3s >/dev/null 2>&1; then
|
||||
echo "✅ k3s already installed, skipping install"
|
||||
else
|
||||
echo "Installing k3s (single node)..."
|
||||
# Keep the built-in Traefik ingress controller — this is a lean standby box,
|
||||
# not a mirror of home's Istio/Envoy-Gateway mesh. servicelb is fine too
|
||||
# since this is a single node with a real public IP.
|
||||
curl -sfL https://get.k3s.io | sh -s - \
|
||||
--write-kubeconfig-mode 644 \
|
||||
--disable metrics-server
|
||||
echo "✅ k3s installed"
|
||||
fi
|
||||
|
||||
echo "Waiting for k3s node to be Ready..."
|
||||
for i in $(seq 1 30); do
|
||||
if k3s kubectl get nodes 2>/dev/null | grep -q " Ready"; then
|
||||
echo "✅ node is Ready"
|
||||
break
|
||||
fi
|
||||
sleep 5
|
||||
done
|
||||
k3s kubectl get nodes
|
||||
|
||||
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
|
||||
|
||||
# --- Netbird ---------------------------------------------------------------
|
||||
if command -v netbird >/dev/null 2>&1 && netbird status 2>/dev/null | grep -q "Management: Connected"; then
|
||||
echo "✅ Netbird already connected, skipping"
|
||||
else
|
||||
echo "Installing Netbird client..."
|
||||
curl -fsSL https://pkgs.netbird.io/install.sh | sh
|
||||
echo "Joining Netbird mesh (${NETBIRD_MGMT_URL})..."
|
||||
netbird up --management-url "${NETBIRD_MGMT_URL}" --setup-key "${NETBIRD_SETUP_KEY}"
|
||||
echo "✅ Netbird joined"
|
||||
fi
|
||||
echo "Note: this Netbird session is used opportunistically for backup/sync traffic"
|
||||
echo "while home is up. It is NOT the path used to reach this VPS when home is down —"
|
||||
echo "that's direct SSH on this box's public IP. See plan doc, decision #2."
|
||||
|
||||
# --- cert-manager ------------------------------------------------------------
|
||||
echo ""
|
||||
echo "Installing cert-manager ${CERT_MANAGER_VERSION}..."
|
||||
if ! command -v helm >/dev/null 2>&1; then
|
||||
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
|
||||
fi
|
||||
|
||||
helm repo add jetstack https://charts.jetstack.io 2>/dev/null || true
|
||||
helm repo update jetstack
|
||||
|
||||
if helm -n cert-manager status cert-manager >/dev/null 2>&1; then
|
||||
echo "✅ cert-manager already installed"
|
||||
else
|
||||
helm install cert-manager jetstack/cert-manager \
|
||||
--namespace cert-manager \
|
||||
--create-namespace \
|
||||
--version "${CERT_MANAGER_VERSION}" \
|
||||
--set installCRDs=true \
|
||||
--wait --timeout 300s
|
||||
echo "✅ cert-manager installed"
|
||||
fi
|
||||
|
||||
echo "Waiting for cert-manager webhook to be ready..."
|
||||
k3s kubectl -n cert-manager rollout status deployment/cert-manager-webhook --timeout=120s
|
||||
|
||||
# --- Cloudflare DNS-01 ClusterIssuer (same pattern as home) -----------------
|
||||
echo ""
|
||||
echo "Applying Cloudflare token Secret + ClusterIssuer..."
|
||||
cat <<EOF | k3s kubectl apply -f -
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: cloudflare-token-secret
|
||||
namespace: cert-manager
|
||||
type: Opaque
|
||||
stringData:
|
||||
cloudflare-token: "${CLOUDFLARE_TOKEN}"
|
||||
---
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: ClusterIssuer
|
||||
metadata:
|
||||
name: letsencrypt-production
|
||||
spec:
|
||||
acme:
|
||||
server: https://acme-v02.api.letsencrypt.org/directory
|
||||
email: ${LETSENCRYPT_EMAIL}
|
||||
privateKeySecretRef:
|
||||
name: letsencrypt-production
|
||||
solvers:
|
||||
- dns01:
|
||||
cloudflare:
|
||||
email: ${LETSENCRYPT_EMAIL}
|
||||
apiTokenSecretRef:
|
||||
name: cloudflare-token-secret
|
||||
key: cloudflare-token
|
||||
selector:
|
||||
dnsZones:
|
||||
- "${DNS_ZONE}"
|
||||
EOF
|
||||
echo "✅ ClusterIssuer letsencrypt-production ready"
|
||||
|
||||
# --- Output for ArgoCD registration -----------------------------------------
|
||||
echo ""
|
||||
echo "================================================="
|
||||
echo "✅ VPS foundation bootstrap complete"
|
||||
echo "================================================="
|
||||
echo ""
|
||||
echo "Next step (run from your workstation, NOT this VPS):"
|
||||
echo ""
|
||||
echo " 1. Copy this VPS's kubeconfig to your workstation, e.g.:"
|
||||
echo " scp root@<vps-ip>:/etc/rancher/k3s/k3s.yaml ~/vps-standby-kubeconfig.yaml"
|
||||
echo " Then edit the 'server:' line inside it to use this VPS's public IP"
|
||||
echo " instead of 127.0.0.1."
|
||||
echo ""
|
||||
echo " 2. Register it with ArgoCD:"
|
||||
echo " KUBECONFIG=~/vps-standby-kubeconfig.yaml argocd cluster add default --name vps-standby"
|
||||
echo ""
|
||||
echo " 3. Confirm registration:"
|
||||
echo " argocd cluster list"
|
||||
echo ""
|
||||
echo "This box is otherwise reachable via:"
|
||||
echo " - Netbird (while home's self-hosted mesh is up)"
|
||||
echo " - Direct SSH on its public IP (always, break-glass path)"
|
||||
Reference in New Issue
Block a user