docker compose 로 개발 환경을 구성했다. 이 환경에서 nginx 를 리버스 프록시로 사용하고 있는데, docker 서비스 컨테이너를 재시작 한 뒤 nginx 에서 Connection Error 가 발생하면서 해당 서비스에 접근하지 못하는 문제가 발생했다.
여러 자료를 찾아보니 컨테이너를 재생성하면서 변경된 IP 가 nginx 에 캐싱된 IP 정보와 달라서 발생하는 문제였다. 문제는 nginx reload 로 DNS 정보를 업데이트 하여 문제를 해결했는데, 그 과정에서 찾아본 여러가지 내용들과 해결법 등을 정리해본다.
1. docker compose network
- default network
docker compose 환경을 실행할 때 따로 네트워크를 명시하지 않으면 자동으로 기본 네트워크를 하나 생성한다. 이 네트워크는 사용자 정의 bridge network 로 각각의 서비스는 이 네트워크에 연결된다.
compose 가 생성한 기본 네트워크는 docker engine 에 의해 동적으로 서브넷과 게이트웨이가 할당된다. 이때 각 컨테이너의 IP 는 따로 정적 할당하지 않았다면, 컨테이너별로 자동으로 IP 가 부여된다.
IP 가 동적으로 할당되는 대신, docker compose 네트워크에서는 서비스 명을 통한 통신을 지원해준다. docker compose 실행 시, 네트워크는 서비스의 이름을 자동으로 DNS 에 등록한다. 이를 통해서 docker compose 내부에서 서비스에 접근 시 동적으로 할당되는 IP 에 상관없이 서비스 명을 호스트 명으로 사용하여 접근할 수 있다.
- 컨테이너 재생성시 IP 변경
docker compose 로 동작하고 있는 컨테이너를 재생성 하는 경우 기존 docker network 다시 연결된다. 이때 docker network 에서는 새로운 IP 를 할당하는데, 이때 할당되는 IP 는 재생성 이전의 IP 와 다를 가능성이 있다. 하지만 DNS 명은 그대로 유지되기 때문에 IP 가 아닌 서비스 명을 이용한 DNS 통신은 정상적으로 이루어진다. 이 때문에 docker network 환경에서는 IP 사용을 통해서 통신하기 보다는 DNS 사용을 추천한다.
2. nginx 의 DNS 캐싱
기본적으로 nginx 는 처음 실행될 때, 또는 설정을 reload 할 때 한번만 DNS 를 조회한다. 이후에는 읽어온 내용을 캐싱하여 가지고 있으면서 이후 요청에서 재사용한다.
매 요청마다 DNS 를 조회하게 되면 오버헤드가 발생하기 때문에 성능적인 이유로 이러한 구조로 설계되었다. 이러한 구조는 이 글에서 발생한 문제와 같이 동적으로 엔드포인트가 변경되는 상황에서는 문제가 될 수 있다.
동일한 도메인으로 요청이 들어오는 경우 nginx 는 docker network DNS 를 통해 변경된 IP 를 가져오는 것이 아니라 nginx 내부에 캐싱된 IP 로 요청을 전달한다. 이때 캐싱 된 IP 는 변경되기 전 IP 이기 때문에 제대로 서비스에 접근하지 못하고 잘못된 주소로 연결을 시도한다.
3. nginx reload
문제를 해결하기 위해서는 nginx 에 캐싱된 DNS 정보를 업데이트 해야 한다. 앞서 설명한 것과 같이 nginx 는 처음 실행될 때, 또는 설정을 업데이트할 때만 DNS 정보를 조회해온다. 이때문에 이를 해결하기 위해서 nginx reload 를 사용할 수 있다.
$ nginx -s reload
reload 를 하게 되면 nginx 설정 파일을 다시 로드하여 적용하고, upstream / proxy_pass 대상 들의 재해석, DNS 재조회 등을 진행한다. 이 과정에서 nginx 의 DNS 캐시도 강제로 갱신된다.
nginx 를 reload 한 후에는 통신이 정상적으로 이루어지는 것을 확인할 수 있다. 그러나 이 방법은 근본적인 해결법은 아니다. 이 방법을 사용하게 되면 컨테이너의 IP 가 변경될 때마다 매번 nginx 를 reload 해주어야 한다. 그렇기 때문에 reload 가 아닌 다른 방법을 적용해야 한다.
4. nginx resolver
nginx 는 resolver 설정을 제공해주는데, resolver 설정을 통해 주기적으로 DNS 정보를 갱신할 수 있다. 아래와 같이 nginx 의 config 파일에 resolver 설정을 추가하고 reload 하여 nginx 에 설정을 반영해주면 resolver 를 사용할 수 있다.
resolver 127.0.0.11 valid=10s ipv6=off;
resolver name server 로 127.0.0.11 을 설정해주었다. 이 주소는 Docker 컨테이너 환경에서 bridge 네트워크가 기본적으로 가지는 DNS 서버의 주소이다. nginx 가 docker network 의 DNS 를 사용하도록 설정하여 도메인 IP 변환을 수행할 수 있도록 한다.
valid 는 DNS 결과를 얼마나 캐싱할 것인지 설정하는 값이다. 예제에서는 DNS 결과를 10초만 캐싱하도록 설정했다. nginx 는 기본값으로 IPv4 와 IPv6 를 모두 조회한다. IPv6 는 조회하지 않아도 되는 경우, 예제와 같이 ipv6 값을 off 로 설정해주면 된다.
이렇게 resolver 를 설정해주면 주기적으로 docker network 로부터 DNS 정보를 조회하기 때문에 docker 서비스의 IP 가 변경되어도 문제없이 정상 통신을 할 수 있다.
[References]
- https://docs.docker.com/engine/network/#ip-address-and-hostname
Networking
Learn how networking works from the container's point of view
docs.docker.com
- https://docs.docker.com/compose/how-tos/networking/
Networking
How Docker Compose sets up networking between containers
docs.docker.com
- https://nginx.org/en/docs/http/ngx_http_core_module.html#resolver
Module ngx_http_core_module
Module ngx_http_core_module Directives Syntax: absolute_redirect on | off; Default: absolute_redirect on; Context: http, server, location This directive appeared in version 1.11.8. If disabled, redirects issued by nginx will be relative. See also server_na
nginx.org
'Infra > nginx' 카테고리의 다른 글
| [nginx] ssl 인증서 적용 (0) | 2025.04.24 |
|---|---|
| [nginx] nginx 란? (1) | 2023.05.11 |