솔직히 말하면, 이 글을 읽을 일이 없기를 바랍니다. 지금 이 페이지를 보고 계신다면 심심해서 들러셨거나, 검색어를 잘못 입력하셨을 가능성도 있겠죠. 하지만 어쩌면 실제로 골치 아픈 상황에 처해 있을 수도 있습니다. 최근까지 멀쩡히 잘 작동하던 Docker 컨테이너가 갑자기 인터넷에 접속하지 못하고, 환경 설정은 아무것도 바꾸지 않았는데 말이죠.
문제 정의가 다소 막연하게 들릴 수 있지만, 실제로 제가 겪었던 상황이 바로 그것이었습니다. 컨테이너에서 네트워크 접근이 불가능했고, 'Temporary failure resolving URL'과 같은 오류 메시지가 출력되었습니다. 이름 확인(DNS) 문제로 보였고, 더 짜증나는 것은 원래 발생해서는 안 되는 상황이라는 점이었습니다. 테스트 환경은 systemd를 사용하는 Ubuntu입니다(뒤에서 중요한 역할을 합니다). 차근차근 살펴보겠습니다.
문제의 구체적인 모습
시스템에 Docker를 구성했고 잘 작동하고 있다고 가정해 봅시다. 여러 개의 이미지와 컨테이너를 운영 중이고, 고급 네트워크 규칙까지 활용하고 있으며, 모든 것이 완벽한 상태처럼 보입니다. 그런데 어느 순간부터 컨테이너 내부에서 업데이트나 패키지 설치 같은 작업이 불가능해졌습니다. 디버깅하는 가장 좋은 방법은 실행 중인 컨테이너에 셸(shell)을 연결하는 것입니다. 컨테이너 내부에서 다음과 같은 결과를 볼 수 있습니다.
# apt-get update
Err:1 https://archive.ubuntu.com/ubuntu bionic InRelease
Temporary failure resolving 'archive.ubuntu.com'
0% [Connecting to security.ubuntu.com]
이는 전형적인 DNS(이름 확인) 문제입니다. 컨테이너가 도메인 이름을 IP 주소로 변환하지 못하기 때문에 업데이트 데이터를 가져올 서버에 연결할 수 없는 것입니다. 여기에는 두 가지 과제가 있습니다. 첫째, 왜 갑자기 이런 문제가 발생했는가? 둘째, 네트워크를 어떻게 실질적으로 고치는가? 지금부터 우아하게 문제를 해결하는 방법을 소개하겠습니다.
해결 방법
먼저 문제가 발생한 원인부터 이해해야 합니다. 즉각적인 답은 없지만, 확인해야 할 사항은 자신의 환경 그 이상일 수 있습니다. 예를 들어 호스트 시스템은 그대로였더라도 네트워크 환경은 변했을 수 있습니다. 라우터, 네트워크 정책, DNS 서버 자체 등이 말이죠. 직접 관리하는 범위 밖의 상황은 통제할 수 없으므로, 문제의 위치를 파악하는 가장 좋은 방법은 단계별로 범위를 좁혀가는 격리(isolation) 방식입니다.
- 호스트 시스템에 네트워크가 있지만 URL 확인이 안 된다면, 먼저 그 부분을 해결해야 합니다. 호스트와 목적지 사이, 그리고 그 사이의 네트워크 인프라에 문제가 있을 가능성이 높습니다.
- 호스트 시스템에 네트워크가 있고 URL도 정상적으로 확인된다면, 문제는 Docker 컨테이너가 URL을 확인하는 방식에 특화되어 있는 것입니다. 이제 여기에 집중해야 합니다.
- Docker 네트워크 인터페이스가 활성화되어 작동 중인지 확인합니다(ip 또는 ifconfig 명령 사용). 그렇지 않다면 다음 단계로 넘어가기 전에 먼저 수정해야 합니다.
ifconfig docker0
- 컨테이너 인스턴스에 IP 주소가 할당되어 있는지 확인합니다. 없다면 이를 먼저 해결해야 합니다.
docker inspect <컨테이너 이름 또는 ID> | grep -i "ipaddr"
- 가능하다면 컨테이너 내부에서도 동일한 확인을 진행합니다(ip 또는 ifconfig). 앞선 명령의 결과와 일치하지 않으면 수정이 필요합니다.
- 외부에서 컨테이너로 ping이 가능한지 확인합니다(반대 방향도 마찬가지). ping이 성공한다면 네트워크가 올바르게 구성되어 있고, 방화벽 규칙이 트래픽을 차단하고 있지 않을 가능성이 높다는 좋은 신호입니다.
이름 확인(DNS)
모든 검사에서 이상이 없다면, 다음 단계는 DNS 확인에 집중하는 것입니다. 관련 설정은 /etc/resolv.conf 파일에서 찾을 수 있습니다. 이는 물리적 Linux 시스템뿐 아니라 가상 머신과 컨테이너에도 동일하게 적용됩니다. 컨테이너 내부에서 대략 다음과 같은 내용을 발견하게 될 것입니다.
...
# See man:systemd-resolved.service(8) for details about the supported modes
# of operation for /etc/resolv.conf.
search xyz
nameserver X.Y.Z.W
...
nameserver IP 주소는 외부 IP(ISP 제공 주소 등)이거나 localhost(127.0.0.X)일 가능성이 높습니다. 핵심 질문은 이것입니다. 이 값이 호스트의 /etc/resolv.conf 파일과 일치합니까?
대부분의 경우 호스트의 /etc/resolv.conf 파일에는 localhost가 정의되어 있을 것입니다. 이 값을 컨테이너에 적용해 보세요. resolv.conf 파일을 편집하여 nameserver 줄의 IP 주소를 호스트 값, 즉 localhost로 교체합니다. 문제가 해결된다면 다행이지만, 아마 그렇지 않을 가능성이 큽니다.
그렇다면 이 시점에서 호스트의 네트워크 연결에는 문제가 없다는 사실이 확인된 셈입니다. 따라서 우리는 환경에서 실제로 사용 중인 DNS 서버의 '진짜' 주소를 알아내야 합니다. 그런데 대부분의 최신 Linux 배포판이 systemd를 사용하기 때문에 상황은 더 복잡해집니다.
systemd로 DNS 확인하기
네임서버가 무엇인지 알아내려면 systemd 명령을 사용해야 합니다. 시스템에 systemd가 있다면 아마도 네트워크 이름 확인 관리자이자 서비스인 systemd-resolved도 함께 사용 중일 것입니다. 설정은 /etc/systemd/resolved.conf에 저장되어 있으며, 명령줄에서도 systemd-resolve 명령으로 결과를 얻을 수 있습니다.
systemd-resolve --status
Link 3 (wlp59s0)
Current Scopes: DNS
LLMNR setting: yes
MulticastDNS setting: no
DNSSEC setting: no
DNSSEC supported: no
DNS Servers: 10.50.34.1
2001:64c:1462:b023::1
DNS Domain: dedoimedo
흥미로운 정보가 많지만, 정말 중요한 것은 'DNS Servers' 줄입니다. 바로 이 값을 원하는 것입니다. 이 IP 주소를 컨테이너의 /etc/resolv.conf 파일에 넣고 다시 시도해 보세요. 네트워크가 다시 정상적으로 작동할 것입니다.
그럼 왜 이런 문제가 발생하는 걸까?
이제 다시 '원인'을 논의해 봅시다. systemd-resolve 문서를 살펴보면, 시스템에 어떤 변경(정기적인 업데이트로 인한 것일 수도 있음)이 발생하여 선택된 작동 모드가 이름 확인 과정과 충돌을 일으켰을 가능성이 있습니다. 특히 /etc/resolv.conf가 처리되는 방식을 살펴보면, 첫 번째 모드는 다음과 같이 설명합니다.
systemd-resolved는 기존 Linux 프로그램과의 호환성을 위해 /run/systemd/resolve/stub-resolv.conf 파일을 유지 관리합니다. 이 파일은 /etc/resolv.conf에서 심볼릭 링크로 연결될 수 있으며, 127.0.0.53 DNS 스텁(stub)을 유일한 DNS 서버로 나열합니다. 또한 systemd-resolved가 사용 중인 검색 도메인 목록을 포함하며, 이 목록은 항상 최신 상태로 유지됩니다. 참고로 /run/systemd/resolve/stub-resolv.conf는 응용 프로그램이 직접 사용해서는 안 되며, 반드시 /etc/resolv.conf를 통한 심볼릭 링크로만 사용해야 합니다. 이 작동 모드가 권장됩니다.
이 방정식의 연결 고리 중 하나라도 끊어지거나 무언가 변경되면, Docker 서비스가 상황을 제대로 판단하지 못해 이름 확인이 불가능해질 수 있습니다. 따라서 해결책은 컨테이너가 이해하고 사용할 수 있는 실제 네트워크 DNS 주소를 직접 제공하는 것입니다. 다소 추측성이 있지만, 상당히 타당한 분석이라고 생각합니다.
결론
또 하나의 미스터리가 해소되었습니다. 저는 반쯤 마법 같은 해결책을 좋아하지 않지만, 초복잡하고 다층적인 시스템 인프라에서는 때때로 해결책 자체가 문제만큼이나 좋지 않을 때가 있습니다. 문제를 고치지 못한다는 의미가 아니라, 원래 있어야 할 만큼의 가시성과 통제력을 잃게 된다는 의미입니다. 하지만 그것이 Linux, 나아가 IT 전체의 미래입니다. 끝없는 추상화의 시대니까요.
본론으로 돌아가서, 이 작은 가이드가 컨테이너 여정에서 비교적 빠르고 고통 없는 해결책을 제공해 주기를 바랍니다. Docker를 사용 중이고 컨테이너 인스턴스 내부에서 이름 확인이 더 이상 작동하지 않는다면, 위에서 소개한 팁과 요령을 시험해 보세요. 그리고 올바른 수정 사항을 반영한 자체 이미지를 빌드해 두면, 같은 문제로 다시 고생할 필요가 없을 것입니다. 여기까지입니다.
즐거운 개발 되세요!