Docker Compose는 여러 옵션이 붙은 긴 docker run 명령을 compose.yaml 파일 한 개로 정리하고, docker compose up -d 한 줄로 실행하게 해 주는 도구입니다. 지금 쓰는 Compose는 Docker CLI에 붙는 플러그인 방식이라 명령도 하이픈 없이 docker compose로 입력합니다. 이 글에서는 설치 확인부터 nginx 웹서버 하나를 띄워 보는 첫 compose 파일 작성, 자주 쓰는 명령과 오류 해결까지 순서대로 정리했습니다.
컨테이너를 두 개 이상 띄우기 시작하면 가장 먼저 만나는 질문이 “웹 컨테이너에서 데이터베이스 컨테이너에 어떻게 접속하지?”입니다. 결론부터 말씀드리면, 두 컨테이너를 같은 사용자 정의 네트워크에 연결하면 IP 주소 대신 컨테이너 이름이나 Compose 서비스 이름으로 접속할 수 있습니다. 이 글에서는 Docker 네트워크의 기본 구조와 이름으로 통신이 되는 원리, 직접 따라 하는 실습, 그리고 자주 헷갈리는 포트와 localhost 문제까지 정리했습니다.

Docker 설치가 아직이라면 Ubuntu Docker 설치 방법을, compose.yaml 작성이 처음이라면 이전 글 Docker Compose 설치와 사용법을 먼저 보시면 이해가 쉽습니다.
Docker 네트워크는 무엇이고 어떤 종류가 있나요?
Docker 네트워크는 컨테이너들이 연결되는 가상의 스위치라고 생각하면 됩니다. 같은 네트워크에 연결된 컨테이너끼리는 서로 통신할 수 있고, 다른 네트워크에 있는 컨테이너와는 기본적으로 격리됩니다. Docker를 설치하면 네트워크 세 개가 자동으로 만들어집니다.
docker network lsNETWORK ID NAME DRIVER SCOPE
17e324f45964 bridge bridge local
6ed54d316334 host host local
7092879f2cc8 none null localbridge: 기본 네트워크입니다. –network 옵션 없이 실행한 컨테이너는 모두 여기에 연결됩니다.host: 컨테이너가 서버의 네트워크를 그대로 함께 씁니다. 포트 매핑이 필요 없지만 격리도 없습니다.none: 네트워크를 완전히 끈 상태입니다.
특정 네트워크의 서브넷, 게이트웨이, 연결된 컨테이너 목록은 docker network inspect bridge로 확인할 수 있습니다. 문제가 생겼을 때 가장 먼저 보는 명령이니 기억해 두세요.
왜 기본 bridge에서는 컨테이너 이름으로 접속이 안 되나요?
공식 문서에 따르면 기본 bridge 네트워크의 컨테이너끼리는 IP 주소로만 통신할 수 있습니다. 예전에는 --link 옵션으로 이름을 연결했지만 지금은 레거시 기능으로 분류됩니다. 반면 docker network create로 직접 만든 사용자 정의 bridge 네트워크에서는 컨테이너가 서로를 이름이나 별칭으로 찾을 수 있습니다. Docker도 기본 bridge 대신 사용자 정의 네트워크를 쓰라고 권장합니다.

격리 측면에서도 차이가 있습니다. 기본 bridge에는 아무 옵션 없이 띄운 모든 컨테이너가 모이기 때문에 서로 관계없는 서비스끼리도 통신할 수 있습니다. 사용자 정의 네트워크를 쓰면 그 네트워크에 연결한 컨테이너끼리만 통신하므로 더 안전합니다.
컨테이너 이름은 어떻게 IP 주소로 바뀌나요?
비밀은 Docker 엔진에 들어 있는 내장 DNS 서버입니다. 사용자 정의 네트워크에 연결된 컨테이너는 127.0.0.11 주소의 내장 DNS를 사용합니다. 컨테이너 안에서 web이라는 이름을 찾으면 이 DNS가 같은 네트워크에 있는 web 컨테이너의 현재 IP를 알려 줍니다. 목록에 없는 일반 도메인은 서버에 설정된 외부 DNS로 넘겨서 처리합니다.

