Last Updated on 3월 3, 2025 by Jade(정현호)
안녕하세요
이번 글은 차량 공유 서비스로 유명한 Uber(우버)에서 MySQL이 어떻게 구성 되어있고 어떻게 운영되는지 등의 Uber Engineering Blog에 포스팅된 글을 번역한 글입니다.
얼마전(작년) AWS re:Invent 2024에 참석하였을 때 라스베가스에서 이동할 때 자주 이용하였던 Uber(우버) 서비스에서 광범위하고 메인으로 사용되는 MySQL 사용 사례에 대해서 공유해보면 좋을 것 같아 번역 포스팅을 진행하였습니다.
• 원문: MySQL At Uber
Uber에서 MySQL® 시스템은 데이터 인프라의 중추적인 역할을 하며, Uber 플랫폼에 중요한 다양한 작업을 지원합니다. Uber는 2,300개가 넘는 독립 클러스터로 구성된 광범위한 MySQL 시스템을 운영하고 있습니다. 이렇게 대규모의 시스템을 관리하기 위해 제어 플레인을 구축하는 동시에 무중단과 데이터 손실을 보장하는 것은 업계에서 가장 어려운 문제 중 하나입니다.
Architecture
Uber의 MySQL 시스템은 각각 수많은 노드가 있는 여러 클러스터로 구성됩니다. 두 가지 주요 흐름이 있습니다. 클라이언트/서비스가 MySQL 클러스터와 상호 작용하는 데이터 흐름과 클러스터의 수명 주기를 관리하는 제어 흐름입니다.
데이터 흐름 측면에서, Kubernetes®에 호스팅된 상태 비저장(stateless) 서비스는 표준 클라이언트를 통해 MySQL 클러스터에 연결됩니다. 각 서버에는 MySQL 노드의 역할(Primary/Replica/Batch)에 따라 라우팅 매핑을 저장하는 리버스 프록시가 있습니다. 이를 통해 클라이언트는 실행할 쿼리에 따라 적절한 클러스터를 탐색하고, JDBC™ 프로토콜을 사용해 연결할 수 있습니다.
제어 흐름(control flow)은 클러스터와 노드의 프로비저닝, 유지 관리, 해제 과정을 관리하며, 이 과정에서 보안 상태를 유지하고 Uber 생태계와의 통합을 보장합니다.
Uber의 MySQL 시스템은 다음의 주요 구성 요소로 이루어져 있습니다 (그림 1 참고)
- 컨트롤 플레인(Control plane)
- 데이터 플레인(Data plane)
- 디스커버리 플레인(Discovery plane)
- 관찰 가능성(Observability)
- 변경 데이터 캡처(Change data capture) 및 데이터 웨어하우스 수집
- 백업/복원(Backup/restore)
[그림 1: Anatomy of the MySQL control plane.]
Control Plane
MySQL 컨트롤 플레인은 여러 구성 요소 및 서비스(components/services), 그리고 스토어로 이루어진 상태 기반 시스템입니다. 그 중심에는 기술 관리자(technology manager)가 있으며, 이는 다른 컨트롤 플레인 구성 요소들의 오케스트레이션을 담당합니다. 핵심 역할 중 하나는 클러스터의 목표 상태(또는 원하는 상태)를 Uber의 내부 기술 독립적 관리 플랫폼인 Odin에 게시하는 것입니다. Odin은 상태 유지 기술(stateful technologies)을 관리할 뿐만 아니라, 노드 배치도 담당합니다. 관리자는 이러한 목표 상태를 Odin에 전달합니다. 목표 상태에는 리소스 프로필, 노드 수, 역할(Primary/Follower), 데이터 노드에서 실행되어야 할 사이드 컨테이너, 서버 설정(예: bin log 포맷, SQL 모드) 등 핵심 구성 요소가 포함됩니다. 컨트롤 플레인은 실제 MySQL 클러스터 또는 MySQL 노드가 언제든지 정의된 원하는 상태로 수렴하도록 보장합니다.
기술 관리자의 또 다른 핵심 역할은 워크플로우를 통해 시스템 상태를 변경할 수 있도록 하는 것입니다. 워크플로우는 Cadence에 의해 구동되는 내결함성(fault-tolerant) 장기 실행 프로세스입니다. 워크플로우의 예로는 기존 클러스터에 새 노드를 추가하거나, 클러스터에서 기본(primary) 장애 조치를 수행하거나, 노드에 특정 MySQL 변수를 적용하거나, MySQL 복제본의 복제 마스터를 변경하는 작업 등이 있습니다. 이외에도 기술 관리자가 수행하는 주요 기능은 다음과 같습니다.
- 클러스터 관리: 클러스터 생성, 업데이트, 삭제와 같은 운영 작업을 처리합니다.
- 기본 노드 장애 조치(Primary failover): 클러스터의 기본(primary) 노드를 전환합니다.
- 노드 수명 주기 관리: MySQL 서버 노드 추가, 교체, 삭제와 같은 작업을 통해 노드의 수명 주기를 관리합니다.
- 균형 배치(Balanced placement): Uber 인프라가 배포된 모든 지리적 위치에서 서버 노드가 균형 있게 배치되도록 Odin의 배치 엔진에 신호를 제공합니다. 이를 통해 하드웨어 장애나 데이터 센터 장애에도 복원력을 유지할 수 있습니다.
- DB 운영: 시스템 변수 설정, 복제(replication) 구성, 스케일링 작업 등 데이터베이스와 관련된 운영을 관리합니다.
전통적으로 MySQL 컨트롤 플레인은 기본 인프라 프로세스와 긴밀하게 결합되어 있었습니다. 그러나 MySQL 시스템이 확장됨에 따라, MySQL 장애로 인해 인프라 배치 작업이 막히는 문제가 발생했습니다. 이로 인해 MySQL 팀은 이러한 문제를 디버깅하는 데 상당한 시간을 소모해야 했습니다. 이러한 결합은 기본 장애 조치(primary failover), 노드 교체 등 60개 이상의 워크플로우의 운영 안정성에 영향을 미쳤습니다. 게다가 MySQL은 클러스터 상태 관리를 위해 Git 기반 구성 저장소에 의존했는데, 이 시스템은 이러한 대규모 사용 사례에 최적화되어 있지 않았습니다. 결국, 이러한 요소들이 신뢰성과 확장성에 심각한 문제를 일으켰고, 전체 컨트롤 플레인의 재설계가 필요하게 되었습니다.
Controller
컨트롤 플레인 재설계의 일환으로, MySQL 컨트롤 플레인에 컨트롤러(controller)라는 구성 요소를 도입했습니다. 컨트롤러는 모든 MySQL 클러스터의 외부 관찰자로 작동하며, 데이터베이스와 다른 컨트롤 플레인 구성 요소에서 신호를 수집합니다. 컨트롤러는 규칙 평가자(rule evaluator)로 구성되어 있어, 클러스터에서 정의된 규칙이 위반되면 이를 감지하고 즉각 조치를 취합니다. 컨트롤러의 핵심 역할 중 하나는 MySQL 인스턴스의 기본(primary) 노드 상태를 모니터링하고, 현재 기본 노드에 문제가 발생하면 자동으로 기본 장애 조치(primary failover)를 트리거하는 것입니다. 또한, 컨트롤러는 그룹 복제(group replication) 환경에서 합의 그룹(consensus group)에 속한 클러스터 간의 균형을 유지하도록 보장합니다.
Orchestration of Critical Flows
컨트롤 플레인과 상호작용하는 기본 메커니즘은 워크플로(workflow)를 통하는 것입니다. 워크플로는 복잡하고 장기 실행되는 작업을 오케스트레이션하는 일련의 단계로 구성된 비동기 이벤트 기반 프로세스입니다.
MySQL 컨트롤 플레인은 이러한 워크플로를 실행하기 위해 Cadence™를 사용하여 내구성, 내결함성(fault tolerance), 그리고 확장성을 제공합니다. 그림 2는 MySQL 컨트롤 플레인에서의 일반적인 워크플로우 예시를 보여줍니다.
[그림 2: Graceful primary failover workflow.]
이번 재설계를 통해 모든 컨트롤 플레인 운영이 새롭게 정비되었습니다. 다음 섹션에서는 몇 가지 핵심 플로우의 오케스트레이션 과정을 살펴봅니다.
Primary Failover
Uber에서는 싱글 기본(primary) - 다중 복제본(replica) 구성 방식을 사용합니다. 쓰기 작업은 기본 노드가 처리하며, 표준 MySQL binlog 복제를 통해 해당 데이터가 복제본 노드로 전파됩니다.
[그림 3: Single primary, multiple replica setup.]
기본(primary) 장애 조치는 클러스터의 기본 노드를 한 호스트에서 다른 호스트로 전환하는 자동화된 프로세스입니다. 기본 노드는 하나뿐이므로, 이를 정상적으로 유지하고 운영하는 것은 높은 쓰기 가용성을 보장하고 다운타임을 최소화하는 데 매우 중요합니다. 이 기본 장애 조치 워크플로우는 기본 노드의 상태를 지속적으로 모니터링하는 구성 요소들이 사용하며, 노드 성능 저하나 장애가 감지될 경우 이를 완화하기 위한 조치로 자동 실행됩니다.
기존 기본(primary) 노드의 상태에 따라, 정상(graceful)과 긴급(emergency) 두 가지 유형의 장애 조치를 수행합니다.
정상 승격(graceful promotion)은 기본 노드의 일반적인 유지 관리 작업(maintenance activities) 중에 필요합니다. 예를 들어, 기존 기본 노드의 호스트를 수리해야 할 때 사용됩니다. 이 과정에서는 새로운 기본 노드 후보를 선택한 후, 이전 기본 노드에서 새로운 기본 노드로 쓰기 로드를 부드럽게 전환합니다. 정상 장애 조치는 기존 기본 노드가 정상 상태이고 사용 가능하다는 것을 전제로 합니다. 이 방식은 비동기(async) 및 반동기(semi-sync) 복제 설정에 적용됩니다. 또한 그룹 복제(group replication)가 포함된 배포 단계도 있지만, 이는 이번 글의 범위에서 제외됩니다.
정상 장애 조치(graceful failover)는 다음과 같은 단계를 수행합니다.
- 현재 기본 노드를 읽기 전용 모드로 전환합니다.
- 현재 기본 노드의 트래픽을 종료합니다.
- 새 기본 노드(primaryelect)를 선택합니다. 기본적으로 워크플로는 동일한 데이터 센터에서 새 기본 노드를 선택합니다. 복제본 노드의 복제 지연(replication lag)도 고려되며, 가장 앞선 binlog 위치를 가진 노드가 우선적으로 선택됩니다.
- 이전 기본 노드의 binlog 위치를 가져오고, 해당 트랜잭션이 새 기본 노드에 적용될 때까지 대기합니다.
- 새 기본 노드의 쓰기를 활성화합니다.
기존 기본 노드에 접근할 수 없는 경우(예: 데이터 센터 존 장애 또는 네트워크 격리) MySQL은 긴급 장애 조치(emergency fail-over)를 수행합니다. 이 과정은 정상 승격(graceful promotion)과 거의 동일하지만, 기존 기본 노드가 도달 불가능한 상태라고 가정하므로, 새로운 기본 노드에 모든 데이터를 복제하는 단계는 건너뜁니다.
우리는 하위 서비스에 99.99% 가용성을 보장하며, 기본 노드 장애 조치는 이 SLA를 충족하는 데 핵심적인 역할을 합니다.
Node Replacement
제어 플레인에서 노드를 교체하는 과정은 MySQL 노드(및 해당 데이터 전체)를 한 호스트에서 다른 호스트로 옮기면서도 MySQL 데이터베이스 사용자에게 아무런 영향을 주지 않도록 설계되어 있습니다.
Uber의 하드웨어 인프라는 여러 클라우드 제공업체와 온프레미스 데이터 센터에 걸쳐 있으며, 수십만 대의 서버, 다양한 하드웨어, 네트워크 구성 요소로 이루어져 있습니다. 이렇게 방대한 인프라에서 발생할 수 있는 장애로부터 MySQL 시스템을 보호하고, 유연하게 운영하기 위해 노드 교체는 제어 플레인에서 매우 중요한 역할을 합니다. 노드 교체 워크플로는 유지보수 작업의 일환으로, 장애가 발생한 호스트의 노드를 종료하고, 비슷한 리소스와 지리적 위치를 가진 새로운 호스트에 동일한 노드를 생성합니다. 이 모든 과정은 완전히 투명하게 진행되어, 데이터베이스 사용자들은 이와 같은 이동이 일어났다는 사실조차 인지하지 못합니다.
노드 교체는 겉보기에는 단순한 데이터 이동 작업처럼 보일 수 있지만, 실제로는 여러 가지 세부사항을 신중하게 고려해야 합니다.
- 하드웨어 프로필: 새 노드는 교체되는 노드와 동일한 하드웨어 사양을 가져야 합니다. 즉, CPU 코어 수, 디스크 및 메모리 용량, 포트 구성 등이 일치해야 합니다.
- Co-location: 새 노드는 동일한 수준의 내결함성(fault tolerance)을 보장하는 호스트에 배치되어야 하며, 이를 통해 네트워크 지연(latency)이 일관되게 유지됩니다. 사용자들은 노드의 물리적 위치보다는 쿼리 응답 시간이 변함없이 유지되는 것을 가장 중요하게 생각합니다.
- 종속성(Dependencies): 교체되는 노드가 다른 노드들의 복제 상위 노드(Replication Parent)인 경우, 하위 노드들은 새로 교체된 노드를 바라보도록 수정하거나, 클러스터의 다른 노드에 연결되도록 재구성해야 합니다.
- 기본 노드 승격(Primary Promotion): 교체되는 노드가 클러스터의 기본 노드(Primary)라면, 해당 노드를 종료하거나 트래픽에서 제외하기 전에 정상 승격(Graceful Promotion)을 수행해, 쓰기 작업을 다른 기본 노드로 안전하게 넘겨야 합니다.
노드 교체는 내부적으로 노드 추가와 노드 삭제라는 두 개의 독립적인 작업으로 구성됩니다.
노드 추가는 부트스트랩 과정으로, 배치와 데이터 프로비저닝으로 이루어집니다. 배치 과정에서는 새 노드에 필요한 리소스를 갖춘 호스트를 식별해 노드의 위치를 결정합니다. 데이터 동기화 과정에서는 선택된 노드에 MySQL 프로세스를 설치한 후, 기존 노드(가능하면 기본 노드) 중 하나에서 새 노드로 데이터를 전송합니다. 이 노드 추가 과정은 여러 노드를 병렬로 추가하는 것을 지원하며, 이는 재해 복구와 같은 긴급 상황에서 특히 유용합니다.
노드 삭제는 노드의 모든 종속성을 정리한 후(위에 나열됨), 해당 호스트를 안전하게 제거하는 과정입니다.
Schema Changes
MySQL 제어 플레인은 셀프 서비스 워크플로를 통해 스키마 변경을 자동화합니다. 이 프로세스는 MySQL의 instant alter 또는 Percona™의 ptosc(pt-online-schema-change)를 사용해 기본 노드에서 안전하고 비차단(non-blocking) 방식으로 스키마 업데이트를 수행합니다. 워크플로는 스키마 변경 유형과 데이터 크기를 기반으로 가장 적절한 적용 전략을 지능적으로 선택합니다. 예를 들어, 컬럼 추가와 같은 빠르고 안전한 작업에는 즉시 변경(instant-alter)을 사용하고, 데이터 유형 변경과 같은 경우에는 ptosc와 같은 비차단 온라인 방식을 사용합니다.
스키마 변경 워크플로에는 드라이런(dry-run) 기능도 포함되어 있습니다. 드라이런(dry-run)을 사용하면 기본 노드(및 클러스터의 나머지 노드)에 실제로 적용하기 전에 격리된 복제본에 스키마 변경을 시뮬레이션할 수 있습니다. 이를 통해 스키마 변경이 이전 버전과 호환되고, 데이터에 손상을 주지 않으며, 안전하게 적용될 수 있다는 추가적인 확신을 제공합니다.
스키마 변경 워크플로는 Uber의 CI/CD 파이프라인과도 통합되어, 스키마 변경 프로세스가 완전히 자동화되고 검토 절차를 거치도록 구성되어 있습니다. 개발자는 스키마 파일에서 변경 사항을 작성하고, 이를 다른 소스 코드와 함께 저장소에 커밋합니다. 변경 사항이 승인되어 메인 브랜치에 병합되면, CI 시스템이 이를 감지하고 스키마 변경 워크플로를 자동으로 트리거합니다. 이를 통해 개발자는 스키마에 대한 완전한 제어권을 가지면서, 배포된 코드와 데이터베이스 스키마가 항상 일치하도록 보장할 수 있습니다.
Data Plane
실행 중인 MySQL 노드는 하나의 호스트에서 여러 컨테이너로 구성됩니다. 데이터베이스 컨테이너는 MySQL 프로세스를 실행하며, 명확하게 정의된 작업을 수행하는 여러 보조 구성 요소도 함께 운영됩니다. 이러한 구성 요소들은 단일 호스트 내에서 실행되는 독립된 Docker® 컨테이너로, Docker 네트워킹을 통해 서로 통신할 수 있습니다. MySQL 노드의 구조는 그림 4에 나타나 있습니다.
[그림 4: Anatomy of a MySQL node.]
MySQL 노드는 다음과 같은 구성 요소로 이루어집니다.
- 데이터베이스 컨테이너: mysqld 프로세스 내에서 Oracle InnoDB® 엔진을 실행합니다. 필요에 따라 Meta RocksDB™와 같은 다른 MySQL 엔진으로 구성할 수 있습니다.
- 워크 컨테이너: 사이드카 컨테이너로, 노드의 실제 상태를 목표 상태와 일치하도록 조정합니다. 이 컨테이너는 MySQL 노드를 Odin 배치 엔진과 통합합니다.
- 메트릭 컨테이너: MySQL 프로세스에서 방출하는 QPS(초당 쿼리 수), 쿼리 유형, 잠금 시간, 연결 메트릭과 같은 다양한 신호를 수집하고, 이를 모니터링 시스템에 게시합니다.
- 헬스 프로버: MySQL 프로세스의 상태를 주기적으로 추적하며, 기본(primary) 노드의 상태 신호를 전송합니다. 컨트롤러는 이 신호를 수신해 기본 노드 장애를 완화하기 위한 조치를 취하며, 이를 통해 Uber 전역의 MySQL 사용자에게 엄격한 쓰기 가용성 SLA를 제공합니다.
- 백업 컨테이너: 주기적으로 생성되는 일시적인 컨테이너로, 데이터베이스 백업을 수행하고 이를 오브젝트 스토리지에 업로드합니다.
Discovery Plane
라우팅 또는 디스커버리 플레인은 끊임없이 변화하는 Uber의 하드웨어 인프라 위에 추상화를 제공하여, MySQL 클러스터와의 클라이언트 상호작용을 간소화합니다. 이를 통해 서비스는 하드웨어 수준의 모든 변화를 감추고, 단일 가상 IP를 사용해 MySQL 클러스터에 연결할 수 있습니다.
[그림 5: Architecture of the discovery plane.]
라우팅 및 디스커버리 플레인은 세 가지 주요 구성 요소로 이루어져 있습니다.
- 리버스 프록시: 로드 밸런서 역할을 하며, 클라이언트의 요청을 데이터베이스 호스트로 전달하거나 응답을 반환합니다.
- 풀링 서비스: 기본 노드 전환(primary failover) 또는 노드 교체와 같은 클러스터/노드 관리 작업 중 프록시 구성을 업데이트하는 역할을 맡습니다.
- 표준 클라이언트: 요청 유형(읽기/쓰기)에 따라 기본(primary) 또는 복제(replica) 노드에 연결을 생성하는 간단하고 사용하기 쉬운 기능을 제공하며, 연결 풀링, 타임아웃 처리, 클라이언트 관련 메트릭 수집 등을 지원합니다.
제어 플레인의 재설계를 통해 라우팅 플레인은 강력한 일관성을 보장하는 etcd™ 데이터 스토어를 토폴로지 저장소로 사용하도록 업데이트되었습니다. 새로운 노드 추가나 기본(primary) 노드 페일오버 처리와 같은 토폴로지 변경 사항은 매니저가 토폴로지 저장소에 기록합니다.
그런 다음 이러한 업데이트는 etcd™ watch를 통해 풀링 서비스로 전파되며, 이후 역방향 프록시 구성을 조정하여 트래픽을 새 노드로 전달하거나, 노드 교체 시 떠나는 노드에서 트래픽을 정리합니다. 이 모든 과정은 클라이언트에게 완전히 추상화되어 있습니다. 클라이언트는 정적 VIP(가상 IP)를 사용해 리버스 프록시에 연결하며, 프록시는 쓰기 쿼리를 기본 노드로, 읽기 쿼리는 모든 복제본에 로드 밸런싱합니다. 이때, 동일한 지리적 지역에 있는 복제본이 우선시됩니다.
디스커버리 플레인은 특정 노드의 트래픽을 비활성화하는 기능을 지원합니다. 이는 고객 트래픽에 영향을 주지 않고 MySQL 노드의 하드웨어 또는 소프트웨어 장애를 디버깅할 때 매우 유용합니다. 또한, 컨트롤러를 사용해 복제 지연과 같은 문제가 발생한 노드의 트래픽을 자동으로 비활성화하도록 이 기능을 자동화하는 것도 가능합니다.
Observability
컨테이너와 클러스터에서 시스템 메트릭을 수집하는 것 외에도, 프로버는 데이터 흐름을 시뮬레이션하여 각 클러스터의 다양한 구성 요소 상태에 대한 메트릭을 수집하는 데 사용됩니다. 이렇게 수집된 메트릭과 로그는 Uber의 모니터링 및 로깅 시스템에 저장됩니다. 경고는 쓰기 불가능, 복제 지연, 높은 CPU 사용률, 기본 노드의 비정상 연결과 같은 장애를 감지하도록 구성됩니다. 이 관측 가능성(Observability) 시스템 덕분에 MySQL-db-as-a-service 팀은 MySQL 시스템의 상태를 항상 정확하게 파악할 수 있습니다. 또한, 데이터베이스에 연결된 업스트림 서비스 팀도 이러한 경고를 구독해 빠르게 대응할 수 있습니다.
Change Data Capture
변경 데이터 캡처(CDC)를 위해 MySQL 시스템은 Storagetapper를 사용합니다. Storagetapper는 binlog에서 삽입, 수정, 삭제와 같은 변경 사항을 포착해 Apache Kafka®로 스트리밍하고, 이후 이를 Apache Hive™ 데이터 스토어에 수집합니다. 이 시스템은 상위 스키마 변경, 데이터 변환, 포맷 변환까지 처리할 수 있습니다.
Backup and Restore
Uber의 MySQL 백업 및 복구는 완전히 자동화된 프로세스입니다. 백업은 Percona XtraBackup™ 기능을 활용합니다. MySQL의 백업 및 복구는 데이터 크기에 따라 몇 분에서 몇 시간까지 소요되며, 4시간의 RPO(복구 시점 목표)와 짧은 RTO(복구 시간 목표)를 유지합니다.
Conclusion
MySQL은 Uber의 여러 핵심 서비스의 중심에서 중요한 역할을 합니다. 컨트롤 플레인은 이러한 서비스에 안정적이고 확장 가능하며 고성능의 MySQL 플랫폼을 제공하며, Uber 규모의 대규모 MySQL 시스템을 유지하는 데 따르는 운영 부담을 모두 추상화합니다.
이번 소개 블로그에서는 MySQL 컨트롤 플레인의 주요 구성 요소를 살펴보고, 각 구성 요소의 아키텍처와 역할에 대해 논의했습니다. 또한, 시스템을 건강하고 유연하게 유지하면서도 수동 개입이 필요하지 않도록 돕는 중요한 운영 및 자동화 기능에 대해서도 살펴보았습니다. 이 덕분에 우리는 다양한 고객과 사용 사례를 원활하게 지원할 수 있습니다. 시리즈의 다음 블로그에서는 고가용성과 높은 처리량을 보장하기 위해 Uber가 MySQL을 어떻게 운영하는지 더 깊이 탐구해 보겠습니다.
• 원문: MySQL At Uber

Principal DBA(MySQL, AWS Aurora, Oracle)
핀테크 서비스인 핀다에서 데이터베이스를 운영하고 있어요(at finda.co.kr)
Previous - 당근마켓, 위메프, Oracle Korea ACS / Fedora Kor UserGroup 운영중
Database 외에도 NoSQL , Linux , Python, Cloud, Http/PHP CGI 등에도 관심이 있습니다
purityboy83@gmail.com / admin@hoing.io