Golang服务器备份方案探讨

Golang服务器备份方案探讨 你好,我可以通过上传代码的可执行文件,使用 PuTTY 中的代码访问服务器上的文件。 现在,有没有什么方法或函数,能让我在使用的服务器上创建一个完整的备份呢?

image

2 回复

如果您的文件系统支持快照功能,请使用快照并进行传输;如果不支持,请确保关闭数据库服务器并使用 rsync

除了数据库服务器外,可能还有其他不支持热备份的服务,您也需要停止这些服务,否则备份文件中将包含损坏的文件。

请记住在完成备份后启动这些服务。

如果可以进行文件系统快照,这些快照也可能产生损坏的文件,不过通常服务能够从中恢复——从服务的角度来看,这类似于从崩溃或断电中恢复。如果您想确保万无一失,仍然可以在快照期间关闭服务,但这只需要几秒到几分钟的时间,相比之下,使用 rsync 可能需要数小时。

更多关于Golang服务器备份方案探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go中实现服务器备份,可以通过以下方式创建完整的备份方案:

1. 基本文件备份函数

package main

import (
    "archive/tar"
    "compress/gzip"
    "fmt"
    "io"
    "os"
    "path/filepath"
    "time"
)

// 创建tar.gz备份
func CreateBackup(sourceDir, backupPath string) error {
    // 创建备份文件
    backupFile, err := os.Create(backupPath)
    if err != nil {
        return fmt.Errorf("创建备份文件失败: %v", err)
    }
    defer backupFile.Close()

    // 创建gzip写入器
    gzipWriter := gzip.NewWriter(backupFile)
    defer gzipWriter.Close()

    // 创建tar写入器
    tarWriter := tar.NewWriter(gzipWriter)
    defer tarWriter.Close()

    // 遍历源目录
    return filepath.Walk(sourceDir, func(filePath string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }

        // 创建tar头部
        header, err := tar.FileInfoHeader(info, "")
        if err != nil {
            return err
        }

        // 设置相对路径
        relPath, err := filepath.Rel(sourceDir, filePath)
        if err != nil {
            return err
        }
        header.Name = relPath

        // 写入头部
        if err := tarWriter.WriteHeader(header); err != nil {
            return err
        }

        // 如果是普通文件,写入内容
        if !info.Mode().IsRegular() {
            return nil
        }

        file, err := os.Open(filePath)
        if err != nil {
            return err
        }
        defer file.Close()

        _, err = io.Copy(tarWriter, file)
        return err
    })
}

// 恢复备份
func RestoreBackup(backupPath, targetDir string) error {
    backupFile, err := os.Open(backupPath)
    if err != nil {
        return err
    }
    defer backupFile.Close()

    gzipReader, err := gzip.NewReader(backupFile)
    if err != nil {
        return err
    }
    defer gzipReader.Close()

    tarReader := tar.NewReader(gzipReader)

    for {
        header, err := tarReader.Next()
        if err == io.EOF {
            break
        }
        if err != nil {
            return err
        }

        targetPath := filepath.Join(targetDir, header.Name)

        switch header.Typeflag {
        case tar.TypeDir:
            if err := os.MkdirAll(targetPath, 0755); err != nil {
                return err
            }
        case tar.TypeReg:
            // 创建目录
            if err := os.MkdirAll(filepath.Dir(targetPath), 0755); err != nil {
                return err
            }

            // 创建文件
            file, err := os.OpenFile(targetPath, os.O_CREATE|os.O_WRONLY, os.FileMode(header.Mode))
            if err != nil {
                return err
            }
            defer file.Close()

            // 写入内容
            if _, err := io.Copy(file, tarReader); err != nil {
                return err
            }
        }
    }
    return nil
}

2. 完整备份系统示例

package main

import (
    "fmt"
    "log"
    "os"
    "path/filepath"
    "strings"
    "time"
)

type BackupConfig struct {
    SourceDirs      []string
    BackupDir       string
    RetentionDays   int
    ExcludePatterns []string
}

type BackupManager struct {
    config BackupConfig
}

func NewBackupManager(config BackupConfig) *BackupManager {
    return &BackupManager{config: config}
}

func (bm *BackupManager) CreateFullBackup() (string, error) {
    timestamp := time.Now().Format("20060102_150405")
    backupName := fmt.Sprintf("full_backup_%s.tar.gz", timestamp)
    backupPath := filepath.Join(bm.config.BackupDir, backupName)

    // 创建临时目录存放所有备份文件
    tempDir, err := os.MkdirTemp("", "backup_*")
    if err != nil {
        return "", err
    }
    defer os.RemoveAll(tempDir)

    // 收集所有需要备份的文件
    var allFiles []string
    for _, sourceDir := range bm.config.SourceDirs {
        err := filepath.Walk(sourceDir, func(path string, info os.FileInfo, err error) error {
            if err != nil {
                return err
            }

            // 检查排除模式
            for _, pattern := range bm.config.ExcludePatterns {
                matched, _ := filepath.Match(pattern, filepath.Base(path))
                if matched {
                    if info.IsDir() {
                        return filepath.SkipDir
                    }
                    return nil
                }
            }

            if info.Mode().IsRegular() {
                allFiles = append(allFiles, path)
            }
            return nil
        })
        if err != nil {
            return "", err
        }
    }

    // 创建备份
    if err := CreateBackupFromFiles(allFiles, backupPath); err != nil {
        return "", err
    }

    // 清理旧备份
    bm.CleanOldBackups()

    return backupPath, nil
}

