개요
Docker에서 네트워크는 컨테이너 간 통신과 외부 서비스 연동에 매우 중요한 역할을 한다. 컨테이너 기술은 프로세스를 격리하여 실행하지만, 대부분의 실제 애플리케이션은 데이터베이스나 웹 등 다른 서비스와 통신해야 한다. 따라서 Docker에서 네트워킹을 잘 이해하면 컨테이너끼리 연결하고, 호스트 또는 외부와 안전하게 소통하며, 필요한 경우 특정 네트워크 환경을 구성할 수 있다.
이 글에서는 Docker의 네트워크 관련 핵심 개념들을 정리해보자. 먼저 컨테이너를 실행할 때 설정할 수 있는 네 가지 네트워크 모드인 bridge, host, none, container:<name|id> 모드를 알아보고 각각 언제 사용하는지 살펴볼 것이다. 이어서 Docker 네트워크를 사용자가 정의해서 만드는 방법과 종류(예: bridge, overlay, macvlan)를 설명하고, 컨테이너들이 이러한 네트워크에 어떻게 연결되는지 알아본다. 마지막으로 Docker 20.10 버전부터 도입된 host-gateway 기능에 대해 알아보고, extra_hosts 설정과 함께 사용하여 리눅스 환경에서 host.docker.internal로 호스트와 통신하는 방법을 예제와 함께 설명한다. 초심자도 이해할 수 있도록 천천히 풀어가며, 각 섹션별로 코드 예시와 함께 설명해보겠다.
Docker 네트워크 모드

