je개발 회고

[ E2E ] Docker 컨테이너에서 외부 도메인(동일 IDC) 접근 불가 문제 해결

Je-chan 2025. 6. 24. 12:51

🚨 문제 상황 개요

오늘은 E2E 테스트 환경을 구축하면서 겪었던 까다로운 네트워크 문제와 그 해결 과정을 공유하려고 합니다.

 

IDC 센터의 사설 IP 환경에서 Docker 컨테이너를 통해 E2E 테스트를 실행하던 중, 외부 도메인으로 접근이 되지 않는 문제가 발생했습니다. 겉보기에는 단순해 보이는 문제였지만, 네트워크 아키텍처의 근본적인 이해가 필요한 복합적인 문제였습니다.

 

발생 환경

  • 서버: IDC 센터 내 사설 IP 환경 (192.168.x.x)
  • 컨테이너: Docker 기반 E2E 테스트 환경
  • 네트워크: NAT 방화벽 뒤의 사설망
  • 접근 대상: 외부 도메인을 통한 같은 서버 접근

 

문제 증상

# Docker 컨테이너 내부에서 실행
curl https://service-dev.example.com
# ❌ Connection timeout 발생
# ❌ 네트워크 연결 실패

 

기대 동작 vs 실제 동작

구분
기대 동작
실제 동작
DNS 해석
service-dev.example.com → 211.xxx.xxx.xxx ✅
정상 해석됨 ✅
네트워크 접근
공인 IP로 접근 후 내부 서버 연결
방화벽 차단 ❌
최종 결과
웹페이지 정상 로딩
연결 타임아웃 ❌

🔍 근본 원인 분석

1. 네트워크 아키텍처의 이해

IDC 센터의 일반적인 네트워크 구조를 이해하는 것이 문제 해결의 핵심이었습니다.

🏗️ IDC 네트워크 아키텍처

🌐
인터넷
🛡️
방화벽
NAT 라우터
🏢
공인 IP
211.xxx.xxx.xxx
🖥️
물리 서버
192.168.x.x
🐳
Docker 컨테이너
E2E 테스트 환경
문제 지점: 컨테이너에서 공인 IP로 요청 시 방화벽에서 차단

 

2. 방화벽 정책의 작동 원리

 

문제의 핵심은 Hairpin NAT 또는 NAT Loopback 제한이었습니다.

 

🔄 요청 흐름 분석

1
컨테이너 요청
192.168.0.13 → service-dev.example.com
2
DNS 해석
service-dev.example.com → 211.xxx.xxx.xxx
3
공인 IP 접근 시도
내부에서 외부 IP로 접근
방화벽 차단
같은 네트워크에서 외부 IP 접근 차단

 

3. 기술적 배경

 

이 문제는 다음과 같은 기술적 특성들로 인해 발생합니다:

 

NAT (Network Address Translation) 한계

  • Hairpin NAT 또는 NAT Loopback 문제
  • 내부 네트워크에서 자신의 공인 IP로 접근할 때 발생
  • 대부분의 기업용 방화벽에서 보안상 이를 차단

Docker 네트워킹 특성

  • Docker 컨테이너는 기본적으로 bridge 네트워크 사용
  • 호스트와 별도의 네트워크 네임스페이스를 가짐
  • /etc/hosts 파일이 호스트와 독립적으로 관리됨

🌐 네트워크 시나리오 비교

정상 vs 문제 시나리오 분석

✅ 정상적인 외부 접근

외부 PC
인터넷
방화벽
내부 서버
정상 작동

❌ 문제가 되는 내부 접근

컨테이너
호스트
방화벽
차단됨
연결 실패

🛠️ 해결 방법

문제를 해결하기 위해 두 가지 주요 방법을 사용할 수 있습니다.

방법 1: Docker --add-host 옵션 사용 

같은 서버에 있는 도메인의 경우 가장 효과적인 방법입니다.

docker run --name e2e-container \
  --add-host service-dev.example.com:192.168.0.13 \
  -p 8093:8093 \
  my-app:latest

 