func CreateBackupFromFiles(files []string, backupPath string) error {
    backupFile, err := os.Create(backupPath)
    if err != nil {
        return err
    }
    defer backupFile.Close()

    gzipWriter := gzip.NewWriter(backupFile)
    defer gzipWriter.Close()

    tarWriter := tar.NewWriter(gzipWriter)
    defer tarWriter.Close()

    for _, filePath := range files {
        info, err := os.Stat(filePath)
        if err != nil {
            return err
        }

        header, err := tar.FileInfoHeader(info, "")
        if err != nil {
            return err
        }
        header.Name = filepath.ToSlash(filePath)

        if err := tarWriter.WriteHeader(header); err != nil {
            return err
        }

        if info.Mode().IsRegular() {
            file, err := os.Open(filePath)
            if err != nil {
                return err
            }
            defer file.Close()

            if _, err := io.Copy(tarWriter, file); err != nil {
                return err
            }
        }
    }
    return nil
}

func (bm *BackupManager) CleanOldBackups() {
    cutoffTime := time.Now().AddDate(0, 0, -bm.config.RetentionDays)

    files, err := os.ReadDir(bm.config.BackupDir)
    if err != nil {
        log.Printf("读取备份目录失败: %v", err)
        return
    }

    for _, file := range files {
        if strings.HasPrefix(file.Name(), "full_backup_") && strings.HasSuffix(file.Name(), ".tar.gz") {
            filePath := filepath.Join(bm.config.BackupDir, file.Name())
            info, err := os.Stat(filePath)
            if err != nil {
                continue
            }

            if info.ModTime().Before(cutoffTime) {
                if err := os.Remove(filePath); err != nil {
                    log.Printf("删除旧备份失败 %s: %v", file.Name(), err)
                } else {
                    log.Printf("已删除旧备份: %s", file.Name())
                }
            }
        }
    }
}

func (bm *BackupManager) ListBackups() ([]string, error) {
    var backups []string
    
    files, err := os.ReadDir(bm.config.BackupDir)
    if err != nil {
        return nil, err
    }

    for _, file := range files {
        if strings.HasPrefix(file.Name(), "full_backup_") && strings.HasSuffix(file.Name(), ".tar.gz") {
            backups = append(backups, file.Name())
        }
    }
    return backups, nil
}

3. 使用示例

func main() {
    config := BackupConfig{
        SourceDirs: []string{
            "/var/www",
            "/etc/nginx",
            "/home/user/app",
        },
        BackupDir:       "/backups",
        RetentionDays:   30,
        ExcludePatterns: []string{"*.log", "*.tmp", "cache", "node_modules"},
    }

    backupManager := NewBackupManager(config)

    // 创建备份
    backupPath, err := backupManager.CreateFullBackup()
    if err != nil {
        log.Fatalf("创建备份失败: %v", err)
    }
    fmt.Printf("备份已创建: %s\n", backupPath)

    // 列出所有备份
    backups, err := backupManager.ListBackups()
    if err != nil {
        log.Fatalf("列出备份失败: %v", err)
    }
    fmt.Println("可用备份:")
    for _, backup := range backups {
        fmt.Printf("  - %s\n", backup)
    }

    // 恢复备份示例
    // if err := RestoreBackup("/backups/full_backup_20231215_143022.tar.gz", "/restore_target"); err != nil {
    //     log.Fatalf("恢复备份失败: %v", err)
    // }
}

4. 数据库备份集成

func BackupDatabase(dbConfig DatabaseConfig, backupPath string) error {
    // MySQL备份示例
    cmd := exec.Command("mysqldump",
        "-h", dbConfig.Host,
        "-u", dbConfig.User,
        "-p"+dbConfig.Password,
        "--all-databases",
        "--single-transaction",
        "--quick",
    )
    
    output, err := cmd.Output()
    if err != nil {
        return fmt.Errorf("数据库备份失败: %v", err)
    }
    
    return os.WriteFile(backupPath, output, 0644)
}

type DatabaseConfig struct {
    Host     string
    User     string
    Password string
    Port     int
}

这个备份方案提供了完整的文件备份功能,包括压缩、排除模式、备份保留策略和恢复功能。你可以根据服务器具体需求调整配置参数。

回到顶部