Fuwari Banner
지니제스트Tech Archive
Docker7분 소요

Docker 컨테이너 내부 DNS 확인 실패와 외부 연결 문제 해결

•
ggeniezst

Docker 컨테이너가 외부 네트워크 리소스에 접근하지 못하고 DNS 확인 오류를 겪을 때, 호스트 네트워크 설정, Docker 데몬 구성, 그리고 컨테이너 내부 resolv.conf 문제를 심층 분석하고 해결하는 실무 가이드입니다.

Sponsored

프로덕션 환경에서 Docker 컨테이너를 배포하고 운영하다 보면, 컨테이너가 갑자기 외부 인터넷이나 내부 서비스에 접근하지 못하는 난감한 상황에 직면할 때가 있습니다. 특히 Temporary failure in name resolution이나 Could not resolve host와 같은 DNS 관련 오류 메시지는 문제의 원인이 네트워크 계층의 깊숙한 곳에 있음을 암시하며, 단순한 방화벽 설정 이상의 복합적인 분석을 요구합니다. 최근 한 배포 환경에서 Docker 컨테이너 내부의 애플리케이션이 외부 API 엔드포인트에 접근하지 못하고 지속적으로 DNS 확인 실패 오류를 반환하는 문제가 발생했습니다. 호스트 서버 자체에서는 문제없이 외부 연결이 가능했지만, 컨테이너 내부에서는 특정 도메인에 대한 접근이 불가능했습니다.

이러한 문제는 대개 Docker의 네트워크 스택, 호스트 시스템의 DNS 설정, 그리고 때로는 네트워크 방화벽 규칙 사이의 미묘한 상호작용에서 비롯됩니다. 컨테이너가 독립적인 네트워크 네임스페이스를 가지지만, 결국 호스트 시스템의 커널 네트워크 스택과 Docker 데몬의 네트워크 브리지 설정을 통해 외부와 통신하기 때문에, 이들 구성 요소 중 어느 하나라도 잘못되면 전체 연결에 문제가 발생할 수 있습니다. 이번 포스트에서는 Docker 컨테이너 내부에서 발생하는 DNS 확인 실패 및 외부 연결 문제를 심층적으로 분석하고, 실무에서 검증된 단계별 해결 방법을 제시합니다.


Docker 컨테이너 DNS 확인 실패 재현 및 초기 분석

문제는 특정 Docker 컨테이너에서 실행 중인 마이크로서비스가 외부 클라우드 서비스의 API 엔드포인트에 HTTPS 요청을 보낼 때 발생했습니다. 서비스 로그에는 다음과 유사한 오류 메시지가 반복적으로 기록되었습니다.

CODE
WARN  o.s.w.c.RestTemplate - GET request for "https://api.external-service.com/data" resulted in 
status 500 (Internal Server Error); nested exception is org.springframework.web.client.ResourceAccessException: 
I/O error on GET request for "https://api.external-service.com/data": 
api.external-service.com: Temporary failure in name resolution; nested exception is java.net.UnknownHostException: 
api.external-service.com: Temporary failure in name resolution

컨테이너 내부로 접속하여 ping 명령어를 실행해보니, 예상대로 외부 도메인에 대한 DNS 확인이 실패했습니다.

BASH
# docker exec -it [컨테이너_ID_또는_이름] bash
root@container-id:/app# ping api.external-service.com
ping: api.external-service.com: Temporary failure in name resolution

반면, IP 주소를 직접 ping하는 것은 성공했습니다. 이는 네트워크 연결 자체에는 문제가 없지만, 도메인 이름을 IP 주소로 변환하는 DNS 확인 과정에서 문제가 발생했음을 명확히 보여줍니다.

BASH
root@container-id:/app# ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=117 time=12.3 ms

컨테이너 내부의 /etc/resolv.conf 파일을 확인해보니, Docker가 자동으로 설정한 DNS 서버 주소가 포함되어 있었습니다. 기본적으로 Docker는 호스트의 /etc/resolv.conf를 복사하거나, Docker 데몬 설정에 따라 자체 DNS 프록시를 사용합니다.

