infra
Platform

모듈 맵

[Infra Ops] WEB/WAS/DB 망 분리와 이중화 구조 설계

0 / 52 완료

펼치기
0 / 52 완료0%

인프라 운영 & SRE · 10 / 52

[Infra Ops] WEB/WAS/DB 망 분리와 이중화 구조 설계

DMZ, 내부망, DB망 분리 원칙, Active/Standby vs Active/Active 이중화, VIP 개념까지 — 실제 온프레미스 서비스 인프라 구조를 이해합니다

🚨INCIDENT ALERT
HIGH

새벽 2시, 페이지가 완전히 멈췄다는 알림이 옵니다. 확인해보니 WAS 서버 한 대가 다운됐고, keepalived가 Standby 서버로 Failover를 수행해 VIP가 이동됐습니다. 그런데 서비스가 살아나지 않습니다. VIP는 분명 신규 Active로 넘어갔는데 — DB 연결이 안 됩니다.

"Standby 서버에서 DB 방화벽 오픈이 빠졌네요." 선임이 말합니다. 망 분리 구조를 모르면 이 문장이 무슨 뜻인지조차 파악할 수 없습니다.

이번 챕터에서 배울 것
  • 1DMZ, 내부망, DB망의 구분 기준과 각 구간에 위치하는 서버를 설명할 수 있다
  • 2Active/Standby와 Active/Active 이중화의 차이와 선택 기준을 설명할 수 있다
  • 3VIP가 서비스 연속성을 유지하는 원리를 설명할 수 있다
  • 4실제 3-Tier 구성에서 구간별 방화벽 허용 정책을 파악할 수 있다
  • 5WAS→DB 연결 실패 시 방화벽 오픈 여부를 점검하는 절차를 수행할 수 있다

망 분리 구조와 보안 원칙

외부에서 들어오는 트래픽이 DB 서버까지 직접 닿는다면 어떻게 될까요. 공격자가 Web 서버 한 대를 장악하는 순간 DB의 모든 데이터가 노출됩니다. 망 분리는 이 경로를 물리적·논리적으로 끊는 것입니다.

💡개념

DMZ / 내부망 / DB망 — 3단 방어선

망 분리는 보안 요구사항처럼 느껴지지만, 실제로는 침해 범위를 제한하기 위한 인프라 설계 원칙입니다. Web 서버가 뚫렸을 때 DB까지 직선 경로가 있다면 단 한 번의 침해로 전체 데이터가 노출되지만, 구간 사이에 방화벽이 있으면 피해가 그 계층에서 멈춥니다. 이 구조를 이해해야 신규 서버 구성 요청을 받을 때 어느 구간에 어떤 방화벽 정책을 요청해야 하는지 판단할 수 있습니다.

WEB/WAS/DB 망 분리 구조 — DMZ, 내부망, DB망확대

Web 서버가 해킹됐는데 DB 서버까지 접근이 됐다는 보고가 왔습니다. 망 분리 없이 모든 서버가 같은 네트워크에 있었기 때문입니다. 이 한 번의 침해로 고객 전체 데이터가 노출됐습니다. DMZ에 있는 Web 서버가 뚫리더라도 내부망 WAS나 DB망까지 이동할 수 없도록 구간을 나누는 것이 피해를 한 계층으로 제한하는 방법입니다.

서비스 인프라는 외부 노출 정도에 따라 세 구간으로 분리합니다.

DMZ (Demilitarized Zone, 비무장지대)

인터넷에서 직접 접근이 허용되는 구간입니다. Nginx, Apache 같은 Web 서버가 위치합니다. 방화벽은 외부에서 80/443 포트만 허용하고 나머지는 전부 차단합니다. DMZ 서버가 뚫려도 내부망에는 바로 접근할 수 없도록 구간 간 방화벽이 존재합니다.

내부망 (Internal Network)

WAS 서버가 위치하는 구간입니다. 외부 인터넷에서 직접 접근할 수 없습니다. Web 서버(DMZ)가 8080 포트로 WAS에 요청을 전달하는 것만 허용합니다. 개발자나 운영자의 직접 접근도 Bastion Host(점프 서버)를 경유해야 합니다.

