- 新增 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]>
591 lines
16 KiB
Bash
Executable File
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 |