볼륨·네트워크·컴포즈 · 컨테이너 네트워킹 · 이론
컨테이너는 서로를 어떻게 찾는가
한 줄 요약
네트워크 네임스페이스는 인터페이스와 라우팅 테이블만이 아니라 **루프백
주소와 포트 번호 공간까지 통째로 복제**합니다. 그래서 컨테이너의127.0.0.1 은 호스트의 127.0.0.1 과 완전히 다른 주소입니다.
왜 이게 필요했나
초보자가 밟는 세 가지 증상은 전부 이 한 문장에서 설명됩니다.
1. 브라우저에서 연결이 거부된다
2. 옆 컨테이너에서 curl http://api:3000 이 이름을 못 찾는다
3. 컨테이너에서 curl http://127.0.0.1:5432 로 호스트 DB 에 못 붙는다
세 번째가 특히 헷갈립니다. 컨테이너 안에서 127.0.0.1 로 접속하면
그 컨테이너 자신을 가리킵니다. 호스트가 아닙니다.
어떻게 동작하나
기본 브리지 네트워크에서는 컨테이너 이름으로 서로를 찾을 수 없습니다.
docker run -d --name db postgres:16docker run --rm alpine:3.20 ping -c1 db# ping: bad address 'db'사용자 정의 네트워크를 만들어 붙이면 됩니다. 차이는 컨테이너 안의/etc/resolv.conf 에 그대로 드러납니다 — 네임서버가 컨테이너
네임스페이스 안에만 존재하는 주소로 잡히고, 그 요청이 런타임의 리졸버로
넘어갑니다. 리졸버는 같은 네트워크에 있는 컨테이너의 이름과 별칭을 알고
있어 답해 줍니다. **기본 브리지는 하위 호환을 위해 남아 있는 것이고,
실무에서는 항상 사용자 정의 네트워크를 만들어 씁니다.**
두 번째로 중요한 것은 애플리케이션의 바인딩 주소입니다.
docker exec api ss -tlnp# LISTEN 0 511 127.0.0.1:3000 ...이 소켓에는 호스트에서 들어온 패킷이 절대 닿지 않습니다. 패킷은 eth0
주소로 도착하기 때문입니다. 정답은 0.0.0.0:3000 입니다. "0.0.0.0 은
위험하지 않느냐"는 반론이 있는데, **컨테이너 안에서의 0.0.0.0 은 그
컨테이너의 네트워크 네임스페이스 전체를 뜻할 뿐**이고 외부 노출 여부는
퍼블리싱 바인딩 주소가 결정합니다. 두 계층을 섞으면 둘 다 틀립니다.
현장에서 만나는 모습
포트 퍼블리싱의 기본값은 호스트의 모든 인터페이스입니다.
docker run -d -p 5432:5432 postgres:16 # 위험docker run -d -p 127.0.0.1:5432:5432 postgres:16 # 안전첫 줄은 서버가 공인 IP 를 갖고 있다면 인터넷에 데이터베이스를 연 것과
같습니다. 그리고 호스트 방화벽으로 막았다고 안심하면 안 됩니다 — 컨테이너로
향하는 패킷은 호스트가 최종 목적지가 아니라서 일반적인 INPUT 규칙을 지나지
않고, 그보다 앞선 단계에서 이미 목적지가 바뀝니다. **가장 안전하고 단순한
해법은 애초에 바인딩 주소를 제한하는 것**입니다.
컨테이너끼리만 통신하는 서비스는 아예 퍼블리싱하지 않는 것이 맞습니다.
그리고 컨테이너를 여러 네트워크에 동시에 붙일 수 있다는 점도 기억해
두세요. 프론트엔드 네트워크와 백엔드 네트워크를 나누고 앱만 양쪽에
소속시키면, DB 는 외부에서 아예 보이지 않게 됩니다.
다음 실습에서 할 것
사용자 정의 네트워크를 만들어 이름으로 서로를 찾게 하고, 다른 네트워크의
컨테이너가 정말 보이지 않는지 확인한 뒤, 루프백에만 포트를 퍼블리싱해
봅니다.