컨테이너는 각자 격리된 네트워크를 가지므로, 서로 통신하려면 같은 도커 네트워크에 붙인다. 같은 사용자 정의 네트워크(user-defined network) 안에서는 도커 내장 DNS(Domain Name System)가 컨테이너 이름(또는 별칭)을 IP로 풀어 주어, IP 대신 이름으로 접속한다.
docker network create app-net
docker run -d --network app-net --network-alias db \
-e POSTGRES_PASSWORD=secret postgres:17
docker run -d --network app-net -e DB_HOST=db -p 3000:3000 app- 앱은
db:5432로 DB에 닿는다. 컨테이너가 다시 떠서 IP가 바뀌어도 이름은 그대로다 - 문제 해결용으로
nicolaka/netshoot같은 이미지를 같은 네트워크에 띄워dig db로 이름 해석을 확인할 수 있다 - Docker Compose는 프로젝트마다 네트워크를 자동으로 만들고, 서비스 이름이 곧 호스트 이름이 된다
localhost가 가리키는 곳
- 컨테이너 안의
localhost는 그 컨테이너 자신이다. 호스트의 서비스가 아니다 - 컨테이너에서 호스트로 가려면 Docker Desktop에서는
host.docker.internal을 쓴다 - 호스트에서 컨테이너로 가려면
-p 호스트포트:컨테이너포트로 포트를 게시(publish)해야 한다
이 "안에서 보는 주소와 밖에서 보는 주소가 다르다"는 점이 Kafka 같은 서비스에서 함정이 된다(Kafka advertised listeners 함정). 쿠버네티스에서는 Service가 같은 역할(고정 이름 + 로드밸런싱)을 한다(쿠버네티스 핵심 개념). 이름 해석의 일반 원리는 DNS와 CNAME를 본다.
컨테이너로 옮기면 깨지는 주소
개발자 PC에서 localhost:8080이나 hosts 파일에 적은 local.example.test 같은 이름으로 다른 서버를 부르던 설정은 컨테이너 안에서 거의 다 깨진다. localhost는 그 컨테이너 자신이고, PC의 hosts 파일은 컨테이너에 없기 때문이다. 이름을 못 풀면 연결 단계에서 실패하고, 앱에 따라 503 같은 오류로 드러난다. 이미지에 박힌 주소는 고치지 말고 환경변수로 바깥에서 덮어 서비스 이름(http://api:8080)을 가리키게 한다. 같은 이미지를 환경마다 설정만 바꿔 쓰는 원칙은 저장소 역할 분담 (DB·캐시·큐·검색)에도 이어진다.
여러 Compose 프로젝트를 한 네트워크에
Compose는 프로젝트마다 기본 네트워크를 따로 만든다. 인프라(DB·캐시·브로커) Compose와 앱 Compose를 따로 띄우면 서로 이름으로 찾지 못한다. 미리 만든 네트워크를 양쪽에서 external로 선언하면 같은 네트워크에 붙는다.
networks:
default:
name: shared-net
external: true # Compose가 만들지 않는다. 없으면 에러docker network create shared-net 2>/dev/null || true # 이미 있으면 넘어가기
docker compose -f infra.yml up -d
docker compose -f app.yml up -dexternal 네트워크는 Compose 밖에서 수명을 관리한다는 뜻이라 docker compose down이 지우지 않는다.
비밀값
예제에서는 -e로 비밀번호를 넘기지만, 환경변수는 docker inspect나 로그로 새기 쉽다. 운영에서는 비밀 파일 마운트나 시크릿 관리 기능을 쓴다.
출처: Docker 네트워킹: User-defined networks · DNS services · Published ports · Compose networks: external · Networking in Compose: Use an existing network