Kubernetes MariaDB Resource Limits and Health Probes

개요

MariaDB 리소스 제한 설정과 헬스체크(liveness/readiness probe) 구성을 자체 관리형 Kubernetes 클러스터에 반영했습니다. 기존 MariaDB Deployment는 리소스 request/limit이 전혀 설정되지 않은 상태(resources: {})로 2년 넘게 운영되고 있었고, liveness/readiness probe도 없어 mysqld 프로세스가 응답 없이 멈추더라도 Kubernetes가 이를 감지하고 자동으로 재시작할 방법이 없는 구조였습니다.

이번 글에서는 ① 현재 구성 점검 ② 운영 중인 리소스의 YAML 추출 및 Git 반영 ③ 리소스 제한/프로브 설계와 적용, 이렇게 세 가지 작업을 순서대로 정리합니다.

환경

  • Kubernetes v1.33.7 (Control-plane 3대, Worker 3대 HA 구성)
  • Container Runtime: Docker + cri-dockerd
  • Storage: Rook-Ceph RBD (StorageClass rook-ceph-block, ReadWriteOnce)
  • MariaDB: 10.11, mariadb-system 네임스페이스, Deployment 단일 replica
  • PVC: 10Gi, PV ReclaimPolicy: Retain
  • 서비스 노출: MetalLB LoadBalancer (내부망 전용)

단계별 절차

1. 현재 구성 형상 점검

먼저 운영 중인 Deployment, PVC, Service, ConfigMap 현황과 실제 리소스 사용량을 확인했습니다.

$ kubectl get all -n mariadb-system -o wide
$ kubectl get pvc -n mariadb-system -o wide
$ kubectl top pod -n mariadb-system

점검 결과 Deployment의 strategy는 이미 Recreate로 설정되어 있었습니다. MariaDB처럼 ReadWriteOnce 볼륨을 사용하는 단일 파드 워크로드는 RollingUpdate 방식으로 배포하면 이전 파드가 볼륨을 반납하기 전에 새 파드가 같은 볼륨을 마운트하려다 충돌하는 경우가 있어, Recreate 전략이 올바른 선택이었습니다. PV의 reclaimPolicyRetain으로 설정되어 있어 PVC가 실수로 삭제되더라도 실제 데이터는 보존되는 안전한 구조였습니다.

반면 컨테이너 리소스는 request/limit이 전혀 없는 상태였고, 실제 메모리 사용량은 약 1.4Gi 수준이었습니다. ConfigMap으로 주입한 커스텀 설정은 다음과 같았습니다.

[mysqld]
innodb_buffer_pool_size=512M
innodb_log_file_size=256M
max_connections=300

2. YAML 추출 및 Git 반영

운영 중인 리소스(PVC, ConfigMap, Deployment, Service)를 그대로 추출해 저장소에 매니페스트 파일로 문서화했습니다. Secret은 정책상 Git에 포함하지 않고, 클러스터 재구성 시 수동으로 생성할 수 있도록 커맨드만 주석으로 남겼습니다.

# root 비밀번호(mariadb-secret)는 Git에 없음 - 클러스터 재구성 시 수동 생성 필요
#   kubectl create secret generic mariadb-secret -n mariadb-system \
#     --from-literal=password='<ROOT_PASSWORD>'

추출한 매니페스트를 kubectl diff로 실제 클러스터 상태와 비교해, 새로 작성한 YAML이 운영 중인 리소스와 완전히 동일한지 먼저 확인했습니다. 차이가 없다는 것을 확인한 뒤에야 Git에 커밋했습니다.

3. 리소스 Request/Limit 및 Probe 설계

실제 메모리 사용량(~1.4Gi)과 innodb_buffer_pool_size(512M) 설정, 그리고 노드의 여유 용량을 함께 고려해 다음과 같이 값을 산정했습니다.

resources:
  requests:
    cpu: 250m
    memory: 1Gi
  limits:
    cpu: "1"
    memory: 2Gi
livenessProbe:
  exec:
    command:
      - sh
      - -c
      - mysqladmin ping -uroot -p"$MYSQL_ROOT_PASSWORD" --silent
  initialDelaySeconds: 30
  periodSeconds: 20
  timeoutSeconds: 5
  failureThreshold: 3
