티스토리 뷰

Kubernetes(K8s) 환경에서 MariaDB 마스터 서버가 쓰기 작업 중 중단되면서 발생하는 데이터 손상 문제는 심각한 운영상 문제입니다. 이러한 현상이 발생하는 주된 이유는 트랜잭션이 완료되지 않은 상태에서 서버가 비정상적으로 종료되거나, 스토리지와 데이터베이스 간의 동기화 문제 때문입니다. 이를 구체적으로 해결하기 위한 방안들을 더 자세히 설명하겠습니다.

 

1. 데이터 손실 방지와 트랜잭션 안전성 강화

1.1 innodb_flush_log_at_trx_commit 설정

MariaDB에서 InnoDB 스토리지 엔진을 사용한다면, 트랜잭션 안전성을 보장하기 위해 로그 플러시(flush) 설정을 조정하는 것이 중요합니다.

  • 기본값: innodb_flush_log_at_trx_commit=1 설정을 통해 각 트랜잭션 커밋 시점에 로그를 디스크에 기록합니다. 이 값이 1이면, 트랜잭션이 커밋될 때마다 로그를 디스크에 플러시하여 데이터 손실을 방지합니다.
    • 권장 설정: innodb_flush_log_at_trx_commit=1로 설정하면 데이터의 일관성과 무결성이 보장됩니다. 이는 성능에 영향을 미칠 수 있지만, 데이터베이스가 다운되더라도 트랜잭션 로그를 통해 복구할 수 있습니다.
[mysqld]
innodb_flush_log_at_trx_commit = 1
  • 설명: 이 설정은 트랜잭션 커밋 시점에 로그를 디스크에 동기화해주므로, MariaDB가 비정상 종료되더라도 마지막으로 커밋된 트랜잭션까지 데이터를 복구할 수 있게 도와줍니다.

1.2 sync_binlog 설정

MariaDB에서 **바이너리 로그(binlog)**는 리플리케이션에서 중요한 역할을 합니다. 쓰기 작업 중 발생하는 문제를 방지하려면 바이너리 로그 동기화를 활성화해야 합니다.

  • 기본값: sync_binlog=0은 바이너리 로그가 비동기적으로 동기화됨을 의미합니다. 이 설정에서는 서버가 비정상 종료될 경우 일부 바이너리 로그가 손실될 수 있습니다.
  • 권장 설정: sync_binlog=1로 설정하면 매번 트랜잭션이 커밋될 때마다 바이너리 로그가 디스크에 기록됩니다. 이는 데이터 손실을 방지하는 데 매우 효과적입니다.
[mysqld]
sync_binlog = 1

설명: sync_binlog=1로 설정하면, 서버가 중단되더라도 마지막 커밋된 트랜잭션에 대한 로그가 보존되어 리플리케이션이나 데이터 복구 시 문제를 최소화할 수 있습니다.

 

1.3 트랜잭션 복구 절차 설정

MariaDB에서 트랜잭션 복구를 자동화하거나, 문제가 발생할 때 수동으로 복구할 수 있는 절차를 설정해두는 것이 필요합니다.

  • InnoDB 복구 설정: InnoDB는 자동으로 손상된 트랜잭션을 복구할 수 있는 기능을 제공하지만, 심각한 손상이 발생했을 경우에는 수동으로 강제 복구가 필요할 수 있습니다.
    • 강제 복구를 활성화하려면, MariaDB 서버를 시작할 때 innodb_force_recovery 파라미터를 설정하여 복구 단계별로 점진적으로 시도합니다.
SET GLOBAL innodb_force_recovery = 1; -- 복구 모드 단계적으로 1~6
  • 복구 모드 단계:
    • 1: 기본 복구 모드로 트랜잭션 로그를 무시하고 최대한의 데이터 복구를 시도합니다.
    • 6: 가장 강력한 복구 모드로, 데이터 무결성을 보장하지 않지만 가능한 데이터를 최대한 복구합니다.

2. Pod 비정상 종료 방지 및 안정성 강화

2.1 graceful shutdown 설정 (정상 종료 설정)