💡 --add-host 방식의 장단점

✅ 장점
  • 가장 간단하고 확실한 방법
  • 방화벽 정책 변경 불필요
  • 다른 서비스에 영향 없음
  • 컨테이너 격리 유지
❌ 단점
  • 컨테이너마다 개별 설정 필요
  • IP 변경시 수동 업데이트 필요
  • 설정 파일에 IP 하드코딩

 

방법 2: 라우팅 테이블 설정

다른 서버에 있는 도메인의 경우 사용하는 방법입니다.

Step 1: 도메인 정보 확인

# 공인 IP 확인
ping service-dev.example.com
# 또는
nslookup service-dev.example.com

# 결과: 211.xxx.xxx.xxx
# 실제 내부 IP 확인 (관리자 정보 또는 내부 DNS)
# service-dev.example.com → 192.168.1.13

Step 2: 라우팅 규칙 추가

# 특정 공인 IP를 내부 IP로 라우팅
sudo route add -host 211.xxx.xxx.xxx gw 192.168.1.13

# 또는 ip route 명령어 사용
sudo ip route add 211.xxx.xxx.xxx via 192.168.1.13

Step 3: 설정 확인 및 테스트

# 라우팅 테이블 확인
route -n
# 또는
ip route show

# 연결 테스트
curl -I https://service-dev.example.com

💡 실제 적용 예시

시나리오 A: 같은 서버 (Self-hosted)

🏠 동일 서버 시나리오

환경 정보
• 클라이언트 서버: 192.168.0.13
• 대상 서버: 192.168.0.13 (동일!)
• 도메인: service-dev.example.com → 211.xxx.xxx.xxx

 

Docker Compose 예시:

# docker-compose.yml
version: '3.8'
services:
  e2e-test:
    image: my-e2e-app
    extra_hosts:
      - "service-dev.example.com:192.168.0.13"
    ports:
      - "8093:8093"

 

Docker run 예시:

docker run -d \
  --name e2e-service-container \
  --add-host service-dev.example.com:192.168.0.13 \
  -p 8093:8093 \
  e2e-service-dashboard

 

시나리오 B: 다른 서버

🌐 다른 서버 시나리오

환경 정보
• 클라이언트 서버: 192.168.0.13
• 대상 서버: 192.168.1.13
• 도메인: other-service.example.com → 211.xxx.xxx.xxx

 

해결 단계:

# 1. 라우팅 규칙 추가
sudo route add -host 211.xxx.xxx.xxx gw 192.168.1.13

# 2. 일반적인 Docker 실행 (특별한 설정 불필요)
docker run -d \
  --name e2e-other-container \
  -p 8094:8093 \
  e2e-other-dashboard

# 3. 영구 설정 (재부팅 후에도 유지)
echo "211.xxx.xxx.xxx via 192.168.1.13" >> /etc/rc.local

 

✅ 검증 및 테스트

1. 단계별 검증 방법

🔍 검증 체크리스트
DNS 해석 테스트
nslookup service-dev.example.com
네트워크 연결 테스트
curl -I https://service-dev.example.com
라우팅 확인
route -n | grep 211.xxx
Docker 설정 확인
docker exec container cat /etc/hosts

 

2. 상세 테스트 명령어

DNS 해석 테스트:

# 호스트에서 테스트
nslookup service-dev.example.com

# 컨테이너 내부에서 테스트
docker exec -it container_name nslookup service-dev.example.com

 

네트워크 연결 테스트:

# 기본 연결 테스트
curl -I https://service-dev.example.com

# 타임아웃 설정으로 테스트
curl --connect-timeout 10 --max-time 30 https://service-dev.example.com

# 자세한 연결 정보
curl -w "@-" -o /dev/null -s https://service-dev.example.com <<'EOF'
     namelookup:  %{time_namelookup}s\n
        connect:  %{time_connect}s\n
     appconnect:  %{time_appconnect}s\n
          total:  %{time_total}s\n
     http_code:  %{http_code}\n
EOF

 

라우팅 및 Docker 네트워크 확인:

