리버스 프록시와 캐싱: ngx_http_proxy_module 활용

리버스 프록시와 캐싱: ngx_http_proxy_module 활용

ngx_http_proxy_module 은 요청을 다른 서버로 넘겨주는 NGINX 리버스 프록시의 핵심 모듈이에요. proxy_pass 로 요청을 전달하는 기본 원리부터, 응답을 버퍼링하는 법, 그리고 proxy_cache 로 응답을 캐싱해 백엔드 부하를 줄이는 법까지 자주 쓰는 지시어 위주로 설명할게요.

출처: NGINX 공식 문서 - Module ngx_http_proxy_module

예시 설정

가장 기본적인 리버스 프록시 설정은 아래와 같아요. proxy_pass 로 전달 대상을 정하고, proxy_set_header 로 백엔드에 넘길 헤더를 조정해요.

location / {
    proxy_pass http://localhost:8000;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
}

백엔드 서버가 요청을 받을 때 원래 클라이언트의 호스트와 IP를 알 수 있도록 HostX-Real-IP 헤더를 다시 만들어 넘겨줘요.

proxy_pass: 요청 전달

proxy_pass 지시어는 프록시 서버의 프로토콜·주소·선택적 URI를 설정해요. 프로토콜은 http 또는 https, 주소는 도메인 이름이나 IP 주소에 포트를 붙여 지정할 수 있어요. UNIX 도메인 소켓 경로는 unix: 다음에 콜론으로 감싸서 지정해요.

proxy_pass http://localhost:8000/uri/;
proxy_pass http://unix:/tmp/backend.socket:/uri/;

도메인 이름이 여러 주소로 해석되면 그 주소들에 라운드로빈 방식으로 요청을 나눠줘요. 주소는 서버 그룹(upstream)으로도 지정할 수 있어요. 파라미터에는 변수도 쓸 수 있는데, 주소를 도메인 이름으로 쓴 경우 먼저 서버 그룹에서 찾고 없으면 resolver 로 해석해요.

URI를 붙인 경우에는 요청을 넘길 때, location 과 매칭된 요청 URI 부분이 지시어에 지정된 URI로 치환돼요.

location /name/ {
    proxy_pass http://127.0.0.1/remote/;
}

URI 없이 지정한 경우에는 클라이언트가 보낸 요청 URI를 그대로 백엔드로 전달해요.

location /some/path/ {
    proxy_pass http://127.0.0.1;
}

location 이 정규표현식으로 지정됐거나 네임드 location 안에서는 치환할 URI의 범위를 정할 수 없으므로, 이 경우 proxy_pass 는 URI 없이 지정해야 해요.

응답 버퍼링

proxy_buffering 지시어로 프록시 서버 응답의 버퍼링을 켜고 끌 수 있어요. 버퍼링이 켜져 있으면 NGINX는 프록시 서버로부터 응답을 가능한 한 빨리 받아 proxy_buffer_sizeproxy_buffers 지시어가 정한 버퍼에 저장해요. 응답 전체가 메모리에 안 들어가면 일부를 디스크 임시 파일에 저장할 수 있어요. 버퍼링이 꺼져 있으면 응답을 받는 즉시 클라이언트에 동기적으로 전달해요.

proxy_buffer_size 4k;
proxy_buffers 8 4k;

응답의 첫 부분(보통 작은 응답 헤더)을 읽는 버퍼 크기는 proxy_buffer_size, 연결당 버퍼 개수와 크기는 proxy_buffers 로 정해요. 기본 버퍼 크기는 메모리 페이지 하나(플랫폼에 따라 4K 또는 8K)예요.

응답 캐싱

백엔드 응답을 캐싱하면 같은 내용을 반복해서 백엔드로부터 받아오지 않아도 돼요. 캐싱은 크게 두 단계로 나눠요. 먼저 캐시가 저장될 공유 메모리 영역proxy_cache_path 로 정의하고, 이어서 어디서 캐시를 쓸지 proxy_cache 로 지정해요.

proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=one:10m;

proxy_cache_path 는 캐시 데이터가 파일로 저장될 경로와 파라미터를 설정해요. levels 는 캐시 디렉터리의 계층(1~3단계, 각 단계는 1 또는 2)을, keys_zone 은 공유 메모리 존의 이름과 크기(name:size)를 정해요. 캐시 파일 이름은 캐시 키에 MD5 함수를 적용한 결과예요. 위 예시의 파일명은 /data/nginx/cache/c/29/b7f54b2df7773722d382f4809d65029c 처럼 계층 구조로 나뉘게 되요.

캐시 존이 정의되면 proxy_cache 로 그 존을 사용하도록 설정하고, proxy_cache_key 로 캐시 키를 정해요. 기본 키는 $scheme$proxy_host$request_uri 와 가까운 값이에요.

proxy_cache one;
proxy_cache_key "$host$request_uri $cookie_user";

캐시 유효 시간proxy_cache_valid 지시어로 응답 코드별로 정해요.

proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;

이 설정은 200·302 응답은 10분, 404 응답은 1분 동안 캐시해요. 시간만 지정하면 200·301·302 응답만 캐시돼요.

proxy_cache_valid 5m;

any 파라미터를 쓰면 모든 응답을 캐시할 수 있어요.

proxy_cache_valid 200 302 10m;
proxy_cache_valid 301 1h;
proxy_cache_valid any 1m;

캐시 파라미터는 응답 헤더로도 직접 지정할 수 있는데, 이게 지시어보다 우선순위가 높아요. X-Accel-Expires 헤더는 응답 캐시 시간(초)을 정하고, 0이면 캐싱을 끄고, @ 접두사가 붙으면 Epoch 이후 절대 시간까지 캐시를 허용해요. 헤더에 Set-Cookie 필드가 있으면 그 응답은 캐시되지 않고, Vary 가 특수 값 * 면 캐시되지 않아요.

어떤 응답을 캐시할지proxy_cache_methods 로 정해요. 기본값은 GET HEAD 이고, 두 메서드는 항상 목록에 추가돼요.

proxy_cache_methods GET HEAD POST;

캐시를 건너뛰어야 할 조건proxy_cache_bypass 로, 응답을 캐시하지 말 조건proxy_no_cache 로 지정해요. 문자열 파라미터 값 중 하나라도 비어 있지 않고 "0" 도 아니면 해당 동작이 발동돼요.

proxy_cache_bypass $cookie_nocache $arg_nocache$arg_comment;
proxy_cache_bypass $http_pragma $http_authorization;

캐시가 만료됐거나 실패해도 오래된(stale) 응답을 계속 쓰고 싶다면 proxy_cache_use_stale 로 상황을 지정해요.

proxy_cache_use_stale error timeout updating http_500 http_502;

여기서 updating 은 갱신 중일 때 오래된 응답을 쓰라는 뜻이에요. proxy_cache_lock 을 켜면 새 캐시 항목을 채울 때 한 번에 요청 하나만 백엔드로 보내고 나머지는 기다려서, 신규 캐시 생성 시 백엔드 접근 횟수를 줄일 수 있어요. 캐싱 정책에 대한 자세한 지시어는 헬퍼를 참고하면 좋아요.

더 알아보기 (Learn more)