Add migration scripts and manifests for GitLab and Gitea to Longhorn
- Create .vscode/settings.json for YAML schema validation. - Add WISSENSBASIS.md for documentation on HomeLabScripts. - Implement migration job for GitLab from NFS to Longhorn with migrate-to-longhorn.yaml and migrate-to-longhorn.sh. - Add Gitea migration scripts and manifests for PostgreSQL to Longhorn. - Create persistent volume claims and deployments for Gitea and Homarr. - Set up namespaces and services for Homarr and Speedtest Tracker. - Add secrets for Homarr and Speedtest Tracker with sensitive data. - Configure Ingress for Speedtest Tracker with Traefik annotations. Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
Executable
+254
@@ -0,0 +1,254 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
NAMESPACE="gitlab"
|
||||
DEPLOYMENT_NAME="gitlab"
|
||||
JOB_NAME="gitlab-migrate-to-longhorn"
|
||||
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||
MANIFEST_DIR="${SCRIPT_DIR}/manifest"
|
||||
PV_PVC_MANIFEST="${MANIFEST_DIR}/pv-pvc.yaml"
|
||||
MIGRATION_JOB_MANIFEST="${MANIFEST_DIR}/migrate-to-longhorn.yaml"
|
||||
|
||||
SOURCE_DATA_PVC="gitlab-data-pvc"
|
||||
SOURCE_CONFIG_PVC="gitlab-config-pvc"
|
||||
TARGET_DATA_PVC="gitlab-data-longhorn-pvc"
|
||||
TARGET_CONFIG_PVC="gitlab-config-longhorn-pvc"
|
||||
|
||||
BIND_TIMEOUT_SECONDS=900
|
||||
JOB_TIMEOUT_SECONDS=7200
|
||||
POD_STOP_TIMEOUT_SECONDS=300
|
||||
|
||||
ORIGINAL_REPLICAS="1"
|
||||
SCALED_DOWN="false"
|
||||
DRY_RUN="false"
|
||||
AUTO_CONFIRM="false"
|
||||
|
||||
info() {
|
||||
echo "[INFO] $*"
|
||||
}
|
||||
|
||||
warn() {
|
||||
echo "[WARN] $*" >&2
|
||||
}
|
||||
|
||||
err() {
|
||||
echo "[ERROR] $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: migrate-to-longhorn.sh [--dry-run] [--yes] [--help]
|
||||
|
||||
Options:
|
||||
--dry-run Show planned actions without changing cluster state.
|
||||
--yes Skip interactive confirmation before scaling down GitLab.
|
||||
--help Show this help.
|
||||
EOF
|
||||
}
|
||||
|
||||
parse_args() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--dry-run)
|
||||
DRY_RUN="true"
|
||||
;;
|
||||
--yes)
|
||||
AUTO_CONFIRM="true"
|
||||
;;
|
||||
--help|-h)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
err "Unknown argument: $1"
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
}
|
||||
|
||||
restore_deployment_on_error() {
|
||||
if [[ "${SCALED_DOWN}" == "true" ]]; then
|
||||
warn "Migration failed. Restoring deployment replicas to ${ORIGINAL_REPLICAS}."
|
||||
kubectl -n "${NAMESPACE}" scale deployment "${DEPLOYMENT_NAME}" --replicas="${ORIGINAL_REPLICAS}" >/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
on_exit() {
|
||||
local exit_code=$?
|
||||
if [[ ${exit_code} -ne 0 ]]; then
|
||||
restore_deployment_on_error
|
||||
warn "Script failed with exit code ${exit_code}."
|
||||
fi
|
||||
}
|
||||
trap on_exit EXIT
|
||||
|
||||
require_tool() {
|
||||
local tool="$1"
|
||||
command -v "${tool}" >/dev/null 2>&1 || err "Required tool not found: ${tool}"
|
||||
}
|
||||
|
||||
require_file() {
|
||||
local path="$1"
|
||||
[[ -f "${path}" ]] || err "Required file not found: ${path}"
|
||||
}
|
||||
|
||||
wait_for_pvc_bound() {
|
||||
local pvc_name="$1"
|
||||
local timeout="$2"
|
||||
local elapsed=0
|
||||
local interval=5
|
||||
|
||||
info "Waiting for PVC ${pvc_name} to become Bound..."
|
||||
while true; do
|
||||
local phase
|
||||
phase="$(kubectl -n "${NAMESPACE}" get pvc "${pvc_name}" -o jsonpath='{.status.phase}' 2>/dev/null || true)"
|
||||
|
||||
if [[ "${phase}" == "Bound" ]]; then
|
||||
info "PVC ${pvc_name} is Bound."
|
||||
return 0
|
||||
fi
|
||||
|
||||
if (( elapsed >= timeout )); then
|
||||
err "Timeout while waiting for PVC ${pvc_name} to bind."
|
||||
fi
|
||||
|
||||
sleep "${interval}"
|
||||
elapsed=$((elapsed + interval))
|
||||
done
|
||||
}
|
||||
|
||||
wait_for_gitlab_pods_stopped() {
|
||||
local timeout="$1"
|
||||
local elapsed=0
|
||||
local interval=5
|
||||
|
||||
info "Waiting for pods with label app=gitlab to stop..."
|
||||
while true; do
|
||||
local count
|
||||
count="$(kubectl -n "${NAMESPACE}" get pods -l app=gitlab --no-headers 2>/dev/null | wc -l | tr -d ' ')"
|
||||
|
||||
if [[ "${count}" == "0" ]]; then
|
||||
info "All GitLab pods are stopped."
|
||||
return 0
|
||||
fi
|
||||
|
||||
if (( elapsed >= timeout )); then
|
||||
err "Timeout while waiting for GitLab pods to stop."
|
||||
fi
|
||||
|
||||
sleep "${interval}"
|
||||
elapsed=$((elapsed + interval))
|
||||
done
|
||||
}
|
||||
|
||||
wait_for_job_complete() {
|
||||
local timeout="$1"
|
||||
|
||||
info "Waiting for migration job ${JOB_NAME} to complete..."
|
||||
if kubectl -n "${NAMESPACE}" wait --for=condition=complete "job/${JOB_NAME}" --timeout="${timeout}s" >/dev/null; then
|
||||
info "Migration job completed successfully."
|
||||
return 0
|
||||
fi
|
||||
|
||||
warn "Migration job did not complete in time or failed."
|
||||
kubectl -n "${NAMESPACE}" describe job "${JOB_NAME}" || true
|
||||
kubectl -n "${NAMESPACE}" logs "job/${JOB_NAME}" --tail=-1 || true
|
||||
err "Migration job failed."
|
||||
}
|
||||
|
||||
validate_prerequisites() {
|
||||
require_tool kubectl
|
||||
require_file "${PV_PVC_MANIFEST}"
|
||||
require_file "${MIGRATION_JOB_MANIFEST}"
|
||||
|
||||
kubectl get namespace "${NAMESPACE}" >/dev/null 2>&1 || err "Namespace does not exist: ${NAMESPACE}"
|
||||
kubectl -n "${NAMESPACE}" get deployment "${DEPLOYMENT_NAME}" >/dev/null 2>&1 || err "Deployment not found: ${DEPLOYMENT_NAME}"
|
||||
|
||||
kubectl -n "${NAMESPACE}" get pvc "${SOURCE_DATA_PVC}" >/dev/null 2>&1 || err "Source PVC not found: ${SOURCE_DATA_PVC}"
|
||||
kubectl -n "${NAMESPACE}" get pvc "${SOURCE_CONFIG_PVC}" >/dev/null 2>&1 || err "Source PVC not found: ${SOURCE_CONFIG_PVC}"
|
||||
}
|
||||
|
||||
confirm_scale_down() {
|
||||
if [[ "${AUTO_CONFIRM}" == "true" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo
|
||||
warn "GitLab deployment ${DEPLOYMENT_NAME} in namespace ${NAMESPACE} will be scaled to 0 during migration."
|
||||
read -r -p "Type MIGRATE to continue: " confirmation
|
||||
if [[ "${confirmation}" != "MIGRATE" ]]; then
|
||||
err "Confirmation failed. Aborted by user."
|
||||
fi
|
||||
}
|
||||
|
||||
print_dry_run_plan() {
|
||||
info "Dry-run mode active. No cluster changes will be made."
|
||||
info "Planned steps:"
|
||||
info "1) kubectl apply -f ${PV_PVC_MANIFEST}"
|
||||
info "2) Wait for PVCs ${TARGET_DATA_PVC} and ${TARGET_CONFIG_PVC} to be Bound"
|
||||
info "3) Scale deployment ${DEPLOYMENT_NAME} to 0"
|
||||
info "4) Recreate and run job ${JOB_NAME} from ${MIGRATION_JOB_MANIFEST}"
|
||||
info "5) Wait for job completion and verify target PVCs"
|
||||
info "6) Scale deployment ${DEPLOYMENT_NAME} back to ${ORIGINAL_REPLICAS}"
|
||||
info "No PVC switch in deployment is performed by this script."
|
||||
}
|
||||
|
||||
run_migration() {
|
||||
ORIGINAL_REPLICAS="$(kubectl -n "${NAMESPACE}" get deployment "${DEPLOYMENT_NAME}" -o jsonpath='{.spec.replicas}')"
|
||||
ORIGINAL_REPLICAS="${ORIGINAL_REPLICAS:-1}"
|
||||
|
||||
if [[ "${DRY_RUN}" == "true" ]]; then
|
||||
print_dry_run_plan
|
||||
return 0
|
||||
fi
|
||||
|
||||
confirm_scale_down
|
||||
|
||||
info "Applying PVC manifest to ensure Longhorn target PVCs exist."
|
||||
kubectl apply -f "${PV_PVC_MANIFEST}" >/dev/null
|
||||
|
||||
wait_for_pvc_bound "${TARGET_DATA_PVC}" "${BIND_TIMEOUT_SECONDS}"
|
||||
wait_for_pvc_bound "${TARGET_CONFIG_PVC}" "${BIND_TIMEOUT_SECONDS}"
|
||||
|
||||
info "Scaling deployment ${DEPLOYMENT_NAME} down to 0 for consistent copy."
|
||||
kubectl -n "${NAMESPACE}" scale deployment "${DEPLOYMENT_NAME}" --replicas=0 >/dev/null
|
||||
SCALED_DOWN="true"
|
||||
wait_for_gitlab_pods_stopped "${POD_STOP_TIMEOUT_SECONDS}"
|
||||
|
||||
info "Recreating migration job ${JOB_NAME}."
|
||||
kubectl -n "${NAMESPACE}" delete job "${JOB_NAME}" --ignore-not-found >/dev/null
|
||||
kubectl apply -f "${MIGRATION_JOB_MANIFEST}" >/dev/null
|
||||
|
||||
info "Streaming migration job logs."
|
||||
kubectl -n "${NAMESPACE}" logs -f "job/${JOB_NAME}" || true
|
||||
|
||||
wait_for_job_complete "${JOB_TIMEOUT_SECONDS}"
|
||||
|
||||
info "Quick verification: target PVCs are still Bound."
|
||||
wait_for_pvc_bound "${TARGET_DATA_PVC}" 30
|
||||
wait_for_pvc_bound "${TARGET_CONFIG_PVC}" 30
|
||||
|
||||
info "Scaling deployment ${DEPLOYMENT_NAME} back to ${ORIGINAL_REPLICAS}."
|
||||
kubectl -n "${NAMESPACE}" scale deployment "${DEPLOYMENT_NAME}" --replicas="${ORIGINAL_REPLICAS}" >/dev/null
|
||||
SCALED_DOWN="false"
|
||||
|
||||
info "Migration finished successfully."
|
||||
info "No deployment PVC switch was done by this script."
|
||||
info "NFS PVC/PV (gitlab-git-pvc / gitlab-git-pv) were not changed."
|
||||
info "If you want to switch GitLab to Longhorn later, apply the updated deployment manifest manually."
|
||||
}
|
||||
|
||||
main() {
|
||||
parse_args "$@"
|
||||
info "Starting GitLab local PVC to Longhorn migration."
|
||||
if [[ "${DRY_RUN}" == "true" ]]; then
|
||||
info "Running in dry-run mode."
|
||||
fi
|
||||
validate_prerequisites
|
||||
run_migration
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user