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:
2026-04-24 23:08:23 +02:00
parent be9329d313
commit 39079615f5
22 changed files with 1137 additions and 11 deletions
+254
View File
@@ -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 "$@"