DB망 (Database Network)

가장 폐쇄적인 구간입니다. WAS 서버에서 DB 포트(MySQL 3306, PostgreSQL 5432, Oracle 1521)로의 접근만 허용합니다. Web 서버에서 DB망으로의 직접 통신은 차단됩니다.

구간별 방화벽 허용 정책 예시:

출발지도착지허용 포트차단
인터넷 (0.0.0.0/0)DMZ (Web)80, 443나머지 전체
DMZ (Web)내부망 (WAS)8080나머지 전체
내부망 (WAS)DB망 (DB)3306 (MySQL)나머지 전체
인터넷내부망전체 차단
인터넷DB망전체 차단
DMZDB망전체 차단

실무 원칙 — 포트 오픈 최소화:

방화벽 오픈 요청이 들어오면 "왜 필요한가?"를 반드시 확인합니다. "일단 전체 오픈해주세요"는 절대 수용하지 않습니다. 실제 통신에 필요한 IP와 포트만, 방향을 지정해서 오픈합니다.

방화벽 오픈 요청 양식 예시:
- 출발지 IP: 10.10.1.11 (WAS01)
- 목적지 IP: 10.10.3.10 (DB Primary)
- 포트: TCP 3306
- 목적: WAS → DB MySQL 연결
- 신청자: 홍길동 (개발팀)
- 승인자: 팀장 김철수
💡개념

요청이 Web·WAS·DB 3계층을 완주하고 돌아오는 법 — 6단계

앞 절에서 계층을 왜 나누는지(심층 방어)를 봤다면, 이제 요청 하나가 그 세 계층을 어떻게 통과하고 역순으로 돌아오는지를 봅니다. 이 경로를 단계로 쥐고 있으면, 장애 알림이 왔을 때 "어느 계층 경계에서 막혔나"를 바로 좁힐 수 있습니다 — 경계마다 방화벽·타임아웃·커넥션 풀이라는 서로 다른 조절 지점이 있기 때문입니다.

TEXT
[클라이언트]  주문 조회 요청 (HTTPS)
   │
   ① DMZ · Web(Nginx) 수신 — TLS 종단, 정적이면 여기서 직접 응답
   │
   ② 경계1 · Nginx → 내부망 WAS(8080) 중계   (구간 방화벽 8080 허용 필요)
   │
   ③ WAS · 스레드 배정 → 비즈니스 로직 → DB 커넥션 풀에서 커넥션 1개 획득
   │
   ④ 경계2 · WAS → DB망 DB(3306) 쿼리 전송   (구간 방화벽 3306 허용 필요)
   │
   ⑤ DB · 쿼리 실행 → 결과를 WAS로 반환 → 커넥션 풀에 반납
   │
   ⑥ WAS가 결과를 HTML/JSON으로 조립 → Nginx → 클라이언트
   ▼
[클라이언트]  200 OK   (역순: DB → WAS → Web → 클라이언트)

각 단계와 경계에서 무슨 일이 일어나고, 막히면 어떤 증상인가:

단계하는 일여기서 막히면
① Web 수신DMZ의 Nginx가 TLS 종단, 정적은 직접 처리Web 다운·80/443 미개방 → 클라이언트 타임아웃/refused
② 경계1 (Web→WAS)구간 방화벽 8080을 지나 WAS로 중계방화벽 미오픈·WAS 다운 → Nginx 502, connection timed out
③ WAS 로직·커넥션 획득스레드 배정 후 DB 커넥션 풀에서 커넥션 확보풀 고갈 → 스레드가 커넥션 대기로 묶임 → 지연 폭증·pool timeout, 처리량 급락
④ 경계2 (WAS→DB)구간 방화벽 3306을 지나 쿼리 전송방화벽 미오픈 → java.net.ConnectException: timed out · 계정/권한 → Access denied
⑤ DB 실행·반납쿼리 실행 후 커넥션을 풀에 되돌림슬로우 쿼리·락 → DB에서 지연 → 커넥션이 오래 안 반납됨 → ③의 고갈을 가속 → 504
⑥ 조립·응답결과를 응답 본문으로 만들어 역순 전달대용량 결과를 WAS 메모리에서 조립 → GC 압박·OOM 위험

