컨테이너는 정상 실행됐는데 브라우저에서는 접속이 안 되고, 다른 컨테이너에서는 DB 이름을 찾지 못하는 상황이 자주 발생합니다. Docker 컨테이너 네트워크 구성은 “컨테이너끼리 통신하는 경로”와 “호스트 또는 외부 사용자가 컨테이너로 들어오는 경로”를 구분하면 훨씬 쉽게 이해할 수 있습니다.
Docker 컨테이너 네트워크 구성의 핵심 개념
Docker 컨테이너는 기본적으로 격리된 네트워크 네임스페이스 안에서 실행됩니다. 컨테이너 내부에서 보면 독립적인 IP 주소, 네트워크 인터페이스, 라우팅 테이블, DNS 설정이 있는 것처럼 보입니다. 하지만 실제 패킷 전달, NAT, 포트 게시, 브리지 연결은 Docker Engine과 호스트 커널 네트워크 기능이 함께 처리합니다.
Docker 공식 Networking overview에 따르면 Docker는 여러 네트워크 드라이버를 제공하며, Linux 환경에서는 기본적으로 bridge 네트워크가 생성됩니다. 별도 옵션 없이 docker run을 실행하면 컨테이너는 보통 기본 bridge 네트워크에 연결됩니다.
컨테이너 내부 통신과 외부 접근은 다르다
같은 Docker 네트워크 안의 컨테이너끼리 통신하는 것과, 노트북 브라우저나 외부 사용자가 컨테이너 서비스에 접속하는 것은 다른 문제입니다. 컨테이너 간 통신은 같은 네트워크에 붙어 있고 대상 포트에서 애플리케이션이 리스닝하면 됩니다. 반면 외부에서 들어오려면 Docker 포트 매핑, 즉 -p 또는 --publish 옵션으로 호스트 포트를 컨테이너 포트에 연결해야 합니다.
Docker 네트워크 드라이버 선택 기준
bridge 네트워크
Docker bridge 네트워크는 단일 Docker 호스트에서 가장 많이 쓰는 방식입니다. 웹, API, DB처럼 하나의 서버 안에서 여러 컨테이너가 서로 통신해야 하는 개발·소규모 운영 환경에 적합합니다. 특히 기본 bridge보다 사용자 정의 bridge를 만드는 편이 실무적으로 유리합니다. 컨테이너 이름 기반 DNS, 네트워크별 격리, 실행 중 연결과 분리, subnet 같은 개별 설정을 더 깔끔하게 다룰 수 있기 때문입니다.
host 네트워크
host 네트워크는 컨테이너가 Docker 호스트의 네트워크 네임스페이스를 공유하는 방식입니다. NAT를 거치지 않아 성능 최적화나 많은 포트를 다루는 특수 상황에서 검토할 수 있지만, 네트워크 격리가 줄고 포트 충돌 가능성이 커집니다. 또한 이 모드에서는 일반적인 -p, --publish 포트 매핑 개념이 적용되지 않습니다. 플랫폼과 Docker 버전에 따라 지원 방식도 다를 수 있으므로 운영 적용 전 확인이 필요합니다.
overlay, none, macvlan, ipvlan
overlay 네트워크는 여러 Docker 데몬 호스트 사이에서 컨테이너 통신을 구성할 때 사용하며 Swarm 같은 환경에서 자주 등장합니다. none은 네트워크를 끈 격리 용도에 가깝고, macvlan과 ipvlan은 컨테이너를 물리 네트워크나 VLAN에 더 직접적으로 연결하는 고급 구성입니다. 일반적인 Docker 컨테이너 네트워크 구성 입문과 실무 적용은 bridge와 Compose 네트워크를 먼저 확실히 익히는 것이 좋습니다.
기본 bridge와 사용자 정의 bridge의 차이
기본 bridge에서도 컨테이너가 IP 주소로 서로 접근할 수 있습니다. 그러나 컨테이너 IP는 재생성 시 바뀔 수 있고, 이름 기반 통신도 사용자 정의 bridge만큼 자연스럽지 않습니다. 과거의 --link 방식은 현대적인 구성에서는 권장하기 어렵습니다.
사용자 정의 bridge에서는 같은 네트워크에 연결된 컨테이너들이 컨테이너 이름 또는 alias로 서로를 찾을 수 있습니다. 예를 들어 web 컨테이너와 api 컨테이너가 같은 app-net에 있으면 IP를 외우지 않고도 http://web 같은 이름으로 접근할 수 있습니다. 앱별로 네트워크를 분리하면 불필요한 컨테이너 간 통신 범위도 줄일 수 있습니다. 다만 네트워크 분리는 보안 계층 중 하나일 뿐, 인증·권한·방화벽·비밀 관리까지 함께 고려해야 합니다.
사용자 정의 bridge 네트워크 실습
가장 먼저 앱 전용 네트워크를 만듭니다. 네트워크 목록과 상세 설정은 docker network inspect로 확인합니다.
docker network create app-net
docker network ls
docker network inspect app-net
이제 두 컨테이너를 같은 네트워크에 연결해 봅니다.
docker run -d --name web --network app-net nginx
docker run -it --rm --name client --network app-net busybox sh
client 컨테이너 안에서 wget -qO- http://web처럼 요청하면 같은 사용자 정의 bridge 안의 web 컨테이너로 접근할 수 있습니다. 여기서 중요한 점은 호스트 포트가 아니라 컨테이너가 실제로 리스닝하는 내부 포트를 사용한다는 것입니다.
이미 실행 중인 컨테이너도 네트워크에 연결하거나 분리할 수 있습니다.
docker network connect app-net existing-container
docker network disconnect app-net existing-container
Docker 포트 매핑과 EXPOSE의 차이
-p HOST_PORT:CONTAINER_PORT 이해하기
외부에서 컨테이너의 Nginx에 접속하려면 다음처럼 포트를 게시합니다.
docker run -d -p 8080:80 nginx
이 명령은 호스트의 8080 포트로 들어온 트래픽을 컨테이너 내부의 80 포트로 전달합니다. 앞의 8080은 사용자가 브라우저에서 접근하는 호스트 포트이고, 뒤의 80은 컨테이너 안에서 Nginx가 듣고 있는 포트입니다. 많은 장애가 8080:80을 반대로 이해해서 발생합니다.
Docker의 Publishing and exposing ports 문서에서도 포트 게시와 노출 개념을 구분합니다. 포트를 게시하면 기본적으로 여러 네트워크 인터페이스에 열릴 수 있으므로, DB나 관리자 도구를 무심코 -p 5432:5432로 공개하지 않아야 합니다.
EXPOSE는 자동 공개가 아니다
Dockerfile의 EXPOSE 80은 이미지가 어떤 포트를 사용할 의도가 있는지 알려주는 메타데이터에 가깝습니다. 이것만으로 외부 접속이 가능해지지 않습니다. 실제 외부 접근에는 -p, --publish, 또는 Compose의 ports 설정이 필요합니다.
Docker Compose 네트워크 구성
Docker Compose 네트워크는 서비스 단위 개발 환경에서 특히 편리합니다. Compose는 기본적으로 프로젝트 단위의 bridge 네트워크를 만들고, 각 서비스를 그 네트워크에 연결합니다. 같은 Compose 네트워크 안에서는 컨테이너 IP 대신 서비스명을 DNS 이름처럼 사용할 수 있습니다. 예를 들어 웹 애플리케이션이 DB에 접속할 때 postgres://db:5432처럼 쓰는 이유가 여기에 있습니다.
services:
web:
image: nginx
ports:
- "8080:80"
app:
image: busybox
command: sleep 3600
ports는 호스트에 포트를 게시해 로컬 브라우저나 외부에서 접근하게 만드는 설정입니다. 반면 컨테이너 간 내부 통신은 일반적으로 서비스명과 컨테이너 포트를 사용합니다. 컨테이너 IP는 재생성될 때 바뀔 수 있으므로 Compose에서는 IP 고정에 의존하기보다 서비스명 DNS를 사용하는 편이 안정적입니다.
운영에 가까운 구성에서는 네트워크를 나누는 방식도 고려합니다. 예를 들어 reverse proxy는 frontend와 backend에 모두 연결하고, API와 DB는 backend에만 연결합니다. 이렇게 하면 DB가 프론트엔드 네트워크에 불필요하게 노출되지 않습니다.
services:
proxy:
image: nginx
networks: [frontend, backend]
ports:
- "80:80"
api:
image: my-api
networks: [backend]
db:
image: postgres
networks: [backend]
networks:
frontend:
backend:
방화벽, NAT, iptables 주의점
Linux에서 Docker는 bridge 네트워크 격리, 포트 게시, NAT와 masquerading을 위해 방화벽 규칙을 생성합니다. 이 규칙을 임의로 삭제하거나 호스트 방화벽과 충돌시키면 컨테이너 네트워크가 갑자기 동작하지 않을 수 있습니다.
특히 ufw를 사용하는 서버에서는 “ufw에서 막았으니 Docker 포트도 막히겠지”라고 단정하면 위험합니다. Docker 포트 게시 트래픽은 NAT 테이블을 거치며 일반 INPUT/OUTPUT 규칙과 기대와 다르게 보일 수 있습니다. 운영 환경에서는 세 가지를 함께 점검해야 합니다. Docker의 ports 또는 -p 설정, 호스트 방화벽 정책, 그리고 클라우드 보안 그룹 또는 네트워크 ACL입니다.
접속이 안 될 때 확인할 체크리스트
같은 네트워크에 연결되어 있는가
docker network inspect app-net
대상 컨테이너가 같은 네트워크의 Containers 목록에 있는지 확인합니다. 서로 다른 bridge 네트워크에 있으면 이름을 알아도 통신되지 않습니다.
서비스명과 컨테이너명을 혼동하지 않았는가
Compose에서는 보통 서비스명을 사용합니다. 단독 docker run 환경에서는 사용자 정의 bridge에서 컨테이너 이름이나 network alias를 사용합니다.
호스트 포트와 컨테이너 포트를 반대로 쓰지 않았는가
8080:80은 호스트 8080에서 컨테이너 80으로 전달한다는 뜻입니다. 컨테이너 간 통신에서는 대개 호스트 포트 8080이 아니라 컨테이너 포트 80을 사용합니다.
포트가 실제로 게시되었는가
docker ps
docker container port <container>
PORTS 항목에 게시 정보가 없다면 외부에서 직접 들어올 수 없습니다. 또한 애플리케이션이 컨테이너 내부에서 127.0.0.1에만 바인딩되어 있으면 외부 인터페이스에서 접근이 제한될 수 있으므로 0.0.0.0 바인딩 여부도 확인합니다.
방화벽과 보안 그룹이 막고 있지 않은가
로컬 Docker에서는 접속되지만 클라우드 VM에서는 안 된다면 인스턴스 방화벽, 클라우드 보안 그룹, 로드밸런서 리스너, 회사 네트워크 정책까지 확인해야 합니다.
운영 환경 선택 가이드
- 단일 호스트 개발 환경: 사용자 정의 bridge 또는 Docker Compose 기본 네트워크를 우선 고려합니다.
- 외부 접근이 필요한 웹 서비스: 필요한 포트만
-p또는ports로 게시합니다. - DB, 캐시, 내부 큐: 가능하면 호스트에 포트를 공개하지 않고 내부 네트워크에서만 접근하게 둡니다.
- 여러 호스트 간 통신: overlay 네트워크나 Kubernetes 같은 오케스트레이션 환경을 검토합니다.
- 저수준 네트워크 요구: host, macvlan, ipvlan을 검토하되 격리 감소와 운영 복잡도를 함께 평가합니다.
결국 Docker 컨테이너 네트워크 구성의 핵심은 “같은 네트워크에 묶을 대상만 묶고, 외부에 필요한 포트만 열며, IP 대신 이름 기반 통신을 사용하고, 방화벽까지 함께 검증하는 것”입니다. 이 원칙만 지켜도 컨테이너 간 통신 문제와 불필요한 포트 노출 위험을 크게 줄일 수 있습니다.