Skip to document
pieroot log
Markdown Preview

Galera Cluster로 구축하는 OpenStack 환경의 고가용성 MySQL 데이터베이스 완벽 가이드

Frontmatter

이 포스트는 OpenStack 환경에서 고가용성을 위해 Galera Cluster와 HAProxy·Keepalived를 활용하여 MySQL을 동기식 복제 클러스터로 구성하고, 설치·설정·부팅·상태 확인·장애 복구·운영 시 주의사항을 단계별로 안내한다.

Galera Cluster로 구축하는 OpenStack 환경의 고가용성 MySQL 데이터베이스 완벽 가이드

서론: 왜 Galera Cluster를 사용하는가?

OpenStack 환경을 운영하다 보면 데이터베이스의 고가용성이 필수적입니다. 단일 MySQL 서버로 운영할 경우, 해당 서버에 장애가 발생하면 전체 OpenStack 서비스가 중단되는 심각한 문제가 발생합니다.
기존의 Master-Slave 구조를 살펴보겠습니다:
구분
Master-Slave
Galera Cluster
쓰기 가능 노드
Master만 가능
모든 노드 가능
장애 시 복구
수동 승격 필요
쿼럼 유지 시 자동, 전체 전원 손실 시 수동
데이터 동기화
비동기 복제
동기 복제
서비스 중단
발생 가능
쿼럼 유지 시 무중단
Master-Slave 구조에서는 Master 노드에서만 쓰기가 가능하므로, Master 노드 장애 시 다른 노드를 Master로 승격해야 합니다. 자동 승격 기능이 구현되어 있더라도 일정 시간의 서비스 중단이 불가피하죠.
Galera Cluster는 이러한 문제의 대안입니다. 모든 노드가 Master 역할을 수행하며, 실시간으로 데이터를 동기화하고 복제합니다.
💡
Galera Cluster의 장점
  • 모든 노드에서 읽기와 쓰기가 가능합니다
  • 노드 1개 장애 시 나머지 2개가 쿼럼을 유지하면 자동으로 계속 운영됩니다
  • 데이터 일관성이 항상 보장됩니다
⚠️
전체 전원 손실은 자동 복구가 아니다. 3노드가 모두 내려가면 어느 노드도 새 Primary Component를 만들지 않는다. 이는 split-brain을 막기 위한 정상 동작이다. 이때 관례상 1번 노드를 bootstrap하거나 safe_to_bootstrap을 확인 없이 1로 바꾸면 안 된다.
Galera Cluster 3노드 아키텍처와 쿼럼 개념
Galera Cluster 3노드 아키텍처와 쿼럼 개념
OpenStack 서비스가 3개 Galera 노드로 DB 트래픽을 보내고, 노드 간 동기 복제와 쿼럼을 통해 고가용성을 유지하는 구조입니다.
다만 모든 쿼리를 동기화해야 하기 때문에 트래픽이 많은 환경에서는 부하가 발생할 수 있습니다. 따라서 이 포스트에서는 HAProxy와 VIP를 활용한 하이브리드 구성도 함께 다루겠습니다.

시스템 요구사항

Galera Cluster를 구성하기 전에 다음 요구사항을 확인하세요:
항목
요구사항
비고
노드 수
최소 3개 (홀수 권장)
쿼럼 형성을 위해 필수
OS
Ubuntu 20.04+ / CentOS 7+
본 가이드는 Ubuntu 기준
네트워크
노드 간 통신 가능
4567, 4568, 4444 포트 오픈
RAM
노드당 최소 2GB
OpenStack 사용 시 4GB 권장
⚠️
왜 홀수 개의 노드가 필요한가요?
클러스터가 정상 작동하려면 쿼럼(Quorum) 형성이 필수입니다. 예를 들어 2개 노드만 있을 때 1개가 장애가 나면, 남은 1개 노드가 과반수인지 판단할 수 없어 클러스터 전체가 중단됩니다. 3개 노드라면 1개가 장애가 나도 2개가 과반수를 유지하여 정상 운영됩니다.

MySQL 설치

모든 노드에서 MySQL(또는 MariaDB)을 설치합니다.
bash
# 패키지 업데이트
sudo apt update

# MySQL 서버 설치 (MariaDB도 가능)
sudo apt install mysql-server -y
Galera 플러그인은 대부분 자동으로 설치됩니다. 자동 설치되지 않았다면 직접 설치하세요:
bash
# Galera 플러그인 수동 설치 (필요시)
sudo apt install galera-4 -y