정리하면 "연결 자체가 안 된다"는 대개 경계(②의 8080, ④의 3306)의 방화벽·기동 문제이고, nc -zv DB_IP 3306으로 그 경계부터 확인합니다. 반면 "연결은 되는데 느리다·일부만 실패한다"는 대개 ③↔⑤의 커넥션 풀-DB 축입니다 — 슬로우 쿼리가 커넥션을 오래 물면(⑤) 풀이 마르고(③), 스레드가 대기하다 504로 번집니다. 계층을 나눈 이유가 여기서 드러납니다: 경계마다 독립된 방화벽·타임아웃·풀 크기라는 손잡이가 생겨, 장애를 한 계층에 가두고 계층별로 스케일·격리할 수 있습니다.

네트워크 분리 구조 시뮬레이션 점검
1

1. 현재 서버 IP 및 네트워크 인터페이스 확인

ip addr show | grep 'inet ' | grep -v '127.0.0.1'
2

2. DB 포트 외부 노출 여부 확인

ss -tlnp | grep -E ':3306|:5432|:1521' && echo '---DB 포트 바인딩 확인---'
3

3. 서버 간 연결 가능 여부 테스트

timeout 2 bash -c 'echo >/dev/tcp/127.0.0.1/3306' 2>/dev/null && echo 'DB 포트 열림' || echo 'DB 포트 닫힘 또는 없음'
🔍실행 후 확인할 것
  • ss -tlnp | grep 3306 으로 바인딩 IP 먼저 확인, 그 다음 ip addr show 로 서버 IP 대역 확인 — 바인딩 IP와 서버 네트워크 구성을 함께 봐야 외부 접근 가능 여부를 정확히 판단
  • 바인딩 기준: 127.0.0.1=로컬 전용(외부 DB 분리 환경에서는 잘못된 설정), 10.10.3.X=내부 DB망만 허용(정상), 0.0.0.0=모든 IP 허용(즉시 조치) — DB 서버는 WAS망 IP로만 바인딩이 표준
  • DB 포트가 내부망 IP로 바인딩됐는데 WAS에서 연결 실패하면 → 방화벽이 WAS→DB 구간을 차단 중 — nc -zv DB_IP 3306 으로 포트 도달 여부 확인 후 "succeeded" 없으면 방화벽 오픈 요청

이중화 구조 패턴

서버 한 대가 죽었을 때 서비스가 멈추면 안 됩니다. 이중화는 이 상황을 버티기 위한 설계입니다. 하지만 방식에 따라 비용과 복잡도, 세션 관리 방법이 달라집니다.

💡개념

Active/Standby vs Active/Active

이중화 방식을 잘못 선택하면 "서버가 두 대인데 한 대가 고장나자 서비스가 전체 다운됐다"는 역설적인 상황이 생깁니다. 세션을 로컬 메모리에 두는 Stateful 서비스를 Active/Active로 구성하면 로드밸런서가 다른 서버로 요청을 보내는 순간 세션이 사라지고, 쓰기 일관성이 중요한 DB를 Active/Active로 묶으면 Split-Brain으로 데이터가 분기됩니다. 어떤 방식이 맞는지는 서비스가 Stateful인지 Stateless인지, 쓰기 충돌이 발생할 수 있는지에 따라 달라집니다.

Active/Standby vs Active/Active 이중화 방식 비교확대

새벽에 DB 서버 한 대가 다운됐는데 서비스가 멈췄습니다. Standby 서버가 있었지만 systemctl enable을 빠뜨려 자동으로 뜨지 않았기 때문입니다. 이중화 구성 자체보다 "장애 시 어떤 흐름으로 전환되는가"를 이해하지 못하면, 이중화를 만들어놓고도 작동하지 않는 상황이 됩니다.

Active/Standby (장애 대기 방식)