BASH
root@container-id:/app# cat /etc/resolv.conf
# This file is managed by man:systemd-resolved(8). Do not edit.
#
# This is a dynamic resolv.conf file for connecting local clients to the
# internal DNS stub resolver of systemd-resolved. This file lists all
# configured search domains.
#
# Third party programs must not access this file directly, but only through the
# symlink /etc/resolv.conf in order to make sure that they receive the
# proper list of configured DNS servers from systemd-resolved.
#
nameserver 127.0.0.11
options ndots:0

nameserver 127.0.0.11은 Docker의 내장 DNS 서버 주소입니다. 이 주소는 Docker 데몬이 컨테이너 내부에서 DNS 쿼리를 처리하기 위해 제공하는 특별한 IP입니다. 문제는 이 Docker 내장 DNS 서버가 외부 DNS 서버로 쿼리를 제대로 전달하지 못하거나, 호스트 시스템의 DNS 설정이 올바르지 않아 발생할 수 있습니다.


Docker 데몬 DNS 설정 및 호스트 네트워크 분석

Docker 컨테이너의 DNS 확인 실패는 대부분 Docker 데몬의 설정과 호스트 시스템의 네트워크 환경에서 비롯됩니다. Docker 데몬은 dockerd 프로세스를 통해 실행되며, /etc/docker/daemon.json 파일을 통해 다양한 설정을 관리합니다. 이 파일에 DNS 서버를 명시적으로 지정하지 않으면, Docker는 호스트 시스템의 /etc/resolv.conf를 참조하거나, systemd-resolved와 같은 로컬 DNS 스텁 리졸버를 사용하게 됩니다.

먼저 호스트 시스템의 /etc/resolv.conf를 확인하여 올바른 외부 DNS 서버가 설정되어 있는지 확인합니다.

BASH
# 호스트 시스템에서 실행
cat /etc/resolv.conf

만약 nameserver 127.0.0.53과 같이 로컬 스텁 리졸버만 설정되어 있다면, systemd-resolved가 외부 DNS 쿼리를 처리하는 방식에 문제가 있을 수 있습니다. systemd-resolved는 resolv.conf 심볼릭 링크를 관리하며, 때로는 이 설정이 Docker 컨테이너의 DNS 확인에 영향을 미칩니다.

다음으로, Docker 데몬의 DNS 설정을 확인합니다. /etc/docker/daemon.json 파일에 dns 키가 명시적으로 설정되어 있는지 확인합니다.

JSON
{
  "dns": ["8.8.8.8", "8.8.4.4"]
}

만약 daemon.json 파일에 dns 설정이 없다면, Docker는 기본적으로 호스트의 DNS 설정을 상속받으려 시도합니다. 그러나 systemd-resolved와 같은 로컬 스텁 리졸버가 활성화된 환경에서는 Docker가 외부 DNS 서버를 직접 파싱하는 데 어려움을 겪을 수 있습니다. 이 경우, Docker 데몬이 127.0.0.11로 컨테이너에 제공하는 내장 DNS 서버가 외부 DNS 쿼리를 제대로 전달하지 못하게 됩니다.

또한, 호스트 시스템의 방화벽 규칙(firewalld, ufw, iptables)이 Docker 컨테이너에서 외부 DNS 서버(예: 8.8.8.8:53 UDP/TCP)로의 아웃바운드 연결을 차단하고 있을 가능성도 배제할 수 없습니다.


Docker 데몬 DNS 설정 강제 및 컨테이너 재시작

가장 확실한 해결책 중 하나는 Docker 데몬에 직접 신뢰할 수 있는 외부 DNS 서버를 명시적으로 설정하는 것입니다. 이를 통해 Docker는 호스트의 복잡한 DNS 설정에 의존하지 않고, 컨테이너에 일관된 DNS 서비스를 제공할 수 있습니다.

daemon.json 파일을 편집하거나 생성하여 다음과 같이 Google Public DNS 서버를 설정합니다.

JSON
# /etc/docker/daemon.json
{
  "dns": ["8.8.8.8", "8.8.4.4"],
  "dns-opts": ["ndots:0"]
}

dns-opts에 ndots:0을 추가하는 것은 DNS 쿼리 시 도메인 이름에 점(.)이 포함되어 있지 않더라도 검색 도메인을 추가하지 않고 바로 쿼리하도록 지시하는 옵션입니다. 이는 일부 환경에서 DNS 확인 시간을 단축하거나 불필요한 검색 도메 추가로 인한 문제를 방지하는 데 도움이 될 수 있습니다.

설정 변경 후에는 Docker 데몬을 재시작해야 합니다.

BASH
# Docker 데몬 재시작
sudo systemctl daemon-reload
sudo systemctl restart docker

Docker 데몬 재시작 후, 기존에 실행 중이던 컨테이너는 새로운 DNS 설정을 즉시 반영하지 않습니다. 따라서 문제가 발생했던 컨테이너를 재시작해야 합니다.

BASH
# 문제 컨테이너 재시작
docker restart [컨테이너_ID_또는_이름]

컨테이너 재시작 후, 컨테이너 내부의 /etc/resolv.conf 파일을 다시 확인해보면, nameserver 127.0.0.11은 여전히 유지될 수 있습니다. 이는 Docker의 내장 DNS 프록시가 여전히 활성화되어 있음을 의미합니다. 하지만 이제 이 프록시는 daemon.json에 설정된 8.8.8.8을 사용하여 외부 DNS 쿼리를 처리하게 됩니다.

BASH
# 컨테이너 내부에서 DNS 확인 재시도
docker exec -it [컨테이너_ID_또는_이름] bash
root@container-id:/app# ping api.external-service.com
PING api.external-service.com (142.250.199.174) 56(84) bytes of data.
64 bytes from dfw28s01-in-f14.1e100.net (142.250.199.174): icmp_seq=1 ttl=117 time=12.5 ms

이제 DNS 확인이 성공적으로 이루어지고 외부 서비스에 접근할 수 있게 됩니다.


Docker 네트워크 드라이버와 DNS 상호작용 이해

Docker는 다양한 네트워크 드라이버를 제공하며, 각 드라이버는 DNS 확인 방식에 미묘한 영향을 미칠 수 있습니다. 가장 일반적인 bridge 드라이버의 경우, Docker는 docker0 브리지 인터페이스를 생성하고, 여기에 연결된 컨테이너는 172.17.0.0/16과 같은 서브넷에서 IP 주소를 할당받습니다. 이때 Docker 데몬은 컨테이너에 127.0.0.11을 DNS 서버로 제공하며, 이 주소는 Docker 데몬 자체의 내장 DNS 서버를 가리킵니다. 이 내장 DNS 서버는 컨테이너 간의 이름 확인(예: ping my-other-container)을 처리하고, 외부 도메인에 대한 쿼리는 daemon.json에 설정된 DNS 서버나 호스트의 DNS 설정을 통해 외부로 전달합니다.

만약 host 네트워크 드라이버를 사용한다면, 컨테이너는 호스트의 네트워크 네임스페이스를 공유하므로, 호스트의 /etc/resolv.conf를 직접 사용하게 됩니다. 이 경우 Docker 데몬의 DNS 설정은 컨테이너에 직접적인 영향을 미치지 않습니다.

YAML
# docker-compose.yml 예시 (host 네트워크 사용)
version: '3.8'
services:
  my-app:
    image: my-app-image
    network_mode: "host"

host 네트워크를 사용하는 것은 DNS 문제를 해결하는 간단한 방법이 될 수 있지만, 컨테이너가 호스트의 포트를 직접 사용하게 되어 포트 충돌 가능성이 높아지고, 컨테이너 간 네트워크 격리가 사라진다는 단점이 있습니다. 따라서 일반적으로는 bridge 네트워크를 유지하면서 Docker 데몬의 DNS 설정을 올바르게 하는 것이 권장됩니다.


사이드 이펙트 방지 및 추가 트러블슈팅 FAQ

Q. Docker 데몬 재시작 없이 컨테이너 DNS 설정 변경이 가능한가요?

