前言
此前一直在使用 mysqlshell
来备份部署在 k8s 上的 MySQL,虽然这个工具比起 mysqlpump 来说要快很多,支持多线程、可以直接备份到远程 s3 等,但是后面使用过程中也陆陆续续发现了一些问题:
CPU 占用过高
由于开启了多个核来并行执行提升速度,所以每次执行备份的时候 CPU 只能在凌晨时间,这样极端情况下可能会导致丢失一天的数据。
无法增量备份
每次备份的时候都是全量备份,这样也会导致备份的数据占用空间过大。
恢复数据慢
由于 mysqlshell
备份的数据是逻辑备份,所以恢复数据的时候会很慢。如果另外两个问题还是可以忍受的话,这个问题是无法忍受的。比如在进行服务器迁移的时候,系统恢复的时间就会很长。
使用 xtrabackup
之前也曾调研过 xtrabackup,xtrabackup,强大的 MySQL 备份工具,但是由于 k8s 下的 MySQL 是使用的 PVC,所以无法直接使用 xtrabackup 来备份。后面实在忍受不了 mysqlshell
的问题,所以又重新研究了一下,最终找到了一个比较完美的解决方案。
打包一个基础镜像
Dockerfile
这个 Dockerfile 里面安装了 xtrabackup 和 rclone,rclone 是一个支持多种对象存储的命令行工具,可以用来将备份的数据上传到对象存储等。然后启动的时候会执行 entrypoint.sh
,这个脚本会启动一个 cron 定时任务,每小时执行一次 backup.sh
,这个脚本会根据是否存在全量备份来执行全量备份或者增量备份,然后将备份的数据上传到对象存储。
FROM ubuntu
RUN apt update && \
apt install -y wget cron lsb-release curl gnupg2 zstd unzip && \
wget https://repo.percona.com/apt/percona-release_latest.$(lsb_release -sc)_all.deb && \
dpkg -i percona-release_latest.$(lsb_release -sc)_all.deb && \
apt update && \
percona-release enable-only tools release && \
apt update && \
apt install percona-xtrabackup-80 -y && \
rm -rf percona-release_latest.$(lsb_release -sc)_all.deb
RUN curl https://rclone.org/install.sh | bash
COPY backup.sh /backup.sh
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /backup.sh && chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
backup.sh
这个脚本执行了实际的备份操作,备份的数据会存放在 /backup
目录下,然后会将备份的数据同步到 s3 上。
#!/bin/bash
backup_dir=/backup
my_conf=${MYSQL_CONF:-/etc/mysql/my.cnf}
mkdir -p $backup_dir
mkdir -p /root/.config/rclone
echo "[s3]
type = s3
provider = $S3_PROVIDER
access_key_id = $S3_ACCESS_KEY_ID
secret_access_key = $S3_SECRET_ACCESS_KEY
region = $S3_REGION
endpoint = $S3_ENDPOINT
" > /root/.config/rclone/rclone.conf
function backup() {
if [ -z "$(ls -A $backup_dir)" ]; then
xtrabackup --defaults-file="$my_conf" --backup --compress=zstd --target-dir=$backup_dir/base
echo "全量备份完成!"
else
xtrabackup --defaults-file="$my_conf" --backup --compress=zstd --target-dir=$backup_dir/inc-"$(date '+%Y-%m-%d_%H:%M:%S')" --incremental-basedir="$(ls -d $backup_dir/* | tail -n 1)"
echo "增量备份完成!"
fi
echo "开始同步到s3..."
echo "rclone sync $backup_dir s3:/$S3_BUCKET/$S3_PREFIX $RCLONE_OPTIONS" | bash
}
function restore() {
for d in "$backup_dir"/*/; do
xtrabackup --defaults-file="$my_conf" --decompress --target-dir="$d"
done
for d in "$backup_dir"/*/; do
if [ "$d" == "$backup_dir/base/" ]; then
xtrabackup --defaults-file="$my_conf" --prepare --apply-log-only --target-dir="$d"
else
if [ "$d" == "$(ls -d $backup_dir/*/ | tail -n 1)" ]; then
xtrabackup --defaults-file="$my_conf" --prepare --target-dir=$backup_dir/base --incremental-dir="$d"
else
xtrabackup --defaults-file="$my_conf" --prepare --apply-log-only --target-dir=$backup_dir/base --incremental-dir="$d"
fi
fi
done
xtrabackup --defaults-file="$my_conf" --copy-back --target-dir=$backup_dir/base
echo "恢复完成!"
}
case "$1" in
backup)
backup
;;
restore)
restore
;;
*)
echo "无效的参数"
;;
esac
entrypoint.sh
这个脚本会启动一个 cron 定时任务,每小时执行一次 backup.sh
。
#!/bin/bash
echo "backup cron running..."
env >> /etc/environment
touch /var/log/backup.log
echo "0 * * * * /backup.sh backup > /proc/1/fd/1 2>/proc/1/fd/2" | crontab -
cron -f -l 2
部署为 k8s MySQL 的 sidecar
k8s 的 sidecar 提供了多个容器共享一个 pod 的功能,这样我们就可以将这个备份容器部署到 MySQL 的 pod 中,然后通过共享 volume 的方式来访问 MySQL 的数据目录。由于我是使用 bitnami/mysql,所以以下的配置是基于这个 chart 的。
primary:
configuration: |-
[xtrabackup]
password={{ .Values.mysql.password }}
extraVolumes:
- name: socket # 这个 volume 用来共享 MySQL 的 socket 文件
emptyDir: {}
extraVolumeMounts:
- name: socket
mountPath: /opt/bitnami/mysql/tmp
sidecars:
- name: mysql-backup
image: long2ice/mysql-backup
imagePullPolicy: Always
env:
- name: S3_PROVIDER
value: {{ .Values.s3.provider }}
- name: S3_ACCESS_KEY_ID
value: {{ .Values.s3.access_key }}
- name: S3_SECRET_ACCESS_KEY
value: {{ .Values.s3.secret_key }}
- name: S3_ENDPOINT
value: {{ if .Values.s3.secure }}https{{else}}http{{end}}://{{ .Values.s3.endpoint }}
- name: S3_BUCKET
value: {{ .Values.mysql.backup_bucket }}
- name: S3_PREFIX
value: {{ .Values.mysql.backup_prefix }}
- name: RCLONE_OPTIONS
value: "-P --transfers=50 --fast-list"
- name: MYSQL_CONF
value: /opt/bitnami/mysql/conf/my.cnf
volumeMounts:
- name: data # 这个 volume 用来共享 MySQL 的数据目录
mountPath: /bitnami/mysql
- name: config # 这个 volume 用来共享 MySQL 的配置文件
mountPath: /opt/bitnami/mysql/conf/my.cnf
subPath: my.cnf
- name: socket # 这个 volume 用来共享 MySQL 的 socket 文件
mountPath: /opt/bitnami/mysql/tmp
完结
这样就可以完美的解决 MySQL 的备份问题了,备份的数据会存放在 s3 上,如果需要恢复的话,只需要将备份的数据下载到本地,然后执行 backup.sh restore
就可以了。