1번 노드 설정 (Bootstrap 노드)

첫 번째 노드는 클러스터를 초기화하는 Bootstrap 노드입니다.

설정 파일 편집

bash
sudo vi /etc/mysql/mariadb.conf.d/60-galera.cnf
📝
파일명의 숫자 60은 무엇인가요?
MariaDB는 /etc/mysql/mariadb.conf.d/ 디렉토리의 설정 파일들을 숫자 순서대로 로드합니다. 60-galera.cnf는 다른 기본 설정(50-server.cnf 등) 이후에 로드되어 Galera 관련 설정을 오버라이드합니다.
기존 주석 처리된 내용을 아래와 같이 수정합니다:
toml
#
# * Galera-related settings
#
[galera]
# 필수 설정
wsrep_on                 = ON
wsrep_cluster_name       = "openstack-galera-cluster"
wsrep_cluster_address    = "gcomm://"                    # 첫 노드는 비워둡니다
binlog_format            = row
default_storage_engine   = InnoDB
innodb_autoinc_lock_mode = 2

# 네트워크 설정
bind-address = 0.0.0.0

# 노드 식별 설정
wsrep_sst_method         = rsync
wsrep_node_address       = "<1번 노드 IP>"
wsrep_node_name          = "<1번 노드 hostname>"
wsrep_provider           = /usr/lib/galera/libgalera_smm.so

주요 설정 옵션 설명

옵션
설명
wsrep_on
Galera 복제 활성화
wsrep_cluster_name
클러스터 이름 (모든 노드 동일)
wsrep_cluster_address
클러스터 노드 목록
wsrep_sst_method
State Snapshot Transfer 방식
wsrep_node_address
현재 노드의 IP 주소
wsrep_node_name
현재 노드의 식별 이름

클러스터 초기화

bash
sudo galera_new_cluster
✅
초기 구성과 장애 복구를 혼동하지 말 것
galera_new_cluster는 초기 구성 때 1번 노드에서 1회만 실행한다. 전원 복구 때는 1번 노드가 아니다. recovered seqno가 가장 높고 safe_to_bootstrap: 1인 노드에서만 실행한다.

2번 노드 설정

2번 노드부터는 기존 클러스터에 조인하는 방식으로 설정합니다.

설정 파일 편집

bash
sudo vi /etc/mysql/mariadb.conf.d/60-galera.cnf
toml
#
# * Galera-related settings
#
[galera]
# 필수 설정
wsrep_on                 = ON
wsrep_cluster_name       = "openstack-galera-cluster"
wsrep_cluster_address    = "gcomm://<1번 노드 IP>,<2번 노드 IP>"
binlog_format            = row
default_storage_engine   = InnoDB
innodb_autoinc_lock_mode = 2

# 네트워크 설정
bind-address = 0.0.0.0

# 노드 식별 설정
wsrep_sst_method         = rsync
wsrep_node_address       = "<2번 노드 IP>"
wsrep_node_name          = "<2번 노드 hostname>"
wsrep_provider           = /usr/lib/galera/libgalera_smm.so
💡
wsrep_cluster_address에 모든 노드 IP를 넣어야 하나요?
이론적으로 자신의 IP는 생략해도 되지만, 클러스터 간 주소 정보가 항상 제대로 동기화되는 것은 아닙니다. 모든 노드의 IP를 명시적으로 입력하는 것을 권장합니다. 이렇게 하면 재시작 시에도 안정적으로 클러스터에 조인할 수 있습니다.

MySQL 재시작

bash
sudo systemctl restart mysql
2번째 이후 노드들은 위와 같이 설정하고 MySQL을 재시작하면 자동으로 클러스터에 조인됩니다.

3번 노드 설정

3번 노드도 동일한 방식으로 설정합니다:
toml
wsrep_cluster_address    = "gcomm://<1번 노드 IP>,<2번 노드 IP>,<3번 노드 IP>"
wsrep_node_address       = "<3번 노드 IP>"
wsrep_node_name          = "<3번 노드 hostname>"
bash
sudo systemctl restart mysql

클러스터 상태 확인

MySQL 콘솔에 접속하여 클러스터 상태를 확인합니다:
bash
mysql -u root -p

동기화 상태 확인