# 현재 라우팅 테이블
route -n | grep 211.xxx

# 특정 IP로의 경로 추적
traceroute 211.xxx.xxx.xxx

# 컨테이너 hosts 파일 확인
docker exec container_name cat /etc/hosts

# 컨테이너 네트워크 설정 확인
docker exec container_name ip addr show

 

📚 참고 자료 및 FAQ

자주 묻는 질문들

❓ 자주 묻는 질문 (FAQ)

Q1: --add-host와 /etc/hosts 직접 수정의 차이점은?
--add-host는 컨테이너 시작 시 설정되어 재시작 후에도 유지되지만, /etc/hosts 직접 수정은 컨테이너 재시작 시 초기화됩니다.
Q2: 라우팅 설정이 효과가 없을 때는?
권한 확인, 문법 확인, 네트워크 재시작, 방화벽 규칙 확인, DNS 캐시 초기화를 순서대로 점검해보세요.
Q3: 이 설정이 다른 서비스에 영향을 주나요?
Docker --add-host는 해당 컨테이너에만 적용되어 다른 서비스에 영향이 없지만, 라우팅 테이블 수정은 호스트 전체에 적용됩니다.

 

여러 도메인을 한번에 설정하는 방법

# Docker Compose에서 여러 호스트 설정
version: '3.8'
services:
  e2e-test:
    image: my-app
    extra_hosts:
      - "service-dev.example.com:192.168.0.13"
      - "api-dev.example.com:192.168.0.14"
      - "auth-dev.example.com:192.168.0.15"

# Docker run에서 여러 호스트 설정
docker run \
  --add-host service-dev.example.com:192.168.0.13 \
  --add-host api-dev.example.com:192.168.0.14 \
  --add-host auth-dev.example.com:192.168.0.15 \
  my-app

🎯 마무리

💡 핵심 포인트 정리

이번 문제를 통해 네트워크 아키텍처에 대한 깊은 이해의 중요성을 다시 한번 깨달았습니다. 겉보기에는 단순한 DNS나 방화벽 문제로 보였지만, 실제로는 NAT 환경에서의 Hairpin 연결 제한이라는 근본적인 네트워크 개념이 관련되어 있었습니다.

🔍
원인 분석
네트워크 구조 이해
🛠️
실용적 해결
Docker --add-host
체계적 검증
단계별 테스트

 

이번 문제 해결 과정에서 얻은 주요 교훈들입니다:

 

학습 포인트

  1. 네트워크 아키텍처 이해의 중요성: 단순히 증상만 보지 말고 전체 네트워크 구조를 파악해야 합니다.
  2. Docker 네트워킹의 특성: 컨테이너는 호스트와 별도의 네트워크 환경을 가지며, 이를 고려한 설정이 필요합니다.
  3. 단계적 접근법: 문제를 DNS → 네트워크 → 방화벽 → Docker 순으로 체계적으로 분석하는 것이 효과적입니다.
  4. 환경별 맞춤 해결책: 같은 서버인지, 다른 서버인지에 따라 최적의 해결 방법이 다릅니다.

 

실무 적용 시 주의사항

⚠️ 실무 적용 시 고려사항

보안 관점
• 방화벽 정책 변경 시 보안팀과 협의
• 라우팅 변경이 다른 서비스에 미치는 영향 검토
• 네트워크 변경 시 충분한 테스트 수행
운영 관점
• 설정 변경 사항 문서화
• 자동화된 배포 스크립트에 반영
• 모니터링 및 알림 시스템 구축

 

비슷한 네트워크 환경에서 Docker를 운영하시는 분들에게 도움이 되었으면 좋겠습니다. 특히 IDC나 온프레미스 환경에서 컨테이너를 운영하시는 분들께는 반드시 고려해야 할 사항들이라고 생각합니다.

 

궁금한 점이나 유사한 문제를 겪고 계시다면 언제든 댓글로 남겨주세요. 함께 문제를 해결해 나가는 것이 개발 커뮤니티의 가장 큰 장점이라고 생각합니다!