当前位置: 首页 > news >正文

Backrest - 基于Restic的现代化备份解决方案

项目标题与描述

Backrest是一个基于Restic构建的现代化Web可访问备份解决方案。它提供了一个直观的Web界面,封装了restic CLI的功能,使用户能够轻松创建存储库、浏览快照和恢复文件。Backrest可以在后台运行,并采用智能化的方法来调度快照执行和协调存储库健康操作。

该项目使用Go语言构建,作为一个独立的轻量级二进制文件分发,唯一依赖是restic。它可以安全地创建新存储库或管理现有存储库,一旦配置了存储,WebUI可以处理大多数操作,同时在需要时仍然允许直接访问强大的restic CLI进行高级操作。

功能特性

  • Web界面:支持本地或远程访问(非常适合NAS部署)
  • 多平台支持
    • Linux
    • macOS
    • Windows
    • FreeBSD
    • Docker
  • 备份管理
    • 导入现有的restic存储库
    • 基于Cron的备份和维护计划(如清理、检查、遗忘等)
    • 浏览和从快照恢复文件
    • 可配置的备份策略和保留策略
  • 自动化操作
    • 智能调度系统
    • 自动健康检查和维护
    • 钩子系统支持自定义操作
  • 安全特性
    • 基于JWT的身份验证
    • 加密通信支持
    • 安全的密钥管理

安装指南

系统要求

  • 支持的操作系统:Linux、macOS、Windows、FreeBSD
  • 需要安装restic 0.18.0或更高版本
  • 至少512MB内存
  • 足够的磁盘空间用于备份数据

安装步骤

  1. 下载最新版本
# 从GitHub Releases页面下载对应平台的二进制文件
wget https://github.com/garethgeorge/backrest/releases/latest/download/backrest-linux-amd64
  1. 运行安装脚本
# 本地访问模式(默认)
./install.sh# 启用远程访问
./install.sh --allow-remote-access
  1. 验证安装
# 检查服务状态
systemctl status backrest  # Linux
launchctl list | grep backrest  # macOS
  1. 访问Web界面
本地访问:http://localhost:9898
远程访问:http://0.0.0.0:9898

Docker安装

docker pull garethgeorge/backrest
docker run -p 9898:9898 -v /path/to/config:/config garethgeorge/backrest

使用说明

基本使用示例

  1. 初始化备份存储库
# 通过WebUI创建新存储库或导入现有存储库
# 支持本地路径、SFTP、AWS S3、Backblaze B2等多种后端
  1. 创建备份计划
# 示例备份计划配置
plan:id: "daily-backup"paths: ["/home/user/documents", "/home/user/photos"]schedule:cron: "0 2 * * *"  # 每天凌晨2点执行retention:keep-daily: 7keep-weekly: 4keep-monthly: 12
  1. 执行即时备份
# 通过WebUI触发即时备份操作
# 或使用API端点
curl -X POST http://localhost:9898/api/v1/backup \-H "Content-Type: application/json" \-d '{"repoId": "my-repo", "planId": "daily-backup"}'

API概览

Backrest提供完整的gRPC和RESTful API接口,支持以下操作:

  • 配置管理(获取/设置配置)
  • 存储库操作(添加/删除/检查存储库)
  • 备份管理(启动/停止/监控备份)
  • 快照操作(列表/恢复/删除快照)
  • 系统状态监控

核心代码

主程序入口

