Skip to content

Commit

Permalink
*: add binlog backup
Browse files Browse the repository at this point in the history
  • Loading branch information
acekingke committed Sep 19, 2023
1 parent fba7cb7 commit e3f721a
Show file tree
Hide file tree
Showing 8 changed files with 231 additions and 7 deletions.
6 changes: 6 additions & 0 deletions Dockerfile.sidecar
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ COPY utils/ utils/
# Build
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -a -o bin/sidecar cmd/sidecar/main.go

# Build mysql checker for mysql conatiner
COPY cmd/s3cmd/main.go cmd/s3cmd/main.go
COPY cmd/s3cmd/s3upload.go cmd/s3cmd/s3upload.go
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -a -o bin/s3upload cmd/s3cmd/main.go cmd/s3cmd/s3upload.go

# Build mysql checker for mysql conatiner
COPY cmd/mysql/main.go cmd/mysql/main.go
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -a -o bin/mysqlchecker cmd/mysql/main.go
Expand Down Expand Up @@ -57,4 +62,5 @@ RUN set -ex; \
WORKDIR /
COPY --from=builder /workspace/bin/sidecar /usr/local/bin/sidecar
COPY --from=builder /workspace/bin/mysqlchecker /mnt/mysqlchecker
COPY --from=builder /workspace/bin/s3upload /mnt/s3upload
ENTRYPOINT ["sidecar"]
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ build: generate fmt vet ## Build manager binary.
go build -o bin/manager ./cmd/manager/main.go
go build -o bin/sidecar ./cmd/sidecar/main.go
go build -o bin/nfsbcp ./cmd/nfsbcp/main.go
go build -o bin/s3upload ./cmd/s3cmd/*.go

run: manifests generate fmt vet ## Run a controller from your host.
go run ./cmd/manager/main.go
Expand Down
11 changes: 8 additions & 3 deletions api/v1beta1/backup_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,16 @@ type BackupSpec struct {
type BackupOps struct {
// BackupHost
// +optional
BackupHost string `json:"host,omitempty"`
S3 *S3 `json:"s3,omitempty"`
NFS *NFS `json:"nfs,omitempty"`
BackupHost string `json:"host,omitempty"`
S3 *S3 `json:"s3,omitempty"`
NFS *NFS `json:"nfs,omitempty"`
S3Binlog *S3Binlog `json:"s3binlog,omitempty"`
}

type S3Binlog struct {
// +optional
BackupSecretName string `json:"secretName,omitempty"`
}
type S3 struct {
// S3 Bucket
// +optional
Expand Down
20 changes: 20 additions & 0 deletions api/v1beta1/zz_generated.deepcopy.go

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

29 changes: 29 additions & 0 deletions cmd/s3cmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
Copyright 2021 RadonDB.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
"log"
"os"
)

func main() {
if len(os.Args) < 3 {
log.Fatalf("Usage: %s host passwd", os.Args[0])
}
UploadBinLog(os.Args[1], os.Args[2])
}
67 changes: 67 additions & 0 deletions cmd/s3cmd/s3upload.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package main

import (
"context"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"

"log"

"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
)

func UploadBinLog(host, interP string) {

S3EndPoint := os.Getenv("S3_ENDPOINT")
S3AccessKey := os.Getenv("S3_ACCESSKEY")
S3SecretKey := os.Getenv("S3_SECRETKEY")
S3Bucket := os.Getenv("S3_BUCKET")
cluster := os.Getenv("CLUSTER_NAME")
var err error
var minioClient *minio.Client
if minioClient, err = minio.New(strings.TrimPrefix(strings.TrimPrefix(S3EndPoint, "https://"), "http://"), &minio.Options{
Creds: credentials.NewStaticV4(S3AccessKey, S3SecretKey, ""),
Secure: strings.HasPrefix(S3Bucket, "https"),
// Region: "pek3b",
}); err != nil {
log.Fatalf("error in minio %v", err)
return
}
// mysqlbinlog dump and put load
runBinlogDump(host, "3306", "root", interP)
files, _ := ioutil.ReadDir("/tmp/")
ctx := context.TODO()
for _, file := range files {
if strings.HasPrefix(file.Name(), "mysql-bin") {
fh, err := os.Open("/tmp/" + file.Name())
if err != nil {
log.Fatalf("failed to open file %v", err)
return
}
log.Printf("put %s \n", cluster+"/"+filepath.Base(file.Name()))
upinfo, err := minioClient.PutObject(ctx, S3Bucket,
cluster+"-binlog/"+filepath.Base(file.Name()), fh, -1, minio.PutObjectOptions{})
if err != nil {
log.Fatalf("failed to upload file to S3 %v", err)
return
}
log.Println("S3 upload", "upinfo", upinfo)
}
}

}

func runBinlogDump(host, port, user, pass string) error {
cmdstr := fmt.Sprintf(`cd /tmp/;mysql -uroot -p%s -h%s -N -e "SHOW BINARY LOGS" | awk '{print "mysqlbinlog --read-from-remote-server --raw --host=%s --port=3306 --user=root --password=%s --raw", $1}'|bash`,
pass, host, host, pass)

cmd := exec.Command("bash", "-c", cmdstr)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
5 changes: 5 additions & 0 deletions config/crd/bases/mysql.radondb.com_backups.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,11 @@ spec:
description: S3 Bucket
type: string
type: object
s3binlog:
properties:
secretName:
type: string
type: object
type: object
clusterName:
description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
Expand Down
99 changes: 95 additions & 4 deletions controllers/backup/backup_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ func (r *BackupReconciler) reconcileManualBackup(ctx context.Context,
labels := ManualBackupLabels(cluster.Name)
backupJob.ObjectMeta.Labels = labels

spec, err := generateBackupJobSpec(backup, cluster, labels)
spec, err := r.generateBackupJobSpec(backup, cluster, labels)
if err != nil {
return errors.WithStack(err)
}
Expand Down Expand Up @@ -417,7 +417,7 @@ func (r *BackupReconciler) reconcileCronBackup(ctx context.Context, backup *v1be
}
objectMeta.Labels = labels
// objectmeta.Annotations = annotations
jobSpec, err := generateBackupJobSpec(backup, cluster, labels)
jobSpec, err := r.generateBackupJobSpec(backup, cluster, labels)
if err != nil {
return errors.WithStack(err)
}
Expand Down Expand Up @@ -449,7 +449,7 @@ func (r *BackupReconciler) reconcileCronBackup(ctx context.Context, backup *v1be

}

func generateBackupJobSpec(backup *v1beta1.Backup, cluster *v1beta1.MysqlCluster, labels map[string]string) (*batchv1.JobSpec, error) {
func (r *BackupReconciler) generateBackupJobSpec(backup *v1beta1.Backup, cluster *v1beta1.MysqlCluster, labels map[string]string) (*batchv1.JobSpec, error) {

// If backup.Spec.BackupOpts.S3 is not nil then use ENV BACKUP_TYPE=s3 and set the s3SecretName
// If backup.Spec.BackupOpts.NFS is not nil then use ENV BACKUP_TYPE=nfs and mount the nfs volume
Expand Down Expand Up @@ -479,7 +479,9 @@ func generateBackupJobSpec(backup *v1beta1.Backup, cluster *v1beta1.MysqlCluster
backupTypeEnv = corev1.EnvVar{Name: "BACKUP_TYPE", Value: "s3"}

}

if backup.Spec.BackupOpts.S3Binlog != nil {
return r.genBinlogJobTemplate(backup, cluster)
}
if backup.Spec.BackupOpts.NFS != nil {
NFSVolume = &corev1.Volume{
Name: "nfs-backup",
Expand Down Expand Up @@ -689,3 +691,92 @@ func (r *BackupReconciler) createBinlogJob(ctx context.Context, backup *v1beta1.

return nil
}

func (r *BackupReconciler) genBinlogJobTemplate(backup *v1beta1.Backup, cluster *v1beta1.MysqlCluster) (*batchv1.JobSpec, error) {
var S3BackuptEnv []corev1.EnvVar
s3SecretName := backup.Spec.BackupOpts.S3Binlog.BackupSecretName
log := log.FromContext(context.TODO()).WithValues("backup", "BinLog")
S3BackuptEnv = append(S3BackuptEnv,
getEnvVarFromSecret(s3SecretName, "S3_ENDPOINT", "s3-endpoint", false),
getEnvVarFromSecret(s3SecretName, "S3_ACCESSKEY", "s3-access-key", true),
getEnvVarFromSecret(s3SecretName, "S3_SECRETKEY", "s3-secret-key", true),
getEnvVarFromSecret(s3SecretName, "S3_BUCKET", "s3-bucket", true),
corev1.EnvVar{Name: "BACKUP_TYPE", Value: "s3"},
corev1.EnvVar{Name: "CLUSTER_NAME", Value: cluster.Name},
)
hostname := func() string {
if backup.Spec.BackupOpts.BackupHost != "" {
return fmt.Sprintf("%s.%s-mysql.%s", backup.Spec.BackupOpts.BackupHost, backup.Spec.ClusterName, backup.Namespace)
} else {
return backup.Spec.ClusterName + "-follower"
}
}()
c := &v1alpha1.MysqlCluster{}
c.Name = backup.Spec.ClusterName
secret := &corev1.Secret{}
secretKey := client.ObjectKey{Name: mysqlcluster.New(c).GetNameForResource(utils.Secret), Namespace: backup.Namespace}

if err := r.Get(context.TODO(), secretKey, secret); err != nil {
log.V(4).Info("binlogjob", "is is creating", "err", err)
}
internalRootPass := string(secret.Data["internal-root-password"])

jobSpec := &batchv1.JobSpec{
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{
"Host": hostname,
"Type": utils.BackupJobTypeName,

// Cluster used as selector.
"Cluster": backup.Spec.ClusterName,
}},
Spec: corev1.PodSpec{
InitContainers: []corev1.Container{
{
Name: "initial",
Image: cluster.Spec.Backup.Image,
ImagePullPolicy: cluster.Spec.ImagePullPolicy,
Command: []string{
"bash", "-c", "cp /mnt/s3upload /opt/radondb; chown -R 1001.1001 /opt/radondb",
},
VolumeMounts: []corev1.VolumeMount{
{
Name: utils.MySQLcheckerVolumeName,
MountPath: "/opt/radondb",
},
},
},
},
Containers: []corev1.Container{
{
Name: utils.ContainerBackupName,
Image: "percona:8.0",
ImagePullPolicy: cluster.Spec.ImagePullPolicy,
Env: S3BackuptEnv,
Command: []string{
"bash", "-c", fmt.Sprintf("/opt/radondb/s3upload %s %s", hostname, internalRootPass),
},
VolumeMounts: []corev1.VolumeMount{
{
Name: utils.MySQLcheckerVolumeName,
MountPath: "/opt/radondb",
},
},
},
},
RestartPolicy: corev1.RestartPolicyNever,
ServiceAccountName: backup.Spec.ClusterName,
Volumes: []corev1.Volume{
{
Name: utils.MySQLcheckerVolumeName,
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
},
},
},
},
}

return jobSpec, nil
}

0 comments on commit e3f721a

Please sign in to comment.