Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(WIP)Support backup to the local filesystem #850

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions cmd/backup-manager/app/backup/backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import (
"strings"
"time"

"github.com/pingcap/tidb-operator/pkg/apis/pingcap.com/v1alpha1"

"github.com/golang/glog"
"github.com/mholt/archiver"
"github.com/pingcap/tidb-operator/cmd/backup-manager/app/constants"
Expand Down Expand Up @@ -53,8 +55,13 @@ func (bo *BackupOpts) getBackupRelativePath() string {
return fmt.Sprintf("%s_%s/%s", bo.Namespace, bo.TcName, backupName)
}

func (bo *BackupOpts) getDestBucketURI(remotePath string) string {
return fmt.Sprintf("%s://%s", bo.StorageType, remotePath)
func (bo *BackupOpts) getDestBucketURI(destPath string) string {
if bo.BackupName != string(v1alpha1.BackupStorageTypeLocal) {
destPath = strings.TrimPrefix(destPath, constants.BackupRootPath+"/")
} else {
destPath = strings.TrimPrefix(destPath, "/")
}
return fmt.Sprintf("%s://%s", bo.StorageType, destPath)
}

func (bo *BackupOpts) getTikvGCLifeTime(db *sql.DB) (string, error) {
Expand Down Expand Up @@ -106,13 +113,19 @@ func (bo *BackupOpts) dumpTidbClusterData() (string, error) {
}

func (bo *BackupOpts) backupDataToRemote(source, bucketURI string) error {
if bo.StorageType == string(v1alpha1.BackupStorageTypeLocal) {
// if the backup storage type is local, do nothing
return nil
}

destBucket := util.NormalizeBucketURI(bucketURI)
tmpDestBucket := fmt.Sprintf("%s.tmp", destBucket)
// TODO: We may need to use exec.CommandContext to control timeouts.
rcCopy := exec.Command("rclone", constants.RcloneConfigArg, "copyto", source, tmpDestBucket)
if err := rcCopy.Start(); err != nil {
return fmt.Errorf("cluster %s, start rclone copyto command for upload backup data %s failed, err: %v", bo, bucketURI, err)
}

if err := rcCopy.Wait(); err != nil {
return fmt.Errorf("cluster %s, execute rclone copyto command for upload backup data %s failed, err: %v", bo, bucketURI, err)
}
Expand Down
4 changes: 1 addition & 3 deletions cmd/backup-manager/app/backup/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ package backup
import (
"database/sql"
"fmt"
"strings"
"time"

"github.com/golang/glog"
Expand Down Expand Up @@ -153,8 +152,7 @@ func (bm *BackupManager) performBackup(backup *v1alpha1.Backup, db *sql.DB) erro
}
glog.Infof("get cluster %s commitTs %s success", bm, commitTs)

remotePath := strings.TrimPrefix(archiveBackupPath, constants.BackupRootPath+"/")
bucketURI := bm.getDestBucketURI(remotePath)
bucketURI := bm.getDestBucketURI(archiveBackupPath)
err = bm.backupDataToRemote(archiveBackupPath, bucketURI)
if err != nil {
return bm.StatusUpdater.Update(backup, &v1alpha1.BackupCondition{
Expand Down
9 changes: 8 additions & 1 deletion cmd/backup-manager/app/restore/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,14 @@ func (rm *RestoreManager) performRestore(restore *v1alpha1.Restore) error {
return err
}

restoreDataPath := rm.getRestoreDataPath()
restoreDataPath, err := rm.getRestoreDataPath()
return rm.StatusUpdater.Update(restore, &v1alpha1.RestoreCondition{
Type: v1alpha1.RestoreFailed,
Status: corev1.ConditionTrue,
Reason: "InvalidBackupPath",
Message: err.Error(),
})

if err := rm.downloadBackupData(restoreDataPath); err != nil {
return rm.StatusUpdater.Update(restore, &v1alpha1.RestoreCondition{
Type: v1alpha1.RestoreFailed,
Expand Down
35 changes: 26 additions & 9 deletions cmd/backup-manager/app/restore/restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/mholt/archiver"
"github.com/pingcap/tidb-operator/cmd/backup-manager/app/constants"
"github.com/pingcap/tidb-operator/cmd/backup-manager/app/util"
"github.com/pingcap/tidb-operator/pkg/apis/pingcap.com/v1alpha1"
)

// RestoreOpts contains the input arguments to the restore command
Expand All @@ -40,19 +41,37 @@ func (ro *RestoreOpts) String() string {
return fmt.Sprintf("%s/%s", ro.Namespace, ro.TcName)
}

func (ro *RestoreOpts) getRestoreDataPath() string {
func (ro *RestoreOpts) getRestoreDataPath() (string, error) {
pathSlice := strings.Split(ro.BackupPath, "://")
if len(pathSlice) != 2 {
return "", fmt.Errorf("cluster %s, backup path %s is invalid", ro, ro.BackupPath)
}

if pathSlice[0] == string(v1alpha1.BackupStorageTypeLocal) {
return strings.TrimPrefix(ro.BackupPath, "local:/"), nil
}

backupName := filepath.Base(ro.BackupPath)
NsClusterName := fmt.Sprintf("%s_%s", ro.Namespace, ro.TcName)
return filepath.Join(constants.BackupRootPath, NsClusterName, backupName)
return filepath.Join(constants.BackupRootPath, NsClusterName, backupName), nil
}

func (ro *RestoreOpts) downloadBackupData(localPath string) error {
pathSlice := strings.Split(ro.BackupPath, "://")
if len(pathSlice) != 2 {
return fmt.Errorf("cluster %s, backup path %s is invalid", ro, ro.BackupPath)
}

if pathSlice[0] == string(v1alpha1.BackupStorageTypeLocal) {
return nil
}

if err := util.EnsureDirectoryExist(filepath.Dir(localPath)); err != nil {
return err
}

remoteBucket := util.NormalizeBucketURI(ro.BackupPath)
rcCopy := exec.Command("rclone", constants.RcloneConfigArg, "copyto", remoteBucket, localPath)
bucketURI := util.NormalizeBucketURI(ro.BackupPath)
rcCopy := exec.Command("rclone", constants.RcloneConfigArg, "copyto", bucketURI, localPath)
if err := rcCopy.Start(); err != nil {
return fmt.Errorf("cluster %s, start rclone copyto command for download backup data %s falied, err: %v", ro, ro.BackupPath, err)
}
Expand Down Expand Up @@ -87,15 +106,13 @@ func (ro *RestoreOpts) loadTidbClusterData(restorePath string) error {

// unarchiveBackupData unarchive backup data to dest dir
func unarchiveBackupData(backupFile, destDir string) (string, error) {
var unarchiveBackupPath string
if err := util.EnsureDirectoryExist(destDir); err != nil {
return unarchiveBackupPath, err
return "", err
}
backupName := strings.TrimSuffix(filepath.Base(backupFile), constants.DefaultArchiveExtention)
err := archiver.Unarchive(backupFile, destDir)
if err != nil {
return unarchiveBackupPath, fmt.Errorf("unarchive backup data %s to %s failed, err: %v", backupFile, destDir, err)
return "", fmt.Errorf("unarchive backup data %s to %s failed, err: %v", backupFile, destDir, err)
}
unarchiveBackupPath = filepath.Join(destDir, backupName)
return unarchiveBackupPath, nil
return filepath.Join(destDir, backupName), nil
}
2 changes: 2 additions & 0 deletions images/backup-manager/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ storage_class = ${GCS_STORAGE_CLASS:-"MULTI_REGIONAL"}
type = azureblob
account = ${AZUREBLOB_ACCOUNT}
key = ${AZUREBLOB_KEY}
[local]
type = local
EOF

if [[ -n "${GCS_SERVICE_ACCOUNT_JSON_KEY:-}" ]]; then
Expand Down
4 changes: 2 additions & 2 deletions manifests/backup/backup-schedule.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ metadata:
namespace: test1
spec:
maxBackups: 5
storageClassName: rook-ceph-block
storageSize: 100Gi
schedule: "1 */1 * * *"
backupTemplate:
ceph:
endpoint: http://10.233.2.161
secretName: ceph-secret
storageType: ceph
storageClassName: rook-ceph-block
storageSize: 100Gi
cluster: demo1
tidbSecretName: backup-demo1-tidb-secret
4 changes: 2 additions & 2 deletions manifests/backup/restore.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ apiVersion: pingcap.com/v1alpha1
kind: Restore
metadata:
name: demo2-restore
namespace: test2
namespace: test1
spec:
cluster: demo2
backup: demo1-backup-schedule-2019-08-12t10-32-00
secretName: demo2-tidb-secret
backupNamespace: test1
RestoreNamespace: test2
storageClassName: rook-ceph-block
storageSize: 10Gi
18 changes: 14 additions & 4 deletions pkg/apis/pingcap.com/v1alpha1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,14 +292,21 @@ type BackupStorageType string
const (
// BackupStorageTypeCeph represents the backend storage type is ceph.
BackupStorageTypeCeph BackupStorageType = "ceph"

// BackupStorageTypeLocal represents the backend storage type is local filesystem
BackupStorageTypeLocal BackupStorageType = "local"
)

// StorageProvider defines the configuration for storing a backup in backend storage.
// StorageProvider defines the configuration for storing backups on backend storage.
type StorageProvider struct {
// Ceph defines the ceph backup storage spec
Ceph *CephStorageProvider `json:"ceph"`

// Local defines the local filesystem backup storage spec
Local *LocalStorageProvider `json:"local"`
}

// cephStorageProvider represents an ceph compatible bucket for storing backups.
// cephStorageProvider provides the spec how to store backups on ceph.
type CephStorageProvider struct {
// Region in which the ceph bucket is located.
Region string `json:"region"`
Expand All @@ -312,6 +319,9 @@ type CephStorageProvider struct {
SecretName string `json:"secretName"`
}

// LocalStorageProvider provides the spec how to store backups on local filesystem
type LocalStorageProvider struct{}

// BackupType represents the backup type.
type BackupType string

Expand Down Expand Up @@ -478,8 +488,8 @@ type RestoreSpec struct {
Cluster string `json:"cluster"`
// Backup represents the backup object to be restored.
Backup string `json:"backup"`
// Namespace is the namespace of the backup.
BackupNamespace string `json:"backupNamespace"`
//RestoreNamespace is the namespace of the restore tidb cluster.
RestoreNamespace string `json:"backupNamespace"`
// SecretName is the name of the secret which stores
// tidb cluster's username and password.
SecretName string `json:"secretName"`
Expand Down
21 changes: 21 additions & 0 deletions pkg/apis/pingcap.com/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions pkg/backup/backup/backup_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,9 @@ func (bm *backupManager) makeBackupJob(backup *v1alpha1.Backup) (*batchv1.Job, s
OwnerReferences: []metav1.OwnerReference{
controller.GetBackupOwnerRef(backup),
},
Annotations: map[string]string{
label.AnnBackupPVC: backup.GetBackupPVCName(),
},
},
Spec: batchv1.JobSpec{
BackoffLimit: controller.Int32Ptr(0),
Expand Down
Loading