2019-11-07 20:05:39 +01:00
package main
import (
"flag"
"fmt"
"os"
2020-04-24 21:35:03 +02:00
"strings"
2019-11-07 20:05:39 +01:00
2020-04-24 21:35:03 +02:00
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmbackup/snapshot"
2019-11-07 20:05:39 +01:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/backup/actions"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/backup/common"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/backup/fslocal"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/buildinfo"
2020-08-11 21:54:13 +02:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/cgroup"
2020-02-10 12:26:18 +01:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/envflag"
2020-08-16 16:05:52 +02:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/flagutil"
2019-11-07 20:05:39 +01:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
)
var (
2020-04-24 21:35:03 +02:00
storageDataPath = flag . String ( "storageDataPath" , "victoria-metrics-data" , "Path to VictoriaMetrics data. Must match -storageDataPath from VictoriaMetrics or vmstorage" )
snapshotName = flag . String ( "snapshotName" , "" , "Name for the snapshot to backup. See https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/README.md#how-to-work-with-snapshots" )
2020-09-09 20:10:34 +02:00
snapshotCreateURL = flag . String ( "snapshot.createURL" , "" , "VictoriaMetrics create snapshot url. When this is given a snapshot will automatically be created during backup. " +
2020-04-24 21:35:03 +02:00
"Example: http://victoriametrics:8428/snaphsot/create" )
2020-09-09 20:10:34 +02:00
snapshotDeleteURL = flag . String ( "snapshot.deleteURL" , "" , "VictoriaMetrics delete snapshot url. Optional. Will be generated from -snapshot.createURL if not provided. " +
"All created snaphosts will be automatically deleted. Example: http://victoriametrics:8428/snaphsot/delete" )
2020-04-24 21:35:03 +02:00
dst = flag . String ( "dst" , "" , "Where to put the backup on the remote storage. " +
2019-11-07 20:05:39 +01:00
"Example: gcs://bucket/path/to/backup/dir, s3://bucket/path/to/backup/dir or fs:///path/to/local/backup/dir\n" +
"-dst can point to the previous backup. In this case incremental backup is performed, i.e. only changed data is uploaded" )
2019-11-19 19:31:52 +01:00
origin = flag . String ( "origin" , "" , "Optional origin directory on the remote storage with old backup for server-side copying when performing full backup. This speeds up full backups" )
concurrency = flag . Int ( "concurrency" , 10 , "The number of concurrent workers. Higher concurrency may reduce backup duration" )
2020-08-16 16:05:52 +02:00
maxBytesPerSecond = flagutil . NewBytes ( "maxBytesPerSecond" , 0 , "The maximum upload speed. There is no limit if it is set to 0" )
2019-11-07 20:05:39 +01:00
)
func main ( ) {
2020-05-16 10:59:30 +02:00
// Write flags and help message to stdout, since it is easier to grep or pipe.
flag . CommandLine . SetOutput ( os . Stdout )
2019-11-07 20:05:39 +01:00
flag . Usage = usage
2020-02-10 12:26:18 +01:00
envflag . Parse ( )
2019-11-07 20:05:39 +01:00
buildinfo . Init ( )
2020-08-11 21:54:13 +02:00
logger . Init ( )
cgroup . UpdateGOMAXPROCSToCPUQuota ( )
2019-11-07 20:05:39 +01:00
2020-04-24 21:35:03 +02:00
if len ( * snapshotCreateURL ) > 0 {
logger . Infof ( "%s" , "Snapshots enabled" )
logger . Infof ( "Snapshot create url %s" , * snapshotCreateURL )
if len ( * snapshotDeleteURL ) <= 0 {
err := flag . Set ( "snapshot.deleteURL" , strings . Replace ( * snapshotCreateURL , "/create" , "/delete" , 1 ) )
if err != nil {
logger . Fatalf ( "Failed to set snapshot.deleteURL flag: %v" , err )
}
}
logger . Infof ( "Snapshot delete url %s" , * snapshotDeleteURL )
name , err := snapshot . Create ( * snapshotCreateURL )
if err != nil {
logger . Fatalf ( "%s" , err )
}
err = flag . Set ( "snapshotName" , name )
if err != nil {
logger . Fatalf ( "Failed to set snapshotName flag: %v" , err )
}
defer func ( ) {
err := snapshot . Delete ( * snapshotDeleteURL , name )
if err != nil {
logger . Fatalf ( "%s" , err )
}
} ( )
}
2019-11-07 20:05:39 +01:00
srcFS , err := newSrcFS ( )
if err != nil {
logger . Fatalf ( "%s" , err )
}
dstFS , err := newDstFS ( )
if err != nil {
logger . Fatalf ( "%s" , err )
}
originFS , err := newOriginFS ( )
if err != nil {
logger . Fatalf ( "%s" , err )
}
a := & actions . Backup {
Concurrency : * concurrency ,
Src : srcFS ,
Dst : dstFS ,
Origin : originFS ,
}
if err := a . Run ( ) ; err != nil {
logger . Fatalf ( "cannot create backup: %s" , err )
}
2020-10-09 14:11:28 +02:00
srcFS . MustStop ( )
2019-11-07 20:05:39 +01:00
}
func usage ( ) {
const s = `
vmbackup performs backups for VictoriaMetrics data from instant snapshots to gcs , s3
or local filesystem . Backed up data can be restored with vmrestore .
See the docs at https : //github.com/VictoriaMetrics/VictoriaMetrics/blob/master/app/vmbackup/README.md .
`
f := flag . CommandLine . Output ( )
fmt . Fprintf ( f , "%s\n" , s )
flag . PrintDefaults ( )
}
func newSrcFS ( ) ( * fslocal . FS , error ) {
if len ( * snapshotName ) == 0 {
2020-04-24 21:35:03 +02:00
return nil , fmt . Errorf ( "`-snapshotName` or `-snapshot.createURL` must be provided" )
2019-11-07 20:05:39 +01:00
}
snapshotPath := * storageDataPath + "/snapshots/" + * snapshotName
// Verify the snapshot exists.
f , err := os . Open ( snapshotPath )
if err != nil {
2020-06-30 21:58:18 +02:00
return nil , fmt . Errorf ( "cannot open snapshot at %q: %w" , snapshotPath , err )
2019-11-07 20:05:39 +01:00
}
fi , err := f . Stat ( )
_ = f . Close ( )
if err != nil {
2020-06-30 21:58:18 +02:00
return nil , fmt . Errorf ( "cannot stat %q: %w" , snapshotPath , err )
2019-11-07 20:05:39 +01:00
}
if ! fi . IsDir ( ) {
return nil , fmt . Errorf ( "snapshot %q must be a directory" , snapshotPath )
}
fs := & fslocal . FS {
2019-11-19 19:31:52 +01:00
Dir : snapshotPath ,
2020-08-16 16:05:52 +02:00
MaxBytesPerSecond : maxBytesPerSecond . N ,
2019-11-19 19:31:52 +01:00
}
if err := fs . Init ( ) ; err != nil {
2020-06-30 21:58:18 +02:00
return nil , fmt . Errorf ( "cannot initialize fs: %w" , err )
2019-11-07 20:05:39 +01:00
}
return fs , nil
}
func newDstFS ( ) ( common . RemoteFS , error ) {
fs , err := actions . NewRemoteFS ( * dst )
if err != nil {
2020-06-30 21:58:18 +02:00
return nil , fmt . Errorf ( "cannot parse `-dst`=%q: %w" , * dst , err )
2019-11-07 20:05:39 +01:00
}
return fs , nil
}
func newOriginFS ( ) ( common . RemoteFS , error ) {
if len ( * origin ) == 0 {
return nil , nil
}
fs , err := actions . NewRemoteFS ( * origin )
if err != nil {
2020-06-30 21:58:18 +02:00
return nil , fmt . Errorf ( "cannot parse `-origin`=%q: %w" , * origin , err )
2019-11-07 20:05:39 +01:00
}
return fs , nil
}