A. 아니요, /etc/docker/daemon.json 파일을 수정한 경우 반드시 Docker 데몬을 재시작해야 변경 사항이 적용됩니다. Docker 데몬은 시작 시점에 해당 설정을 로드하고, 이 설정을 기반으로 컨테이너에 DNS 프록시를 제공하기 때문입니다. 데몬 재시작 후에는 해당 설정을 사용하는 모든 신규 컨테이너와 재시작된 기존 컨테이너에 새로운 DNS 설정이 적용됩니다.

Q. daemon.json에 DNS 설정을 추가했는데도 여전히 DNS 확인이 실패합니다. 다른 문제는 무엇이 있을까요?

A. 몇 가지 추가적인 원인이 있을 수 있습니다.

  1. 호스트 방화벽: 호스트 시스템의 방화벽(iptables, firewalld, ufw)이 Docker 컨테이너에서 외부 DNS 서버(기본적으로 UDP/TCP 53번 포트)로의 아웃바운드 트래픽을 차단하고 있을 수 있습니다. sudo iptables -L -v 또는 sudo ufw status 등으로 방화벽 규칙을 확인하고, 필요한 경우 DNS 포트를 허용해야 합니다.
  2. DNS 서버 접근성: daemon.json에 설정한 DNS 서버(8.8.8.8 등)가 실제로 호스트 시스템에서 접근 가능한지 확인해야 합니다. 호스트에서 ping 8.8.8.8 또는 dig @8.8.8.8 google.com 명령어를 통해 확인해 볼 수 있습니다.
  3. 네트워크 문제: 호스트 시스템 자체의 네트워크 연결에 문제가 있거나, 라우터/방화벽 단에서 외부 DNS 접근이 제한되어 있을 수 있습니다.
  4. systemd-resolved 충돌: 일부 리눅스 배포판에서 systemd-resolved가 Docker의 DNS 설정과 충돌하는 경우가 있습니다. 이 경우 systemd-resolved를 비활성화하거나, /etc/systemd/resolved.conf에서 DNSStubListener=no로 설정하고 resolv.conf를 수동으로 관리하도록 변경하는 방법도 고려할 수 있습니다. 하지만 이 방법은 시스템 전반의 DNS 동작에 영향을 미치므로 신중하게 접근해야 합니다.

Q. 특정 컨테이너만 다른 DNS 서버를 사용하게 할 수 있나요?

A. 네, docker run 명령어를 사용할 때 --dns 옵션을 통해 특정 컨테이너에만 개별 DNS 서버를 지정할 수 있습니다.

BASH
docker run -it --rm --dns 1.1.1.1 --dns 9.9.9.9 my-image bash

docker-compose를 사용하는 경우, 서비스 정의 내부에 dns 키를 추가하여 설정할 수 있습니다.

YAML
version: '3.8'
services:
  my-app:
    image: my-app-image
    dns:
      - 1.1.1.1
      - 9.9.9.9

이러한 개별 설정은 daemon.json의 전역 설정을 오버라이드하며, 특정 컨테이너의 요구사항에 맞춰 유연하게 DNS 설정을 관리할 때 유용합니다. 하지만 일관된 환경을 위해서는 daemon.json을 통한 전역 설정이 더 효율적입니다.


Docker 컨테이너의 DNS 확인 실패는 겉보기에는 간단해 보이지만, 실제로는 Docker의 네트워크 스택, 호스트 시스템의 DNS 설정, 그리고 방화벽 규칙 등 여러 계층의 복합적인 상호작용에서 발생하는 경우가 많습니다. 이번 가이드에서 제시된 daemon.json을 통한 Docker 데몬의 DNS 설정 강제는 대부분의 bridge 네트워크 기반 컨테이너 DNS 문제를 해결하는 가장 효과적이고 안정적인 방법입니다. 실무에서 이러한 문제가 발생했을 때, 당황하지 않고 체계적으로 원인을 분석하고 해결하는 데 이 가이드가 큰 도움이 되기를 바랍니다.

Sponsored