Files
macos-tools/gitlab/gitlab-data-protection.sh
gaoandClaude 70f845cd17 feat: 添加 Harbor 和 GitLab 模块,完善系统配置文档
- 新增 Harbor 容器镜像仓库维护模块
- 新增 GitLab 代码托管平台维护模块
- 创建 SYSTEM-CONFIG.md 系统配置概览文档
- 完善 README.md 项目说明文档
- 更新部署脚本支持新模块
- 集成 Ollama AI 模型服务配置信息

主要功能:
- Harbor: Docker 镜像仓库管理 (端口 8080/8443)
- GitLab: 企业级代码托管 (端口 8890/8929)
- 系统配置文档:便于 Claude 和 CI/CD 理解架构
- 统一备份策略和健康监控
- 模块化部署和管理

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
2025-10-20 21:22:20 +08:00

591 lines
16 KiB
Bash
Executable File

#!/bin/bash
# GitLab 数据保护和管理脚本
# 支持完整的 GitLab 服务管理、备份和恢复
set -euo pipefail
# 配置变量
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_NAME="${PROJECT_NAME:-gitlab}"
GITLAB_DIR="${GITLAB_DIR:-${SCRIPT_DIR}}"
GITLAB_DATA_DIR="${GITLAB_DATA_DIR:-~/ops/data/gitlab}"
GITLAB_CONFIG_DIR="${GITLAB_CONFIG_DIR:-${GITLAB_DIR}/config}"
LOG_FILE="${LOG_FILE:-~/ops/logs/gitlab-protection.log}"
BACKUP_DIR="${BACKUP_DIR:-~/ops/backups/gitlab}"
DOCKER_COMPOSE_FILE="${DOCKER_COMPOSE_FILE:-${GITLAB_DIR}/docker-compose.yml}"
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 日志函数
log() {
echo -e "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "${LOG_FILE}"
}
info() {
log "${BLUE}[INFO]${NC} $1"
}
success() {
log "${GREEN}[SUCCESS]${NC} $1"
}
warning() {
log "${YELLOW}[WARNING]${NC} $1"
}
error() {
log "${RED}[ERROR]${NC} $1"
}
# 检查必要的依赖
check_dependencies() {
local deps=("docker" "docker-compose")
for dep in "${deps[@]}"; do
if ! command -v "$dep" &> /dev/null; then
error "$dep 未安装,请先安装 $dep"
exit 1
fi
done
# 检查 Docker 是否运行
if ! docker info &> /dev/null; then
error "Docker 未运行,请启动 Docker Desktop"
exit 1
fi
}
# 创建必要的目录
ensure_directories() {
mkdir -p "$(dirname "${LOG_FILE}")"
mkdir -p "${BACKUP_DIR}"
mkdir -p "$(dirname "${GITLAB_DATA_DIR}")"
mkdir -p "${GITLAB_CONFIG_DIR}"
}
# 检查 GitLab 配置
check_gitlab_config() {
if [[ ! -f "${DOCKER_COMPOSE_FILE}" ]]; then
error "GitLab Docker Compose 配置文件不存在: ${DOCKER_COMPOSE_FILE}"
return 1
fi
return 0
}
# 服务状态检查
check_service_status() {
info "检查 GitLab 服务状态..."
local running_containers
running_containers=$(docker-compose -f "${DOCKER_COMPOSE_FILE}" ps -q 2>/dev/null || echo "")
if [[ -z "$running_containers" ]]; then
warning "GitLab 服务未运行"
return 1
fi
local healthy_containers
healthy_containers=$(docker-compose -f "${DOCKER_COMPOSE_FILE}" ps --filter "status=running" --format "table {{.Service}}\t{{.Status}}" 2>/dev/null | grep -c "healthy\|Up" || echo "0")
info "运行中的容器数量: $(echo "$running_containers" | wc -l)"
info "健康容器数量: $healthy_containers"
# 检查 GitLab 主服务
if docker-compose -f "${DOCKER_COMPOSE_FILE}" ps --services --filter "status=running" | grep -q "gitlab"; then
info "GitLab 主服务运行正常"
else
warning "GitLab 主服务未运行"
return 1
fi
return 0
}
# 启动 GitLab 服务
start_services() {
info "启动 GitLab 服务..."
# 检查配置
if ! check_gitlab_config; then
error "GitLab 配置检查失败,无法启动服务"
return 1
fi
# 确保数据目录存在
mkdir -p "${GITLAB_DATA_DIR}"
# 启动服务
cd "${GITLAB_DIR}"
docker-compose -f "${DOCKER_COMPOSE_FILE}" up -d
# 等待服务启动
info "等待 GitLab 服务启动完成..."
info "这可能需要几分钟时间,请耐心等待..."
# 等待 GitLab 健康检查通过
local max_wait=300 # 5 分钟
local wait_time=0
while [[ $wait_time -lt $max_wait ]]; do
if docker-compose -f "${DOCKER_COMPOSE_FILE}" ps --format "table {{.Service}}\t{{.Status}}" | grep -q "healthy"; then
success "GitLab 服务启动成功"
return 0
fi
info "等待服务启动... ($((wait_time/60))分$((wait_time%60))秒)"
sleep 10
wait_time=$((wait_time + 10))
done
warning "GitLab 服务可能还在启动中,请稍后检查状态"
return 0
}
# 停止 GitLab 服务
stop_services() {
info "停止 GitLab 服务..."
if [[ ! -f "${DOCKER_COMPOSE_FILE}" ]]; then
warning "Docker Compose 配置文件不存在,跳过停止操作"
return 0
fi
cd "${GITLAB_DIR}"
docker-compose -f "${DOCKER_COMPOSE_FILE}" down || true
success "GitLab 服务已停止"
}
# 重启 GitLab 服务
restart_services() {
info "重启 GitLab 服务..."
stop_services
sleep 5
start_services
}
# 健康检查
health_check() {
info "执行 GitLab 健康检查..."
local status=0
# 检查服务状态
if ! check_service_status; then
error "服务状态检查失败"
status=1
fi
# 检查端口
local ports=("8890" "8929")
for port in "${ports[@]}"; do
if lsof -i ":$port" &> /dev/null; then
info "端口 $port 正在使用"
else
warning "端口 $port 未被使用"
fi
done
# 检查数据目录
if [[ -d "${GITLAB_DATA_DIR}" ]]; then
local data_size
data_size=$(du -sh "${GITLAB_DATA_DIR}" 2>/dev/null | cut -f1)
info "GitLab 数据目录大小: $data_size"
else
warning "GitLab 数据目录不存在"
status=1
fi
# 检查配置目录
if [[ -d "${GITLAB_CONFIG_DIR}" ]]; then
info "GitLab 配置目录存在"
else
warning "GitLab 配置目录不存在"
status=1
fi
if [[ $status -eq 0 ]]; then
success "GitLab 健康检查通过"
else
error "GitLab 健康检查发现问题"
fi
return $status
}
# 获取 GitLab 统计信息
get_stats() {
info "获取 GitLab 统计信息..."
# 服务状态
check_service_status
# 数据目录统计
if [[ -d "${GITLAB_DATA_DIR}" ]]; then
echo ""
info "=== 数据目录统计 ==="
du -sh "${GITLAB_DATA_DIR}"/* 2>/dev/null | sort -hr || true
# 仓库数量统计
local repos_dir="${GITLAB_DATA_DIR}/repositories"
if [[ -d "$repos_dir" ]]; then
local repo_count
repo_count=$(find "$repos_dir" -maxdepth 2 -type d -name "*.git" 2>/dev/null | wc -l || echo "0")
info "Git 仓库数量: $repo_count"
fi
fi
# 备份文件统计
if [[ -d "${BACKUP_DIR}" ]]; then
echo ""
info "=== 备份文件统计 ==="
ls -lah "${BACKUP_DIR}"/*.tar.gz 2>/dev/null | tail -10 || true
fi
}
# 备份 GitLab 数据
backup_gitlab() {
local backup_name="${1:-gitlab-$(date +%Y%m%d-%H%M%S)}"
local backup_file="${BACKUP_DIR}/${backup_name}.tar.gz"
info "开始备份 GitLab 数据..."
# 检查服务状态
local service_running=false
if check_service_status &>/dev/null; then
service_running=true
info "检测到服务运行中,将创建在线备份"
fi
# 创建备份
local backup_items=()
# GitLab 配置
if [[ -d "${GITLAB_CONFIG_DIR}" ]] && [[ -n "$(ls -A "${GITLAB_CONFIG_DIR}" 2>/dev/null)" ]]; then
backup_items+=("${GITLAB_CONFIG_DIR}")
fi
# GitLab 数据目录
if [[ -d "${GITLAB_DATA_DIR}" ]]; then
backup_items+=("${GITLAB_DATA_DIR}")
fi
# Docker Compose 配置
if [[ -f "${DOCKER_COMPOSE_FILE}" ]]; then
backup_items+=("${DOCKER_COMPOSE_FILE}")
fi
if [[ ${#backup_items[@]} -eq 0 ]]; then
error "没有找到需要备份的文件或目录"
return 1
fi
# 执行备份
info "创建备份文件: $backup_file"
tar -czf "${backup_file}" "${backup_items[@]}" 2>/dev/null
if [[ $? -eq 0 ]]; then
# 创建备份元数据
local meta_file="${backup_file}.meta"
cat > "${meta_file}" << EOF
backup_name="${backup_name}"
backup_date="$(date -Iseconds)"
backup_size="$(stat -f%z "${backup_file}" 2>/dev/null || stat -c%s "${backup_file}" 2>/dev/null || echo "unknown")"
service_running="${service_running}"
gitlab_version="16.9.1"
items="$(printf '%s;' "${backup_items[@]}")"
EOF
success "GitLab 备份完成: ${backup_file}"
# 显示备份信息
local backup_size
backup_size=$(ls -lh "${backup_file}" | awk '{print $5}')
info "备份大小: ${backup_size}"
# 清理旧备份 (保留最近 10 个)
cleanup_old_backups 10
else
error "GitLab 备份失败"
return 1
fi
}
# 恢复 GitLab 数据
restore_gitlab() {
local backup_file="${1:-}"
if [[ -z "$backup_file" ]]; then
error "请指定备份文件"
show_backups
return 1
fi
if [[ ! -f "$backup_file" ]]; then
error "备份文件不存在: $backup_file"
return 1
fi
info "开始从备份恢复 GitLab..."
# 创建安全备份
local safety_backup="${BACKUP_DIR}/safety-backup-$(date +%Y%m%d-%H%M%S).tar.gz"
if [[ -d "${GITLAB_DATA_DIR}" ]] || [[ -d "${GITLAB_CONFIG_DIR}" ]]; then
info "创建当前状态的安全备份: $safety_backup"
backup_gitlab "safety-backup-$(date +%Y%m%d-%H%M%S)"
fi
# 停止现有服务
stop_services
# 恢复备份
info "恢复备份文件: $backup_file"
cd /
tar -xzf "$backup_file" 2>/dev/null
if [[ $? -eq 0 ]]; then
success "GitLab 数据恢复完成"
# 启动服务
sleep 5
start_services
# 验证恢复结果
if health_check; then
success "GitLab 恢复验证通过"
else
error "GitLab 恢复验证失败,请检查日志"
return 1
fi
else
error "GitLab 数据恢复失败"
return 1
fi
}
# 显示可用备份
show_backups() {
info "可用的 GitLab 备份文件:"
if [[ -d "${BACKUP_DIR}" ]]; then
local backups=($(ls -t "${BACKUP_DIR}"/gitlab-*.tar.gz 2>/dev/null || true))
if [[ ${#backups[@]} -gt 0 ]]; then
for i in "${!backups[@]}"; do
local backup_file="${backups[$i]}"
local backup_size
backup_size=$(ls -lh "$backup_file" | awk '{print $5}')
local backup_date
backup_date=$(stat -f%Sm -t%Y-%m-%d\ %H:%M:%S "$backup_file" 2>/dev/null || stat -c%y "$backup_file" 2>/dev/null | cut -d'.' -f1)
printf "%2d. %-40s %10s %s\n" $((i+1)) "$(basename "$backup_file")" "$backup_size" "$backup_date"
done
else
warning "没有找到 GitLab 备份文件"
fi
else
warning "备份目录不存在: ${BACKUP_DIR}"
fi
}
# 清理旧备份
cleanup_old_backups() {
local keep_count="${1:-5}"
info "清理旧备份文件,保留最近 $keep_count 个..."
if [[ -d "${BACKUP_DIR}" ]]; then
local old_backups
old_backups=($(ls -t "${BACKUP_DIR}"/gitlab-*.tar.gz 2>/dev/null | tail -n +$((keep_count+1)) || true))
if [[ ${#old_backups[@]} -gt 0 ]]; then
for backup in "${old_backups[@]}"; do
info "删除旧备份: $(basename "$backup")"
rm -f "$backup" "${backup}.meta"
done
success "已清理 ${#old_backups[@]} 个旧备份文件"
else
info "没有需要清理的旧备份文件"
fi
fi
}
# 设置定时任务
setup_cron() {
local cron_interval="${1:-"0 3 * * *"}" # 默认每天凌晨 3 点
info "设置 GitLab 备份定时任务..."
# 创建 cron 脚本
local cron_script="${SCRIPT_DIR}/gitlab-cron-backup.sh"
cat > "$cron_script" << EOF
#!/bin/bash
# GitLab 自动备份 cron 任务
$(declare -f log info success warning error)
$(declare -f check_dependencies ensure_directories backup_gitlab)
LOG_FILE="${LOG_FILE}"
BACKUP_DIR="${BACKUP_DIR}"
GITLAB_DATA_DIR="${GITLAB_DATA_DIR}"
GITLAB_CONFIG_DIR="${GITLAB_CONFIG_DIR}"
DOCKER_COMPOSE_FILE="${DOCKER_COMPOSE_FILE}"
# 执行备份
check_dependencies
ensure_directories
backup_gitlab
EOF
chmod +x "$cron_script"
# 添加到 crontab
local cron_entry="$cron_interval $cron_script >> ${LOG_FILE} 2>&1"
(crontab -l 2>/dev/null | grep -v "gitlab-cron-backup" || true; echo "$cron_entry") | crontab -
# 设置 launchd 任务 (macOS 原生)
local launchd_plist="$HOME/Library/LaunchAgents/com.ljwx.gitlab.backup.plist"
cat > "$launchd_plist" << EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.ljwx.gitlab.backup</string>
<key>ProgramArguments</key>
<array>
<string>$cron_script</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>3</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
<key>StandardOutPath</key>
<string>${LOG_FILE}.cron.stdout.log</string>
<key>StandardErrorPath</key>
<string>${LOG_FILE}.cron.stderr.log</string>
<key>RunAtLoad</key>
<false/>
</dict>
</plist>
EOF
# 加载 launchd 任务
launchctl unload "$launchd_plist" 2>/dev/null || true
launchctl load -w "$launchd_plist"
success "GitLab 备份定时任务已设置"
info "Cron 任务: $cron_entry"
info "Launchd 任务已加载"
}
# 显示帮助信息
show_help() {
cat << EOF
GitLab 数据保护和管理脚本
用法: $0 [命令] [参数]
命令:
start 启动 GitLab 服务
stop 停止 GitLab 服务
restart 重启 GitLab 服务
status 检查服务状态
health 执行健康检查
stats 显示统计信息
backup [名称] 创建备份
restore [文件] 从备份恢复
list 显示可用备份
cleanup [数量] 清理旧备份 (默认保留 5 个)
setup 设置定时任务
help 显示帮助信息
示例:
$0 start # 启动服务
$0 backup # 创建备份
$0 backup my-backup # 创建命名备份
$0 restore backup-2023.tar.gz # 从备份恢复
$0 cleanup 10 # 保留最近 10 个备份
环境变量:
GITLAB_DIR GitLab 配置目录 (默认: 脚本所在目录)
GITLAB_DATA_DIR GitLab 数据目录 (默认: ~/ops/data/gitlab)
GITLAB_CONFIG_DIR GitLab 配置目录 (默认: ~/ops/gitlab/config)
BACKUP_DIR 备份目录 (默认: ~/ops/backups/gitlab)
LOG_FILE 日志文件 (默认: ~/ops/logs/gitlab-protection.log)
EOF
}
# 主函数
main() {
local command="${1:-help}"
local parameter="${2:-}"
# 设置环境变量
export PATH="/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:$PATH"
case "$command" in
"start")
check_dependencies
ensure_directories
start_services
;;
"stop")
stop_services
;;
"restart")
check_dependencies
ensure_directories
restart_services
;;
"status")
check_service_status
;;
"health")
check_dependencies
ensure_directories
health_check
;;
"stats")
get_stats
;;
"backup")
check_dependencies
ensure_directories
backup_gitlab "$parameter"
;;
"restore")
check_dependencies
ensure_directories
restore_gitlab "$parameter"
;;
"list"|"ls")
show_backups
;;
"cleanup")
cleanup_old_backups "$parameter"
;;
"setup")
setup_cron "$parameter"
;;
"help"|"-h"|"--help")
show_help
;;
*)
error "未知命令: $command"
show_help
exit 1
;;
esac
}
# 如果脚本被直接执行,调用主函数
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi