LLM WikiAccess-protected knowledge portal
← 스터디 홈
3편 · 약 22분

네트워킹과 볼륨 심화

컨테이너는 어떻게 서로 통신하고 데이터를 유지하는가

docker run -p 8080:80 nginx 한 줄로 웹 서버가 뜬다. 그런데 실제로 어떤 일이 일어나는가? 패킷이 호스트의 8080 포트에 도착해서 컨테이너 안의 80 포트에 전달되기까지, 그리고 데이터베이스 컨테이너가 재시작되어도 데이터가 사라지지 않으려면 어떤 구조가 필요한가.

Docker 네트워킹과 스토리지는 각각 Linux 네트워크 스택파일시스템 레이어를 추상화한 것이다. 추상화 아래를 이해하면 왜 특정 설정이 느린지, 왜 컨테이너 간 통신이 안 되는지를 직접 진단할 수 있다.

Docker 네트워크 드라이버 전체 지도

Docker는 교체 가능한 네트워크 드라이버 모델을 가진다. docker network create --driver <driver> 로 선택한다.

bridge (기본값) 가상 스위치(docker0)
veth 쌍 + iptables NAT
포트 매핑(-p) 필요
단일 호스트
host 호스트 네트워크 공유
격리 없음
NAT 오버헤드 없음
포트 충돌 주의
overlay VXLAN 캡슐화
다중 호스트 클러스터
Swarm / Kubernetes
암호화 옵션 있음
macvlan / ipvlan 물리 NIC에 직접 연결
컨테이너가 LAN IP 소유
NAT 없음
네트워크 장비 제어 필요
none 루프백만 존재
완전 네트워크 격리
배치 작업·보안 샌드박스
Docker 네트워크 드라이버 비교

bridge 네트워크: 내부 동작

가장 자주 쓰이는 bridge 드라이버가 어떻게 동작하는지 Linux 수준에서 추적한다.

docker0와 veth 쌍

Docker 데몬이 시작되면 Linux 커널에 docker0 라는 가상 브리지 인터페이스가 생성된다. 기본 서브넷은 172.17.0.0/16이고 게이트웨이는 172.17.0.1(docker0 자신)이다.

컨테이너가 시작될 때마다 Docker는 veth(virtual Ethernet) 쌍을 생성한다. veth 쌍은 양 끝이 연결된 가상 케이블이다. 한쪽 끝(eth0)은 컨테이너 네트워크 네임스페이스 안에 배치되고, 반대쪽 끝(vethXXXXXX)은 호스트의 docker0 브리지에 연결된다.

Linux Host Container A eth0 172.17.0.2 veth peer → Container B eth0 172.17.0.3 veth peer → docker0 (172.17.0.1) — Linux Bridge iptables MASQUERADE (POSTROUTING NAT) eth0 (Host NIC) -p 8080:80 → DNAT 규칙 추가 ↕ 인터넷 / 외부 네트워크
veth 쌍과 docker0 브리지 동작 원리

iptables와 NAT

docker run -p 8080:80 를 실행하면 Docker는 두 가지 iptables 규칙을 추가한다.

  1. DNAT (PREROUTING) — 호스트의 8080 포트로 들어오는 패킷을 컨테이너 IP:80으로 리다이렉트
  2. MASQUERADE (POSTROUTING) — 컨테이너가 외부와 통신할 때 출발지 IP를 호스트 IP로 변환(SNAT)

Docker는 자체 체인 DOCKERDOCKER-USER를 만들고, 트래픽이 이 체인을 먼저 거치도록 설정한다. 관리자가 추가하는 커스텀 규칙은 DOCKER-USER 체인에 넣어야 Docker 업데이트 후에도 유지된다.

# 실제 iptables 규칙 확인
iptables -t nat -L DOCKER -n -v
iptables -t filter -L DOCKER -n -v

사용자 정의 bridge vs 기본 bridge

docker network create my-net 으로 만든 사용자 정의 bridge는 기본 docker0 브리지와 다음 두 가지 점에서 다르다.

항목기본 bridge (docker0)사용자 정의 bridge
DNS 해석컨테이너 이름으로 불가컨테이너 이름·서비스명으로 가능
격리모든 컨테이너가 같은 L2 세그먼트네트워크 단위로 격리
연결/해제재시작 필요실행 중에도 가능

실무 권장: docker-composedocker run 모두 사용자 정의 bridge를 기본으로 사용하라. 컨테이너 이름으로 DNS 해석이 되기 때문에 하드코딩된 IP를 제거할 수 있다.

내장 DNS 서버 (127.0.0.11)

사용자 정의 bridge에서 컨테이너는 127.0.0.11 에 위치한 Docker 임베디드 DNS 서버를 사용한다. 이 DNS 서버는 같은 네트워크의 컨테이너 이름을 해석하고, 나머지 쿼리는 호스트의 /etc/resolv.conf에 설정된 DNS로 포워드한다.

# 컨테이너 안에서 확인
cat /etc/resolv.conf
# nameserver 127.0.0.11
# options ndots:0

overlay 네트워크: 다중 호스트 통신

Docker Swarm이나 Kubernetes 환경에서 컨테이너가 다른 호스트의 컨테이너와 통신해야 할 때 overlay 드라이버를 사용한다.

overlay는 VXLAN(Virtual eXtensible LAN) 을 사용해 L3 네트워크 위에 L2 네트워크를 에뮬레이트한다. 컨테이너 패킷이 UDP 포트 4789를 통해 VXLAN 헤더로 캡슐화되어 전달되며, 목적지 호스트에서 복원된다.

Container A (Host 1, 10.0.0.1) → [원본 패킷]
→ VXLAN 캡슐화 (UDP/4789) → Host 2 (10.0.0.2)
→ 디캡슐화 → Container B eth0

암호화 옵션(--opt encrypted)을 켜면 IPsec으로 VXLAN 트래픽을 보호한다.

macvlan과 ipvlan: 컨테이너에 LAN IP 직접 부여

두 드라이버 모두 컨테이너를 물리 NIC에 직접 연결해 NAT 없이 LAN 서브넷의 IP를 할당한다. 레거시 애플리케이션이 특정 IP를 필요로 하거나 네트워크 장비(VoIP, IoT)가 컨테이너와 직접 통신해야 할 때 적합하다.

항목macvlanipvlan L2
MAC 주소컨테이너마다 고유호스트와 공유
스위치 요구사항프로미스큐어스 모드불필요
호스트 ↔ 컨테이너 직접 통신불가 (커널 제한)불가
사용 사례MAC 기반 정책 필요 시포트 수 제한 환경

주의: macvlan/ipvlan 컨테이너는 호스트 자신과 직접 통신할 수 없다. 이는 Linux 커널의 구조적 제한이다.


Docker 스토리지: 세 가지 마운트 유형

컨테이너의 writable layer는 컨테이너가 삭제되면 사라진다. 데이터를 유지하려면 외부 스토리지를 마운트해야 한다.

Named Volume /var/lib/docker/volumes/
Docker가 생명주기 관리
overlay2 스토리지 드라이버 우회
이식성 높음 (드라이버 교체 가능)
✅ 프로덕션 DB, 영속 데이터
Bind Mount 호스트 경로를 그대로 마운트
절대경로 지정 필요
호스트 파일시스템 의존
✅ 개발 시 소스코드 핫리로드
tmpfs Mount 호스트 RAM에 저장
컨테이너 종료 시 소멸
디스크 I/O 없음
✅ 세션 캐시, 민감 데이터
Docker 마운트 유형 비교

Named Volume: Docker가 관리하는 스토리지

Named Volume은 Docker가 완전히 관리하는 스토리지다. 실제 데이터는 호스트의 /var/lib/docker/volumes/<name>/_data 에 저장되지만, 컨테이너는 이 경로를 알 필요가 없다.

# 볼륨 생성
docker volume create pgdata

# 컨테이너에 마운트
docker run -v pgdata:/var/lib/postgresql/data postgres:16

# --mount 문법 (권장 - 명시적)
docker run --mount type=volume,source=pgdata,target=/var/lib/postgresql/data postgres:16

# 볼륨 상세 정보
docker volume inspect pgdata

빈 볼륨과 이미지 초기화

Named Volume이 비어 있을 때 컨테이너에 마운트하면, Docker는 이미지 해당 경로의 파일을 볼륨으로 복사한다. PostgreSQL 이미지가 /var/lib/postgresql/data 에 초기 파일을 갖고 있다면, 첫 실행 시 그 파일들이 볼륨에 복사된다. 이미 데이터가 있는 볼륨에는 이 동작이 일어나지 않는다.

