Post

[Docker] 포트포워딩과 호스트 [Localhost](http://Localhost) 접근

Docker를 사용하다 보면 컨테이너의 서비스를 외부에 노출하거나, 반대로 컨테이너 내부에서 개발 환경의 다른 서비스(예: 호스트 머신에서 실행 중인 DB)에 접근해야 하는 경우가 있다. 오늘은 Docker 네트워킹에서 가장 자주 마주치는 두 가지 시나리오인 포트 포워딩 (-p 옵션)과 컨테이너에서 호스트의 localhost 서비스에 접근하는 방법에 대해 정리 해보고자 한다.

핵심 포인트

  • Docker 컨테이너는 기본적으로 호스트와 분리된 네트워크 환경(브릿지 모드)을 가진다.
  • p <호스트 포트>:<컨테이너 포트> 옵션은 외부에서 컨테이너 내부 서비스로 접근하기 위한 포트 포워딩 설정이다. (Host -> Container)
  • -net="host" 옵션은 컨테이너가 호스트의 네트워크를 그대로 공유하게 만들어, 네트워크 격리를 포기하는 대신 호스트 서비스 접근을 쉽게 한다. (p 사용 불가 및 보안 위험 증가)
  • 컨테이너에서 호스트 머신의 localhost 서비스에 접근하려면, host.docker.internal (Docker Desktop 또는 Linux + -add-host) 또는 호스트 게이트웨이 IP (구버전 Linux)를 사용해야 한다.
  • 가장 권장되는 방법은 기본 브릿지 네트워크를 유지하며, p 옵션과 host.docker.internal (또는 -add-host)을 함께 사용하는 방법.
  • 컨테이너에서 호스트 서비스 접근 시 Connection refused 오류가 발생하면, 호스트 서비스가 127.0.0.1이 아닌 0.0.0.0에서 리스닝하는지 확인.
  • Linux 환경에서 host.docker.internal을 사용하려면 Docker 버전 20.10 이상에서 -add-host=host.docker.internal:host-gateway 플래그를 추가하는 것이 좋다.

Docker의 기본 네트워크: 브릿지(Bridge) 모드와 포트 포워딩 (p)

Docker 컨테이너를 별다른 네트워크 설정 없이 실행하면, 기본적으로 ‘bridge’라는 가상 네트워크에 연결된다. 이 네트워크는 호스트 머신과는 독립된 공간처럼 작동하며, 컨테이너는 자신만의 내부 IP 주소(예: 172.17.0.2)를 할당받는다.

p 옵션: 포트포워딩

  • 목적: 호스트 머신 외부 또는 호스트 머신 자체에서 컨테이너 내부에서 실행 중인 서비스(예: 웹 서버, API)에 접근할 수 있도록 경로를 열어주는 역할
  • 작동 방식: 호스트 머신의 특정 포트로 들어오는 네트워크 요청을 컨테이너 내부의 지정된 포트로 전달(포워딩)한다.
  • 사용법: docker run -p <호스트 포트>:<컨테이너 포트> [이미지 이름]
    • 예시: docker run -d -p 8080:80 nginx
      • 호스트 머신의 8080 포트로 들어오는 모든 요청이 컨테이너 내부의 80 포트(Nginx 기본 포트)로 전달된다.
      • 이제 웹 브라우저에서 http://<호스트 IP>:8080 또는 http://localhost:8080으로 접속하면 Nginx 기본 페이지를 볼 수 있다.
  • 핵심: p 옵션은 외부에서 컨테이너 내부로 들어오는 길을 열어주는 것입니다.

호스트 네트워크 직접 사용: -net="host" 옵션

컨테이너가 호스트 머신의 네트워크 환경을 그대로 사용해야 하는 경우, 예를 들어, 컨테이너가 호스트의 localhost나 특정 네트워크 인터페이스에 직접 접근해야 하거나, 네트워크 성능 오버헤드를 최소화하고 싶을 때 사용한다.

  • 작동 방식: 컨테이너가 독립된 네트워크 공간을 갖지 않고, 호스트 머신의 네트워크 스택(네임스페이스)을 공유한다. 컨테이너는 별도 IP를 갖지 않으며, 호스트의 IP와 포트를 직접 사용하게 된다.
  • 사용법: docker run --net="host" [이미지 이름]
  • 주요 특징:
    • 컨테이너 내부에서 localhost는 호스트 머신의 localhost를 의미
    • p 옵션은 사용할 수 없거나 무시됩니다. 컨테이너가 사용하는 포트가 곧 호스트의 포트가 된다.
    • 네트워크 성능은 약간 향상될 수 있다.
  • 장점:
    • 호스트의 localhost 서비스 접근 용이.
    • 네트워크 성능 오버헤드 감소.
    • 포트 매핑 불필요.
  • 단점:
    • 보안 위험 증가: 컨테이너가 호스트 네트워크에 완전히 노출되어 격리의 이점이 사라지게 된다. 또한 컨테이너가 침해되면 호스트 전체 네트워크에 영향을 줄 수 있다.
    • 포트 충돌: 컨테이너가 사용하려는 포트를 호스트가 이미 사용 중이면 실행되지 않는다.
    • 이식성 제한: 주로 Linux 환경에서 사용되며, Docker Desktop 환경에서는 동작 방식이 다르다.

⭐ 포트 포워딩과 호스트 localhost 동시 접근

컨테이너의 서비스를 외부에도 노출하고(-p 사용), 동시에 컨테이너 내부에서 호스트의 localhost 서비스(예: DB)에도 접근하고 싶다면 –net=”host”는 -p를 못 쓰게 하니 사용하면 안된다. 이럴때, host.docker.internal 을 사용해서 기본 브릿지 네트워크를 유지하면서, -p 옵션으로 포트를 노출하고, 컨테이너에서 호스트로 접근할 수 있다.

  1. Docker Desktop (Mac/Windows) 환경:
    • 별도의 설정 없이 컨테이너 내부에서 host.docker.internal이라는 특수 DNS 이름을 사용하면 호스트 머신에 접근할 수 있다.
    • 예시: 컨테이너 앱 설정에서 DB 호스트를 localhost 대신 host.docker.internal로 지정 (host.docker.internal:11434)
  2. Linux 네이티브 Docker 환경 (Docker 20.10 이상 권장):
    • docker run 명령어에 -add-host=host.docker.internal:host-gateway 플래그를 추가
    • host-gateway는 Docker가 자동으로 호스트의 게이트웨이 IP 주소로 해석해주는 값이다.
    • 컨테이너 내부에서는 Docker Desktop과 동일하게 host.docker.internal을 사용.
    • 예시: docker run -d -p 8080:80 --add-host=host.docker.internal:host-gateway my-app-image
  3. Linux 네이티브 Docker 환경 (구 버전):
    • 호스트의 docker0 브릿지 인터페이스 IP 주소(보통 172.17.0.1)를 직접 알아내서 사용한다. (ip addr show docker0 명령 등으로 확인)
    • 컨테이너 앱 설정에서 이 IP 주소를 직접 사용한다. (예: 172.17.0.1:11434)
    • 이 방법은 IP가 변경될 수 있어 권장되지 않는다.

실제 적용 예시 (Linux 기준, Docker 20.10+)

1
2
3
4
5
6
7
8
9
10
# 호스트의 11434 포트에서 Ollama 서비스가 실행 중이라고 가정
# 컨테이너의 9090 포트를 호스트의 49090 포트로 노출하고,
# 컨테이너 내부에서 호스트의 Ollama 서비스에 접근 가능하도록 실행

docker run -d --name my-dev-container \\
  -p 49090:9090 \\
  --add-host=host.docker.internal:host-gateway \\
  -e OLLAMA_API_ENDPOINT=http://host.docker.internal:11434 \\
  my-image:latest

: 위 예시에서 컨테이너 내부 애플리케이션은 OLLAMA_API_ENDPOINT 환경 변수를 읽어 http://host.docker.internal:11434 주소로 호스트의 Ollama 서비스에 접근할 수 있으며 동시에 외부에서는 http://<호스트 IP>:49090으로 컨테이너의 9090 포트 서비스에 접근할 수 있다.


외부 서비스를 노출할 때는 -p 옵션을, 컨테이너에서 호스트 localhost에 접근해야 할 때는 host.docker.internal (또는 –add-host 플래그)을 사용하는 것이 일반적이고 안전한 방법이다. –net=”host”는 강력하지만 보안과 충돌 위험을 고려하여 신중하게 사용해야 한다.

This post is licensed under CC BY 4.0 by the author.

© chanho. Some rights reserved.