해당 게시물은 k8s로 올린 mysql을 백업하고 복구하는 방법에 대하여 작성하였습니다.
직접 설치된 mysql을 백업하고 복구하는 방법은 이곳의 게시물에 잘 작성해 두었으니 저기서 살펴보시면 됩니다.
mysql을 사용하여 db를 사용 시, 데이터를 백업해두어야 만일의 경우 db의 정보가 날라갈 경우 백업본을 바탕으로 복구할 수 있으므로 db 정보를 백업하는 작업은 필요합니다.
백업을 위해 사용할 툴은 xtrabackup입니다.
Percona에서 만든 백업 유틸리티로 MySQL에 사용되는 온라인 백업
비교 툴 : mysqldump (=논리적인 백업)
백업 방식 : 물리적인 파일을 통째로 특정 디렉토리에 복사하는 방법
풀백업, 증분백업, 암호화 백업, 압축백업 지원
MySQL 엔터프라이즈 라이센스에 포함된 백업 도구의 기능을 모두 제공할 뿐만 아니라 더 유용한 기능들도 제공합니다.
Percona XtraBackup은 InnoDB의 충돌 복구 기능을 기반
InnoDB 데이터 파일을 복사하므로 내부적으로 일관성이 없는 데이터가 생성됩니다. 그런 다음 파일에 대해 응급 복구를 수행하여 일관되고 사용 가능한 데이터베이스로 다시 만듭니다. 이것은 InnoDB가 트랜잭션 로그라고 하는 리두 로그를 유지하기 때문에 작동합니다.
여기에는 InnoDB 데이터의 모든 변경에 대한 기록이 있습니다. InnoDB가 시작되면 데이터 파일과 트랜잭션 로그를 검사하고 두 단계를 수행합니다.
커밋된 트랜잭션 로그 항목을 데이터 파일에 적용하고 데이터를 수정하지만 커밋하지 않은 모든 트랜잭션에 대해 실행취소 작업을 수행
Percona XtraBackup은 시작할 때 로그 시퀀스 번호 (LSN)를 기억한 다음 데이터 파일을 복사하는 방식으로 작동
이 작업을 수행하는 데 약간의 시간이 걸리므로 파일이 변경되면 다른 시점의 데이터베이스 상태를 반영
동시에 Percona XtraBackup은 트랜잭션 로그 파일을 감시하는 백그라운드 프로세스를 실행하고 여기에서 변경 사항을 복사합니다. Percona XtraBackup은 트랜잭션 로그가 라운드 로빈 방식으로 작성되고 잠시 후에 재사용 될 수 있기 때문에 이를 지속적으로 수행해야합니다.
Percona XtraBackup은 실행을 시작한 이후 데이터 파일이 변경 될 때마다 트랜잭션 로그 레코드가 필요합니다.
< 백업 >
백업 완료 시, xtrabackup_checkpoints 파일 생성됨.
xtrabackup_checkpoints → 데이터 베이스 LSN 행 존재
증분백업은 이를 기반으로 LSN 이후의 변경사항을 백업
< 복구 >
증분 백업을 사용한 복구 시,
일반 백업( 데이터베이스 일관성을 위해 커밋된 트랜잭션은 로그 파일에서 데이터 파일에 대해 재생 & 커밋되지 않은 트랜잭션은 롤백)과 달리
백업을 준비 시, 커밋되지 않은 트랜잭션 롤백 하지 않음!! → 백업 시점에 커밋되지 않은 트랜잭션이 진행중이어서 다음 증분 백업에서 커밋될 가능성이 높기 때문 → 롤백 단계 방지하려면 —apply-log-only
옵션 사용
롤백 단계를 방지하기 위해 --apply-log-only 옵션을 사용하지 않으면 증분 백업이 쓸모 없게 됩니다.
– Mysql / Mariadb 등 에서 백업에 사용되는 오픈소스 백업 유틸리티
– xtrabackup은 엔진 데이터를 그대로 복사하여 백업 / 복구 하는 방식입니다.
– 장점 :
대용량 백업의 경우 mysqldump 대비 xtrabackup 이 서버의 리소스 사용 및 속도면에서 매우 강력한 이점
– 단점 :
데이터를 복사하여 백업하는방식이기 때문에 대용량 백업을 진행할 시, Backup Server에 충분한 용량 확보 필요
mysql : 8.0.29(cbt) 8.0.27(dev)
xtrabackup : 8.0.29
→ 설치된 mysql 버전과 동일해야함.
백업 툴 : xtrabackup
https://www.percona.com/software/mysql-database/percona-xtrabackup
버전 : 8.0.29 (cbt)
2점대, 8점대 제공 (mysql 버전과 일치해야함)
제한사항 : mysql 8.0.29까지 지원
mysql 8점대 ALTER algorithm instant로 인한 백업 기능 제한 [자세히](https://www.notion.so/Xtrabackup-49bbec4a6bd84c1bb542f25e13e9e527)
백업 방식 : 풀 백업 & 증분 백업 혼합
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: mysql-sc
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: mysql-pv
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
storageClassName: mysql-sc
local:
path: /tmp
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- controlplane
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: mysql-pv-2
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
storageClassName: mysql-sc
local:
path: /tmp
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- node01
---
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql
labels:
app: mysql
app.kubernetes.io/name: mysql
data:
my.cnf: |
[mysqld]
!includedir /etc/mysql/conf.d/
primary.cnf: |
# Apply this config only on the primary.
[mysqld]
log-bin
gtid_mode=ON
enforce_gtid_consistency=ON
replica.cnf: |
# Apply this config only on replicas.
[mysqld]
gtid_mode=ON
enforce_gtid_consistency=ON
super-read-only
log_bin=OFF
---
# Headless service for stable DNS entries of StatefulSet members.
apiVersion: v1
kind: Service
metadata:
name: mysql
labels:
app: mysql
app.kubernetes.io/name: mysql
spec:
ports:
- name: mysql
port: 3306
clusterIP: None
selector:
app: mysql
---
# Client service for connecting to any MySQL instance for reads.
# For writes, you must instead connect to the primary: mysql-0.mysql.
apiVersion: v1
kind: Service
metadata:
name: mysql-read
labels:
app: mysql
app.kubernetes.io/name: mysql
readonly: "true"
spec:
ports:
- name: mysql
port: 3306
selector:
app: mysql
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
selector:
matchLabels:
app: mysql
app.kubernetes.io/name: mysql
serviceName: mysql
replicas: 2
template:
metadata:
labels:
app: mysql
app.kubernetes.io/name: mysql
spec:
initContainers:
- name: init-mysql
image: mysql:8.0.28
command:
- bash
- "-c"
- |
set -ex
# Generate mysql server-id from pod ordinal index.
[[ `uname -n` =~ -([0-9]+)$ ]] || exit 1
ordinal=${BASH_REMATCH[1]}
echo [mysqld] > /mnt/conf.d/server-id.cnf
# Add an offset to avoid reserved server-id=0 value.
echo server-id=$((100 + $ordinal)) >> /mnt/conf.d/server-id.cnf
# Copy my.cnf file from config-map to conf-2 volume(emptyDir).
cp /mnt/config-map/my.cnf /mnt/conf-2/
# Copy appropriate conf.d files from config-map to emptyDir.
if [[ $ordinal -eq 0 ]]; then
cp /mnt/config-map/primary.cnf /mnt/conf.d/
else
cp /mnt/config-map/replica.cnf /mnt/conf.d/
fi
volumeMounts:
- name: conf
mountPath: /mnt/conf.d
- name: conf-2
mountPath: /mnt/conf-2
- name: config-map
mountPath: /mnt/config-map
- name: clone-mysql
image: hiroaki2020/dtuj_xtrabackup:1.0
securityContext:
runAsUser: 0
runAsGroup: 0
command:
- bash
- "-c"
- |
set -ex
# Skip the clone if data already exists.
[[ -d /var/lib/mysql/mysql ]] && exit 0
# Skip the clone on primary (ordinal index 0).
[[ `uname -n` =~ -([0-9]+)$ ]] || exit 1
ordinal=${BASH_REMATCH[1]}
[[ $ordinal -eq 0 ]] && exit 0
# Clone data from previous peer.
ncat --recv-only mysql-$(($ordinal-1)).mysql 3307 | xbstream -x -C /var/lib/mysql
# Prepare the backup.
xtrabackup --prepare --target-dir=/var/lib/mysql
volumeMounts:
- name: data
mountPath: /var/lib/mysql
subPath: mysql
- name: conf
mountPath: /etc/mysql/conf.d
- name: conf-2
mountPath: /etc/mysql
containers:
- name: mysql
image: mysql:8.0.28
resources:
limits:
memory: 512Mi
cpu: 500m
requests:
memory: 128Mi
cpu: 100m
env:
- name: MYSQL_ALLOW_EMPTY_PASSWORD
value: "1"
ports:
- name: mysql
containerPort: 3306
volumeMounts:
- name: data
mountPath: /var/lib/mysql
subPath: mysql
- name: conf
mountPath: /etc/mysql/conf.d
livenessProbe:
exec:
command: ["mysqladmin", "ping"]
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
readinessProbe:
exec:
# Check we can execute queries over TCP (skip-networking is off).
command: ["mysql", "-h", "127.0.0.1", "-e", "SELECT 1"]
initialDelaySeconds: 5
periodSeconds: 2
timeoutSeconds: 1
- name: xtrabackup
image: hiroaki2020/dtuj_xtrabackup:1.0
securityContext:
runAsUser: 0
runAsGroup: 0
ports:
- name: xtrabackup
containerPort: 3307
command:
- bash
- "-c"
- |
set -ex
cd /var/lib/mysql
# Determine binlog position of cloned data, if any.
if [[ -f xtrabackup_slave_info && "x$(<xtrabackup_slave_info)" != "x" ]]; then
# XtraBackup already generated a partial "CHANGE MASTER TO" query
# because we're cloning from an existing replica. (Need to remove the tailing semicolon!)
cat xtrabackup_slave_info | sed -E 's/;$//g' > change_master_to.sql.in
# Ignore xtrabackup_binlog_info in this case (it's useless).
rm -f xtrabackup_slave_info xtrabackup_binlog_info
elif [[ -f xtrabackup_binlog_info ]]; then
# We're cloning directly from primary. Parse binlog position.
[[ `cat xtrabackup_binlog_info` =~ ^([^.]*\.[0-9]+)[[:space:]]+([0-9]+) ]] || exit 1
rm -f xtrabackup_binlog_info xtrabackup_slave_info
echo "CHANGE MASTER TO MASTER_LOG_FILE='${BASH_REMATCH[1]}',\
MASTER_LOG_POS=${BASH_REMATCH[2]}" > change_master_to.sql.in
fi
# Check if we need to complete a clone by starting replication.
if [[ -f change_master_to.sql.in ]]; then
echo "Waiting for mysqld to be ready (accepting connections)"
until mysql -h 127.0.0.1 -e "SELECT 1"; do sleep 1; done
echo "Initializing replication from clone position"
mysql -h 127.0.0.1 \
-e "$(<change_master_to.sql.in), \
MASTER_HOST='mysql-0.mysql', \
MASTER_USER='root', \
MASTER_PASSWORD='', \
MASTER_CONNECT_RETRY=10; \
START SLAVE;" || exit 1
# In case of container restart, attempt this at-most-once.
mv change_master_to.sql.in change_master_to.sql.orig
fi
# Start a server to send backups when requested by peers.
exec ncat --listen --keep-open --send-only --max-conns=1 3307 -c \
"xtrabackup --backup --slave-info --stream=xbstream --host=127.0.0.1 --user=root"
volumeMounts:
- name: data
mountPath: /var/lib/mysql
subPath: mysql
- name: conf
mountPath: /etc/mysql/conf.d
- name: conf-2
mountPath: /etc/mysql
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 100m
memory: 100Mi
volumes:
- name: conf
emptyDir: {}
- name: conf-2
emptyDir: {}
- name: config-map
configMap:
name: mysql
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: mysql-sc
resources:
requests:
storage: 5Gi