Docker 컨테이너를 실행할 때 --network (또는 예전 버전의 --net) 옵션을 통해 네트워킹 동작 방식을 지정할 수 있다. Docker에는 기본적으로 네 가지 네트워크 모드가 있다: bridge, host, none, 그리고 container:<다른 컨테이너> 모드이다. 각 모드는 컨테이너의 네트워크 격리 수준과 호스트와의 연결 방식에 차이가 있다. 하나씩 그 특징과 사용 예를 살펴보자.
Bridge 모드
Bridge 모드는 Docker의 기본 네트워크 모드이다. 별도로 지정하지 않고 컨테이너를 실행하면 자동으로 bridge 모드로 실행된다. 이 모드에서는 Docker가 호스트 머신에 가상 네트워크 브리지(기본 이름: docker0)를 만들어 두고, 컨테이너마다 독립된 네트워크 네임스페이스와 가상 이더넷 인터페이스를 생성한 뒤 이 브리지에 연결한다. 마치 호스트 내에 가상 스위치가 있고 각 컨테이너가 그 스위치에 연결되는 셈이다.
Bridge 네트워크에 연결된 컨테이너들은 서로 간에 통신이 가능하며, 동일한 브리지에 있는 컨테이너 끼리는 IP를 통해 직접 연결할 수 있다. 컨테이너는 브리지 네트워크상에서 고유한 IP 주소 (예: 기본 bridge의 경우 172.17.0.X 대역)를 할당받는다. 외부(호스트 바깥)에서 이 컨테이너로 접근하려면 포트 포워딩(포트 매핑)을 설정해야 한다. 즉, 호스트의 포트를 컨테이너의 포트에 연결해 주어야 외부 요청이 컨테이너로 전달된다.
사용 예시
대부분의 경우 기본 bridge 모드를 사용한다. 예를 들어 Nginx 웹 서버 컨테이너를 실행하면서 호스트의 8080포트를 컨테이너의 80포트에 연결하려면 다음과 같이 한다.
# 기본 bridge 네트워크로 Nginx 컨테이너 실행 (호스트의 8080 -> 컨테이너의 80 포트 연결)
docker run -d --name web -p 8080:80 nginx
이렇게 실행된 컨테이너 web은 Docker의 기본 bridge 네트워크에 연결되고, 호스트의 localhost:8080으로 접속하면 컨테이너 내부의 Nginx (80포트)와 통신할 수 있다. 컨테이너 간에 통신하려면 같은 bridge 네트워크에 속해야 하며, 기본 bridge 네트워크에서는 컨테이너 이름으로는 서로를 찾을 수 없고 IP로 접근해야 한다는 점을 기억하자 (사용자 정의 브리지는 다르므로, 이는 뒤에서 설명한다).
Host 모드
Host 모드는 컨테이너를 호스트의 네트워크 스택과 공유하여 실행하는 모드이다. 이 모드로 실행된 컨테이너는 별도의 가상 네트워크 인터페이스나 IP를 가지지 않고, 호스트 머신의 네트워크 인터페이스와 IP를 그대로 사용한다. 쉽게 말해, 해당 컨테이너는 네트워크 측면에서는 호스트와 완전히 동일하게 동작한다.
Host 모드에서는 컨테이너 격리된 네트워크 네임스페이스를 만들지 않기 때문에, 네트워크 성능 상의 오버헤드가 가장 적고 (가상 브리지를 통과하지 않으므로), 지연(latency)도 거의 없다. 또한 호스트와 IP를 공유하므로 포트 포워딩이 필요 없게 된다. 컨테이너에서 서비스가 80포트를 열면 호스트의 80포트에서 바로 접근할 수 있다. 하지만 그만큼 주의할 점은, 포트 충돌이다. 예를 들어 호스트에서 이미 80포트를 사용하는 서비스가 있다면, host 모드로 실행한 Nginx 컨테이너는 80포트를 쓸 수 없어 충돌하거나 실행이 실패할 것이다. 또한 네트워크를 공유하므로 보안 격리가 줄어든다는 점도 고려해야 한다.
사용 예시
host 모드는 주로 컨테이너의 성능이 매우 중요하거나, 호스트 네트워크 자원을 직접 사용해야 하는 경우(예: 특별한 네트워크 구성, 로우레벨 패킷 캡처 등)에 사용된다. 간단한 예로 Nginx를 host 모드로 실행하면
# Host 네트워크 모드로 컨테이너 실행
docker run -d --network host --name web_host nginx
이렇게 하면 web_host 컨테이너는 호스트와 네트워크를 공유하므로, 호스트의 IP 주소 자체에서 Nginx 서비스에 접근할 수 있다(예: http://localhost로 바로 Nginx 응답). 기본 bridge 모드와 달리 -p 옵션(포트 매핑)은 필요 없으며, -p를 지정해도 무시된다. Host 모드는 Docker 데몬이 사용하는 리눅스 네트워크 네임스페이스 격리 기능을 생략하는 것이므로, 컨테이너를 네트워크적으로 격리하지 않아도 될 때 선택적으로 사용하자.
None 모드
None 모드는 컨테이너를 아예 네트워크에 연결하지 않는 모드이다. 컨테이너는 독립된 네트워크 네임스페이스는 갖지만, 어떠한 네트워크 인터페이스도 연결되지 않은 채로 실행된다. loopback 인터페이스(lo)만 있고, eth0와 같은 인터페이스가 없기 때문에 컨테이너는 다른 컨테이너나 외부와 어떤 네트워크 통신도 할 수 없는 완전 격리 상태가 된다.
이 모드의 주요 용도는 컨테이너 내부에서 네트워크가 전혀 필요 없는 작업을 수행하거나, 컨테이너 시작 후 수동으로 커스텀 네트워크 인터페이스를 붙이는 등의 특수한 상황이다. 예를 들어 보안상 어떤 네트워크 접근도 허용하지 않는 실행 환경을 만들 때 None 모드를 사용할 수 있다. 또는 컨테이너를 생성한 뒤 docker network connect 명령으로 특정 네트워크에 붙이는 식으로 네트워크를 나중에 수동 구성할 수도 있다.
# None 네트워크 모드로 컨테이너 실행 (네트워크 없이 동작)
docker run -d --network none --name isolated busybox sleep 3600
위 명령으로 실행된 isolated 컨테이너는 busybox 이미지를 기반으로 3600초 동안 대기(sleep)하며 실행되는데, 네트워크 인터페이스가 전혀 없기 때문에 다른 호스트나 컨테이너와 통신이 불가능하다. docker exec isolated ip addr로 내부 IP 주소를 확인해보면 loopback(127.0.0.1)만 나오고 eth0 인터페이스가 없다. 이러한 깨끗한 네트워크 상태를 필요에 따라 사용자가 직접 구성할 수 있다는 것이 none 모드의 특징이다.
Container:<name|id> 모드
Container 모드는 조금 특별한 네트워크 모드로, 이미 실행 중인 다른 컨테이너의 네트워크 네임스페이스를 공유하도록 설정한다. 옵션 형식은 --network container:<다른 컨테이너 이름 또는 ID>로 지정한다. 이렇게 하면 새로운 컨테이너는 자신만의 새로운 네트워크 환경을 만들지 않고, 지정된 기존 컨테이너의 네트워크 인터페이스와 IP를 함께 사용한다. 즉, 두 컨테이너가 동일한 IP 주소와 네트워크 스택을 갖게 되는 셈이다.
이 모드에서 네트워크를 공유하는 컨테이너들은 마치 하나의 컨테이너 안의 프로세스들처럼 localhost를 통해 서로 통신할 수 있다. 포트도 공유되기 때문에, 한 컨테이너에서 80포트를 열면 같은 네트워크를 공유한 다른 컨테이너에서도 그 포트를 이용할 수 있고, 외부에서 볼 때 두 컨테이너는 하나의 IP/포트 세트를 사용한다. 이러한 설정은 주로 사이드카 패턴(한 컨테이너는 메인 애플리케이션, 다른 하나는 보조 역할로 같은 네트워크를 공유)이나, 컨테이너의 네트워크 트러블슈팅(네트워크 상태를 분석하기 위해 다른 컨테이너와 같은 네트워크 네임스페이스에 들어가 보기) 등에 활용된다.
사용 예시
# 1) 먼저 DB 컨테이너 실행 (bridge 모드로 IP 할당 받음)
docker run -d --name db mysql:latest
# 2) 애플리케이션 컨테이너를 DB 컨테이너와 네트워크 공유하여 실행
docker run -d --name app --network container:db myapp:latest
위의 app 컨테이너는 자체 네트워크를 만들지 않고 db 컨테이너의 네트워크를 함께 사용한다. 따라서 app과 db는 똑같은 IP 주소를 가지며, app 컨테이너에서 localhost로 데이터베이스에 접속하면 실제로는 db 컨테이너 내부의 MySQL에 접속하게 된다. app 컨테이너는 따로 포트 매핑을 할 수도 없고, 네트워크적으로는 db와 완전히 겹쳐있다. 이 모드는 특수한 경우에 사용되므로, 필요할 때만 활용하면 된다.
Docker 사용자 정의 네트워크
위에서 살펴본 네트워크 모드는 컨테이너 실행 시 즉각 적용되는 동작 방식이었다. 이번에는 Docker에서 네트워크를 미리 만들어 두고 컨테이너들을 그 네트워크에 연결하는 개념을 알아보자. Docker는 기본적으로 몇 가지 내장 네트워크를 제공하며 (bridge, host, none 세 가지가 기본 정의됨), 사용자는 필요에 따라 추가로 사용자 정의 네트워크를 생성할 수 있다. 사용자 정의 네트워크를 사용하면 컨테이너 그룹 간 통신 제어, 네트워크 격리, 컨테이너 이름 해석(DNS) 등의 이점을 얻을 수 있다.
docker network create 명령을 통해 새로운 네트워크를 생성할 수 있다. 네트워크를 생성할 때는 네트워크 드라이버(driver)를 선택할 수 있는데, Docker는 여러 가지 드라이버를 제공하지만 여기서는 대표적으로 자주 사용하는 bridge, overlay, macvlan 드라이버 기반 네트워크를 다뤄보겠다.
Bridge 네트워크 (사용자 정의 브리지)
브리지 네트워크는 앞서 본 기본 bridge 모드와 동일한 방식으로 동작하지만, 사용자가 별도로 만드는 커스텀 브리지 네트워크이다. 기본 bridge 네트워크(docker0)와 구분되는 독립적인 브리지 네트워크를 만들 수 있으며, 여기에 원하는 컨테이너들만 연결하면 다른 네트워크와 격리된 작은 네트워크를 구성할 수 있다.
특징
사용자 정의 브리지 네트워크에 연결된 컨테이너들은 서로를 컨테이너 이름으로 접근할 수 있다. Docker가 자체 DNS를 제공하여 같은 네트워크 내에서 컨테이너 이름을 호스트네임으로 해석해주기 때문이다. 반면 기본 bridge 네트워크에서는 이런 DNS 해석이 기본적으로 되지 않아서 IP를 직접 지정하거나 --link라는 구식 옵션을 사용해야 했다. 따라서 애플리케이션을 구성할 때 기본 bridge보다 사용자 정의 브리지를 사용하면 훨씬 편리하다. 또한 사용자 정의 네트워크는 네트워크 단위로 격리되므로, 서로 다른 네트워크에 속한 컨테이너들은 기본적으로 통신할 수 없다 (필요하다면 한 컨테이너를 여러 네트워크에 연결할 수도 있다).
생성 및 사용
별도의 브리지 네트워크를 만들 때는 driver로 bridge를 지정한다. 예를 들어 my-bridge-network라는 이름의 브리지 네트워크를 만들어보자. 이렇게 하면 Docker 호스트에 my-bridge-network라는 가상 브리지가 추가되고, docker network ls로 확인할 수 있다.
이제 컨테이너를 실행할 때 --network my-bridge-network 옵션을 주면 그 컨테이너는 해당 네트워크에 접속된다.위처럼 web과 db 두 컨테이너를 같은 my-bridge-network에 연결하면, web 컨테이너에서 환경변수나 설정에 db라는 호스트네임을 사용해 데이터베이스에 연결할 수 있다. Docker가 db라는 이름을 db 컨테이너의 IP로 자동 해석해주기 때문이다. 사용자 정의 브리지 네트워크는 단일 호스트 내에서 동작하므로, 이 방식을 통해 하나의 호스트에서 여러 컨테이너들로 구성된 애플리케이션 스택을 손쉽게 구축하고 격리할 수 있다.
# 새로운 브리지 네트워크 생성
docker network create -d bridge my-bridge-network
이렇게 하면 Docker 호스트에 my-bridge-network라는 가상 브리지가 추가되고, docker network ls로 확인할 수 있다. 이제 컨테이너를 실행할 때 --network my-bridge-network 옵션을 주면 그 컨테이너는 해당 네트워크에 접속된다.
# 두 컨테이너를 사용자 정의 브리지 네트워크에 연결하여 실행
docker run -d --name web --network my-bridge-network nginx
docker run -d --name db --network my-bridge-network mysql:5.7
위처럼 web과 db 두 컨테이너를 같은 my-bridge-network에 연결하면, web 컨테이너에서 환경변수나 설정에 db라는 호스트네임을 사용해 데이터베이스에 연결할 수 있다. Docker가 db라는 이름을 db 컨테이너의 IP로 자동 해석해주기 때문이다. 사용자 정의 브리지 네트워크는 단일 호스트 내에서 동작하므로, 이 방식을 통해 하나의 호스트에서 여러 컨테이너들로 구성된 애플리케이션 스택을 손쉽게 구축하고 격리할 수 있다.
Overlay 네트워크
오버레이 네트워크는 여러 Docker 호스트에 걸쳐 동작하는 분산 네트워크이다. 앞서 브리지 네트워크는 한 호스트 내에 국한되지만, Docker Swarm과 같은 기능을 이용하면 다수의 호스트에 있는 컨테이너들을 하나의 가상 네트워크로 묶을 수 있다. Overlay 네트워크는 이러한 상황에서 사용되는 드라이버로, Docker가 호스트들 사이에 가상의 L2 네트워크를 구성하여 컨테이너들이 서로 통신하게 한다. (기술적으로는 VXLAN 등의 프로토콜을 사용하여 오버레이 터널링을 한다.)
특징
특징: Overlay 네트워크에 연결된 컨테이너들은 마치 동일한 호스트 내에 있는 것처럼 통신할 수 있지만, 실제로는 분산되어 있는 구조이다. 예를 들어 세 대의 물리 서버에 걸쳐 Docker Swarm으로 클러스터를 구성하고 overlay 네트워크를 설정하면, 각각의 서버에서 실행된 컨테이너들이 같은 네트워크 IP 대역을 공유하며 서로 이름으로 통신할 수 있다. 이때도 Docker가 DNS 해석을 제공하므로 컨테이너 이름으로 접근 가능하다. Overlay 네트워크는 스웜(Swarm) 모드 또는 Docker Enterprise 등의 오케스트레이션 환경에서 주로 사용된다. 단일 호스트 환경에서 overlay 네트워크를 만들려면 그 호스트를 swarm init 해서 스웜 클러스터(사실 단일 노드 클러스터) 모드로 전환해야 한다.
생성 및 사용
Overlay 네트워크를 만들기 위해서는 먼저 docker swarm init 등으로 스웜을 활성화해야 한다. 그 후 다음과 같이 네트워크를 생성할 수 있다.
# (Swarm 모드 활성화 필요)
docker network create -d overlay my-overlay-network
이후 컨테이너 실행 시 --network my-overlay-network로 지정하면, 동일한 overlay 네트워크 이름을 사용하는 클러스터 내 모든 컨테이너들이 하나의 네트워크에 속하게 된다. Overlay 네트워크는 다중 호스트에 걸친 마이크로서비스 아키텍처에서 유용하며, Docker Compose v3 + Swarm 조합이나 Docker Stack 배포 시에 서비스들을 overlay에 붙여서 배포하게 된다. 만약 여러 대의 서버에 걸쳐 컨테이너 통신이 필요하지 않다면, 일반적인 브리지 네트워크로 충분하다.
Macvlan 네트워크
Macvlan 네트워크는 컨테이너에게 호스트 네트워크의 일부를 분할하여 직접 연결하는 방식이다. Macvlan 드라이버를 사용하면 Docker 컨테이너가 자체의 가상 MAC 주소를 갖고, 호스트의 물리 네트워크에 직접 접속하는 것처럼 동작하도록 설정할 수 있다. 이렇게 하면 컨테이너마다 고유한 IP 주소를 물리 네트워크(예: 회사 LAN 또는 집에서 사용하는 동일한 서브넷)에서 직접 할당받아 사용할 수 있다.
특징
Macvlan을 사용한 컨테이너는 마치 네트워크 상에서 하나의 독립된 물리 장비처럼 보인다. 호스트가 아니라 컨테이너 자체가 네트워크에 노출되므로, 포트 포워딩 없이도 외부에서 컨테이너의 IP로 직접 접근이 가능하고, 컨테이너에서 나가는 트래픽도 NAT를 거치지 않고 바로 외부 네트워크로 나간다. 이러한 특성 때문에, Macvlan 네트워크를 쓰면 호스트와 컨테이너 간 통신이 일반적인 방법과 조금 다르게 동작할 수 있는데 (동일 네트워크 상에서 호스트 <-> 컨테이너 통신 이슈 등), 이를 해결하려면 별도의 설정이 필요할 수도 있다. Macvlan은 주로 컨테이너를 별도의 장비처럼 취급해야 하는 경우나, 기존 네트워크 구조에 통합해야 하는 경우, 혹은 NAT를 피하고 싶을 때 사용된다. 다만 설정이 비교적 복잡하고, 호스트 네트워크 환경에 대한 이해가 필요하므로 초심자에게 익숙한 설정은 아니다.
생성 및 사용
Macvlan 네트워크를 생성할 때는 호스트의 네트워크 인터페이스를 지정하고, 컨테이너에게 할당할 IP 대역 등을 설정해야 한다. 예를 들어 호스트의 물리 인터페이스 eth0를 통해 192.168.1.0/24 네트워크에 컨테이너들을 직접 연결시키고 싶다면
# Macvlan 네트워크 생성 예시 (192.168.1.0/24 대역, 게이트웨이 192.168.1.1, 호스트 인터페이스 eth0 사용)
docker network create -d macvlan \
--subnet=192.168.1.0/24 \
--gateway=192.168.1.1 \
-o parent=eth0 \
my-macvlan-network
이 명령으로 my-macvlan-network를 생성하면, 해당 네트워크에 컨테이너를 연결할 때 컨테이너들은 192.168.1.0/24 범위 내에서 IP를 부여받고 eth0를 통해 통신하게 된다. 이제 --network my-macvlan-network 옵션으로 컨테이너를 실행하면, 컨테이너는 호스트와 동일한 192.168.1.x 대역의 IP를 갖게 되어 외부 라우터나 다른 장비에서도 그 IP로 컨테이너에 접근할 수 있다.
Docker Host-Gateway (host.docker.internal 활용)
Docker 20.10 버전부터 도입된 Host-Gateway 기능은 리눅스 환경에서 컨테이너가 호스트(machine)와 통신하는 것을 쉽게 해주는 기능이다. 기존에 Docker for Mac/Windows 환경에서는 컨테이너 안에서 host.docker.internal이라는 특수 도메인 이름을 사용하면 호스트(예: 개발 PC 자체)를 가리켜서 통신할 수 있었다. 하지만 리눅스 Docker 환경에서는 기본적으로 이 도메인이 제공되지 않아, 컨테이너에서 호스트로 연결하려면 호스트의 IP (예: 172.17.0.1 혹은 호스트의 실제 네트워크 IP)를 수동으로 알아내어 사용해야 하는 불편함이 있었다. Host-Gateway는 이러한 문제를 해결하기 위해 Docker 엔진에 추가된 개선사항이다.
의미와 역할
Host-Gateway는 Docker의 가상 브리지 네트워크 게이트웨이 IP(일반적으로 docker0의 IP, 보통 172.17.0.1)를 가리키는 키워드이다. 이 키워드를 이용하면 Docker 컨테이너의 hosts 파일에 호스트를 대표하는 엔트리를 손쉽게 추가할 수 있다. 예를 들어 host.docker.internal이라는 이름을 Host-Gateway IP에 매핑시키면, 컨테이너 입장에서는 이 도메인으로 호스트를 가리킬 수 있게 된다. 한 마디로 리눅스에서도 host.docker.internal 이름을 쓸 수 있게 된 것이다.
사용 방법
Host-Gateway 기능은 docker run 시 --add-host 옵션이나 Docker Compose의 extra_hosts 설정을 통해 사용할 수 있다. host-gateway라는 예약어를 호스트 IP 대신 쓰면 Docker가 알아서 현재 호스트의 게이트웨이 주소로 치환해 준다. 사용 형식은 "호스트이름:host-gateway" 형태로 지정하면 된다. 일반적으로 호스트를 가리키는 이름으로 host.docker.internal을 많이 사용하므로, 다음과 같이 설정한다.
# 컨테이너 실행 시 host.docker.internal을 호스트 게이트웨이 IP로 추가
docker run -d --add-host host.docker.internal:host-gateway myapp-image
위와 같이 설정한 컨테이너 myapp 내부에서는 host.docker.internal이라는 호스트명을 호스트 머신의 IP로 인식하게 된다. 예를 들어 호스트에서 데이터베이스가 돌아가고 있고, 컨테이너 내부 애플리케이션에서 데이터베이스 호스트를 host.docker.internal로 지정하면 리눅스 환경에서도 문제없이 호스트의 데이터베이스에 접속할 수 있다.
작동 원리
기능을 사용하면 Docker가 컨테이너의 /etc/hosts 파일에 host.docker.internal 엔트리를 추가하게 된다. 기본 bridge 네트워크에서는 호스트의 게이트웨이 IP가 172.17.0.1이므로, /etc/hosts에 172.17.0.1 host.docker.internal 항목이 생긴다. 컨테이너에서 ping host.docker.internal 등을 해보면 응답이 오는 것을 확인할 수 있다. 이를 통해 이전까지 리눅스 Docker에서 번거로웠던 "컨테이너 -> 호스트" 접근이 간편해진다.
응용 예시
개발 환경에서 애플리케이션 일부는 컨테이너화했지만, 데이터베이스는 로컬 호스트에 그대로 두는 경우가 있을 수 있다. 이때 컨테이너화된 애플리케이션이 호스트의 DB에 접근하려면 --add-host 설정으로 host.docker.internal을 추가해주는 것이 편리하다. Windows나 Mac에서는 Docker가 자동으로 이 주소를 제공하지만, Linux에서는 위와 같이 수동 지정이 필요하다. Host-Gateway 기능 도입 전에는 컨테이너 실행 스크립트에 호스트 IP를 하드코딩하거나, 네트워크를 host 모드로 쓰는 등의 우회가 필요했지만 이제는 Compose 파일에 한 줄 추가하는 것으로 해결된다.
개요
Docker에서 네트워크는 컨테이너 간 통신과 외부 서비스 연동에 매우 중요한 역할을 한다. 컨테이너 기술은 프로세스를 격리하여 실행하지만, 대부분의 실제 애플리케이션은 데이터베이스나 웹 등 다른 서비스와 통신해야 한다. 따라서 Docker에서 네트워킹을 잘 이해하면 컨테이너끼리 연결하고, 호스트 또는 외부와 안전하게 소통하며, 필요한 경우 특정 네트워크 환경을 구성할 수 있다.
이 글에서는 Docker의 네트워크 관련 핵심 개념들을 정리해보자. 먼저 컨테이너를 실행할 때 설정할 수 있는 네 가지 네트워크 모드인 bridge, host, none, container:<name|id> 모드를 알아보고 각각 언제 사용하는지 살펴볼 것이다. 이어서 Docker 네트워크를 사용자가 정의해서 만드는 방법과 종류(예: bridge, overlay, macvlan)를 설명하고, 컨테이너들이 이러한 네트워크에 어떻게 연결되는지 알아본다. 마지막으로 Docker 20.10 버전부터 도입된 host-gateway 기능에 대해 알아보고, extra_hosts 설정과 함께 사용하여 리눅스 환경에서 host.docker.internal로 호스트와 통신하는 방법을 예제와 함께 설명한다. 초심자도 이해할 수 있도록 천천히 풀어가며, 각 섹션별로 코드 예시와 함께 설명해보겠다.
Docker 네트워크 모드

Docker 컨테이너를 실행할 때 --network (또는 예전 버전의 --net) 옵션을 통해 네트워킹 동작 방식을 지정할 수 있다. Docker에는 기본적으로 네 가지 네트워크 모드가 있다: bridge, host, none, 그리고 container:<다른 컨테이너> 모드이다. 각 모드는 컨테이너의 네트워크 격리 수준과 호스트와의 연결 방식에 차이가 있다. 하나씩 그 특징과 사용 예를 살펴보자.
Bridge 모드
Bridge 모드는 Docker의 기본 네트워크 모드이다. 별도로 지정하지 않고 컨테이너를 실행하면 자동으로 bridge 모드로 실행된다. 이 모드에서는 Docker가 호스트 머신에 가상 네트워크 브리지(기본 이름: docker0)를 만들어 두고, 컨테이너마다 독립된 네트워크 네임스페이스와 가상 이더넷 인터페이스를 생성한 뒤 이 브리지에 연결한다. 마치 호스트 내에 가상 스위치가 있고 각 컨테이너가 그 스위치에 연결되는 셈이다.
Bridge 네트워크에 연결된 컨테이너들은 서로 간에 통신이 가능하며, 동일한 브리지에 있는 컨테이너 끼리는 IP를 통해 직접 연결할 수 있다. 컨테이너는 브리지 네트워크상에서 고유한 IP 주소 (예: 기본 bridge의 경우 172.17.0.X 대역)를 할당받는다. 외부(호스트 바깥)에서 이 컨테이너로 접근하려면 포트 포워딩(포트 매핑)을 설정해야 한다. 즉, 호스트의 포트를 컨테이너의 포트에 연결해 주어야 외부 요청이 컨테이너로 전달된다.
사용 예시
대부분의 경우 기본 bridge 모드를 사용한다. 예를 들어 Nginx 웹 서버 컨테이너를 실행하면서 호스트의 8080포트를 컨테이너의 80포트에 연결하려면 다음과 같이 한다.
# 기본 bridge 네트워크로 Nginx 컨테이너 실행 (호스트의 8080 -> 컨테이너의 80 포트 연결)
docker run -d --name web -p 8080:80 nginx
이렇게 실행된 컨테이너 web은 Docker의 기본 bridge 네트워크에 연결되고, 호스트의 localhost:8080으로 접속하면 컨테이너 내부의 Nginx (80포트)와 통신할 수 있다. 컨테이너 간에 통신하려면 같은 bridge 네트워크에 속해야 하며, 기본 bridge 네트워크에서는 컨테이너 이름으로는 서로를 찾을 수 없고 IP로 접근해야 한다는 점을 기억하자 (사용자 정의 브리지는 다르므로, 이는 뒤에서 설명한다).
Host 모드
Host 모드는 컨테이너를 호스트의 네트워크 스택과 공유하여 실행하는 모드이다. 이 모드로 실행된 컨테이너는 별도의 가상 네트워크 인터페이스나 IP를 가지지 않고, 호스트 머신의 네트워크 인터페이스와 IP를 그대로 사용한다. 쉽게 말해, 해당 컨테이너는 네트워크 측면에서는 호스트와 완전히 동일하게 동작한다.
Host 모드에서는 컨테이너 격리된 네트워크 네임스페이스를 만들지 않기 때문에, 네트워크 성능 상의 오버헤드가 가장 적고 (가상 브리지를 통과하지 않으므로), 지연(latency)도 거의 없다. 또한 호스트와 IP를 공유하므로 포트 포워딩이 필요 없게 된다. 컨테이너에서 서비스가 80포트를 열면 호스트의 80포트에서 바로 접근할 수 있다. 하지만 그만큼 주의할 점은, 포트 충돌이다. 예를 들어 호스트에서 이미 80포트를 사용하는 서비스가 있다면, host 모드로 실행한 Nginx 컨테이너는 80포트를 쓸 수 없어 충돌하거나 실행이 실패할 것이다. 또한 네트워크를 공유하므로 보안 격리가 줄어든다는 점도 고려해야 한다.
사용 예시
host 모드는 주로 컨테이너의 성능이 매우 중요하거나, 호스트 네트워크 자원을 직접 사용해야 하는 경우(예: 특별한 네트워크 구성, 로우레벨 패킷 캡처 등)에 사용된다. 간단한 예로 Nginx를 host 모드로 실행하면
# Host 네트워크 모드로 컨테이너 실행
docker run -d --network host --name web_host nginx
이렇게 하면 web_host 컨테이너는 호스트와 네트워크를 공유하므로, 호스트의 IP 주소 자체에서 Nginx 서비스에 접근할 수 있다(예: http://localhost로 바로 Nginx 응답). 기본 bridge 모드와 달리 -p 옵션(포트 매핑)은 필요 없으며, -p를 지정해도 무시된다. Host 모드는 Docker 데몬이 사용하는 리눅스 네트워크 네임스페이스 격리 기능을 생략하는 것이므로, 컨테이너를 네트워크적으로 격리하지 않아도 될 때 선택적으로 사용하자.
None 모드
None 모드는 컨테이너를 아예 네트워크에 연결하지 않는 모드이다. 컨테이너는 독립된 네트워크 네임스페이스는 갖지만, 어떠한 네트워크 인터페이스도 연결되지 않은 채로 실행된다. loopback 인터페이스(lo)만 있고, eth0와 같은 인터페이스가 없기 때문에 컨테이너는 다른 컨테이너나 외부와 어떤 네트워크 통신도 할 수 없는 완전 격리 상태가 된다.
이 모드의 주요 용도는 컨테이너 내부에서 네트워크가 전혀 필요 없는 작업을 수행하거나, 컨테이너 시작 후 수동으로 커스텀 네트워크 인터페이스를 붙이는 등의 특수한 상황이다. 예를 들어 보안상 어떤 네트워크 접근도 허용하지 않는 실행 환경을 만들 때 None 모드를 사용할 수 있다. 또는 컨테이너를 생성한 뒤 docker network connect 명령으로 특정 네트워크에 붙이는 식으로 네트워크를 나중에 수동 구성할 수도 있다.
# None 네트워크 모드로 컨테이너 실행 (네트워크 없이 동작)
docker run -d --network none --name isolated busybox sleep 3600
위 명령으로 실행된 isolated 컨테이너는 busybox 이미지를 기반으로 3600초 동안 대기(sleep)하며 실행되는데, 네트워크 인터페이스가 전혀 없기 때문에 다른 호스트나 컨테이너와 통신이 불가능하다. docker exec isolated ip addr로 내부 IP 주소를 확인해보면 loopback(127.0.0.1)만 나오고 eth0 인터페이스가 없다. 이러한 깨끗한 네트워크 상태를 필요에 따라 사용자가 직접 구성할 수 있다는 것이 none 모드의 특징이다.
Container:<name|id> 모드
Container 모드는 조금 특별한 네트워크 모드로, 이미 실행 중인 다른 컨테이너의 네트워크 네임스페이스를 공유하도록 설정한다. 옵션 형식은 --network container:<다른 컨테이너 이름 또는 ID>로 지정한다. 이렇게 하면 새로운 컨테이너는 자신만의 새로운 네트워크 환경을 만들지 않고, 지정된 기존 컨테이너의 네트워크 인터페이스와 IP를 함께 사용한다. 즉, 두 컨테이너가 동일한 IP 주소와 네트워크 스택을 갖게 되는 셈이다.
이 모드에서 네트워크를 공유하는 컨테이너들은 마치 하나의 컨테이너 안의 프로세스들처럼 localhost를 통해 서로 통신할 수 있다. 포트도 공유되기 때문에, 한 컨테이너에서 80포트를 열면 같은 네트워크를 공유한 다른 컨테이너에서도 그 포트를 이용할 수 있고, 외부에서 볼 때 두 컨테이너는 하나의 IP/포트 세트를 사용한다. 이러한 설정은 주로 사이드카 패턴(한 컨테이너는 메인 애플리케이션, 다른 하나는 보조 역할로 같은 네트워크를 공유)이나, 컨테이너의 네트워크 트러블슈팅(네트워크 상태를 분석하기 위해 다른 컨테이너와 같은 네트워크 네임스페이스에 들어가 보기) 등에 활용된다.
사용 예시
# 1) 먼저 DB 컨테이너 실행 (bridge 모드로 IP 할당 받음)
docker run -d --name db mysql:latest
# 2) 애플리케이션 컨테이너를 DB 컨테이너와 네트워크 공유하여 실행
docker run -d --name app --network container:db myapp:latest
위의 app 컨테이너는 자체 네트워크를 만들지 않고 db 컨테이너의 네트워크를 함께 사용한다. 따라서 app과 db는 똑같은 IP 주소를 가지며, app 컨테이너에서 localhost로 데이터베이스에 접속하면 실제로는 db 컨테이너 내부의 MySQL에 접속하게 된다. app 컨테이너는 따로 포트 매핑을 할 수도 없고, 네트워크적으로는 db와 완전히 겹쳐있다. 이 모드는 특수한 경우에 사용되므로, 필요할 때만 활용하면 된다.
Docker 사용자 정의 네트워크
위에서 살펴본 네트워크 모드는 컨테이너 실행 시 즉각 적용되는 동작 방식이었다. 이번에는 Docker에서 네트워크를 미리 만들어 두고 컨테이너들을 그 네트워크에 연결하는 개념을 알아보자. Docker는 기본적으로 몇 가지 내장 네트워크를 제공하며 (bridge, host, none 세 가지가 기본 정의됨), 사용자는 필요에 따라 추가로 사용자 정의 네트워크를 생성할 수 있다. 사용자 정의 네트워크를 사용하면 컨테이너 그룹 간 통신 제어, 네트워크 격리, 컨테이너 이름 해석(DNS) 등의 이점을 얻을 수 있다.
docker network create 명령을 통해 새로운 네트워크를 생성할 수 있다. 네트워크를 생성할 때는 네트워크 드라이버(driver)를 선택할 수 있는데, Docker는 여러 가지 드라이버를 제공하지만 여기서는 대표적으로 자주 사용하는 bridge, overlay, macvlan 드라이버 기반 네트워크를 다뤄보겠다.
Bridge 네트워크 (사용자 정의 브리지)
브리지 네트워크는 앞서 본 기본 bridge 모드와 동일한 방식으로 동작하지만, 사용자가 별도로 만드는 커스텀 브리지 네트워크이다. 기본 bridge 네트워크(docker0)와 구분되는 독립적인 브리지 네트워크를 만들 수 있으며, 여기에 원하는 컨테이너들만 연결하면 다른 네트워크와 격리된 작은 네트워크를 구성할 수 있다.
특징
사용자 정의 브리지 네트워크에 연결된 컨테이너들은 서로를 컨테이너 이름으로 접근할 수 있다. Docker가 자체 DNS를 제공하여 같은 네트워크 내에서 컨테이너 이름을 호스트네임으로 해석해주기 때문이다. 반면 기본 bridge 네트워크에서는 이런 DNS 해석이 기본적으로 되지 않아서 IP를 직접 지정하거나 --link라는 구식 옵션을 사용해야 했다. 따라서 애플리케이션을 구성할 때 기본 bridge보다 사용자 정의 브리지를 사용하면 훨씬 편리하다. 또한 사용자 정의 네트워크는 네트워크 단위로 격리되므로, 서로 다른 네트워크에 속한 컨테이너들은 기본적으로 통신할 수 없다 (필요하다면 한 컨테이너를 여러 네트워크에 연결할 수도 있다).
생성 및 사용
별도의 브리지 네트워크를 만들 때는 driver로 bridge를 지정한다. 예를 들어 my-bridge-network라는 이름의 브리지 네트워크를 만들어보자. 이렇게 하면 Docker 호스트에 my-bridge-network라는 가상 브리지가 추가되고, docker network ls로 확인할 수 있다.
이제 컨테이너를 실행할 때 --network my-bridge-network 옵션을 주면 그 컨테이너는 해당 네트워크에 접속된다.위처럼 web과 db 두 컨테이너를 같은 my-bridge-network에 연결하면, web 컨테이너에서 환경변수나 설정에 db라는 호스트네임을 사용해 데이터베이스에 연결할 수 있다. Docker가 db라는 이름을 db 컨테이너의 IP로 자동 해석해주기 때문이다. 사용자 정의 브리지 네트워크는 단일 호스트 내에서 동작하므로, 이 방식을 통해 하나의 호스트에서 여러 컨테이너들로 구성된 애플리케이션 스택을 손쉽게 구축하고 격리할 수 있다.
# 새로운 브리지 네트워크 생성
docker network create -d bridge my-bridge-network
이렇게 하면 Docker 호스트에 my-bridge-network라는 가상 브리지가 추가되고, docker network ls로 확인할 수 있다. 이제 컨테이너를 실행할 때 --network my-bridge-network 옵션을 주면 그 컨테이너는 해당 네트워크에 접속된다.
# 두 컨테이너를 사용자 정의 브리지 네트워크에 연결하여 실행
docker run -d --name web --network my-bridge-network nginx
docker run -d --name db --network my-bridge-network mysql:5.7
위처럼 web과 db 두 컨테이너를 같은 my-bridge-network에 연결하면, web 컨테이너에서 환경변수나 설정에 db라는 호스트네임을 사용해 데이터베이스에 연결할 수 있다. Docker가 db라는 이름을 db 컨테이너의 IP로 자동 해석해주기 때문이다. 사용자 정의 브리지 네트워크는 단일 호스트 내에서 동작하므로, 이 방식을 통해 하나의 호스트에서 여러 컨테이너들로 구성된 애플리케이션 스택을 손쉽게 구축하고 격리할 수 있다.
Overlay 네트워크
오버레이 네트워크는 여러 Docker 호스트에 걸쳐 동작하는 분산 네트워크이다. 앞서 브리지 네트워크는 한 호스트 내에 국한되지만, Docker Swarm과 같은 기능을 이용하면 다수의 호스트에 있는 컨테이너들을 하나의 가상 네트워크로 묶을 수 있다. Overlay 네트워크는 이러한 상황에서 사용되는 드라이버로, Docker가 호스트들 사이에 가상의 L2 네트워크를 구성하여 컨테이너들이 서로 통신하게 한다. (기술적으로는 VXLAN 등의 프로토콜을 사용하여 오버레이 터널링을 한다.)
특징
특징: Overlay 네트워크에 연결된 컨테이너들은 마치 동일한 호스트 내에 있는 것처럼 통신할 수 있지만, 실제로는 분산되어 있는 구조이다. 예를 들어 세 대의 물리 서버에 걸쳐 Docker Swarm으로 클러스터를 구성하고 overlay 네트워크를 설정하면, 각각의 서버에서 실행된 컨테이너들이 같은 네트워크 IP 대역을 공유하며 서로 이름으로 통신할 수 있다. 이때도 Docker가 DNS 해석을 제공하므로 컨테이너 이름으로 접근 가능하다. Overlay 네트워크는 스웜(Swarm) 모드 또는 Docker Enterprise 등의 오케스트레이션 환경에서 주로 사용된다. 단일 호스트 환경에서 overlay 네트워크를 만들려면 그 호스트를 swarm init 해서 스웜 클러스터(사실 단일 노드 클러스터) 모드로 전환해야 한다.
생성 및 사용
Overlay 네트워크를 만들기 위해서는 먼저 docker swarm init 등으로 스웜을 활성화해야 한다. 그 후 다음과 같이 네트워크를 생성할 수 있다.
# (Swarm 모드 활성화 필요)
docker network create -d overlay my-overlay-network
이후 컨테이너 실행 시 --network my-overlay-network로 지정하면, 동일한 overlay 네트워크 이름을 사용하는 클러스터 내 모든 컨테이너들이 하나의 네트워크에 속하게 된다. Overlay 네트워크는 다중 호스트에 걸친 마이크로서비스 아키텍처에서 유용하며, Docker Compose v3 + Swarm 조합이나 Docker Stack 배포 시에 서비스들을 overlay에 붙여서 배포하게 된다. 만약 여러 대의 서버에 걸쳐 컨테이너 통신이 필요하지 않다면, 일반적인 브리지 네트워크로 충분하다.
Macvlan 네트워크
Macvlan 네트워크는 컨테이너에게 호스트 네트워크의 일부를 분할하여 직접 연결하는 방식이다. Macvlan 드라이버를 사용하면 Docker 컨테이너가 자체의 가상 MAC 주소를 갖고, 호스트의 물리 네트워크에 직접 접속하는 것처럼 동작하도록 설정할 수 있다. 이렇게 하면 컨테이너마다 고유한 IP 주소를 물리 네트워크(예: 회사 LAN 또는 집에서 사용하는 동일한 서브넷)에서 직접 할당받아 사용할 수 있다.
특징
Macvlan을 사용한 컨테이너는 마치 네트워크 상에서 하나의 독립된 물리 장비처럼 보인다. 호스트가 아니라 컨테이너 자체가 네트워크에 노출되므로, 포트 포워딩 없이도 외부에서 컨테이너의 IP로 직접 접근이 가능하고, 컨테이너에서 나가는 트래픽도 NAT를 거치지 않고 바로 외부 네트워크로 나간다. 이러한 특성 때문에, Macvlan 네트워크를 쓰면 호스트와 컨테이너 간 통신이 일반적인 방법과 조금 다르게 동작할 수 있는데 (동일 네트워크 상에서 호스트 <-> 컨테이너 통신 이슈 등), 이를 해결하려면 별도의 설정이 필요할 수도 있다. Macvlan은 주로 컨테이너를 별도의 장비처럼 취급해야 하는 경우나, 기존 네트워크 구조에 통합해야 하는 경우, 혹은 NAT를 피하고 싶을 때 사용된다. 다만 설정이 비교적 복잡하고, 호스트 네트워크 환경에 대한 이해가 필요하므로 초심자에게 익숙한 설정은 아니다.
생성 및 사용
Macvlan 네트워크를 생성할 때는 호스트의 네트워크 인터페이스를 지정하고, 컨테이너에게 할당할 IP 대역 등을 설정해야 한다. 예를 들어 호스트의 물리 인터페이스 eth0를 통해 192.168.1.0/24 네트워크에 컨테이너들을 직접 연결시키고 싶다면
# Macvlan 네트워크 생성 예시 (192.168.1.0/24 대역, 게이트웨이 192.168.1.1, 호스트 인터페이스 eth0 사용)
docker network create -d macvlan \
--subnet=192.168.1.0/24 \
--gateway=192.168.1.1 \
-o parent=eth0 \
my-macvlan-network
이 명령으로 my-macvlan-network를 생성하면, 해당 네트워크에 컨테이너를 연결할 때 컨테이너들은 192.168.1.0/24 범위 내에서 IP를 부여받고 eth0를 통해 통신하게 된다. 이제 --network my-macvlan-network 옵션으로 컨테이너를 실행하면, 컨테이너는 호스트와 동일한 192.168.1.x 대역의 IP를 갖게 되어 외부 라우터나 다른 장비에서도 그 IP로 컨테이너에 접근할 수 있다.
Docker Host-Gateway (host.docker.internal 활용)
Docker 20.10 버전부터 도입된 Host-Gateway 기능은 리눅스 환경에서 컨테이너가 호스트(machine)와 통신하는 것을 쉽게 해주는 기능이다. 기존에 Docker for Mac/Windows 환경에서는 컨테이너 안에서 host.docker.internal이라는 특수 도메인 이름을 사용하면 호스트(예: 개발 PC 자체)를 가리켜서 통신할 수 있었다. 하지만 리눅스 Docker 환경에서는 기본적으로 이 도메인이 제공되지 않아, 컨테이너에서 호스트로 연결하려면 호스트의 IP (예: 172.17.0.1 혹은 호스트의 실제 네트워크 IP)를 수동으로 알아내어 사용해야 하는 불편함이 있었다. Host-Gateway는 이러한 문제를 해결하기 위해 Docker 엔진에 추가된 개선사항이다.
의미와 역할
Host-Gateway는 Docker의 가상 브리지 네트워크 게이트웨이 IP(일반적으로 docker0의 IP, 보통 172.17.0.1)를 가리키는 키워드이다. 이 키워드를 이용하면 Docker 컨테이너의 hosts 파일에 호스트를 대표하는 엔트리를 손쉽게 추가할 수 있다. 예를 들어 host.docker.internal이라는 이름을 Host-Gateway IP에 매핑시키면, 컨테이너 입장에서는 이 도메인으로 호스트를 가리킬 수 있게 된다. 한 마디로 리눅스에서도 host.docker.internal 이름을 쓸 수 있게 된 것이다.
사용 방법
Host-Gateway 기능은 docker run 시 --add-host 옵션이나 Docker Compose의 extra_hosts 설정을 통해 사용할 수 있다. host-gateway라는 예약어를 호스트 IP 대신 쓰면 Docker가 알아서 현재 호스트의 게이트웨이 주소로 치환해 준다. 사용 형식은 "호스트이름:host-gateway" 형태로 지정하면 된다. 일반적으로 호스트를 가리키는 이름으로 host.docker.internal을 많이 사용하므로, 다음과 같이 설정한다.
# 컨테이너 실행 시 host.docker.internal을 호스트 게이트웨이 IP로 추가
docker run -d --add-host host.docker.internal:host-gateway myapp-image
위와 같이 설정한 컨테이너 myapp 내부에서는 host.docker.internal이라는 호스트명을 호스트 머신의 IP로 인식하게 된다. 예를 들어 호스트에서 데이터베이스가 돌아가고 있고, 컨테이너 내부 애플리케이션에서 데이터베이스 호스트를 host.docker.internal로 지정하면 리눅스 환경에서도 문제없이 호스트의 데이터베이스에 접속할 수 있다.
작동 원리
기능을 사용하면 Docker가 컨테이너의 /etc/hosts 파일에 host.docker.internal 엔트리를 추가하게 된다. 기본 bridge 네트워크에서는 호스트의 게이트웨이 IP가 172.17.0.1이므로, /etc/hosts에 172.17.0.1 host.docker.internal 항목이 생긴다. 컨테이너에서 ping host.docker.internal 등을 해보면 응답이 오는 것을 확인할 수 있다. 이를 통해 이전까지 리눅스 Docker에서 번거로웠던 "컨테이너 -> 호스트" 접근이 간편해진다.
응용 예시
개발 환경에서 애플리케이션 일부는 컨테이너화했지만, 데이터베이스는 로컬 호스트에 그대로 두는 경우가 있을 수 있다. 이때 컨테이너화된 애플리케이션이 호스트의 DB에 접근하려면 --add-host 설정으로 host.docker.internal을 추가해주는 것이 편리하다. Windows나 Mac에서는 Docker가 자동으로 이 주소를 제공하지만, Linux에서는 위와 같이 수동 지정이 필요하다. Host-Gateway 기능 도입 전에는 컨테이너 실행 스크립트에 호스트 IP를 하드코딩하거나, 네트워크를 host 모드로 쓰는 등의 우회가 필요했지만 이제는 Compose 파일에 한 줄 추가하는 것으로 해결된다.