평시에는 Active 서버 한 대가 모든 트래픽을 처리합니다. Standby 서버는 대기 상태로 Health Check만 수행합니다. Active 서버에 장애가 발생하면 VIP가 Standby로 이동(Failover)하고 Standby가 서비스를 인계받습니다.

  • 장점: 구성 단순, 세션 동기화 부담 적음
  • 단점: Standby 서버 리소스 낭비, Failover 중 수십 초의 순단 발생 가능
  • 적합한 상황: DB 서버(Primary/Standby), 스케줄러, 상태 유지가 중요한 서비스

Active/Active (부하 분산 방식)

두 대 모두 실제 트래픽을 나눠서 처리합니다. 한 대가 장애 나면 남은 서버가 전체 트래픽을 받습니다.

  • 장점: 리소스 효율적, 평시 처리 용량 2배
  • 단점: 세션 동기화 필요(Stateful 서비스), 구성 복잡도 상승
  • 적합한 상황: Stateless한 Web 서버, 로드밸런서 뒤의 WAS

세션 동기화 — Stateful vs Stateless:

WAS가 로그인 세션을 메모리에 저장하면(Stateful), 로드밸런서가 요청을 다른 WAS로 보낼 때 "로그아웃됐다"는 현상이 발생합니다. 이를 해결하는 방법은 두 가지입니다.

  1. 세션 외부화: Redis 같은 중앙 세션 저장소에 세션을 저장 → Stateless WAS 구현
  2. Sticky Session(세션 고정): 같은 사용자의 요청은 항상 같은 WAS로 보내도록 로드밸런서 설정

Split-Brain 문제:

Active/Standby 구성에서 두 노드 간 Heartbeat 통신이 끊기면, 두 노드가 모두 "내가 Active"라고 판단하는 Split-brain이 발생합니다. DB 같은 쓰기 서비스에서 Split-brain이 발생하면 데이터가 분기됩니다. STONITH(Shoot The Other Node In The Head) — 즉, 상대 노드를 강제로 끄는 메커니즘 — 로 이를 방지합니다.

Active/Standby vs Active/Active 선택 기준
DB 서버 — 쓰기 일관성이 중요한 경우Active/Standby (Primary/Standby 복제)
Web 서버 — 정적 파일 처리, StatelessActive/Active (로드밸런서로 분산)
WAS 서버 — 세션을 Redis에 외부화한 경우Active/Active (세션 동기화 문제 없음)
WAS 서버 — 로컬 메모리에 세션 저장 중Active/Active + Sticky Session 또는 Active/Standby
배치 스케줄러 — 중복 실행 절대 불가Active/Standby (한 대만 실행 보장)

VIP와 로드밸런서

IP 주소는 서버에 종속됩니다. 서버가 교체되면 IP가 바뀌고, 클라이언트는 새 IP를 알아야 합니다. VIP는 이 의존성을 끊는 방법입니다.

💡개념

VIP(Virtual IP)와 keepalived

Failover가 됐는데도 서비스가 살아나지 않는다면 VIP 개념을 모른 채 설정한 경우가 대부분입니다. 실제 서버 IP가 아닌 VIP로 접속하도록 구성해두면 Active 서버가 교체될 때 클라이언트나 상위 로드밸런서는 아무것도 바꾸지 않아도 됩니다. VIP가 없으면 DB 접속 문자열에 Primary 서버의 실제 IP를 하드코딩하게 되고, Failover 직후 새 Primary로 자동 전환이 되지 않아 수동 개입이 필요한 상황이 반복됩니다.

VIP Failover — keepalived VRRP로 서버 장애 시 투명한 IP 전환확대

Failover가 됐는데 서비스가 살아나지 않았습니다. 확인해보니 WAS 설정에 DB 서버 IP가 실제 IP로 하드코딩되어 있었고, Failover 후 새로운 Primary IP로 자동 전환이 안 됐습니다. VIP를 쓰면 실제 서버가 바뀌어도 접속 IP는 변하지 않습니다. 이 차이를 모르면 이중화 구성이 완성돼 있어도 장애 복구가 안 됩니다.

VIP란:

실제 서버 NIC에 할당된 IP가 아닌, 소프트웨어로 관리되는 가상 IP입니다. 클라이언트나 로드밸런서는 항상 VIP로 접속합니다. Active 서버가 바뀌면 VIP만 새 Active 서버로 이동합니다.

keepalived와 VRRP:

Linux 환경에서 VIP를 구현하는 가장 일반적인 방법입니다. keepalived는 VRRP(Virtual Router Redundancy Protocol) 프로토콜을 이용해 두 서버가 Heartbeat를 주고받으며 Master(Active)를 결정합니다.

서버 터미널
# keepalived 상태 확인
systemctl status keepalived

# 현재 VIP가 어느 인터페이스에 붙어 있는지 확인
ip addr show | grep -A2 'eth0'
# Active 서버: inet 10.10.1.100/24 scope global secondary eth0:vip
# Standby 서버: eth0:vip 항목 없음

Failover 발생 시: Active 서버에서 keepalived 응답이 끊기면 Standby가 자신을 Master로 선언하고 VIP를 자신의 인터페이스로 가져옵니다. 이 전환은 보통 1~3초 내에 완료됩니다.

L4 로드밸런서 vs L7 로드밸런서:

구분L4 LBL7 LB
판단 기준IP + PortHTTP URL, 헤더, 쿠키
속도빠름 (패킷 레벨)상대적으로 느림 (패킷 조립 후 분석)
주요 사용처TCP 기반 분산 (DB, WAS)HTTP 기반 분산 (Web, API)
Sticky SessionIP Hash 방식쿠키 기반 가능
대표 솔루션L4 Switch, HAProxy (TCP)Nginx, HAProxy (HTTP), AWS ALB

Health Check:

로드밸런서는 주기적으로 서버에 요청을 보내 응답 여부를 확인합니다. 일정 횟수 이상 실패하면 해당 서버를 분산 대상에서 제외합니다.

로컬 터미널
# Nginx upstream health check 설정 예시
upstream was_pool {
    server 10.10.2.10:8080;
    server 10.10.2.11:8080;
}
# HAProxy에서는 'check' 옵션으로 Health Check 활성화
# server was01 10.10.2.10:8080 check inter 3s fall 3 rise 2

실제 3-Tier 구성 예시

이론이 아닌 실제 서버 목록과 IP, 포트, 방화벽 정책을 전부 나열해봅니다.

💡개념

웹 2대 + WAS 4대 + DB 2대 구성

이론으로만 배운 3-Tier 구조는 실제 서버 목록과 IP 대역을 눈으로 보기 전까지 추상적으로 느껴집니다. was02가 다운됐을 때 나머지 3대에 어떻게 트래픽이 재분배되는지, db01 장애 시 db-vip가 db02로 이동하는 흐름이 실제로 어떻게 동작하는지를 구체적인 숫자와 함께 보면 운영 중 받는 장애 알림과 판단이 실제 서버 구성과 연결됩니다. 특히 어느 서버의 방화벽이 열려있어야 하는지를 파악하는 것이 운영 엔지니어의 핵심 역량입니다.

실제 3-Tier 서버 구성 — 구간별 IP와 포트 배치확대

이론과 달리 실제 서버 목록과 IP를 보면 이해가 달라집니다. was02가 다운됐을 때 나머지 3대에 트래픽이 어떻게 분산되는지, db01 장애 시 db-vip가 db02로 이동하는 흐름이 어떻게 되는지 — 구체적인 숫자와 함께 보면 추상적인 이중화 개념이 실제 운영 판단과 연결됩니다.

서버 목록:

서버명역할IP구간OS/미들웨어
web01-prdWeb (Active)10.10.1.11DMZRHEL 8 / Nginx
web02-prdWeb (Active)10.10.1.12DMZRHEL 8 / Nginx
web-vipWeb VIP10.10.1.100DMZ— (가상)
was01-prdWAS10.10.2.11내부망RHEL 8 / Tomcat
was02-prdWAS10.10.2.12내부망RHEL 8 / Tomcat
was03-prdWAS10.10.2.13내부망RHEL 8 / Tomcat
was04-prdWAS10.10.2.14내부망RHEL 8 / Tomcat
db01-prdDB Primary (Active)10.10.3.11DB망RHEL 8 / MySQL
db02-prdDB Standby10.10.3.12DB망RHEL 8 / MySQL
db-vipDB VIP10.10.3.100DB망— (가상)