컨테이너 IP는 다시 만들 때마다 바뀔 수 있습니다. 그래서 애플리케이션 설정에는 IP를 직접 적지 말고 이름을 적어야 합니다. 이름은 컨테이너가 새로 만들어져도 그대로 유지되기 때문입니다. 다만 공식 문서에 따르면 Docker가 자동으로 붙인 임의의 이름이 아니라 직접 지정한 이름만 이렇게 해석됩니다.
두 컨테이너를 같은 네트워크에서 통신시키려면 어떻게 하나요?
직접 확인해 보겠습니다. 먼저 사용자 정의 네트워크를 만들고, 여기에 nginx 컨테이너를 web이라는 이름으로 연결합니다.
docker network create my-net
docker run -d --name web --network my-net nginx:1.27-alpine이제 같은 네트워크에 busybox 컨테이너를 잠깐 띄워서 이름으로 접속해 봅니다. –rm은 명령이 끝나면 컨테이너를 자동으로 지우는 옵션입니다.
docker run --rm --network my-net busybox ping -c 2 web
docker run --rm --network my-net busybox wget -qO- http://webping에 응답이 오고 wget 결과로 nginx 환영 페이지 HTML이 출력되면 성공입니다. 비교를 위해 –network 없이 기본 bridge에서 같은 명령을 실행해 보세요.
docker run --rm busybox ping -c 2 web
# ping: bad address 'web'
이미 실행 중인 컨테이너도 docker network connect my-net 컨테이너이름으로 나중에 연결할 수 있습니다. 실습을 마쳤다면 아래 명령으로 정리합니다.
docker rm -f web
docker network rm my-netDocker Compose에서는 왜 서비스 이름으로 바로 접속되나요?
Compose는 이 과정을 자동으로 해 줍니다. docker compose up을 실행하면 프로젝트이름_default라는 bridge 네트워크를 만들고 모든 서비스를 여기에 연결합니다. 프로젝트 이름은 기본적으로 compose.yaml이 있는 폴더 이름입니다. 각 서비스는 서비스 이름으로 내장 DNS에 등록되므로 networks를 따로 적지 않아도 서로 이름으로 찾을 수 있습니다.
mkdir -p ~/net-demo && cd ~/net-demo이 폴더에 compose.yaml을 만듭니다. 파일 이름은 예전 이름인 docker-compose.yml을 써도 동작하지만, 이 글에서는 공식 기본 이름인 compose.yaml로 통일합니다.
services:
web:
image: nginx:1.27-alpine
ports:
- "8080:80"
client:
image: busybox
command: sleep 3600docker compose up -d
docker network ls | grep net-demo
docker compose exec client wget -qO- http://webnet-demo_default 네트워크가 보이고 client 컨테이너에서 http://web으로 nginx 페이지를 받아 오면 됩니다. 데이터베이스를 붙일 때도 같은 원리라서, 앱 설정의 DB 호스트에 db처럼 서비스 이름을 적으면 됩니다.
ports와 expose는 무엇이 다르고 localhost는 왜 안 되나요?
많이 헷갈리는 부분입니다. ports: "8080:80"은 서버(호스트)의 8080번 포트를 컨테이너의 80번 포트로 연결하는 설정입니다. 즉 브라우저나 서버 바깥에서 접속할 때만 필요합니다. 같은 네트워크의 컨테이너끼리는 공개 여부와 상관없이 컨테이너 포트로 직접 통신합니다. 그래서 client에서는 http://web:8080이 아니라 http://web:80, 즉 http://web으로 접속해야 합니다.

expose는 호스트에 공개하지 않고 이 서비스가 어떤 포트를 쓰는지 적어 두는 설정입니다. 같은 네트워크에서는 expose가 없어도 접속되므로 사실상 문서 역할에 가깝습니다. 데이터베이스처럼 바깥에서 접속할 필요가 없는 서비스는 ports를 아예 빼는 것이 보안상 좋습니다.
또 하나, 컨테이너 안에서 localhost는 서버가 아니라 그 컨테이너 자기 자신을 뜻합니다. 앱 컨테이너 설정에 DB 주소를 localhost로 적으면 앱 컨테이너 안에서 DB를 찾다가 실패합니다. 다른 컨테이너에 접속할 때는 항상 서비스 이름을 쓰세요.
Docker 네트워크에서 자주 나는 오류는 어떻게 해결하나요?

bad address 또는 Name does not resolve (이름을 찾지 못함)
두 컨테이너가 서로 다른 네트워크에 있거나, 하나가 기본 bridge에 있는 경우입니다. 다른 Compose 프로젝트에서 띄운 컨테이너도 각자 다른 default 네트워크에 있어서 이름이 통하지 않습니다. 아래 명령으로 두 컨테이너가 같은 네트워크에 들어 있는지 확인합니다.
docker network inspect my-net --format '{{range .Containers}}{{.Name}} {{end}}'Connection refused (연결 거부)
이름은 찾았는데 해당 포트에서 프로그램이 기다리고 있지 않다는 뜻입니다. 호스트 포트(8080)를 적었는지, 컨테이너 포트(80)를 적었는지 먼저 확인합니다. 또 앱이 컨테이너 안에서 127.0.0.1에만 바인딩되어 있으면 다른 컨테이너에서 접속할 수 없으니 0.0.0.0으로 대기하도록 설정을 바꿉니다.
localhost로 접속했더니 실패함
앞에서 설명한 것처럼 컨테이너 안의 localhost는 자기 자신입니다. 설정 파일이나 환경변수에 적힌 localhost, 127.0.0.1을 서비스 이름으로 바꾸면 대부분 해결됩니다.
다음에는 무엇을 배우면 좋을까요?
컨테이너끼리 이름으로 통신하는 원리를 이해했다면, 이제 웹서비스와 데이터베이스를 한 compose.yaml에 묶을 준비가 된 것입니다. 다음 단계로 Docker로 PostgreSQL을 실행하고, .env 파일로 비밀번호를 관리하는 방법을 익히면 WordPress 같은 실제 서비스를 안정적으로 구성할 수 있습니다. 오늘 만든 net-demo 폴더는 docker compose down으로 정리해 두세요.