diff --git a/api/v1alpha1/backup_types.go b/api/v1alpha1/backup_types.go index a72dec626..7b346816a 100644 --- a/api/v1alpha1/backup_types.go +++ b/api/v1alpha1/backup_types.go @@ -35,7 +35,12 @@ type BackupSpec struct { Image string `json:"image"` // HostName represents the host for which to take backup - HostName string `json:"hostname"` + // If is empty, is use leader HostName + HostName string `json:"hostname,omitempty"` + + // Represents the name of backup to NFS + // +optional + BackupToNFS string `json:"BackupToNFS,omitempty"` // ClusterName represents the cluster name to backup ClusterName string `json:"clustname"` diff --git a/api/v1alpha1/mysqlcluster_types.go b/api/v1alpha1/mysqlcluster_types.go index f0aae24bb..5de011682 100644 --- a/api/v1alpha1/mysqlcluster_types.go +++ b/api/v1alpha1/mysqlcluster_types.go @@ -81,6 +81,10 @@ type MysqlClusterSpec struct { // Represents the name of the cluster restore from backup path // +optional RestoreFrom string `json:"restoreFrom,omitempty"` + + // Represents the name of the cluster restore from NFS + // +optional + RestoreFromNFS string `json:"restoreFromNFS,omitempty"` } // MysqlOpts defines the options of MySQL container. diff --git a/backup/backup.go b/backup/backup.go index bb11f8e70..b8201f391 100644 --- a/backup/backup.go +++ b/backup/backup.go @@ -49,7 +49,11 @@ func (b *Backup) GetNameForJob() string { return fmt.Sprintf("%s-backup", b.Name) } -// Create the backup Domain Name. -func (b *Backup) GetBackupURL(cluster_name string, hostname string) string { - return fmt.Sprintf("%s.%s-mysql.%s:%v", hostname, cluster_name, b.Namespace, utils.XBackupPort) +// Create the backup Domain Name or leader DNS. +func (b *Backup) GetBackupURL(clusterName string, hostName string) string { + if len(hostName) != 0 { + return fmt.Sprintf("%s.%s-mysql.%s:%v", hostName, clusterName, b.Namespace, utils.XBackupPort) + } else { + return fmt.Sprintf("%s-leader.%s:%v", clusterName, b.Namespace, utils.XBackupPort) + } } diff --git a/backup/syncer/job.go b/backup/syncer/job.go index 3e85c7754..d41f0f147 100644 --- a/backup/syncer/job.go +++ b/backup/syncer/job.go @@ -116,9 +116,43 @@ func (s *jobSyncer) ensurePodSpec(in corev1.PodSpec) corev1.PodSpec { sctName := fmt.Sprintf("%s-secret", s.backup.Spec.ClusterName) in.Containers[0].Name = utils.ContainerBackupName in.Containers[0].Image = fmt.Sprintf("%s%s", mysqlcluster.GetPrefixFromEnv(), s.backup.Spec.Image) - in.Containers[0].Args = []string{ - "request_a_backup", - s.backup.GetBackupURL(s.backup.Spec.ClusterName, s.backup.Spec.HostName), + + if len(s.backup.Spec.BackupToNFS) != 0 { + // add volumn about pvc + in.Volumes = []corev1.Volume{ + { + Name: utils.XtrabackupPV, + VolumeSource: corev1.VolumeSource{ + NFS: &corev1.NFSVolumeSource{ + Server: s.backup.Spec.BackupToNFS, + Path: "/", + }, + }, + }, + } + //"rm -rf /backup/*;curl --user sys_backups:sys_backups sample-mysql-0.sample-mysql.default:8082/download|xbstream -x -C /backup" + in.Containers[0].Command = []string{ + "/bin/bash", "-c", "--", + } + var backupToDir string = utils.BuildBackupName() + in.Containers[0].Args = []string{ + fmt.Sprintf("mkdir -p /backup/%s;"+ + "curl --user $BACKUP_USER:$BACKUP_PASSWORD %s/download|xbstream -x -C /backup/%s; exit ${PIPESTATUS[0]}", + backupToDir, + s.backup.GetBackupURL(s.backup.Spec.ClusterName, s.backup.Spec.HostName), backupToDir), + } + in.Containers[0].VolumeMounts = []corev1.VolumeMount{ + { + Name: utils.XtrabackupPV, + MountPath: utils.XtrabckupLocal, + }, + } + } else { + // in.Containers[0].ImagePullPolicy = s.opt.ImagePullPolicy + in.Containers[0].Args = []string{ + "request_a_backup", + s.backup.GetBackupURL(s.backup.Spec.ClusterName, s.backup.Spec.HostName), + } } var optTrue bool = true in.Containers[0].Env = []corev1.EnvVar{ diff --git a/charts/mysql-operator/crds/mysql.radondb.com_backups.yaml b/charts/mysql-operator/crds/mysql.radondb.com_backups.yaml index 4b4f2c1c3..1d63ef0e0 100644 --- a/charts/mysql-operator/crds/mysql.radondb.com_backups.yaml +++ b/charts/mysql-operator/crds/mysql.radondb.com_backups.yaml @@ -36,6 +36,9 @@ spec: spec: description: BackupSpec defines the desired state of Backup properties: + BackupToNFS: + description: Represents the name of backup to NFS + type: string clustname: description: ClusterName represents the cluster name to backup type: string @@ -46,6 +49,7 @@ spec: type: integer hostname: description: HostName represents the host for which to take backup + If is empty, is use leader HostName type: string image: default: radondb/mysql57-sidecar:v2.2.0 @@ -53,7 +57,6 @@ spec: type: string required: - clustname - - hostname type: object status: description: BackupStatus defines the observed state of Backup diff --git a/charts/mysql-operator/crds/mysql.radondb.com_mysqlclusters.yaml b/charts/mysql-operator/crds/mysql.radondb.com_mysqlclusters.yaml index fe8d22588..cc82b6b4d 100644 --- a/charts/mysql-operator/crds/mysql.radondb.com_mysqlclusters.yaml +++ b/charts/mysql-operator/crds/mysql.radondb.com_mysqlclusters.yaml @@ -1245,6 +1245,9 @@ spec: description: Represents the name of the cluster restore from backup path type: string + restoreFromNFS: + description: Represents the name of the cluster restore from NFS + type: string xenonOpts: default: admitDefeatHearbeatCount: 5 diff --git a/config/crd/bases/mysql.radondb.com_backups.yaml b/config/crd/bases/mysql.radondb.com_backups.yaml index 4b4f2c1c3..1d63ef0e0 100644 --- a/config/crd/bases/mysql.radondb.com_backups.yaml +++ b/config/crd/bases/mysql.radondb.com_backups.yaml @@ -36,6 +36,9 @@ spec: spec: description: BackupSpec defines the desired state of Backup properties: + BackupToNFS: + description: Represents the name of backup to NFS + type: string clustname: description: ClusterName represents the cluster name to backup type: string @@ -46,6 +49,7 @@ spec: type: integer hostname: description: HostName represents the host for which to take backup + If is empty, is use leader HostName type: string image: default: radondb/mysql57-sidecar:v2.2.0 @@ -53,7 +57,6 @@ spec: type: string required: - clustname - - hostname type: object status: description: BackupStatus defines the observed state of Backup diff --git a/config/crd/bases/mysql.radondb.com_mysqlclusters.yaml b/config/crd/bases/mysql.radondb.com_mysqlclusters.yaml index fe8d22588..cc82b6b4d 100644 --- a/config/crd/bases/mysql.radondb.com_mysqlclusters.yaml +++ b/config/crd/bases/mysql.radondb.com_mysqlclusters.yaml @@ -1245,6 +1245,9 @@ spec: description: Represents the name of the cluster restore from backup path type: string + restoreFromNFS: + description: Represents the name of the cluster restore from NFS + type: string xenonOpts: default: admitDefeatHearbeatCount: 5 diff --git a/config/samples/backup_secret.yaml b/config/samples/backup_secret.yaml index 93fa9e2e7..47e86e6e6 100644 --- a/config/samples/backup_secret.yaml +++ b/config/samples/backup_secret.yaml @@ -1,5 +1,5 @@ kind: Secret -apiVersion: v1 +apiVersion: mysql.radondb.com/v1alpha1 metadata: name: sample-backup-secret namespace: default diff --git a/config/samples/mysql_v1alpha1_backup.yaml b/config/samples/mysql_v1alpha1_backup.yaml index 7fd5a66e4..669293b14 100644 --- a/config/samples/mysql_v1alpha1_backup.yaml +++ b/config/samples/mysql_v1alpha1_backup.yaml @@ -5,5 +5,7 @@ metadata: spec: # Add fields here image: radondb/mysql-sidecar:latest + # hostname if empty, use the leader as hostname hostname: sample-mysql-0 clustname: sample + # BackupToNFS: "IP of NFS server" diff --git a/config/samples/mysql_v1alpha1_mysqlcluster.yaml b/config/samples/mysql_v1alpha1_mysqlcluster.yaml index f2c7021d3..90565c452 100644 --- a/config/samples/mysql_v1alpha1_mysqlcluster.yaml +++ b/config/samples/mysql_v1alpha1_mysqlcluster.yaml @@ -1,7 +1,7 @@ apiVersion: mysql.radondb.com/v1alpha1 kind: MysqlCluster metadata: - name: sample + name: sample2 spec: replicas: 3 mysqlVersion: "5.7" @@ -11,8 +11,12 @@ spec: # backupSecretName: # if you want create mysqlcluster from S3, uncomment and fill the directory in S3 bucket below: + # such as restoreFrom: "backup_202241423817" # restoreFrom: + # Restore from NFS, uncomment below and set the ip of NFS server + # such as restoreFromNFS: "10.233.55.172" + # restoreFromNFS: mysqlOpts: rootPassword: "RadonDB@123" rootHost: localhost diff --git a/config/samples/nfs_rc.yaml b/config/samples/nfs_rc.yaml new file mode 100644 index 000000000..5927ca1fc --- /dev/null +++ b/config/samples/nfs_rc.yaml @@ -0,0 +1,32 @@ +apiVersion: v1 +kind: ReplicationController +metadata: + name: nfs-server +spec: + replicas: 1 + selector: + role: nfs-server + template: + metadata: + labels: + role: nfs-server + spec: + containers: + - name: nfs-server + image: gcr.azk8s.cn/google_containers/volume-nfs:0.8 + ports: + - name: nfs + containerPort: 2049 + - name: mountd + containerPort: 20048 + - name: rpcbind + containerPort: 111 + securityContext: + privileged: true + volumeMounts: + - mountPath: /exports + name: nfs-export-fast + volumes: + - name: nfs-export-fast + persistentVolumeClaim: + claimName: backup-pv-claim diff --git a/config/samples/nfs_server.yaml b/config/samples/nfs_server.yaml new file mode 100644 index 000000000..9654d1583 --- /dev/null +++ b/config/samples/nfs_server.yaml @@ -0,0 +1,14 @@ +kind: Service +apiVersion: v1 +metadata: + name: nfs-server +spec: + ports: + - name: nfs + port: 2049 + - name: mountd + port: 20048 + - name: rpcbind + port: 111 + selector: + role: nfs-server diff --git a/config/samples/pv-claim.yml b/config/samples/pv-claim.yml new file mode 100644 index 000000000..b98d38c6c --- /dev/null +++ b/config/samples/pv-claim.yml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: backup-pv-claim +spec: + storageClassName: manual + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 30Gi diff --git a/config/samples/pv-volume.yaml b/config/samples/pv-volume.yaml new file mode 100644 index 000000000..c9bd08a80 --- /dev/null +++ b/config/samples/pv-volume.yaml @@ -0,0 +1,32 @@ +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + annotations: + name: manual +provisioner: kubernetes.io/no-provisioner +reclaimPolicy: Retain +volumeBindingMode: WaitForFirstConsumer +--- + +apiVersion: v1 +kind: PersistentVolume +metadata: + name: backup-pv-volume + labels: + type: local +spec: + storageClassName: manual + nodeAffinity: + required: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/hostname + operator: In + values: + - node2 + capacity: + storage: 40Gi + accessModes: + - ReadWriteOnce + hostPath: + path: "/mnt/backup" diff --git a/docs/deploy_backup_restore_nfs.md b/docs/deploy_backup_restore_nfs.md new file mode 100644 index 000000000..99ed0893e --- /dev/null +++ b/docs/deploy_backup_restore_nfs.md @@ -0,0 +1,60 @@ +# mysql-operator + +## Quickstart for NFS backup +### Create NFS server +First create your PVC, such as "neosan1", +Or use local storage pvc, see `config/sample/pv-claim.yalm` +and `pv-volume.yaml` for more details. +Then create NFS server, such as "nfs-server", +```yaml +fill it to `config/samples/nfs_rc.yaml ` +```yaml +... + volumes: + - name: nfs-export-fast + persistentVolumeClaim: + claimName: neosan1 // or backup-pv-claim +``` + ```shell +# create the nfs pod +kubectl apply -f config/samples/nfs_rc.yaml +# create the nfs service +kubectl apply -f config/samples/nfs_server.yaml + ``` +if create the nfs server successful, get the then: + +## config `mysql_v1alpha1_backup.yaml ` and backup +Add field in `mysql_v1alpha1_backup.yaml ` as follow: +```yaml +BackupToNFS: "IP of NFS server" +``` +use command as follow to backup +```shell +kubectl apply -f config/samples/mysql_v1alpha1_backup.yaml +``` + + ## Restore cluster from exist NFS backup + first, configure the `mysql_v1alpha1_cluster.yaml`, uncomment the `restoreFromNFS` field: + ```yaml + .... + restoreFrom: "backup_202196152239" + restoreFromNFS : 10.96.253.82 + ``` + `backup_202196152239` is the nfs server backup path, change it to yours. + the `10.96.253.82` is the NFS server ip, change it to yours. + use command as follow to create cluster from NFS server backup copy: + + ## build your own image + such as : + ``` + docker build -f Dockerfile.sidecar -t acekingke/sidecar:0.1 . && docker push acekingke/sidecar:0.1 + docker build -t acekingke/controller:0.1 . && docker push acekingke/controller:0.1 + ``` + you can replace acekingke/sidecar:0.1 with your own tag + + ## deploy your own manager +```shell +make manifests +make install +make deploy IMG=acekingke/controller:0.1 KUSTOMIZE=~/radondb-mysql-kubernetes/bin/kustomize +``` diff --git a/docs/en-us/deploy_backup_restore_s3.md b/docs/en-us/deploy_backup_restore_s3.md index aa80ac5ed..a0a4dded4 100644 --- a/docs/en-us/deploy_backup_restore_s3.md +++ b/docs/en-us/deploy_backup_restore_s3.md @@ -1,6 +1,6 @@ # mysql-operator -## Quickstart for backup +## Quickstart for backup S3 Install the operator named `test`: @@ -8,7 +8,7 @@ Install the operator named `test`: helm install test charts/mysql-operator ``` -### configure backup +### configure backup for S3 add the secret file ```yaml @@ -110,6 +110,47 @@ kubectl apply -f config/samples/mysql_v1alpha1_mysqlcluster.yaml ``` could restore a cluster from the `backup_2021720827 ` copy in the S3 bucket. +if you want backup to NFS server or restore from NFS server, do it as follow: + +## Quickstart for NFS backup + +### Create NFS server +first create your PVC, such as "neosan1", fill it to `config/samples/nfs_rc.yaml ` +```yaml +... + volumes: + - name: nfs-export-fast + persistentVolumeClaim: + claimName: neosan1 +``` + ```shell +# create the nfs pod +kubectl apply -f config/samples/nfs_rc.yaml +# create the nfs service +kubectl apply -f config/samples/nfs_server.yaml + ``` +if create the nfs server successful, get the then: + +## config `mysql_v1alpha1_backup.yaml ` and backup +Add field in `mysql_v1alpha1_backup.yaml ` as follow: +```yaml +BackupToNFS: "IP of NFS server" +``` +use commadn as follow to backup +```shell +kubectl apply -f config/samples/mysql_v1alpha1_backup.yaml +``` + + ## Restore cluster from exist NFS backup + first, configure the `mysql_v1alpha1_cluster.yaml`, uncomment the `restoreFromNFS` field: + ```yaml + .... + restoreFrom: "backup_202196152239" + restoreFromNFS : 10.96.253.82 + ``` + `backup_202196152239` is the nfs server backup path, change it to yours. + the `10.96.253.82` is the NFS server ip, change it to yours. + use command as follow to create cluster from NFS server backup copy: ## build your own image such as : diff --git a/mysqlcluster/container/init_sidecar.go b/mysqlcluster/container/init_sidecar.go index 7193c0da7..d1a08502a 100644 --- a/mysqlcluster/container/init_sidecar.go +++ b/mysqlcluster/container/init_sidecar.go @@ -123,7 +123,12 @@ func (c *initSidecar) getEnvVars() []corev1.EnvVar { getEnvVarFromSecret(sctNamebackup, "S3_BUCKET", "s3-bucket", true), ) } - + if len(c.Spec.RestoreFromNFS) != 0 { + envs = append(envs, corev1.EnvVar{ + Name: "RESTORE_FROM_NFS", + Value: c.Spec.RestoreFromNFS, + }) + } if c.Spec.MysqlOpts.InitTokuDB { envs = append(envs, corev1.EnvVar{ Name: "INIT_TOKUDB", @@ -205,6 +210,15 @@ func (c *initSidecar) getVolumeMounts() []corev1.VolumeMount { ) } + if len(c.Spec.RestoreFromNFS) != 0 { + volumeMounts = append(volumeMounts, + corev1.VolumeMount{ + Name: utils.XtrabackupPV, + MountPath: utils.XtrabckupLocal, + }, + ) + } + if c.Spec.Persistence.Enabled { volumeMounts = append(volumeMounts, corev1.VolumeMount{ diff --git a/mysqlcluster/mysqlcluster.go b/mysqlcluster/mysqlcluster.go index 44a1f742d..d224d9d6f 100644 --- a/mysqlcluster/mysqlcluster.go +++ b/mysqlcluster/mysqlcluster.go @@ -257,7 +257,18 @@ func (c *MysqlCluster) EnsureVolumes() []corev1.Volume { }, }, ) - + // add the nfs volumn mount + if len(c.Spec.RestoreFromNFS) != 0 { + volumes = append(volumes, corev1.Volume{ + Name: utils.XtrabackupPV, + VolumeSource: corev1.VolumeSource{ + NFS: &corev1.NFSVolumeSource{ + Server: c.Spec.RestoreFromNFS, + Path: "/", + }, + }, + }) + } return volumes } diff --git a/mysqlcluster/syncer/follower_service.go b/mysqlcluster/syncer/follower_service.go index 6960c00c7..642b3eaae 100644 --- a/mysqlcluster/syncer/follower_service.go +++ b/mysqlcluster/syncer/follower_service.go @@ -58,7 +58,7 @@ func NewFollowerSVCSyncer(cli client.Client, c *mysqlcluster.MysqlCluster) synce service.Spec.Ports[0].Name = utils.MysqlPortName service.Spec.Ports[0].Port = utils.MysqlPort service.Spec.Ports[0].TargetPort = intstr.FromInt(utils.MysqlPort) - //xtrabckup + // xtrabackup service.Spec.Ports[1].Name = utils.XBackupPortName service.Spec.Ports[1].Port = utils.XBackupPort service.Spec.Ports[1].TargetPort = intstr.FromInt(utils.XBackupPort) diff --git a/mysqlcluster/syncer/leader_service.go b/mysqlcluster/syncer/leader_service.go index 2bae1904d..32b5c66e7 100644 --- a/mysqlcluster/syncer/leader_service.go +++ b/mysqlcluster/syncer/leader_service.go @@ -58,7 +58,7 @@ func NewLeaderSVCSyncer(cli client.Client, c *mysqlcluster.MysqlCluster) syncer. service.Spec.Ports[0].Port = utils.MysqlPort service.Spec.Ports[0].TargetPort = intstr.FromInt(utils.MysqlPort) - //xtrabckup + // xtrabackup service.Spec.Ports[1].Name = utils.XBackupPortName service.Spec.Ports[1].Port = utils.XBackupPort service.Spec.Ports[1].TargetPort = intstr.FromInt(utils.XBackupPort) diff --git a/sidecar/config.go b/sidecar/config.go index cfbd48ae8..2974acc2b 100644 --- a/sidecar/config.go +++ b/sidecar/config.go @@ -125,6 +125,9 @@ type Config struct { // GtidPurged is the gtid set of the slave cluster to purged. GtidPurged string + + // NFS server which Restore from + XRestoreFromNFS string } // NewInitConfig returns a pointer to Config. @@ -188,6 +191,7 @@ func NewInitConfig() *Config { existMySQLData: existMySQLData, XRestoreFrom: getEnvValue("RESTORE_FROM"), + XRestoreFromNFS: getEnvValue("RESTORE_FROM_NFS"), XCloudS3EndPoint: getEnvValue("S3_ENDPOINT"), XCloudS3AccessKey: getEnvValue("S3_ACCESSKEY"), XCloudS3SecretKey: getEnvValue("S3_SECRETKEY"), @@ -639,3 +643,76 @@ func GetXtrabackupGTIDPurged(backuppath string) (string, error) { // Replace multi gtidset \n return strings.Replace(ss[2], "\n", "", -1), nil } + +/* +`#!/bin/sh + if [ ! -d {{.DataDir}} ]; then + echo "is not exist the var lib mysql" + mkdir {{.DataDir}} + chown -R mysql.mysql {{.DataDir}} + fi + rm -rf {{.DataDir}}/* + xtrabackup --defaults-file={{.MyCnfMountPath}} --use-memory=3072M --prepare --apply-log-only --target-dir=/backup/{{.XRestoreFrom}} + xtrabackup --defaults-file={{.MyCnfMountPath}} --use-memory=3072M --prepare --target-dir=/backup/{{.XRestoreFrom}} + chown -R mysql.mysql /backup/{{.XRestoreFromNFS}} + xtrabackup --defaults-file={{.MyCnfMountPath}} --datadir={{.DataDir}} --copy-back --target-dir=/backup/{{.XRestoreFrom}} + exit_code=$? + chown -R mysql.mysql {{.DataDir}} + exit $exit_code +*/ +func (cfg *Config) ExecuteNFSRestore() error { + if len(cfg.XRestoreFromNFS) == 0 { + return fmt.Errorf("parameter XRestoreFromNFS empty, do next step") + } + if len(cfg.XRestoreFrom) == 0 { + return fmt.Errorf("xrestore from is empty, do next step") + } + // Restore from NFS + + // Check /var/lib/mysql exists or not. + if _, err := os.Stat(utils.DataVolumeMountPath); os.IsNotExist(err) { + err = os.MkdirAll(utils.DataVolumeMountPath, 0755) + if err != nil { + return fmt.Errorf("create /var/lib/mysql fail : %s", err) + } + // Change the owner of /var/lib/mysql + cmd := exec.Command("chown", "mysql.mysql", utils.DataVolumeMountPath) + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to chown -R mysql.mysql : %s", err) + } + } + // Remove the data directory + cmd := exec.Command("rm", "-rf", utils.DataVolumeMountPath+"/*") + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to rm -rf %s : %s", utils.DataVolumeMountPath, err) + } + // Prepare the append-only file + cmd = exec.Command("xtrabackup", "--defaults-file="+utils.MysqlConfVolumeMountPath+"/my.cnf", "--use-memory=3072M", "--prepare", "--apply-log-only", "--target-dir=/backup/"+cfg.XRestoreFrom) + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to xtrabackup prepare append-only: %s", err) + } + // Prepare the data directory + cmd = exec.Command("xtrabackup", "--defaults-file="+utils.MysqlConfVolumeMountPath+"/my.cnf", "--use-memory=3072M", "--prepare", "--target-dir=/backup/"+cfg.XRestoreFrom) + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to xtrabackup prepare: %s", err) + } + // Copy the data directory. + cmd = exec.Command("xtrabackup", "--defaults-file="+utils.MysqlConfVolumeMountPath+"/my.cnf", "--datadir="+utils.DataVolumeMountPath, "--copy-back", "--target-dir=/backup/"+cfg.XRestoreFrom) + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to xtrabackup copy-back: %s", err) + } + // Change owner of data directory + log.Info(fmt.Sprintf("change owner of data directory %s", utils.DataVolumeMountPath)) + cmd = exec.Command("chown", "-R", "mysql.mysql", utils.DataVolumeMountPath) + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to chown -R mysql.mysql : %s", err) + } + + return nil +} diff --git a/sidecar/init.go b/sidecar/init.go index 15be1bb02..2b8a1aad5 100644 --- a/sidecar/init.go +++ b/sidecar/init.go @@ -234,8 +234,11 @@ func runInitCommand(cfg *Config) error { return fmt.Errorf("failed to execute Clone Restore : %s", err_f) } } else { - if err = cfg.executeS3Restore(cfg.XRestoreFrom); err != nil { - return fmt.Errorf("failed to restore from %s: %s", cfg.XRestoreFrom, err) + if err_f = cfg.ExecuteNFSRestore(); err_f != nil { + // No nfs , do s3 restore. + if err_f = cfg.executeS3Restore(cfg.XRestoreFrom); err_f != nil { + return fmt.Errorf("failed to restore from %s: %s", cfg.XRestoreFrom, err_f) + } } } // Check has initialized again. @@ -251,6 +254,7 @@ func runInitCommand(cfg *Config) error { if err = ioutil.WriteFile(xenonFilePath, cfg.buildXenonConf(), 0644); err != nil { return fmt.Errorf("failed to write xenon.json: %s", err) } + log.Info("init command success") return nil } diff --git a/sidecar/server.go b/sidecar/server.go index 2f0bf762e..dcc424c9a 100644 --- a/sidecar/server.go +++ b/sidecar/server.go @@ -31,14 +31,6 @@ import ( ) const ( - serverPort = utils.XBackupPort - serverProbeEndpoint = "/health" - serverBackupEndpoint = "/xbackup" - serverConnectTimeout = 5 * time.Second - - // DownLoad server url. - serverBackupDownLoadEndpoint = "/download" - // backupStatus http trailer backupStatusTrailer = "X-Backup-Status" @@ -47,6 +39,14 @@ const ( // failure string backupFailed = "Failed" + + serverPort = utils.XBackupPort + serverProbeEndpoint = "/health" + serverBackupEndpoint = "/xbackup" + serverConnectTimeout = 5 * time.Second + + // DownLoad server url. + serverBackupDownLoadEndpoint = "/download" ) type server struct { @@ -68,8 +68,10 @@ func newServer(cfg *Config, stop <-chan struct{}) *server { // Add handle functions. mux.HandleFunc(serverProbeEndpoint, srv.healthHandler) mux.Handle(serverBackupEndpoint, maxClients(http.HandlerFunc(srv.backupHandler), 1)) + mux.Handle(serverBackupDownLoadEndpoint, maxClients(http.HandlerFunc(srv.backupDownLoadHandler), 1)) + // Shutdown gracefully the http server. go func() { <-stop // wait for stop signal @@ -95,7 +97,7 @@ func (s *server) backupHandler(w http.ResponseWriter, r *http.Request) { http.Error(w, "Not authenticated!", http.StatusForbidden) return } - err := RunTakeBackupCommand(s.cfg, "") + err := RunTakeBackupCommand(s.cfg) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } else { diff --git a/sidecar/takebackup.go b/sidecar/takebackup.go index 76d796d8f..2490af6e2 100644 --- a/sidecar/takebackup.go +++ b/sidecar/takebackup.go @@ -23,7 +23,7 @@ import ( ) // RunTakeBackupCommand starts a backup command -func RunTakeBackupCommand(cfg *Config, name string) error { +func RunTakeBackupCommand(cfg *Config) error { // cfg->XtrabackupArgs() xtrabackup := exec.Command(xtrabackupCommand, cfg.XtrabackupArgs()...) diff --git a/utils/constants.go b/utils/constants.go index 6e90971d0..0d5377bb3 100644 --- a/utils/constants.go +++ b/utils/constants.go @@ -63,8 +63,12 @@ const ( ContainerBackupName = "backup" ContainerBackupJobName = "backup-job" + // xtrabackup XBackupPortName = "xtrabackup" XBackupPort = 8082 + XtrabackupPV = "backup" + XtrabckupLocal = "/backup" + // MySQL port. MysqlPortName = "mysql" MysqlPort = 3306