트래픽 경로:

인터넷 클라이언트
  → web-vip (10.10.1.100:443)
  → Nginx (web01 또는 web02, Active/Active)
  → WAS 로드밸런서 (Nginx upstream, 4대 분산)
  → WAS 01~04 (Active/Active)
  → db-vip (10.10.3.100:3306)
  → MySQL Primary (db01, Active/Standby)

장애 시나리오별 서비스 영향:

장애 서버VIP 이동서비스 영향
web01 다운web-vip는 web02가 흡수순단 없음 (Active/Active)
was02 다운LB가 was02 제외나머지 3대로 분산, 처리량 감소
db01 다운db-vip → db02로 이동Failover 중 수십 초 순단
db-vip 미이동서비스 전체 중단 (DB 연결 불가)

서버 네트워크 구성 확인

구간별 네트워크 통신 상태 점검
1

서버 IP 및 네트워크 인터페이스 확인

ip addr show
2

라우팅 테이블 확인

ip route show
3

WAS → DB 포트 연결 확인 (방화벽 점검)

nc -zv 10.10.3.100 3306
4

Web → WAS Health Check 확인

curl -o /dev/null -s -w "%{http_code}" http://10.10.2.11:8080/health
5

keepalived 상태와 VIP 위치 확인

systemctl status keepalived && ip addr show | grep secondary
🔍실행 후 확인할 것
  • ip addr show 로 VIP 보유 여부 먼저 확인, 그 다음 nc -zv DB_IP 3306 으로 DB 포트 도달 확인, 마지막으로 curl 로 서비스 응답 확인 — 이 순서로 네트워크 레이어를 단계별 검증
  • nc 결과 기준: succeeded=방화벽 오픈 정상, Connection refused=방화벽은 열렸지만 DB 프로세스 미기동, 타임아웃=방화벽 차단 — Connection refused와 타임아웃을 구분해야 조치 방향이 달라짐
  • nc succeeded인데 WAS에서 DB 연결 실패하면 → DB 계정 권한 문제 또는 DB가 127.0.0.1 바인딩 중 — mysql -h DB_IP -u appuser -p 로 직접 연결 테스트 후 "Access denied" 이면 계정/권한 확인

트러블슈팅

심화 — keepalived가 Master를 정하는 방식, 그리고 Split-Brain

💡개념

심화: VRRP는 '상대가 들리는가'로 생사를 판정한다 — '안 들린다'와 '죽었다'의 간극

앞에서 keepalived가 Heartbeat로 Master를 정하고, Heartbeat가 끊기면 Split-Brain이 난다고 했습니다. 그 판정 방식을 한 단계 들여다보면 왜 Split-Brain이 이중화의 가장 위험한 함정인지가 보입니다.

  • VRRP의 판정 방식: 각 노드는 우선순위(priority)를 갖고, Master는 주기적으로 VRRP advertisement를 멀티캐스트(224.0.0.18, IP 프로토콜 112)로 보냅니다. Backup은 이 광고를 '들으며' Master가 살아있다고 믿습니다. 광고가 일정 시간(대개 광고 주기의 3배) 안 들리면 Backup은 자신을 Master로 승격하고 VIP를 가져간 뒤, GARP(gratuitous ARP)를 뿌려 스위치의 MAC 테이블을 새 위치로 갱신합니다.
  • 핵심 함정: 이 판정은 오로지 '광고가 들리는가'에만 의존합니다. 그런데 '광고가 안 들린다'는 두 가지를 구분하지 못합니다 — 상대가 정말 죽은 것과, 둘 사이 하트비트 경로만 끊긴 것. 후자라면 두 노드 모두 상대가 죽었다고 믿고 둘 다 Master가 되어 VIP를 동시에 보유합니다. 이것이 Split-Brain입니다. '나는 네가 안 들려' 는 '너는 죽었다' 와 결코 같지 않습니다.
  • 그래서 설계에서 지켜야 할 것: ① 하트비트 경로를 서비스 트래픽 경로와 물리적으로 분리·이중화해 한 링크가 끊겨도 다른 경로로 서로를 확인. ② 노드 사이의 방화벽·보안그룹이 VRRP(멀티캐스트·프로토콜 112, 또는 unicast_peer 설정 시 그 경로)를 반드시 허용. ③ virtual_router_id를 같은 세그먼트 안에서 유일하게 — 다른 keepalived 쌍과 겹치면 서로를 같은 그룹으로 오인. ④ 쓰기 DB에는 STONITH/펜싱을 걸어 Split-Brain 순간 한 노드를 강제로 격리해 진짜 하나만 살아남게 함.

