blob: b8ac549962ef108d28d9c500b4d384692ea2fd99 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
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))
|