#!/usr/bin/env bash set -euo pipefail umask 077 # 由宿主机 cron 每分钟调用。调度配置来自后台 system_configs,实际执行仍在宿主机, # Web 后端不接触 Docker socket、备份载荷或客户端加密密码。 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # shellcheck source=backup-common.sh source "${SCRIPT_DIR}/backup-common.sh" config_value() { local key="$1" fallback="$2" value value="$(backup_mysql_root_query "SELECT value FROM system_configs WHERE \`key\` = '${key}' LIMIT 1;" 2>/dev/null || true)" value="${value//$'\n'/}" printf '%s' "${value:-${fallback}}" } write_job_state() { local status_dir="$1" type="$2" status="$3" message="$4" mkdir -p "${status_dir}" { printf 'job_type=%q\n' "${type}" printf 'job_status=%q\n' "${status}" printf 'job_at=%q\n' "$(date -Iseconds)" printf 'job_message=%q\n' "${message}" } > "${status_dir}/last-job.env" chmod 600 "${status_dir}/last-job.env" } run_job() { local status_dir="$1" type="$2" script="$3" shift 3 if "${script}" "$@"; then write_job_state "${status_dir}" "${type}" success '执行成功' "${SCRIPT_DIR}/backup-status.sh" write || true return fi local code=$? write_job_state "${status_dir}" "${type}" failed "执行失败(退出码 ${code})" "${SCRIPT_DIR}/backup-status.sh" write || true return "${code}" } main() { backup_require_cmd docker backup_require_cmd flock local backup_dir status_dir enabled hour minute interval now_hour now_minute backup_dir="$(backup_require_env BACKUP_DIR)" status_dir="${BACKUP_STATUS_DIR:-${backup_dir}/status}" backup_validate_dir "${status_dir}" backup_lock_or_exit "${backup_dir}/.scheduler.lock" enabled="$(config_value 'backup.schedule_enabled' 'true')" hour="$(config_value 'backup.full_hour' '4')" minute="$(config_value 'backup.full_minute' '30')" interval="$(config_value 'backup.binlog_interval_minutes' '1')" [[ "${enabled}" == true || "${enabled}" == false ]] || { backup_error 'backup.schedule_enabled 必须为 true 或 false'; exit 1; } [[ "${hour}" =~ ^[0-9]+$ && "${hour}" -le 23 && "${minute}" =~ ^[0-9]+$ && "${minute}" -le 59 ]] || { backup_error '备份全量时间配置无效' exit 1 } [[ "${interval}" =~ ^[0-9]+$ && "${interval}" -ge 1 && "${interval}" -le 5 ]] || { backup_error 'binlog 归档间隔必须为 1-5 分钟' exit 1 } if [[ "${enabled}" == false ]]; then "${SCRIPT_DIR}/backup-status.sh" write || true exit 0 fi now_hour="$((10#$(date +%H)))" now_minute="$((10#$(date +%M)))" if ((now_minute % interval == 0)); then run_job "${status_dir}" binlog "${SCRIPT_DIR}/archive-binlog.sh" archive fi if ((now_hour == hour && now_minute == minute)); then run_job "${status_dir}" full "${SCRIPT_DIR}/backup-online.sh" full fi "${SCRIPT_DIR}/backup-status.sh" write || true } main "$@"