ice rabbit programming

[nginx] nginx에서 reverse proxy 사용하기(feat. 우선 순위) 본문

Development/Web etc.

[nginx] nginx에서 reverse proxy 사용하기(feat. 우선 순위)

판교토끼 2021. 2. 18. 02:59

nginx아파치와 더불어 굉장히 널리 쓰이는 웹 서버이다. 2021년 기준으로 아파치가 약 41.48%로 1위, nginx가 약 26.41%로 2위의 점유율을 보이고 있다. 2000년대~2010년대 초반까지만 해도 아파치가 거의 60%에 육박하거나 상회하는 수준이었다는 것을 생각하면 nginx의 성장세가 무섭다고 할 수 있다.

어쨌거나 웹 서버는 HTTP 서버라고도 할 수 있는데, 클라이언트의 요청에 따라 서버 쪽에서 처리하여 정보를 제공하는 소프트웨어라고 할 수 있다.

nginx가 제공하는 기능이 여러가지 있는데, 그 중에 이번 포스팅에서는 reverse proxy를 간단하게 다루려고 한다.

reverse proxy

Flask나 Express.js 등 자체적으로 serving을 지원하는 프레임워크를 사용할 경우 사실 웹 서버를 따로 설치하지 않아도 포트를 개방하고 접근이 가능하다. 하지만 SPA 프론트엔드 코드를 분리해서 운영하거나, 3rd party 서비스를 따로 운영하는 등 코드 내에서 라우팅하기 힘든 상황이 많이 발생한다. 이럴 때 코드 단에서는 각자의 서비스 운영에만 집중하도록 하고, serving에 관한 문제는 nginx에서 다루도록 하면 깔끔하고 높은 성능으로 해결이 된다.

reverse proxy는 외부에서 내부 서버가 제공하는 서비스에 접근할 경우, proxy server를 통해서 들어오는 방식이다. 접근하는 사용자는 실제로 어디에 접근하는지는 관계가 없고, 운영자가 제공해준 방법에 따라서 serving을 할 수 있으므로 보안이 뛰어나고 로드 밸런싱을 통해 부하를 줄일 수 있다. 반대 개념으로는 forward proxy가 있다.

nginx는 /etc/nginx/nginx.conf 파일의 설정을 통해서 구동된다. 이 설정에서 우리는 proxy 설정을 할 수 있다. site-modules/에 분리하는 등의 방법이 있지만 편의상 이 글에서는 nginx.conf 한 파일인 것으로 간주학겠다.

location / {
    proxy_pass http://127.0.0.1:8080;
}

이것이 기본적인 구문이다. 즉, 현재 서버에 / 로 시작하는 path로 접근하면, http://127.0.0.1:8080;으로 돌려준다는 의미이다. 그러면 실제 사용자는 8080포트에 접근하지 않았지만 8080 포트에 접근한 것과 동일한 효과가 발생한다.

location에 대해서 좀 더 자세히 적자면, nginx는 클라이언트가 접근한 path를 보고, 가장 적합한 location의 블럭으로 요청을 보내서 처리하게 된다. 여러 개가 일치할 경우 우선 순위가 있는데, 다음과 같다.

1. = (exactly), 정확히 일치할 경우
ex) location = /

2. ^~ (priority prefix match), 우선 순위를 부여하고, 앞 부분이 일치할 경우. 여러 개가 충돌할 경우 긴 것이 적용(longest first)
ex) location ^~ /api

3. ~ (regex match with sensitive case), 대소문자를 구분하는 정규표현식 일치할 경우
ex) location ~ /path

4. *~ (regex math with insensitive case), 대소문자를 무시하는 정규표현식 일치할 경우
ex) location *~ /path

5. / (prefix match), 앞 부분이 일치할 경우, 여러 개가 충돌할 경우 긴 것이 적용(longest first)
ex) location /

위 사용법에 따라 location을 작성하여 주면, nginx.conf에 설정한대로 reverse proxy가 동작한다. 만일 아래와 같이 작성하면, /api로 시작하는 요청은 5000번 포트로, 그 외에는 8080번 포트로 보내게 된다.

location / {
    proxy_pass http://127.0.0.1:8080;
}

location /api {
    proxy_pass http://127.0.0.1:5000;
}