sql
SHOW GLOBAL STATUS LIKE 'wsrep_local_state_comment';
javascript
+---------------------------+--------+
| Variable_name             | Value  |
+---------------------------+--------+
| wsrep_local_state_comment | Synced |
+---------------------------+--------+

클러스터 크기 확인

sql
SHOW GLOBAL STATUS LIKE 'wsrep_cluster_size';
javascript
+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| wsrep_cluster_size | 3     |
+--------------------+-------+

클러스터 상태 및 연결된 노드 확인

sql
-- Primary 상태 확인 (마스터가 될 자격이 있는지)
SHOW GLOBAL STATUS LIKE 'wsrep_cluster_status';

-- 연결 상태 확인
SHOW GLOBAL STATUS LIKE 'wsrep_connected';

-- 연결된 노드 IP 확인
SHOW GLOBAL STATUS LIKE 'wsrep_incoming_addresses';
✅
정상 상태 체크리스트
  • wsrep_local_state_comment = Synced
  • wsrep_cluster_size = 노드 수와 일치
  • wsrep_cluster_status = Primary
  • wsrep_connected = ON

1번 노드 wsrep_cluster_address 재설정

⚠️
중요: 이 단계를 반드시 수행하세요!
1번 노드의 wsrep_cluster_address가 비어 있으면 시스템 재시작 시 클러스터가 정상적으로 연결되지 않습니다.
1번 노드의 설정 파일을 다시 열어 모든 클러스터 IP를 입력합니다:
bash
sudo vi /etc/mysql/mariadb.conf.d/60-galera.cnf
toml
# 변경 전
wsrep_cluster_address    = "gcomm://"

# 변경 후
wsrep_cluster_address    = "gcomm://<1번 노드 IP>,<2번 노드 IP>,<3번 노드 IP>"
설정 파일을 수정한 후에는 MySQL을 재시작하지 않아도 됩니다. 다음 재시작 시 적용됩니다.

트러블슈팅: 전체 재시작 또는 전원 손실 후

노드 1개가 죽으면 나머지 2개가 Primary를 유지한다. 세 노드가 모두 내려가면 어느 노드도 새 Primary를 자동으로 만들지 않는다. 이때 빠른 점검보다 정확한 기준 노드 선택이 중요하다.
mermaid
flowchart TD
  A["모든 MariaDB inactive?"] -->|아니오| Z["중단: 이미 Primary가 있을 수 있음"]
  A -->|예| B["cluster UUID 동일?"]
  B -->|아니오| Z2["중단: 다른 클러스터"]
  B -->|예| C["recovered seqno 최고 노드 = safe_to_bootstrap 1?"]
  C -->|예| D["그 노드만 galera_new_cluster"]
  C -->|아니오| Z3["중단: 수동 변경 금지, 일관성 검토"]
  D --> E["나머지 노드를 Synced/Ready 확인 후 순차 합류"]

하지 말 것

  • 관례상 1번 노드라는 이유만으로 galera_new_cluster
  • wsrep_cluster_address를 비우고 아무 노드나 bootstrap
  • seqno 비교 없이 grastate.dat의 safe_to_bootstrap을 1로 수정
  • 세 노드에서 동시에 galera_new_cluster
  • Galera보다 앞에 OpenStack 컨테이너를 일괄 재시작

해야 할 것

  1. 모든 MariaDB가 중지 또는 실패 상태인지 확인한다.
  1. 각 노드의 cluster UUID가 같은지 확인한다.
  1. grastate.dat와 journal의 Recovered position을 비교한다. seqno: -1이어도 실패로 단정하지 말 것.
  1. recovered seqno가 가장 높은 노드가 safe_to_bootstrap: 1인지 확인한다.
  1. 두 조건이 일치할 때만 그 노드에서 galera_new_cluster를 실행한다.
  1. 나머지 노드는 systemctl start mariadb로 하나씩 합류한다. Synced / Ready ON을 확인한 뒤에 다음 노드를 시작한다.
  1. UUID가 다르거나 최고 seqno 노드와 safe 노드가 다르면 중단한다.
bash
# 각 노드에서 수집
systemctl is-active mariadb || true
sudo sed -n '1,20p' /var/lib/mysql/grastate.dat
sudo journalctl -u mariadb -b --no-pager | grep "Recovered position" | tail -1

# 기준 노드만, MariaDB가 안 돌고 있을 때
sudo galera_new_cluster

