Kubernetes 환경 데이터베이스 미드레벨 기술면접
새 면접Q. Kubernetes 환경에서 MySQL StatefulSet을 운영 중입니다. Pod가 재시작될 때마다 데이터가 유실되는 문제가 발생했습니다. PersistentVolumeClaim을 사용하고 있음에도 불구하고 이런 문제가 발생할 수 있는 원인과 해결 방법을 설명해주세요.
volumeClaimTemplates 설정과 PVC의 생명주기, 그리고 storageClass의 reclaimPolicy를 고려해보세요.
데이터 유실의 주요 원인은 PVC가 StatefulSet의 volumeClaimTemplates로 정의되지 않고 일반 volume으로 마운트되었거나, storageClass의 reclaimPolicy가 Delete로 설정되어 PVC 삭제 시 데이터가 함께 삭제되는 경우입니다. 해결을 위해서는 StatefulSet의 volumeClaimTemplates를 사용하여 각 Pod마다 고유한 PVC를 자동 생성하도록 설정해야 합니다. storageClass의 reclaimPolicy는 Retain으로 설정하여 PVC 삭제 시에도 실제 볼륨이 보존되도록 해야 합니다. 또한 Pod의 terminationGracePeriodSeconds를 충분히 설정하여 데이터베이스가 정상적으로 종료될 시간을 확보해야 합니다. 마지막으로 백업 전략을 수립하여 정기적으로 데이터를 외부 스토리지에 백업하는 것이 안전합니다.
- • volumeClaimTemplates를 사용한 PVC 자동 생성
- • storageClass의 reclaimPolicy를 Retain으로 설정
- • terminationGracePeriodSeconds 적절한 설정으로 graceful shutdown 보장
운영 환경에서 데이터베이스 Pod 재배포 시 데이터 손실 없이 안정적으로 서비스를 유지해야 할 때 필수적인 설정입니다.
StatefulSet을 사용하는 데이터베이스 클러스터에서 Rolling Update 시 데이터 정합성을 보장하기 위한 전략은 무엇인가요?
Q. Kubernetes에서 마이크로서비스 아키텍처로 운영 중인 서비스가 있습니다. 애플리케이션 Pod를 HPA로 10개까지 스케일아웃했더니 데이터베이스에서 'Too many connections' 에러가 발생했습니다. 커넥션 풀 설정과 관련하여 어떻게 문제를 해결하고 최적화할 수 있는지 설명해주세요.
각 Pod의 커넥션 풀 크기와 전체 Pod 수의 관계, 그리고 데이터베이스의 max_connections 설정을 함께 고려해야 합니다.
문제의 원인은 각 Pod가 독립적인 커넥션 풀을 가지고 있어 전체 커넥션 수가 데이터베이스의 max_connections를 초과한 것입니다. 해결을 위해서는 먼저 데이터베이스의 max_connections 값을 확인하고, 예상되는 최대 Pod 수를 고려하여 각 Pod의 커넥션 풀 크기를 조정해야 합니다. 예를 들어 max_connections가 200이고 최대 Pod 수가 10개라면 각 Pod의 최대 커넥션은 15~20개 정도로 제한하는 것이 안전합니다. 또한 minIdle 값을 낮게 설정하여 유휴 상태에서 불필요한 커넥션을 유지하지 않도록 하고, connectionTimeout과 idleTimeout을 적절히 설정하여 사용하지 않는 커넥션을 빠르게 반환하도록 해야 합니다. 근본적으로는 데이터베이스를 Read Replica로 확장하거나 PgBouncer, ProxySQL 같은 커넥션 풀러를 중간에 배치하여 커넥션을 효율적으로 관리하는 방법도 고려할 수 있습니다.
- • 전체 Pod 수 × 각 Pod의 커넥션 풀 크기가 max_connections를 초과하지 않도록 설정
- • minIdle, connectionTimeout, idleTimeout 등 커넥션 풀 파라미터 최적화
- • PgBouncer, ProxySQL 등 커넥션 풀러 도입으로 커넥션 재사용 극대화
트래픽 증가에 따라 자동으로 스케일아웃되는 환경에서 데이터베이스 커넥션 고갈을 방지하기 위한 필수 설정입니다.
커넥션 풀의 최적 크기를 결정하기 위해 어떤 메트릭을 모니터링하고 어떤 기준으로 조정하시겠습니까?
Q. 대용량 주문 테이블에서 사용자별 최근 주문 목록을 조회하는 쿼리의 성능이 느립니다. user_id와 created_at 컬럼에 각각 단일 인덱스가 있지만, WHERE user_id = ? ORDER BY created_at DESC LIMIT 10 쿼리가 느립니다. 복합 인덱스 설계 시 컬럼 순서를 어떻게 결정해야 하며, Covering Index를 적용한다면 어떤 이점이 있는지 설명해주세요.
WHERE 조건의 카디널리티와 ORDER BY, 그리고 SELECT 절의 컬럼들을 모두 고려해야 합니다.
이 경우 복합 인덱스는 (user_id, created_at DESC) 순서로 생성해야 합니다. user_id를 첫 번째 컬럼으로 하는 이유는 WHERE 절의 등호 조건으로 먼저 범위를 좁힌 후, 이미 정렬된 created_at으로 빠르게 상위 10개를 찾을 수 있기 때문입니다. 만약 순서를 바꾸면 created_at으로 먼저 정렬된 후 user_id를 필터링해야 하므로 비효율적입니다. Covering Index를 적용하려면 SELECT 절에서 조회하는 모든 컬럼을 인덱스에 포함시켜야 합니다. 예를 들어 주문번호, 금액, 상태를 함께 조회한다면 (user_id, created_at DESC, order_number, amount, status) 형태로 인덱스를 구성합니다. Covering Index의 이점은 인덱스만으로 모든 데이터를 조회할 수 있어 실제 테이블 접근이 불필요하므로 랜덤 I/O가 대폭 줄어들고 쿼리 성능이 크게 향상됩니다. 다만 인덱스 크기가 커지고 INSERT/UPDATE 시 오버헤드가 증가하므로 읽기와 쓰기의 비율을 고려하여 적용해야 합니다.
- • 복합 인덱스는 WHERE 등호 조건 컬럼을 먼저, ORDER BY 컬럼을 나중에 배치
- • Covering Index는 SELECT 절의 모든 컬럼을 포함하여 테이블 접근 없이 인덱스만으로 조회
- • 쓰기 성능 저하와 인덱스 크기 증가를 고려한 트레이드오프 판단 필요
대용량 사용자 데이터에서 개인화된 목록을 빠르게 조회해야 하는 전자상거래, 소셜미디어 서비스에서 필수적인 최적화 기법입니다.
인덱스를 추가했음에도 옵티마이저가 인덱스를 사용하지 않는 경우는 언제이며, 어떻게 해결할 수 있나요?
Q. 재고 차감 로직을 처리하는 마이크로서비스에서 동시에 여러 주문이 들어올 때 데드락이 자주 발생합니다. 트랜잭션 내에서 여러 상품의 재고를 차감하는 로직인데, 트랜잭션 격리 수준은 READ COMMITTED를 사용하고 있습니다. 데드락의 원인과 해결 방법, 그리고 격리 수준 변경이 필요한지 설명해주세요.
여러 레코드를 업데이트할 때 락 획득 순서와 트랜잭션 격리 수준에 따른 락 동작을 생각해보세요.
데드락의 주요 원인은 두 개 이상의 트랜잭션이 서로 다른 순서로 상품 레코드에 락을 획득하려고 할 때 발생합니다. 예를 들어 트랜잭션 A가 상품1, 상품2 순서로 락을 획득하고, 트랜잭션 B가 상품2, 상품1 순서로 락을 획득하려 하면 서로 상대방이 보유한 락을 기다리며 데드락이 발생합니다. 해결 방법은 첫째, 애플리케이션 레벨에서 상품 ID를 정렬하여 항상 동일한 순서로 락을 획득하도록 강제하는 것입니다. 둘째, SELECT FOR UPDATE를 사용하여 명시적으로 락을 획득하고 NOWAIT 또는 SKIP LOCKED 옵션으로 데드락을 회피할 수 있습니다. 격리 수준은 READ COMMITTED가 적절하며, REPEATABLE READ나 SERIALIZABLE로 높이면 오히려 락 범위가 넓어져 데드락 가능성이 증가할 수 있습니다. 추가적으로 트랜잭션의 실행 시간을 최소화하고, 재고 차감을 별도 테이블로 분리하여 이벤트 소싱 패턴을 적용하는 것도 고려할 수 있습니다.
- • 락 획득 순서를 일관되게 유지하여 순환 대기 방지 (상품 ID 정렬)
- • SELECT FOR UPDATE NOWAIT/SKIP LOCKED로 명시적 락 제어
- • 트랜잭션 실행 시간 최소화 및 이벤트 소싱 패턴 고려
전자상거래에서 동시에 여러 상품을 구매하는 주문 처리 시 재고 정합성을 보장하면서 데드락을 방지해야 하는 상황에서 필수적입니다.
SERIALIZABLE 격리 수준을 사용하면 데드락 문제가 해결되나요? 그렇지 않다면 어떤 새로운 문제가 발생할 수 있나요?
Q. Kubernetes에서 PostgreSQL StatefulSet을 운영 중이며, 24시간 운영되는 서비스입니다. RTO(Recovery Time Objective) 30분, RPO(Recovery Point Objective) 5분 이내를 목표로 백업 및 복구 전략을 수립해야 합니다. 어떤 백업 방식을 조합하여 사용하고, Kubernetes 환경에서 어떻게 자동화할 수 있는지 설명해주세요.
Full 백업, Incremental 백업, WAL 아카이빙의 조합과 CronJob, VolumeSnapshot을 활용하는 방법을 생각해보세요.
RPO 5분을 만족하기 위해서는 WAL(Write-Ahead Logging) 아카이빙을 연속적으로 수행하여 트랜잭션 로그를 외부 스토리지(S3, GCS 등)에 실시간으로 백업해야 합니다. 베이스 백업은 매일 한 번 pg_basebackup을 사용하여 전체 백업을 수행하고, Kubernetes CronJob으로 자동화합니다. RTO 30분을 만족하기 위해서는 VolumeSnapshot을 활용하여 PVC의 스냅샷을 주기적으로 생성하면 빠른 복구가 가능합니다. 복구 시에는 최신 베이스 백업을 복원한 후 WAL 파일을 replay하여 특정 시점으로 복구(PITR)할 수 있습니다. 자동화는 Velero나 Stash 같은 Kubernetes 네이티브 백업 도구를 사용하거나, 커스텀 CronJob과 init container를 조합하여 구현할 수 있습니다. 정기적으로 백업 데이터로 실제 복구 테스트를 수행하여 RTO/RPO 목표를 검증하는 것도 중요합니다.
- • WAL 아카이빙으로 RPO 5분 달성 (연속적인 트랜잭션 로그 백업)
- • pg_basebackup + VolumeSnapshot 조합으로 RTO 30분 달성
- • CronJob, Velero, Stash 등으로 백업 자동화 및 정기적인 복구 테스트
운영 중인 데이터베이스에서 장애 발생 시 빠른 복구와 최소한의 데이터 손실을 보장하기 위한 필수 전략입니다.
Multi-AZ 환경에서 데이터베이스 고가용성을 위해 Streaming Replication을 구성한다면 백업 전략은 어떻게 달라지나요?
아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!