Pod가 종료될 때 쓰기 작업이 아직 완료되지 않은 상태에서 종료되면 데이터 손상이 발생할 수 있습니다. 이를 방지하기 위해 preStop 훅을 사용해 MariaDB가 안전하게 종료되도록 해야 합니다.

  • preStop 훅 사용: Pod가 종료되기 전에 MariaDB 서버가 안전하게 모든 트랜잭션을 종료할 수 있도록 shutdown 명령어를 실행합니다.
lifecycle:
  preStop:
    exec:
      command: ["/usr/bin/mysqladmin", "shutdown"]
  • 설명: 이 설정은 Pod 종료 시 MariaDB가 새로운 쓰기 작업을 받지 않고, 기존 트랜잭션을 모두 완료한 후 안전하게 종료되도록 합니다.

2.2 terminationGracePeriodSeconds 설정

Pod가 종료되기 전에 MariaDB가 충분한 시간을 가지고 안전하게 종료될 수 있도록 K8s의 termination grace period를 적절하게 설정하는 것이 중요합니다.

 

terminationGracePeriodSeconds: 60
  • 설명: 이 설정을 통해 Pod가 종료될 때 60초간의 여유 시간을 부여하여, MariaDB가 트랜잭션을 안전하게 마무리할 수 있는 시간을 확보합니다. 데이터베이스가 종료되지 않는 상황에서는 트랜잭션 손실 가능성이 커지므로, 적절한 종료 시간을 제공하는 것이 매우 중요합니다.

2.3 StatefulSet을 통한 데이터베이스 배포

Kubernetes에서 데이터베이스는 StatefulSet을 통해 배포해야 합니다. StatefulSet은 Pod 간 네트워크 ID스토리지 일관성을 유지하고, 종료 및 재시작 시 순차적으로 처리됩니다.

 

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mariadb
spec:
  serviceName: "mariadb"
  replicas: 3
  selector:
    matchLabels:
      app: mariadb
  template:
    metadata:
      labels:
        app: mariadb
    spec:
      containers:
      - name: mariadb
        image: mariadb:10.5
        ports:
        - containerPort
          containerPort: 3306
          name: mariadb
        volumeMounts:
        - name: mariadb-pv-storage
          mountPath: /var/lib/mysql
  volumeClaimTemplates:
  - metadata:
      name: mariadb-pv-storage
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 10Gi
  • 설명: 이 설정은 StatefulSet을 통해 MariaDB를 배포하는 예시입니다. StatefulSet은 각 Pod에 고유한 네트워크 ID와 스토리지를 유지하므로, 쓰기 작업 중 서버가 재시작되더라도 데이터 일관성이 보장됩니다. StatefulSet은 MariaDB처럼 상태를 유지해야 하는 애플리케이션을 관리하는 데 필수적입니다.

3. 스토리지 성능 및 안정성 개선

3.1 고성능 스토리지 사용

MariaDB와 같은 데이터베이스 애플리케이션은 I/O 성능이 중요한 요소입니다. Kubernetes에서 Persistent Volume(PV) 및 **Persistent Volume Claim(PVC)**을 사용할 때 고성능 SSDNVMe 기반 스토리지를 사용하는 것이 좋습니다.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mariadb-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  storageClassName: fast-ssd
  • 설명: storageClassName: fast-ssd와 같은 고성능 스토리지 클래스를 사용하여 디스크 쓰기/읽기 성능을 향상시킵니다. I/O 성능이 낮으면 MariaDB의 쓰기 작업 중 데이터 손상 가능성이 높아지므로, 고성능 스토리지를 사용하는 것이 매우 중요합니다.

3.2 스토리지 동기화 문제 방지

스토리지의 동기화 문제를 방지하기 위해 Kubernetes의 PersistentVolume(PV) 및 **PersistentVolumeClaim(PVC)**가 올바르게 설정되어 있는지 확인해야 합니다. 이때 **스토리지 클래스(StorageClass)**를 적절히 선택하고, 스토리지가 Pod 재시작 시에도 데이터 일관성을 유지할 수 있도록 설정합니다.

  • 동기화 성능 확인: MariaDB에서 데이터를 쓰는 도중 스토리지 I/O 성능이 떨어지지 않도록 스토리지 성능을 지속적으로 모니터링해야 합니다. Prometheus나 Grafana를 통해 스토리지의 IOPS를 실시간으로 모니터링할 수 있습니다.