# 나머지 노드는 일반 시작
sudo systemctl start mariadb
⚠️
safe_to_bootstrap 수동 변경은 기본 금지
조건이 충돌할 때 값을 1로 바꾸고 시작하면, 낮은 seqno 노드로 최신 트랜잭션을 잃거나 split-brain을 만들 수 있다. 마지막 수단이며, 사람이 모든 노드의 recovered position과 InnoDB 상태를 검토한 뒤에만 최고 seqno 노드에 적용한다.

전원 복구 시 OpenStack·Ceph

Galera만 살렸다고 끝난 것이 아니다. 스토리지가 같이 내려갔다면
Ceph 클러스터 안전한 종료 및 재부팅 가이드: 전원 꺼짐과 문제 해결 방법
를 먼저 확인하고, 이어서 OpenStack API를 검증한다.
bash
openstack token issue
openstack compute service list
openstack volume service list

HAProxy + Keepalived로 고가용성 구성

Galera Cluster는 모든 노드에서 쓰기가 가능하지만, 동시 쓰기로 인한 충돌을 방지하기 위해 단일 진입점을 구성하는 것이 좋습니다.
HAProxy와 Keepalived를 이용한 Galera Cluster 고가용성 아키텍처
HAProxy와 Keepalived를 이용한 Galera Cluster 고가용성 아키텍처
클라이언트와 OpenStack 서비스는 VIP로 접근하고, HAProxy/Keepalived 계층이 활성 DB 노드 장애 시 백업 노드로 트래픽을 넘겨 단일 진입점을 유지합니다.

HAProxy 설정

bash
sudo vi /etc/haproxy/haproxy.cfg
plain text
listen galera_cluster
    bind <VIP>:3306
    mode tcp
    option tcpka
    balance source
    
    # 옵션: MySQL 헬스체크
    # option mysql-check user haproxy
    
    server galera-node1 <1번 노드 IP>:3306 check weight 1
    server galera-node2 <2번 노드 IP>:3306 backup check weight 1
    server galera-node3 <3번 노드 IP>:3306 backup check weight 1
📝
설정 설명
  • bind <VIP>:3306: 가상 IP로 들어오는 연결을 수신합니다
  • balance source: 동일 클라이언트는 동일 서버로 연결됩니다
  • backup: 기본 서버 장애 시에만 활성화됩니다
이 설정으로 VIP를 통해 들어오는 모든 요청은 1번 노드로 전달되며, 1번 노드 장애 시 자동으로 2번 노드가 요청을 받습니다.

핵심 정리

이 포스트에서 다룬 MySQL Galera Cluster 구성을 정리하면 다음과 같습니다:
✅ Galera Cluster: 모든 노드가 Master 역할을 수행하는 동기식 복제 클러스터
✅ 노드 구성: 최소 3개 이상의 홀수 노드로 구성하여 쿼럼 유지
✅ Bootstrap: 첫 번째 노드는 galera_new_cluster 명령으로 초기화
✅ 클러스터 조인: 2번째 이후 노드는 wsrep_cluster_address에 기존 노드 IP 입력 후 재시작
✅ 상태 확인: wsrep_cluster_size, wsrep_local_state_comment 등으로 모니터링
✅ 장애 복구: 전체 전원 손실 후에는 최고 recovered seqno + safe_to_bootstrap: 1 노드만 bootstrap. 수동 플래그 변경은 금지
✅ 고가용성: HAProxy + Keepalived로 단일 진입점 및 자동 장애 조치 구현

주의사항

⚠️
운영 시 주의할 점
  • 정기 백업: Galera Cluster도 완벽하지 않습니다. 정기적인 백업을 권장합니다
  • 네트워크 지연: 노드 간 네트워크 지연이 크면 성능 저하가 발생합니다
  • 동시 쓰기: 같은 행을 여러 노드에서 동시에 수정하면 충돌이 발생할 수 있습니다
  • 클러스터 확장: 새 노드 추가 시 SST(State Snapshot Transfer)로 인해 일시적 성능 저하 가능
  • 전체 종료 전 기록: 마지막 Primary, 각 노드 seqno, safe_to_bootstrap, 마지막으로 내린 노드를 남긴다
  • 복구 기준: 관례상 1번 노드가 아니라 가장 높은 recovered seqno + safe 플래그 노드

참고 자료