정리하면 이중화에서 '서비스 경로'만큼 중요한 것이 '하트비트 경로'입니다. 서비스는 멀쩡한데 노드끼리 서로를 확인하는 길만 막혀도, 이중화가 오히려 장애의 원인이 됩니다.

상황: keepalived로 db-vip를 이중화했는데, 애플리케이션이 db-vip로 붙으면 연결이 '됐다 안 됐다'를 반복합니다. 두 DB 노드에 각각 들어가 ip addr를 찍어 보면 둘 다 VIP를 들고 있고, journalctl -u keepalived에도 양쪽 모두 MASTER 진입 로그가 찍혀 있습니다. 서버는 둘 다 멀쩡히 살아 있습니다.

원인: Split-Brain입니다. 두 노드 사이의 VRRP 광고가 서로에게 닿지 못하고 있습니다. 방화벽·보안그룹이 노드 간 VRRP(멀티캐스트 224.0.0.18·프로토콜 112, 또는 unicast VRRP 경로)를 막았거나, 최근 네트워크 정책 변경으로 하트비트 경로만 끊긴 상황입니다. 서로 상대의 광고를 못 들으니 둘 다 '상대가 죽었다'고 판단해 둘 다 Master가 되고, 둘 다 GARP를 뿌려 스위치의 MAC 매핑이 요청마다 오락가락합니다 — 그래서 '됐다 안 됐다'가 됩니다.

진단: 각 노드에서 journalctl -u keepalived | grep -i state로 양쪽이 동시에 MASTER인지 확인합니다. 그다음 tcpdump -ni eth0 proto 112로 상대 노드의 VRRP 광고가 실제로 도착하는지 봅니다 — 자기 것만 보이고 상대 것이 안 보이면 하트비트 경로 단절이 확정입니다. virtual_router_id가 두 노드에서 같은지, 같은 세그먼트의 다른 keepalived 쌍과 겹치지 않는지도 함께 확인합니다.

해결: 노드 간 VRRP 경로(방화벽·보안그룹의 멀티캐스트·프로토콜 112 또는 지정 unicast)를 다시 열어 하트비트를 복구하면 우선순위 낮은 쪽이 Backup으로 내려갑니다. 근본 예방은 하트비트 링크를 서비스망과 분리·이중화하고, 쓰기 DB에는 STONITH/펜싱을 걸어 Split-Brain 시 한 노드를 강제 격리하는 것입니다(L4/L7 로드밸런서와 VIP 기반 이중화 구성). virtual_router_id 중복이 있었다면 그것부터 유일하게 정리합니다.

💼
실무 맥락
현업 패턴

실제 업무에서 이 지식이 쓰이는 상황:

금융사 온프레미스 신규 프로젝트의 서버 구성 요청서가 들어왔습니다. "Web 서버 2대, WAS 서버 2대, DB 서버 1대 주세요."

인프라 엔지니어로서 이 요청을 검토할 때 확인해야 하는 것들입니다.

첫째, DB 서버가 1대면 이중화가 없습니다. DB 서버 장애 시 서비스 전체가 멈춥니다. "DB도 2대로 Active/Standby 구성이 필요합니다. 1대면 SLA를 맞출 수 없습니다."