readinessProbe:
  exec:
    command:
      - sh
      - -c
      - mysqladmin ping -uroot -p"$MYSQL_ROOT_PASSWORD" --silent
  initialDelaySeconds: 10
  periodSeconds: 10
  timeoutSeconds: 5
  failureThreshold: 3

Probe는 이미 컨테이너 환경변수로 주입되어 있던 MYSQL_ROOT_PASSWORD를 그대로 활용하는 mysqladmin ping exec 방식을 선택했습니다. 별도 Secret을 추가로 마운트할 필요 없이, 실제로 mysqld가 인증까지 정상 처리하는지 확인할 수 있는 방식입니다. Limit 산정 시에는 노드 전체 할당량 대비 여유가 충분한지(CPU 할당 비율, 메모리 할당 비율) 함께 확인한 뒤 값을 확정했습니다.

4. 적용 및 롤아웃 검증

kubectl apply 전에 반드시 kubectl diff로 변경 범위를 먼저 확인했습니다. Deployment의 strategyRecreate이기 때문에 적용 시 기존 파드가 먼저 종료되고 새 파드가 그 자리에 생성되는, 짧은 다운타임이 있는 변경이라는 점을 미리 인지하고 진행했습니다.

$ kubectl diff -f manifests/mariadb/mariadb.yaml
$ kubectl apply -f manifests/mariadb/mariadb.yaml
deployment.apps/mariadb configured

$ kubectl rollout status deployment/mariadb -n mariadb-system --timeout=120s
Waiting for deployment "mariadb" rollout to finish: 0 of 1 updated replicas are available...
deployment "mariadb" successfully rolled out

$ kubectl get pods -n mariadb-system -l app=mariadb
NAME                       READY   STATUS    RESTARTS   AGE
mariadb-xxxxxxxxxx-xxxxx   1/1     Running   0          12s

새 파드가 정상적으로 Ready 상태가 되었고, readiness probe가 통과하는 것을 확인했습니다. 로그에도 mariadbd: ready for connections가 정상 출력되어 별다른 문제 없이 전환이 완료되었습니다.

트러블슈팅

mysql.event 테이블 정의 불일치

현상: 파드 재시작 로그에 Incorrect definition of table mysql.event 에러와 함께 Event Scheduler가 비활성화된다는 메시지가 출력되었습니다.

원인: 과거 MariaDB 마이너 버전 업그레이드 이후 mysql_upgrade를 실행하지 않아, 시스템 테이블(mysql.event)의 컬럼 정의가 현재 바이너리 버전이 기대하는 스키마와 어긋난 상태로 남아있었습니다.

해결: 현재 Event Scheduler(예약 이벤트) 기능을 사용하고 있지 않아 서비스에는 영향이 없는 것으로 확인해, 이번 작업 범위에서는 별도 조치 없이 별도 후속 작업으로 분리했습니다. Event Scheduler를 사용할 계획이라면 mysql_upgrade 실행이 선행되어야 합니다.

결과 확인

최종적으로 다음 항목들을 확인해 이번 MariaDB 리소스 제한 및 헬스체크 반영 작업을 마무리했습니다.

$ kubectl get deployment mariadb -n mariadb-system \
  -o jsonpath='{.spec.template.spec.containers[0].resources}'
{"limits":{"cpu":"1","memory":"2Gi"},"requests":{"cpu":"250m","memory":"1Gi"}}

$ kubectl get pod -n mariadb-system -l app=mariadb
NAME                       READY   STATUS    RESTARTS   AGE
mariadb-xxxxxxxxxx-xxxxx   1/1     Running   0          2m
  • 리소스 request/limit 적용 완료 (request 250m/1Gi, limit 1core/2Gi)
  • liveness/readiness probe 정상 동작 확인
  • 클러스터 재기동 없이 무중단으로 서비스(LoadBalancer)가 재공지되어 애플리케이션 연결 영향 없음

참고

관련 포스트:

참고 문서: Kubernetes – Configure Liveness, Readiness and Startup Probes · Kubernetes – Resource Management for Pods and Containers

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

이 사이트는 Akismet을 사용하여 스팸을 줄입니다. 댓글 데이터가 어떻게 처리되는지 알아보세요.