4. Failover 및 고가용성(HA) 구성

MariaDB의 고가용성(HA) 환경을 구축하여, 마스터 서버에서 쓰기 작업 중 다운되더라도 자동으로 새로운 마스터를 선출하거나 쓰기 작업을 지속할 수 있게 해야 합니다.

4.1 Galera Cluster 사용

Galera Cluster는 MariaDB에서 제공하는 고가용성 솔루션으로, 마스터-마스터 동기 복제를 지원합니다. 이를 통해 모든 노드가 동일한 데이터를 보유하며, 한 노드가 다운되더라도 다른 노드에서 쓰기 작업을 계속할 수 있습니다.

  • 장점: 동기식 복제를 통해 데이터 손실 가능성이 낮으며, 고가용성을 제공하여 쓰기 작업 중 서버가 다운되더라도 자동으로 다른 노드에서 쓰기 작업을 이어갈 수 있습니다.

4.2 MHA(Master High Availability) 도입

MHA는 마스터 서버에서 장애가 발생하면 자동으로 새로운 마스터를 선출하는 Failover 솔루션입니다. MHA는 슬레이브 노드를 마스터로 승격시키고, 리플리케이션을 재설정하여 MariaDB의 가용성을 유지할 수 있습니다.

  • 장점: 마스터 서버가 다운되었을 때 빠르게 Failover를 처리하여 쓰기 작업을 중단하지 않고, 데이터 손상을 방지할 수 있습니다.

5. 백업 및 복구 전략 수립

쓰기 작업 중 문제가 발생했을 때 데이터를 손실하지 않기 위해 백업 전략을 마련하는 것이 필수적입니다.

5.1 정기 백업

Kubernetes에서는 CronJob을 이용해 MariaDB 데이터를 정기적으로 백업할 수 있습니다. 정기적으로 백업된 데이터를 통해 장애 발생시 데이터를 복구할 수 있습니다.

 

apiVersion: batch/v1
kind: CronJob
metadata:
  name: mariadb-backup
spec:
  schedule: "0 3 * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: backup
            image: mariadb:10.5
            command: ["mysqldump", "--all-databases", ">", "/backup/db-backup.sql"]
            volumeMounts:
            - name: backup-storage
              mountPath: /backup
          restartPolicy: OnFailure
  • 설명: 이 CronJob 설정은 매일 새벽 3시에 MariaDB 데이터를 백업하는 작업을 실행합니다. 이를 통해 서버 다운 시 복구할 수 있는 최신 백업 파일을 확보할 수 있습니다.

5.2 즉시 복구 절차 준비

MariaDB의 바이너리 로그와 백업 파일을 사용하여 데이터 손실이 발생했을 때 빠르게 복구할 수 있는 절차를 마련해야 합니다.

  • 백업 복구: 문제가 발생했을 경우, 주기적으로 백업한 데이터를 이용해 복구할 수 있습니다.
mysql < /backup/db-backup.sql

바이너리 로그 복구: 복구 후 바이너리 로그를 재적용하여 최근 트랜잭션까지 복구합니다.

mysqlbinlog /var/lib/mysql/mysql-bin.000001 | mysql -u root -p

결론

K8s에서 MariaDB 마스터 서버가 쓰기 작업 중 중단될 때 발생하는 문제는 트랜잭션 안정성, 스토리지 성능, 고가용성(HA) 구성을 통해 해결할 수 있습니다. 이를 위해 MariaDB의 트랜잭션 플러시 설정을 최적화하고, Pod 종료 시 안전하게 트랜잭션을 마무리할 수 있도록 환경을 구성해야 합니다. 또한, 고성능 스토리지를 사용하고 정기적인 백업을 통해 데이터 손실을 방지하는 전략도 필수적입니다.

댓글