package mainimport ("context""flag""os""os/signal""syscall"v1 "github.com/garethgeorge/backrest/gen/go/v1""github.com/garethgeorge/backrest/internal/api""github.com/garethgeorge/backrest/internal/config""github.com/garethgeorge/backrest/internal/orchestrator""go.uber.org/zap"
)var InstallDepsOnly = flag.Bool("install-deps-only", false, "安装依赖并退出")func main() {flag.Parse()// 初始化日志系统installLoggers()// 查找或安装restic二进制文件resticPath, err := resticinstaller.FindOrInstallResticBinary()if err != nil {zap.S().Fatalf("查找或安装restic时出错: %v", err)}if *InstallDepsOnly {zap.S().Info("依赖已安装,正在退出")return}// 创建上下文和取消函数ctx, cancel := context.WithCancel(context.Background())defer cancel()// 加载配置configStore := createConfigProvider()cfg, err := configStore.Get()if err != nil {zap.S().Fatalf("加载配置时出错: %v", err)}// 创建编排器实例orc := orchestrator.NewOrchestrator(configStore, oplog, logStore)// 启动HTTP服务器startHTTPServer(cfg, orc, oplog)// 等待终止信号waitForShutdown(cancel)
}func waitForShutdown(cancel context.CancelFunc) {sigCh := make(chan os.Signal, 1)signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)<-sigChcancel()
}

备份任务实现

package tasksimport ("context""fmt""time"v1 "github.com/garethgeorge/backrest/gen/go/v1"
)// BackupTask 表示一个计划的备份操作
type BackupTask struct {BaseTaskforce  booldidRun bool
}// NewScheduledBackupTask 创建新的计划备份任务
func NewScheduledBackupTask(repo *v1.Repo, plan *v1.Plan) *BackupTask {return &BackupTask{BaseTask: BaseTask{TaskType:   "backup",TaskName:   fmt.Sprintf("plan %q backup", plan.Id),TaskRepo:   repo,TaskPlanID: plan.Id,},}
}// Next 计算下一次运行时间
func (t *BackupTask) Next(now time.Time, runner TaskRunner) (ScheduledTask, error) {if t.force {if t.didRun {return NeverScheduledTask, nil}t.didRun = truereturn ScheduledTask{Task:  t,RunAt: now,Op: &v1.Operation{Op: &v1.Operation_OperationBackup{},},}, nil}plan, err := runner.GetPlan(t.PlanID())if err != nil {return NeverScheduledTask, err}if plan.Schedule == nil {return NeverScheduledTask, nil}// 计算基于计划的下一次运行时间// ... 计划调度逻辑return nextScheduledTime, nil
}// Run 执行备份任务
func (t *BackupTask) Run(ctx context.Context, st ScheduledTask, runner TaskRunner) error {op := st.Oprepo, err := runner.GetRepoOrchestrator(t.RepoID())if err != nil {return fmt.Errorf("get repo %q: %w", t.RepoID(), err)}// 执行备份前钩子if err := runner.ExecuteHooks(ctx, []v1.Hook_Condition{v1.Hook_CONDITION_SNAPSHOT_START,}, HookVars{}); err != nil {return fmt.Errorf("snapshot start hook: %w", err)}// 执行实际备份操作// ... 备份执行逻辑// 执行备份后钩子if err := runner.ExecuteHooks(ctx, []v1.Hook_Condition{v1.Hook_CONDITION_SNAPSHOT_END,}, HookVars{}); err != nil {return fmt.Errorf("snapshot end hook: %w", err)}return nil
}

Web API处理程序

package apiimport ("context""connectrpc.com/connect"v1 "github.com/garethgeorge/backrest/gen/go/v1""github.com/garethgeorge/backrest/internal/orchestrator"
)// BackrestHandler 处理Backrest API请求
type BackrestHandler struct {config       config.ConfigStoreorchestrator *orchestrator.Orchestrator
}// NewBackrestHandler 创建新的API处理程序
func NewBackrestHandler(config config.ConfigStore, orchestrator *orchestrator.Orchestrator) *BackrestHandler {return &BackrestHandler{config:       config,orchestrator: orchestrator,}
}// GetConfig 获取当前配置
func (s *BackrestHandler) GetConfig(ctx context.Context, req *connect.Request[emptypb.Empty]) (*connect.Response[v1.Config], error) {config, err := s.config.Get()if err != nil {return nil, connect.NewError(connect.CodeInternal, err)}return connect.NewResponse(config), nil
}// Backup 执行备份操作
func (s *BackrestHandler) Backup(ctx context.Context, req *connect.Request[v1.BackupRequest]) (*connect.Response[v1.Operation], error) {repo, err := s.orchestrator.GetRepo(req.Msg.RepoId)if err != nil {return nil, connect.NewError(connect.CodeNotFound, err)}// 创建并调度备份任务task := tasks.NewOneoffBackupTask(repo, req.Msg.PlanId, time.Now())if err := s.orchestrator.ScheduleTask(task, tasks.TaskPriorityInteractive); err != nil {return nil, connect.NewError(connect.CodeInternal, err)}return connect.NewResponse(&v1.Operation{Status: v1.OperationStatus_STATUS_PENDING,}), nil
}

