MySQL 2026.09.07 6 min read

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

초기에는 DB도 애플리케이션과 같은 서비스망에 있었다. 구축은 쉬웠지만 application traffic, 관리 traffic, replication traffic의 경계가 흐렸다. 나는 DB 두 대를 별도 subnet으로 옮기고 VyOS를 통해 필요한 노드만 접근하도록 만들었다.

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

VyOS에는 서비스망 interface와 DB망 interface를 두었다. DB server의 default route는 VyOS를 향하게 하고, Worker와 Monitoring에서 DB망으로 가는 route를 추가했다.

MySQL은 DB private address에만 listen하도록 했다. Application account는 두 Worker source에서만, replication account는 Replica에서만 접근할 수 있게 제한했다. 방화벽에서도 같은 통신 matrix를 다시 적용했다.

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

망을 분리했다고 계정 인증과 TLS가 필요 없어지는 것은 아니다. Network route, host firewall, MySQL account Host, TLS 요구 조건을 겹쳐 적용했다.

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

DB에서 서비스망으로 가는 route가 보이는데도 외부 통신이 되지 않는 문제가 있었다. Route는 패킷을 어디로 보낼지 정하고, NAT는 외부가 private DB subnet의 응답 경로를 모를 때 source를 변환한다.

나는 내부 서비스망 목적지에는 NO-NAT를 적용하고, DB가 외부로 나갈 때만 masquerade하도록 source NAT를 나눴다.

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

한 번은 설정 문장은 맞았지만 commit이 되지 않아 running configuration에 반영되지 않았다. VyOS에서는 candidate configuration과 실제 동작 설정을 구분하고 commit, save, 재부팅 후 검증까지 해야 했다.

또 처음에는 live ISO 환경에서 설정을 마쳤다가 재부팅 지속성을 다시 확인해야 했다. 설치 이미지 상태와 영구 설치 상태는 다르다. install image, save, reboot 이후 interface와 route를 다시 확인했다.

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

MySQL 8 Primary/Replica는 GTID auto-position을 사용했다. Replica에는 read_onlysuper_read_only를 모두 적용해 애플리케이션이나 운영 실수로 쓰기가 들어가지 않게 했다.

복제 연결과 애플리케이션 JDBC는 TLS를 사용했다. 최종 상태에서 application과 replication connection 모두 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 암호화와 서버 인증서 검증은 다르다. 현재는 암호화는 사용하지만 server certificate verification은 켜지 않았다. 장기 운영이라면 CA를 배포하고 VERIFY_CA 또는 VERIFY_IDENTITY로 올려야 한다.

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

강한 임의 비밀번호를 만들면 무조건 좋다고 생각해 긴 값을 넣었는데 replication channel 설정에서 길이 제한 오류가 발생했다. 일반 사용자 password 정책과 replication metadata가 받아들이는 입력 조건을 따로 확인해야 했다.

나는 애플리케이션 DB 계정과 replication 전용 계정을 분리하고, replication 기능이 허용하는 범위 안에서 별도 강한 비밀번호를 생성했다. 권한도 replication에 필요한 최소 범위로 제한했다.

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

Primary 주소 변경이나 재시작 직후 Replica IO thread가 잠시 Connecting으로 보였다. 네트워크 단절, 계정 오류, TLS 오류일 수도 있지만 reconnect interval 때문에 정상적으로 기다리는 상태일 수도 있다.

나는 다음 순서로 나눠 봤다.

  1. Replica에서 Primary port까지 TCP 연결 확인
  2. MySQL error log의 인증·TLS 오류 확인
  3. Last_IO_Error 확인
  4. reconnect interval 뒤 IO thread 재확인

최종적으로 IO/SQL thread가 모두 Yes로 돌아오고 lag 0을 확인했다.

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

복제 thread가 Running이어도 특정 transaction 반영을 직접 확인해야 한다. 나는 Primary에 검증용 schema와 marker를 만들고 Replica에서 동일 값을 확인했다. 그다음 Primary에서 검증 schema를 삭제하고 Replica에서도 사라졌는지 확인했다.

Replica에 직접 schema를 만들려고 했을 때는 super-read-only 오류로 거부됐다. Flyway migration 2개와 application table 18개도 양쪽에서 일치했다.

자동 failover를 넣지 않은 이유

DB가 두 대뿐인 상태에서 자동 승격을 넣으면 network partition과 Primary 장애를 구분할 별도 quorum과 fencing 전략이 필요하다. 나는 프로젝트 기간 안에 이 조건을 충분히 검증할 수 없다고 판단했다.

현재 운영 구조는 수동 failover로 정했다. 운영자는 replication 상태와 GTID를 확인하고 Replica를 승격한 뒤 application endpoint, monitoring label, backup timer 역할을 함께 전환한다.

비동기 복제이므로 장애 순간의 최근 transaction 일부가 손실될 가능성도 있다. 이 한계까지 포함해야 현재 구조를 정확히 설명할 수 있다.

다음 글에서는 DB data 자체가 아니라 application file, DB dump와 service log를 보관한 NFS·RAID5 계층을 정리한다.

실제 검증 화면

  1. VyOS에서 서비스망과 DB망 interface, connected route, 내부 NO-NAT와 외부 masquerade를 확인한 화면이다.
  2. Primary가 DB 전용 주소에서 3306을 listen하고 GTID ON, writable 상태로 실행되는 것을 확인했다.
  3. Replica의 IO·SQL thread가 모두 Yes였고 lag는 0초였다. read_onlysuper_read_only도 함께 확인했다.
    08-vyos-network-nat.png

06-db1-primary.png

07-db2-replica.png