246 lines
7.9 KiB
Bash
Executable File
246 lines
7.9 KiB
Bash
Executable File
#!/bin/bash
|
|
# Simple backup script for your personal cloud
|
|
|
|
set -e
|
|
set -o pipefail
|
|
|
|
# Parse command line flags
|
|
BACKUP_HOME=true
|
|
BACKUP_APPS=true
|
|
BACKUP_CLUSTER=true
|
|
|
|
show_help() {
|
|
echo "Usage: $0 [OPTIONS]"
|
|
echo "Backup components of your wild-cloud infrastructure"
|
|
echo ""
|
|
echo "Options:"
|
|
echo " --home-only Backup only WC_HOME (wild-cloud configuration)"
|
|
echo " --apps-only Backup only applications (databases and PVCs)"
|
|
echo " --cluster-only Backup only Kubernetes cluster resources"
|
|
echo " --no-home Skip WC_HOME backup"
|
|
echo " --no-apps Skip application backups"
|
|
echo " --no-cluster Skip cluster resource backup"
|
|
echo " -h, --help Show this help message"
|
|
echo ""
|
|
echo "Default: Backup all components (home, apps, cluster)"
|
|
}
|
|
|
|
# Process command line arguments
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--home-only)
|
|
BACKUP_HOME=true
|
|
BACKUP_APPS=false
|
|
BACKUP_CLUSTER=false
|
|
shift
|
|
;;
|
|
--apps-only)
|
|
BACKUP_HOME=false
|
|
BACKUP_APPS=true
|
|
BACKUP_CLUSTER=false
|
|
shift
|
|
;;
|
|
--cluster-only)
|
|
BACKUP_HOME=false
|
|
BACKUP_APPS=false
|
|
BACKUP_CLUSTER=true
|
|
shift
|
|
;;
|
|
--no-home)
|
|
BACKUP_HOME=false
|
|
shift
|
|
;;
|
|
--no-apps)
|
|
BACKUP_APPS=false
|
|
shift
|
|
;;
|
|
--no-cluster)
|
|
BACKUP_CLUSTER=false
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
show_help
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "Unknown option: $1"
|
|
show_help
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Initialize Wild Cloud environment
|
|
if [ -z "${WC_ROOT}" ]; then
|
|
echo "WC_ROOT is not set."
|
|
exit 1
|
|
else
|
|
source "${WC_ROOT}/scripts/common.sh"
|
|
init_wild_env
|
|
fi
|
|
|
|
if `wild-config cloud.backup.root --check`; then
|
|
export RESTIC_REPOSITORY="$(wild-config cloud.backup.root)"
|
|
else
|
|
echo "WARNING: Could not get cloud backup root."
|
|
exit 1
|
|
fi
|
|
|
|
if `wild-secret cloud.backupPassword --check`; then
|
|
export RESTIC_PASSWORD="$(wild-secret cloud.backupPassword)"
|
|
else
|
|
echo "WARNING: Could not get cloud backup secret."
|
|
exit 1
|
|
fi
|
|
|
|
if `wild-config cloud.backup.staging --check`; then
|
|
STAGING_DIR="$(wild-config cloud.backup.staging)"
|
|
else
|
|
echo "WARNING: Could not get cloud backup staging directory."
|
|
exit 1
|
|
fi
|
|
|
|
echo "Backup at '$RESTIC_REPOSITORY'."
|
|
|
|
# Initialize the repository if needed.
|
|
echo "Checking if restic repository exists..."
|
|
if restic cat config >/dev/null 2>&1; then
|
|
echo "Using existing backup repository."
|
|
else
|
|
echo "No existing backup repository found. Initializing restic repository..."
|
|
restic init
|
|
echo "Repository initialized successfully."
|
|
fi
|
|
|
|
# Backup entire WC_HOME
|
|
if [ "$BACKUP_HOME" = true ]; then
|
|
echo "Backing up WC_HOME..."
|
|
restic --verbose --tag wild-cloud --tag wc-home --tag "$(date +%Y-%m-%d)" backup $WC_HOME
|
|
echo "WC_HOME backup completed."
|
|
# TODO: Ignore wild cloud cache?
|
|
else
|
|
echo "Skipping WC_HOME backup."
|
|
fi
|
|
|
|
mkdir -p "$STAGING_DIR"
|
|
|
|
# Run backup for all apps at once
|
|
if [ "$BACKUP_APPS" = true ]; then
|
|
echo "Running backup for all apps..."
|
|
wild-app-backup --all
|
|
|
|
# Upload each app's backup to restic individually
|
|
for app_dir in "$STAGING_DIR"/apps/*; do
|
|
if [ ! -d "$app_dir" ]; then
|
|
continue
|
|
fi
|
|
app="$(basename "$app_dir")"
|
|
echo "Uploading backup for app: $app"
|
|
restic --verbose --tag wild-cloud --tag "$app" --tag "$(date +%Y-%m-%d)" backup "$app_dir"
|
|
echo "Backup for app '$app' completed."
|
|
done
|
|
else
|
|
echo "Skipping application backups."
|
|
fi
|
|
|
|
# --- etcd Backup Function ----------------------------------------------------
|
|
backup_etcd() {
|
|
local cluster_backup_dir="$1"
|
|
local etcd_backup_file="$cluster_backup_dir/etcd-snapshot.db"
|
|
|
|
echo "Creating etcd snapshot..."
|
|
|
|
# For Talos, we use talosctl to create etcd snapshots
|
|
if command -v talosctl >/dev/null 2>&1; then
|
|
# Try to get etcd snapshot via talosctl (works for Talos clusters)
|
|
local control_plane_nodes
|
|
control_plane_nodes=$(kubectl get nodes -l node-role.kubernetes.io/control-plane -o jsonpath='{.items[*].status.addresses[?(@.type=="InternalIP")].address}' | tr ' ' '\n' | head -1)
|
|
|
|
if [[ -n "$control_plane_nodes" ]]; then
|
|
echo "Using talosctl to backup etcd from control plane node: $control_plane_nodes"
|
|
if talosctl --nodes "$control_plane_nodes" etcd snapshot "$etcd_backup_file"; then
|
|
echo " etcd backup created: $etcd_backup_file"
|
|
return 0
|
|
else
|
|
echo " talosctl etcd snapshot failed, trying alternative method..."
|
|
fi
|
|
else
|
|
echo " No control plane nodes found for talosctl method"
|
|
fi
|
|
fi
|
|
|
|
# Alternative: Try to backup via etcd pod if available
|
|
local etcd_pod
|
|
etcd_pod=$(kubectl get pods -n kube-system -l component=etcd -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || true)
|
|
|
|
if [[ -n "$etcd_pod" ]]; then
|
|
echo "Using etcd pod: $etcd_pod"
|
|
# Create snapshot using etcdctl inside the etcd pod
|
|
if kubectl exec -n kube-system "$etcd_pod" -- etcdctl \
|
|
--endpoints=https://127.0.0.1:2379 \
|
|
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
|
|
--cert=/etc/kubernetes/pki/etcd/server.crt \
|
|
--key=/etc/kubernetes/pki/etcd/server.key \
|
|
snapshot save /tmp/etcd-snapshot.db; then
|
|
|
|
# Copy snapshot out of pod
|
|
kubectl cp -n kube-system "$etcd_pod:/tmp/etcd-snapshot.db" "$etcd_backup_file"
|
|
|
|
# Clean up temporary file in pod
|
|
kubectl exec -n kube-system "$etcd_pod" -- rm -f /tmp/etcd-snapshot.db
|
|
|
|
echo " etcd backup created: $etcd_backup_file"
|
|
return 0
|
|
else
|
|
echo " etcd pod snapshot failed"
|
|
fi
|
|
else
|
|
echo " No etcd pod found in kube-system namespace"
|
|
fi
|
|
|
|
# Final fallback: Try direct etcdctl if available on local system
|
|
if command -v etcdctl >/dev/null 2>&1; then
|
|
echo "Attempting local etcdctl backup..."
|
|
# This would need proper certificates and endpoints configured
|
|
echo " Local etcdctl backup not implemented (requires certificate configuration)"
|
|
fi
|
|
|
|
echo " Warning: Could not create etcd backup - no working method found"
|
|
echo " Consider installing talosctl or ensuring etcd pods are accessible"
|
|
return 1
|
|
}
|
|
|
|
# Back up Kubernetes cluster resources
|
|
if [ "$BACKUP_CLUSTER" = true ]; then
|
|
echo "Backing up Kubernetes cluster resources..."
|
|
CLUSTER_BACKUP_DIR="$STAGING_DIR/cluster"
|
|
|
|
# Clean up any existing cluster backup files
|
|
if [[ -d "$CLUSTER_BACKUP_DIR" ]]; then
|
|
echo "Cleaning up existing cluster backup files..."
|
|
rm -rf "$CLUSTER_BACKUP_DIR"
|
|
fi
|
|
mkdir -p "$CLUSTER_BACKUP_DIR"
|
|
|
|
kubectl get all -A -o yaml > "$CLUSTER_BACKUP_DIR/all-resources.yaml"
|
|
kubectl get secrets -A -o yaml > "$CLUSTER_BACKUP_DIR/secrets.yaml"
|
|
kubectl get configmaps -A -o yaml > "$CLUSTER_BACKUP_DIR/configmaps.yaml"
|
|
kubectl get persistentvolumes -o yaml > "$CLUSTER_BACKUP_DIR/persistentvolumes.yaml"
|
|
kubectl get persistentvolumeclaims -A -o yaml > "$CLUSTER_BACKUP_DIR/persistentvolumeclaims.yaml"
|
|
kubectl get storageclasses -o yaml > "$CLUSTER_BACKUP_DIR/storageclasses.yaml"
|
|
|
|
echo "Backing up etcd..."
|
|
backup_etcd "$CLUSTER_BACKUP_DIR"
|
|
|
|
echo "Cluster resources backed up to $CLUSTER_BACKUP_DIR"
|
|
|
|
# Upload cluster backup to restic
|
|
echo "Uploading cluster backup to restic..."
|
|
restic --verbose --tag wild-cloud --tag cluster --tag "$(date +%Y-%m-%d)" backup "$CLUSTER_BACKUP_DIR"
|
|
echo "Cluster backup completed."
|
|
else
|
|
echo "Skipping cluster backup."
|
|
fi
|
|
|
|
echo "Backup completed: $BACKUP_DIR"
|