diff options
Diffstat (limited to 'vps-backup')
| -rwxr-xr-x | vps-backup/backup.clj | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/vps-backup/backup.clj b/vps-backup/backup.clj new file mode 100755 index 0000000..b8ac549 --- /dev/null +++ b/vps-backup/backup.clj @@ -0,0 +1,101 @@ +#!/usr/bin/env bb +;; Usage: bb backup.clj +;; +;; 备份脚本功能: +;; 1. 备份所有 Docker volumes 到 /home/eltrac/Backup +;; 2. 备份 Akkoma 数据库到 akkoma_full_backup.sql +;; 3. 使用 restic 备份 /home 和 /etc 到 contabo-backup 仓库 +;; +;; 说明: +;; 1. forgejo 等应用使用挂载到 Home 目录子目录的 SQLite 数据库,所以直接备份整个 /home 目录就好 +;; 2. 由于 docker volumes 已经单独备份,所以不必备份 /var +;; 3. 目前暂时不能自动化,因为 restic 备份需要输入 repository 密码 + +(require '[babashka.tasks :refer [shell]]) +(require '[babashka.fs :as fs]) +(require '[clojure.string :as str]) + +(def backup-path "/home/eltrac/Backup") +(def timestamp (java.time.LocalDateTime/now)) +(def log-file (str backup-path "/backup-" (.format java.time.format.DateTimeFormatter/ISO_LOCAL_DATE timestamp) ".log")) + +(defn log-msg [msg] + (let [timestamp (.format (java.time.LocalDateTime/now) java.time.format.DateTimeFormatter/ISO_LOCAL_DATE_TIME)] + (println (str "[" timestamp "] " msg)))) + +(defn ensure-backup-dir [] + (log-msg (str "确保备份目录存在: " backup-path)) + (try + (fs/create-dirs backup-path) + (log-msg "✓ 备份目录已创建/已存在") + (catch Exception e + (log-msg (str "⚠ 无法创建备份目录: " (.getMessage e))) + (log-msg "⚠ 请手动创建目录或检查权限")))) + +(defn backup-docker-volumes [] + (log-msg "开始备份 Docker volumes...") + (let [output (:out (shell {:out :string} "docker" "volume" "ls" "--format" "{{.Name}}")) + ;; filter out anonymous volumes based on length + volumes (filter #(< (count %) 60) (str/split-lines output))] + (log-msg (str "找到 " (count volumes) " 个 volumes")) + (doseq [volume volumes] + (log-msg (str "备份 volume: " volume)) + (let [backup-file (str backup-path "/docker-volumes/" volume "-backup.tar.gz")] + (try + (shell "docker" "run" "--rm" + "-v" (str volume ":/data") + "-v" (str backup-path ":/backup") + "alpine" + "tar" "czf" (str "/backup/" volume "-backup.tar.gz") "-C" "/data" ".") + (log-msg (str "✓ 成功备份 volume: " volume " 到 " backup-file)) + (catch Exception e + (log-msg (str "✗ 备份 volume " volume " 失败: " (.getMessage e))))))))) + +(defn backup-akkoma-database [] + (log-msg "开始备份 Akkoma 数据库...") + (let [backup-file (str backup-path "/akkoma_full_backup.sql")] + (try + (shell {:out backup-file} + "docker" "exec" "akkoma-db-1" "pg_dumpall" "-U" "akkoma") + (log-msg (str "✓ 成功备份 Akkoma 数据库到 " backup-file)) + (catch Exception e + (log-msg (str "✗ 备份 Akkoma 数据库失败: " (.getMessage e))))))) + +(defn backup-with-restic [] + (log-msg "开始使用 restic 备份 /home 和 /etc 目录...") + (try + ;; 备份 /home 目录 + (log-msg "备份 /home 目录...") + (shell "restic" "--repo" "rclone:contabo-backup:" + "backup" "/home" + "--password-file" (str backup-path "/password")) + (log-msg "✓ 成功备份 /home 目录") + + ;; 备份 /etc 目录 + (log-msg "备份 /etc 目录...") + (shell "restic" "--repo" "rclone:contabo-backup:" + "backup" "/etc" + "--password-file" (str backup-path "/password")) + (log-msg "✓ 成功备份 /etc 目录") + + (catch Exception e + (log-msg (str "✗ restic 备份失败: " (.getMessage e)))))) + +(defn -main [] + (log-msg "=== 开始备份流程 ===") + (ensure-backup-dir) + + (log-msg "1. 备份 Docker volumes") + (backup-docker-volumes) + + (log-msg "2. 备份 Akkoma 数据库") + (backup-akkoma-database) + + (log-msg "3. 使用 restic 备份系统目录") + (backup-with-restic) + + (log-msg "=== 备份完成 ===")) + +;; 当脚本直接运行时执行主函数 +(when (= *file* (System/getProperty "babashka.file")) + (-main)) |
