Skip to content

Commit e3f721a

Browse files
committed
*: add binlog backup
1 parent fba7cb7 commit e3f721a

File tree

8 files changed

+231
-7
lines changed

8 files changed

+231
-7
lines changed

Dockerfile.sidecar

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ COPY utils/ utils/
2525
# Build
2626
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -a -o bin/sidecar cmd/sidecar/main.go
2727

28+
# Build mysql checker for mysql conatiner
29+
COPY cmd/s3cmd/main.go cmd/s3cmd/main.go
30+
COPY cmd/s3cmd/s3upload.go cmd/s3cmd/s3upload.go
31+
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
32+
2833
# Build mysql checker for mysql conatiner
2934
COPY cmd/mysql/main.go cmd/mysql/main.go
3035
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -a -o bin/mysqlchecker cmd/mysql/main.go
@@ -57,4 +62,5 @@ RUN set -ex; \
5762
WORKDIR /
5863
COPY --from=builder /workspace/bin/sidecar /usr/local/bin/sidecar
5964
COPY --from=builder /workspace/bin/mysqlchecker /mnt/mysqlchecker
65+
COPY --from=builder /workspace/bin/s3upload /mnt/s3upload
6066
ENTRYPOINT ["sidecar"]

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ build: generate fmt vet ## Build manager binary.
9191
go build -o bin/manager ./cmd/manager/main.go
9292
go build -o bin/sidecar ./cmd/sidecar/main.go
9393
go build -o bin/nfsbcp ./cmd/nfsbcp/main.go
94+
go build -o bin/s3upload ./cmd/s3cmd/*.go
9495

9596
run: manifests generate fmt vet ## Run a controller from your host.
9697
go run ./cmd/manager/main.go

api/v1beta1/backup_types.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,16 @@ type BackupSpec struct {
4545
type BackupOps struct {
4646
// BackupHost
4747
// +optional
48-
BackupHost string `json:"host,omitempty"`
49-
S3 *S3 `json:"s3,omitempty"`
50-
NFS *NFS `json:"nfs,omitempty"`
48+
BackupHost string `json:"host,omitempty"`
49+
S3 *S3 `json:"s3,omitempty"`
50+
NFS *NFS `json:"nfs,omitempty"`
51+
S3Binlog *S3Binlog `json:"s3binlog,omitempty"`
5152
}
5253

54+
type S3Binlog struct {
55+
// +optional
56+
BackupSecretName string `json:"secretName,omitempty"`
57+
}
5358
type S3 struct {
5459
// S3 Bucket
5560
// +optional

api/v1beta1/zz_generated.deepcopy.go

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/s3cmd/main.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
Copyright 2021 RadonDB.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package main
18+
19+
import (
20+
"log"
21+
"os"
22+
)
23+
24+
func main() {
25+
if len(os.Args) < 3 {
26+
log.Fatalf("Usage: %s host passwd", os.Args[0])
27+
}
28+
UploadBinLog(os.Args[1], os.Args[2])
29+
}

cmd/s3cmd/s3upload.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"io/ioutil"
7+
"os"
8+
"os/exec"
9+
"path/filepath"
10+
"strings"
11+
12+
"log"
13+
14+
"github.com/minio/minio-go/v7"
15+
"github.com/minio/minio-go/v7/pkg/credentials"
16+
)
17+
18+
func UploadBinLog(host, interP string) {
19+
20+
S3EndPoint := os.Getenv("S3_ENDPOINT")
21+
S3AccessKey := os.Getenv("S3_ACCESSKEY")
22+
S3SecretKey := os.Getenv("S3_SECRETKEY")
23+
S3Bucket := os.Getenv("S3_BUCKET")
24+
cluster := os.Getenv("CLUSTER_NAME")
25+
var err error
26+
var minioClient *minio.Client
27+
if minioClient, err = minio.New(strings.TrimPrefix(strings.TrimPrefix(S3EndPoint, "https://"), "http://"), &minio.Options{
28+
Creds: credentials.NewStaticV4(S3AccessKey, S3SecretKey, ""),
29+
Secure: strings.HasPrefix(S3Bucket, "https"),
30+
// Region: "pek3b",
31+
}); err != nil {
32+
log.Fatalf("error in minio %v", err)
33+
return
34+
}
35+
// mysqlbinlog dump and put load
36+
runBinlogDump(host, "3306", "root", interP)
37+
files, _ := ioutil.ReadDir("/tmp/")
38+
ctx := context.TODO()
39+
for _, file := range files {
40+
if strings.HasPrefix(file.Name(), "mysql-bin") {
41+
fh, err := os.Open("/tmp/" + file.Name())
42+
if err != nil {
43+
log.Fatalf("failed to open file %v", err)
44+
return
45+
}
46+
log.Printf("put %s \n", cluster+"/"+filepath.Base(file.Name()))
47+
upinfo, err := minioClient.PutObject(ctx, S3Bucket,
48+
cluster+"-binlog/"+filepath.Base(file.Name()), fh, -1, minio.PutObjectOptions{})
49+
if err != nil {
50+
log.Fatalf("failed to upload file to S3 %v", err)
51+
return
52+
}
53+
log.Println("S3 upload", "upinfo", upinfo)
54+
}
55+
}
56+
57+
}
58+
59+
func runBinlogDump(host, port, user, pass string) error {
60+
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`,
61+
pass, host, host, pass)
62+
63+
cmd := exec.Command("bash", "-c", cmdstr)
64+
cmd.Stdout = os.Stdout
65+
cmd.Stderr = os.Stderr
66+
return cmd.Run()
67+
}

config/crd/bases/mysql.radondb.com_backups.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,11 @@ spec:
221221
description: S3 Bucket
222222
type: string
223223
type: object
224+
s3binlog:
225+
properties:
226+
secretName:
227+
type: string
228+
type: object
224229
type: object
225230
clusterName:
226231
description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster

controllers/backup/backup_controller.go

Lines changed: 95 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ func (r *BackupReconciler) reconcileManualBackup(ctx context.Context,
293293
labels := ManualBackupLabels(cluster.Name)
294294
backupJob.ObjectMeta.Labels = labels
295295

296-
spec, err := generateBackupJobSpec(backup, cluster, labels)
296+
spec, err := r.generateBackupJobSpec(backup, cluster, labels)
297297
if err != nil {
298298
return errors.WithStack(err)
299299
}
@@ -417,7 +417,7 @@ func (r *BackupReconciler) reconcileCronBackup(ctx context.Context, backup *v1be
417417
}
418418
objectMeta.Labels = labels
419419
// objectmeta.Annotations = annotations
420-
jobSpec, err := generateBackupJobSpec(backup, cluster, labels)
420+
jobSpec, err := r.generateBackupJobSpec(backup, cluster, labels)
421421
if err != nil {
422422
return errors.WithStack(err)
423423
}
@@ -449,7 +449,7 @@ func (r *BackupReconciler) reconcileCronBackup(ctx context.Context, backup *v1be
449449

450450
}
451451

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

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

481481
}
482-
482+
if backup.Spec.BackupOpts.S3Binlog != nil {
483+
return r.genBinlogJobTemplate(backup, cluster)
484+
}
483485
if backup.Spec.BackupOpts.NFS != nil {
484486
NFSVolume = &corev1.Volume{
485487
Name: "nfs-backup",
@@ -689,3 +691,92 @@ func (r *BackupReconciler) createBinlogJob(ctx context.Context, backup *v1beta1.
689691

690692
return nil
691693
}
694+
695+
func (r *BackupReconciler) genBinlogJobTemplate(backup *v1beta1.Backup, cluster *v1beta1.MysqlCluster) (*batchv1.JobSpec, error) {
696+
var S3BackuptEnv []corev1.EnvVar
697+
s3SecretName := backup.Spec.BackupOpts.S3Binlog.BackupSecretName
698+
log := log.FromContext(context.TODO()).WithValues("backup", "BinLog")
699+
S3BackuptEnv = append(S3BackuptEnv,
700+
getEnvVarFromSecret(s3SecretName, "S3_ENDPOINT", "s3-endpoint", false),
701+
getEnvVarFromSecret(s3SecretName, "S3_ACCESSKEY", "s3-access-key", true),
702+
getEnvVarFromSecret(s3SecretName, "S3_SECRETKEY", "s3-secret-key", true),
703+
getEnvVarFromSecret(s3SecretName, "S3_BUCKET", "s3-bucket", true),
704+
corev1.EnvVar{Name: "BACKUP_TYPE", Value: "s3"},
705+
corev1.EnvVar{Name: "CLUSTER_NAME", Value: cluster.Name},
706+
)
707+
hostname := func() string {
708+
if backup.Spec.BackupOpts.BackupHost != "" {
709+
return fmt.Sprintf("%s.%s-mysql.%s", backup.Spec.BackupOpts.BackupHost, backup.Spec.ClusterName, backup.Namespace)
710+
} else {
711+
return backup.Spec.ClusterName + "-follower"
712+
}
713+
}()
714+
c := &v1alpha1.MysqlCluster{}
715+
c.Name = backup.Spec.ClusterName
716+
secret := &corev1.Secret{}
717+
secretKey := client.ObjectKey{Name: mysqlcluster.New(c).GetNameForResource(utils.Secret), Namespace: backup.Namespace}
718+
719+
if err := r.Get(context.TODO(), secretKey, secret); err != nil {
720+
log.V(4).Info("binlogjob", "is is creating", "err", err)
721+
}
722+
internalRootPass := string(secret.Data["internal-root-password"])
723+
724+
jobSpec := &batchv1.JobSpec{
725+
Template: corev1.PodTemplateSpec{
726+
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{
727+
"Host": hostname,
728+
"Type": utils.BackupJobTypeName,
729+
730+
// Cluster used as selector.
731+
"Cluster": backup.Spec.ClusterName,
732+
}},
733+
Spec: corev1.PodSpec{
734+
InitContainers: []corev1.Container{
735+
{
736+
Name: "initial",
737+
Image: cluster.Spec.Backup.Image,
738+
ImagePullPolicy: cluster.Spec.ImagePullPolicy,
739+
Command: []string{
740+
"bash", "-c", "cp /mnt/s3upload /opt/radondb; chown -R 1001.1001 /opt/radondb",
741+
},
742+
VolumeMounts: []corev1.VolumeMount{
743+
{
744+
Name: utils.MySQLcheckerVolumeName,
745+
MountPath: "/opt/radondb",
746+
},
747+
},
748+
},
749+
},
750+
Containers: []corev1.Container{
751+
{
752+
Name: utils.ContainerBackupName,
753+
Image: "percona:8.0",
754+
ImagePullPolicy: cluster.Spec.ImagePullPolicy,
755+
Env: S3BackuptEnv,
756+
Command: []string{
757+
"bash", "-c", fmt.Sprintf("/opt/radondb/s3upload %s %s", hostname, internalRootPass),
758+
},
759+
VolumeMounts: []corev1.VolumeMount{
760+
{
761+
Name: utils.MySQLcheckerVolumeName,
762+
MountPath: "/opt/radondb",
763+
},
764+
},
765+
},
766+
},
767+
RestartPolicy: corev1.RestartPolicyNever,
768+
ServiceAccountName: backup.Spec.ClusterName,
769+
Volumes: []corev1.Volume{
770+
{
771+
Name: utils.MySQLcheckerVolumeName,
772+
VolumeSource: corev1.VolumeSource{
773+
EmptyDir: &corev1.EmptyDirVolumeSource{},
774+
},
775+
},
776+
},
777+
},
778+
},
779+
}
780+
781+
return jobSpec, nil
782+
}

0 commit comments

Comments
 (0)