성능 특성

Named Volume은 컨테이너 writable layer(overlay2)를 거치지 않고 호스트 파일시스템에 직접 쓰기 때문에 쓰기 성능이 writable layer보다 훨씬 낫다. 데이터베이스처럼 쓰기가 많은 워크로드는 반드시 Named Volume을 사용해야 한다.

볼륨 드라이버: 외부 스토리지 연동

기본 드라이버(local) 외에 플러그인 형태로 외부 스토리지를 연결할 수 있다.

# NFS 볼륨 예시
docker volume create \
  --driver local \
  --opt type=nfs \
  --opt o=addr=192.168.1.100,rw \
  --opt device=:/exports/data \
  nfsdata

AWS EBS, GCP Persistent Disk, NetApp 등의 공식 볼륨 드라이버도 존재한다.

Bind Mount: 호스트 경로 직접 공유

# 소스코드 개발용 바인드 마운트
docker run -v $(pwd)/src:/app/src node:20-alpine

# --mount 문법
docker run --mount type=bind,source=$(pwd)/src,target=/app/src node:20-alpine

# 읽기 전용 (설정 파일, 인증서 등)
docker run --mount type=bind,source=/etc/nginx/ssl,target=/ssl,readonly nginx

개발 환경에서 유용하지만, 프로덕션에서는 호스트 경로 의존성과 권한 문제 때문에 Named Volume을 선호한다. Linux/macOS/Windows 간 경로 형식이 달라 이식성도 낮다.

tmpfs Mount: 메모리에 저장, 디스크에 남기지 않음

# JWT 토큰, 세션 키 등 디스크에 남기면 안 되는 임시 데이터
docker run --mount type=tmpfs,target=/run/secrets \
           --tmpfs-size=64m \
           myapp

tmpfs는 Linux 커널의 메모리 기반 파일시스템을 그대로 사용한다. 컨테이너가 종료되면 데이터가 완전히 사라지며, 스왑 공간이 없는 환경에서는 지정한 크기가 실제 RAM 사용량이 된다.


실전 패턴

Docker Compose에서 네트워크와 볼륨 선언

# compose.yaml
services:
  api:
    image: myapi:latest
    networks:
      - backend
    ports:
      - "8080:8080"

  db:
    image: postgres:16
    networks:
      - backend
    volumes:
      - pgdata:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_pass

networks:
  backend:
    driver: bridge     # 사용자 정의 bridge: DNS 해석 자동 지원

volumes:
  pgdata:              # Named Volume: 컨테이너 삭제해도 데이터 유지

이 구성에서 api 컨테이너는 db라는 호스트명으로 데이터베이스에 접근할 수 있다. 사용자 정의 bridge 덕분에 IP 하드코딩이 필요 없다.

볼륨 백업과 복구

# 볼륨 데이터 tar로 백업
docker run --rm \
  -v pgdata:/data \
  -v $(pwd):/backup \
  alpine tar czf /backup/pgdata-backup.tar.gz -C /data .

# 복구
docker run --rm \
  -v pgdata:/data \
  -v $(pwd):/backup \
  alpine tar xzf /backup/pgdata-backup.tar.gz -C /data

컨테이너 간 볼륨 공유 패턴

같은 Named Volume을 여러 컨테이너에 마운트할 수 있다. 단, 동시 쓰기는 애플리케이션 레벨에서 충돌을 관리해야 한다. 일반적으로는 한 컨테이너가 쓰고 나머지는 읽기 전용으로 마운트한다.

docker run -v shared-logs:/logs writer-app
docker run -v shared-logs:/logs:ro log-aggregator

네트워크 드라이버 선택 기준

상황권장 드라이버
단일 호스트, 컨테이너 간 통신사용자 정의 bridge
최고 성능, 포트 충돌 없는 환경host
다중 호스트 (Swarm/K8s)overlay
컨테이너에 실제 LAN IP 필요macvlan 또는 ipvlan
완전한 네트워크 격리none

마운트 유형 선택 기준

상황권장 방식
데이터베이스, 영속 파일Named Volume
개발 시 소스코드 핫리로드Bind Mount
CI/CD 빌드 캐시Named Volume
민감 정보, 세션 캐시tmpfs
설정 파일 주입 (읽기 전용)Bind Mount (readonly)

References