MySQL 2026.09.07 6 min read

VyOS 뒤에 MySQL Primary Replica를 격리한 이유

DB 두 대를 VyOS 뒤 전용망으로 옮기고 GTID·TLS 복제를 구성했다. 라우팅과 NAT, 비밀번호 길이 제한, 복제 재접속을 확인한 과정과 수동 전환의 한계를 기록했다.

초기에는 DB도 애플리케이션과 같은 서비스망에 있었다. 애플리케이션 요청, 관리 접속, 복제 통신을 구분해 제한하려고 DB 두 대를 별도 서브넷으로 옮겼다. 필요한 노드만 VyOS를 통해 접근하도록 구성했다.

DB 전용망을 먼저 만들고 연결 대상을 줄였다

VyOS에 서비스망과 DB망 인터페이스를 각각 연결했다. DB 서버의 기본 경로는 VyOS로 바꾸고 Worker와 모니터링 서버에는 DB망으로 가는 경로를 추가했다.

MySQL은 DB의 내부 주소에서만 접속을 받도록 했다. 애플리케이션 계정은 두 Worker에서만, 복제 계정은 Replica에서만 접속하도록 제한했다. 방화벽에도 같은 출발지 조건을 적용했다.

Worker 1·2 → Primary 3306: 허용
Replica → Primary 3306: 허용
Monitoring → mysqld_exporter: 허용
그 외 DB 신규 연결: 차단

전용망을 만든 뒤에도 인증과 TLS를 유지했다. 라우팅, 호스트 방화벽, MySQL 계정의 Host 조건, TLS 요구 조건을 각각 적용했다.

Route와 NAT를 혼동하면 통신이 절반만 된다

DB의 경로 표에는 서비스망 경로가 있었지만 외부 통신은 되지 않았다. 라우팅은 패킷을 보낼 방향을 정한다. 외부에서 DB 서브넷으로 응답할 경로를 모르는 경우에는 NAT로 출발지 주소를 바꿔 줘야 했다.

내부 서비스망으로 보낼 때는 NO-NAT로 주소를 유지하고, 외부로 나갈 때만 masquerade하도록 나눴다.

DB망 → 내부 서비스망: NAT 제외
DB망 → 외부: VyOS 주소로 masquerade

설정을 입력하고도 commit이 성공하지 않아 실제 동작에 반영되지 않은 적이 있었다. 후보 설정과 실행 중인 설정을 비교하고 commit, save, 재부팅 뒤 확인까지 순서대로 진행했다.

처음에는 Live ISO에서 작업한 상태였다. 재부팅 뒤에도 설정이 남도록 install image로 영구 설치하고 save했다. 재부팅 후 인터페이스와 경로를 다시 확인했다.

GTID와 TLS 기반 비동기 복제를 구성했다

MySQL 8 Primary/Replica는 GTID auto-position으로 연결했다. Replica에는 read_only와 super_read_only를 함께 설정해 직접 쓰기가 들어가지 않도록 했다.

복제 연결과 애플리케이션 JDBC 연결에 TLS를 적용했다. 최종 점검에서 두 연결 모두 TLS 1.3을 사용하는 것을 확인했다.

Replica 검증값은 다음과 같았다.

Replica_IO_Running: Yes
Replica_SQL_Running: Yes
Seconds_Behind_Source: 0
Auto_Position: 1
Source_SSL_Allowed: Yes
Last_IO_Error: 없음
Last_SQL_Error: 없음

다만 이 구성에서는 TLS 암호화만 사용했고 서버 인증서 검증은 켜지 않았다. 장기 운영에 적용하려면 CA를 배포하고 VERIFY_CA 또는 VERIFY_IDENTITY로 인증서 검증을 추가해야 한다.

복제 비밀번호 길이 제한에서 막혔다

복제 비밀번호를 긴 임의 문자열로 만들었는데 채널 설정 단계에서 길이 제한 오류가 발생했다. 일반 계정으로 로그인할 수 있는 비밀번호여도 복제 메타데이터의 입력 조건은 따로 확인해야 했다.

애플리케이션 DB 계정과 복제 전용 계정을 나누고 복제 채널이 허용하는 길이 안에서 강한 비밀번호를 새로 만들었다. 복제 계정의 권한도 필요한 범위만 부여했다.

Connecting 상태를 즉시 장애로 단정하지 않았다

Primary 주소를 변경하거나 재시작한 직후 IO 스레드가 잠시 Connecting에 머물렀다. 인증·TLS·네트워크 오류인지, 재접속 간격에 따라 다음 시도를 기다리는 중인지 먼저 구분해야 했다.

확인은 아래 순서로 진행했다.

  1. Replica에서 Primary 포트까지 TCP 연결 확인
  2. MySQL 오류 로그에서 인증·TLS 오류 확인
  3. Last_IO_Error 확인
  4. 재접속 간격이 지난 뒤 IO 스레드 재확인

이후 IO/SQL 스레드가 모두 Yes로 돌아왔고 복제 지연은 0초였다.

상태값보다 실제 데이터 왕복으로 검증했다

Primary에 검증용 스키마와 표식 데이터를 만들고 Replica에서 같은 값을 조회했다. 이어서 Primary에서 스키마를 삭제하고 Replica에서도 없어졌는지 확인했다. 상태값과 실제 데이터 반영 결과를 함께 확인한 것이다.

Replica에 직접 스키마를 만들려는 요청은 super-read-only 오류로 거부됐다. Flyway 마이그레이션 2개와 애플리케이션 테이블 18개도 양쪽에서 일치했다.

자동 failover를 넣지 않은 이유

DB 두 대만으로 자동 승격하려면 Primary 장애와 네트워크 단절을 구분할 정족수와 기존 Primary 차단 방식을 더 마련해야 했다. 프로젝트 기간 안에 이 부분까지 충분히 검증하기는 어렵다고 판단했다.

그래서 수동 전환으로 정했다. 복제 상태와 GTID를 확인한 뒤 Replica를 승격하고, 애플리케이션 접속 주소와 모니터링 역할 표시, 백업 타이머를 함께 바꾸는 절차를 남겼다.

비동기 복제라 장애 직전에 반영된 트랜잭션 일부가 Replica에 도착하지 않았을 수 있다. 수동 전환 시에는 이 손실 가능성도 확인해야 한다.

애플리케이션 파일, DB 덤프, 서비스 로그를 보관한 NFS·RAID5 구성은 별도 글에 정리했다.

실제 검증 화면

  1. VyOS의 서비스망·DB망 인터페이스와 직접 연결 경로, 내부 NO-NAT, 외부 masquerade 설정을 확인했다.
  2. Primary가 DB 전용 주소의 3306에서 접속을 받고 GTID ON, 쓰기 가능 상태로 실행되는지 확인했다.
  3. Replica의 IO·SQL 스레드는 모두 Yes, 복제 지연은 0초였다. read_only와 super_read_only 설정도 확인했다.
    VyOS의 서비스망·DB망 인터페이스, 경로와 내부 통신 NAT 제외·외부 통신 masquerade 설정

MySQL Primary의 3306 포트 수신과 GTID 활성화, read_only·super_read_only가 0인 조회 결과

Replica의 I/O·SQL 스레드가 Yes이고 복제 지연은 0초이며 읽기 전용 설정이 켜진 조회 결과

SEARCH JOURNAL

기록 검색

글 제목, 요약, 태그에서 찾습니다.

검색을 준비하고 있어요.

검색어를 입력해 주세요.

    Ctrl/⌘ K 검색 Esc 닫기 ↑ ↓ 결과 이동