둘째, 서버 구간이 명시되어 있지 않습니다. Web 서버는 DMZ, WAS는 내부망, DB는 DB망에 위치해야 하고, 각 구간 사이의 방화벽 오픈 정책을 사전에 확인해야 합니다.

셋째, WAS에서 DB로 접속할 때 DB VIP를 사용해야 합니다. 실제 DB 서버 IP(10.10.3.11)로 하드코딩하면 Failover 후 신규 Primary로 자동 전환되지 않습니다.

이 검토를 서버 발주 전에 해야 구성 완료 후 "DB 접속이 안 된다"는 장애를 막을 수 있습니다. 신규 구성 검토부터 방화벽 오픈 요청, Failover 테스트까지 — 인프라 엔지니어의 업무 흐름입니다.


명령어·단축키 빠른 참조

이 모듈에서 다룬 망 분리·이중화 점검 명령을 실전 옵션과 함께 모았습니다. "예" 열의 조합을 그대로 써도 됩니다.

명령어/단축키용도자주 쓰는 예
nc -zv 10.10.3.100 3306WAS→DB 포트 도달(방화벽) 확인succeeded=열림, 타임아웃=차단, refused=DB 미기동
telnet DB_IP 3306nc 없을 때 포트 확인 대안연결되면 방화벽 통과
ss -tlnp | grep -E ':3306|:5432'DB 포트 바인딩 IP 확인0.0.0.0=전체 노출(위험), 내부망 IP=정상
ip addr show서버 IP·VIP 보유 확인VIP는 secondary/:vip로 표시
ip route show타 구간 라우팅 경로 확인경로 없으면 통신 자체 불가
curl -o /dev/null -s -w "%{http_code}"Web→WAS Health Checkhttp://10.10.2.11:8080/health 200 확인
systemctl status keepalivedVIP 관리 데몬 상태자동기동: systemctl enable nginx tomcat
journalctl -u keepalived | grep -i stateMaster/Backup 전환 이력양쪽 다 MASTER면 Split-Brain
tcpdump -ni eth0 proto 112VRRP 광고 도달 확인자기 것만 보이면 하트비트 단절
mysql -h DB_IP -u appuser -pDB 직접 연결 테스트Access denied=계정·권한 문제

관련 모듈로 더 깊이:

다음 모듈에서는 실제 Nginx 설정에서 upstream WAS 풀을 구성하고 Health Check를 붙이는 구체적인 방법을 다룹니다.

지식 확인

퀴즈 — 8문제

Q1

DMZ 구간에 위치해야 하는 서버와 내부망에 위치해야 하는 서버를 구분하는 기준은?

Q2

Active/Standby와 Active/Active 이중화의 차이를 트래픽 처리 관점에서 설명한 것 중 옳은 것은?

Q3

VIP(Virtual IP)를 사용하는 핵심 이유는 무엇인가?

Q4

DB 서버가 별도 DB망에 있을 때, WAS에서 DB 연결이 실패했다면 가장 먼저 확인해야 할 것은?

Q5

DMZ·내부망·DB망으로 3단 분리하는 보안 원칙은?

Q6

Active/Standby 이중화에서 Active 서버가 죽으면 어떻게 서비스가 이어지나?

Q7

[심화] keepalived(VRRP) 이중화에서 두 노드 자체는 멀쩡한데 노드 사이의 하트비트 경로만 끊겼다. 이때 흔히 벌어지는 문제와 그 근본 이유로 옳은 것은?

Q8

[심화] db-vip로 접속하면 연결이 됐다 안 됐다 하고, 두 DB 노드 모두 ip addr에 VIP가 붙어 있으며 양쪽 keepalived 로그에 MASTER 진입이 찍혀 있다. 원인을 확정하기 위해 가장 유효한 확인은?

0 / 8 답변

🧪 실습으로 확인하기

Nginx 설치 및 기동

초급

Linux 서버에 Nginx를 설치하고 systemd 서비스로 등록하여 80포트에서 응답하는 상태까지 만든다.

40📋 3단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요