配置管理

package configimport ("encoding/json""os""sync"v1 "github.com/garethgeorge/backrest/gen/go/v1"
)// ConfigStore 管理应用程序配置
type ConfigStore interface {Get() (*v1.Config, error)Set(*v1.Config) error
}// FileConfigStore 基于文件的配置存储
type FileConfigStore struct {path stringmu   sync.RWMutex
}// NewFileConfigStore 创建新的文件配置存储
func NewFileConfigStore(path string) *FileConfigStore {return &FileConfigStore{path: path}
}// Get 从文件加载配置
func (s *FileConfigStore) Get() (*v1.Config, error) {s.mu.RLock()defer s.mu.RUnlock()data, err := os.ReadFile(s.path)if err != nil {if os.IsNotExist(err) {return &v1.Config{}, nil}return nil, err}var config v1.Configif err := json.Unmarshal(data, &config); err != nil {return nil, err}return &config, nil
}// Set 保存配置到文件
func (s *FileConfigStore) Set(config *v1.Config) error {s.mu.Lock()defer s.mu.Unlock()data, err := json.MarshalIndent(config, "", "  ")if err != nil {return err}if err := os.MkdirAll(s.path, 0700); err != nil {return err}return os.WriteFile(s.path, data, 0600)
}

更多精彩内容 请关注我的个人公众号 公众号(办公AI智能小助手)
公众号二维码

http://www.sczhlp.com/news/75975/

相关文章:

  • 高压直流系统及相关电气件
  • 电子商务网站例wordpress弹幕视频主题
  • 东莞长安网站设计网易云邮箱
  • 遵义企业做网站crack wordpress
  • 专业网站建设公司电话wordpress 文章太窄
  • 深圳seo网站推广方案有赞小程序官网
  • 做网站平台的公司济南住房和城乡建设厅网站
  • 怎样在网站做推广WordPress如何调用
  • 网站改了标题会怎么样网站 div
  • 2025/09/06 leetCode101. 对称二叉树
  • 百度网站建设前期都有哪些费用win8网站模板
  • 淄博网站建设-至信网络中国建设综合门户网站
  • 主机开设成功 网站正在建设中降低
  • 网站的网站建设公司成都市住房与城乡建设厅网站
  • 滴道网站建设织梦做的网站怎么上传视频
  • 企业网站建设总结官方网站建设
  • 3合1网站建设电话做设计找图片的网站
  • 新网站做seo工业互联网平台体系
  • 第八-九天C# 19 委托委托
  • 2025.9.5 刷题日记
  • 常州网站建站wordpress 体育主题公园
  • 邗江建设局网站800元网站建设
  • .net 建网站樟木头镇网站建设
  • 网站推广方法及特点wordpress首页加注册
  • 网站开发拓扑图网站 开发 周期
  • 在 Ubuntu 上用HarmonyOS Next SDK编译 libsodium
  • 【题解】 [MX-X19-T2 LAOI-14] SPECIALZ
  • 商业API接口源码
  • 网站正在维护中啥意思网上的推广公司
  • 自己做的网站打不开怎么搞如何建设企业网站呢