본문 바로가기

TroubleShooting/DevOps

Docker - Special DNS 문제

배경

실무 회사 앱은 대부분 docker 환경으로 구성되어 있으며 일부분 kubernetes로 구성되어 있습니다.

그중 오늘의 주제와 관련돼서 확인했던 서비스의 개발, 운영 환경은  macOS / Windows(WSL) 
기반의 Dockerized 환경이었습니다.


각 서비스는 컨테이너로 구성돼 프록시(nginx)를 거치거나, 필요하면 호스트의 지정 포트로
직접 연결했는데 host.docker.internal:포트 라는 방식을 이용해 통신했었습니다.

 

각 서비스는 패키징 되어 window OS에서 wsl 기반으로 배포되는 환경이었는데 테스트는 mac OS, 
wsl 각각의 환경에서 진행되어 패키징 하는 환경에서 window OS를 ubuntu로 migration을 진행했고
그에 따라 linux로 각 서비스를 옮겨서 배포하고 테스트를 진행해야 했습니다.

 

그 과정에서 문제가 발생했는데 mac, wsl에선 통신이 잘 되던 서비스들이 linux로 옮겨 통신하자
dns를 읽지 못하고 통신이 되지 않는 이슈가 발생했던 것이었습니다.

 

지금부터 이 이슈를 분석하고 이해한 뒤 어떻게 해결해 나가는지 글을 통해 공유해 드리겠습니다.

문제 현상

현업에서 이슈를 확인했을 때 자료를 남겨놓지 못해 ubuntu 22.04 환경에서 똑같이 문제를 재현하여 현상을
보여드리도록 하겠습니다.

 

호스트의 80 포트로 바인딩되어있는 nginx의 서비스입니다.

 

해당 nginx 서비스 내부로 들어가 host.docker.internal이라는 dns를 사용해 테스트를 진행한 결과 ubuntu에선  해당 dns를 
사용하여 통신할 수 없는 현상이 발생했습니다.


실무에서도 마찬가지로 linux로 서비스를 옮겨 테스트를 진행했을 때 mac, wsl에선 잘만 사용하던 dns를 사용하지 못해
서비스 간의 통신 장애가 발생했고 linux 계열의 Docker Engine에선 해당 dns를 사용하지 못하는 걸 알 수 있었습니다.

여기서 사용되는 special dns가 뭘까?

이런 DNS는 컨테이너에서 호스트(개발 머신)로 접속하기 위한 특수 DNS 이름입니다.

 

Docker Desktop이 내부 네트워크 관리 컴포넌트(vpnkit for macOS, docker-desktop WSL intergration for windows)가
컨테이너 시작 시 네임 리졸버에 직접 주입하게 됩니다.

 

이 매핑은 docker-desktop 네트워크 내부 dns 서버에서 처리되며 실제 IP는 192.168.65.2(mac) 혹은 wsl VM의 NAT 
게이트웨이 주소로 해석되어 처리되며  호스트의 내부 게이트웨이 IP로 해석됩니다.


제 경우엔 host.docker.internal이었지만 같은 맥락으로 gateway.docker.internal도 제공됩니다.

현상 분석

mac, wsl에선 docker desktop에서 docker 서비스가 구동되어 있었고 linux에선 docker engine을 통해 서비스를 구동하므로
이러한 차이에서 이슈를 파악하고 해결하려고 했던 과정은 다음과 같습니다.

 

1. wsl, mac 에선 해당 dns를 어떻게 인식하고 사용하고 있었는지

 

위 사진은 mac OS 기준에서 nginx 서비스를 구동시켜 host.docker.internal:80 포트로 get 요청을 보냈었는데 이를 통해
docker desktop에서는 호스트 서비스끼리 통신할 때 해당 dns를 사용할 수 있었구나 라는걸 알 수 있었습니다.

 

2. linux계열의 docker engine에선 되지 않는 이유는 무엇인가?

 

docker 공식 문서 스택 오버플로우에서 확인한 결과 docker desktop 같은 개발 환경에서만 사용이 가능했으며 docker engine에선 
컨테이너가 구동했을 때 로컬 dns로 host.docker.internal과 같은 special dns를 지정해주지 않았기 때문임을 알 수 있었습니다.


조금 더 구체적으로 파고들자면 container에선 dns를 질의하는 방식이 /etc/nsswitch.conf라는 파일에 정의된 대로 files dns를 
질의하게 되어있는데 special dns는 외부 dns를 질의해선 알 수 없는 custom dns이며 /etc/hosts 등 내부 캐시에 정의되어 있어야
하는데 docker desktop의 사용 유무에 따라 files에 되어있지 않았기 때문에 linux에선 사용하지 못하는 것이었습니다.

그럼 Docker Desktop을 제외한 환경에서 호스트 서비스로 dns를 통해 연결하는 방법은?

스택 오버플로우에선 다음과 같은 방법으로 똑같이 special dns를 사용할 수 있다고 명시하고 있습니다.

### docker cli 
docker run --add-host=host.docker.internal:host-gateway nginx

### docker compose

services:
	container:
    	extra_hosts:
		    - "host.docker.internal:host-gateway"

 

위와 같이 special dns를 사용할 수 있게 정의한다면 dns의 Ip를 제일 먼저 질의하는 files에 해당 dns의 Ip가 매핑되어
사용될 수 있는 것임을 알 수 있게 됩니다.

이 Special DNS를 사용하는 이유는?

  • 호스트 IP는 환경/가상화에 따라 달라질 수 있고 개발하는 단계에서 mac이나 wsl 등 host ip로 접근하는 경우
    ip의 접근을 효율화 하기 때문입니다.
  • 이 이름을 쓰면 IP를 하드코딩하지 않고 컨테이너→호스트 접근을 안정적으로 구성할 수 있습니다.

Special Dns를 꼭 사용해야 하는가?(반론)

  • Special Dns는 docker desktop에서도 개발을 위해 편의성으로 나온 기능이며 실무에서는 활용될 여지가 적고 
    다른 대안을 무시하며 사용해야 할 만큼 특별한 dns가 아닙니다.
  • Special Dns는 bridge network를 사용하는 컨테이너에서 호스트를 편하게 접근하기 위한 개발용도가 가장 크며 
    호스트의 IP를 직접 명시하거나 컨테이너를 bridge Network의 대역대로 두어 Container:port로 접근할 수도 있습니다.
  • /etc/hosts에 접근하기 위한 대역대를 별칭으로 둔다면 Special Dns가 아니라 Custom Dns로 활용하여 대안으로도
    활용이 가능하여 host.docker.internal을 쓰지 않아도 됩니다.

Special Dns에 대한 생각

  • 호스트의 IP를 자동으로 매핑해 주는 Special Dns는 개발단계에서의 편의성과 더불어 다른 노드로 배치될 수 있는 
    유연성을 고려한다면 매 Host의 Ip를 알지 못해도 해당 호스트 Ip에 접근할 수 있는 좋은 방법이 될 수 있습니다.

  • host.docker.internal은 어디까지나 docker를 사용하는 컨테이너 환경에서만 사용가능한 방법이며 /etc/hosts에
    정보를 적거나 갱신해야 하는 복잡성을 고려한다면 개발 환경과 실무 환경에서의 환경 편차를 줄이고 항상 접근 가능한
    endpoint를 두어 통신하게 하는 방법이 될 수 있습니다.

  • Container가 호스트에 바인딩되어 있지 않다면 무의미하며 이 방법은 모든 상황에서의 최선이라거나 정답에 해당하는
    방법은 될 수 있지 않지만 Docker Desktop에서 제공하는 이유와 사용하게 될 거라면 사용하게 될 이유,
    사용하지 않아야 할 이유를 모두 고려했을 때의 최선의 방법은 될 수 있지 않을까라고 생각합니다.