k8s mysql xtrabackup 백업 및 복구

snooby·2022년 11월 10일
1

🐳 Docker & K8S

목록 보기
48/51
post-thumbnail

해당 게시물은 k8s로 올린 mysql을 백업하고 복구하는 방법에 대하여 작성하였습니다.
직접 설치된 mysql을 백업하고 복구하는 방법은 이곳의 게시물에 잘 작성해 두었으니 저기서 살펴보시면 됩니다.

✔️ 백업 방식 및 개념 파악

백업의 필요성

mysql을 사용하여 db를 사용 시, 데이터를 백업해두어야 만일의 경우 db의 정보가 날라갈 경우 백업본을 바탕으로 복구할 수 있으므로 db 정보를 백업하는 작업은 필요합니다.

xtrabackup

백업을 위해 사용할 툴은 xtrabackup입니다.

Percona에서 만든 백업 유틸리티로 MySQL에 사용되는 온라인 백업

비교 툴 : mysqldump (=논리적인 백업)
백업 방식 : 물리적인 파일을 통째로 특정 디렉토리에 복사하는 방법

        풀백업, 증분백업, 암호화 백업, 압축백업 지원

         MySQL 엔터프라이즈 라이센스에 포함된 백업 도구의 기능을 모두 제공할 뿐만 아니라 더 유용한 기능들도 제공합니다.

XtraBackup 특징

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 버전과 동일해야함.

백업 주의

  • 백업 시 table lock false
  • 압축, 속도 효율성 체크

✔️ Backup 방식

백업 툴 : 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)

백업 방식 : 풀 백업 & 증분 백업 혼합

  • 풀 백업 : 전체 다 백업
  • 증분 백업 : 이전 백업 ~ 현재까지 변경된 내역만 백업 (변경사항 없다면 백업 용량 0)
  • 시간
    증분 백업은 금방될 것 같지만 증분백업도 풀백업과 동일하게 데이터파일 모두 읽어야하므로 시간은 비슷
    백업할 데이터양과 백업 시간은 비례
  • 용량
    풀백업보다 증분백업이 용량은 확실히 작다.
  • 매일매일 자정 풀백업 1회 & 1시간 마다 증분 백업
    • 장점: 용량 최소화 & 풀 백업 이외 백업 시간 가장 짧다
    • 단점: 복구 시 오래 걸림

    ✔️ 백업 소스

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
profile
데이터를 가치있게 다루고 싶은 개발자 🐥

0개의 댓글