본문 바로가기
BackEnd/개발

[개발] NGINX 기초

by 경험의 가치 2024. 11. 12.

Nginx란?

Nginx란 비동기 이벤트 기반 구조의 경량화 웹서버이다. 주로, 정적 파일을 응답해주는 웹서버로 사용하거나, Reverse Proxy Server로 활용하거나, 로드밸런서의 역할을 하거나, HTTPS 인증을 할 때 쓰인다.

 

시작하기 앞서, Nginx 설치에 어려움을 겪거나, 귀찮게 설치 안해보고도 테스트 해보고 싶은 분들은 Nginx를 웹에서 테스트 해볼 수 있는 Nginx Playgorund를 사용해보자.

 

 

nginx playground

 

nginx-playground.wizardzines.com

Nginx를 활용할 수 있는 방안

1. 정적 캐싱 서버 : Nginx의 캐싱 기능을 이용해 정적 콘텐츠(예: 이미지, CSS, JavaScript 파일 등)를 캐시함으로써 서버 부하를 줄이고 콘텐츠를 더 빠르게 전달할 수 있다.

2. 로드밸런싱 : On-Premise 환경 등에서 로드 밸런싱을 해준다.

3. 리버스 프록시 : 클라이언트의 요청을 백엔드 서버에 전달하는 중간 서버 역할을 수행한다.

4. 라우팅 : 특정 요청 패턴이나 도메인에 따라 서로 다른 백엔드 서버 또는 서비스로 요청을 전달한다. 쿠키에 따른 A/B 테스트 등을 손쉽게 구현할 수 있고, 서브 도메인에 따른 라우팅도 가능해진다.

5. TLS/SSL 인증 : TLS 종료를 Nginx가 담당해 백엔드 서버에 도달하기 전 데이터를 암호화 및 복호화가 가능하다. Nginx 이후로는 TLS가 종료되어 암호화를 하지 않아, 이에 따른 리소스를 절약할 수도 있다. (일반적으로 Nginx가 일종의 관문이기에 Nginx에 도착하는 순간 외부에서 내부로 들어왔기에 TLS 인증이 필요하지 않게 된 것)

6. 접근 권한 제어 : IP 기반 접근 제어, 인증 등으로 접근 권한을 관리할 수 있다.

7. 웹서버 : 이미지, CSS, JavaScript 파일 등 정적 파일을 직접 제공할 수 있다.

8. 파일 크기 제한 : 사전에 젤 앞단에서 파일 크기를 감지해서 제한할 수 있다.

 

등등... 활용할 수 있는 방안이 많다.

 

Nginx vs Apache

 

웹서버로서 흔히 쓰이는 두가지가 Apache랑 Nginx이다. Nginx와 Apache는 스레드 사용 측면에서 큰 차이가 있다. Apache 같은 경우는 위에 그림처럼, 하나의 커넥션 당 하나의 스레드를 잡아먹는다.

 

Nginx는 위 사진처럼, 하나의 Master Process와 여러개의 Worker Process로 나누어져 있다. 이때, master process는 worker process를 관리 및 생하고, worker process에서 실질적인 사용자 응답을 처리한다. worker process는 Nginx 설정에 따라서 바꿀 수 있다.

이때, 이 Worker Process가 한 개 또느 고정된 프로세스만 생성하고 사용하는 방식으로 작동한다. 이때, 비동기 Event-driven 방식을 통해 동시에 많은 요청을 처리할 수 있어 Apache보다 훨씬 더 효율적이다. 또한, 새로운 요청이 들어오더라도 Apache처럼 새로운 프로세스 및 스레드를 생성하지 않기 때문에 스레드 생성 비용이 존재하지 않고 훨씬 자원 효율적이다. 또한, Nginx 구조 자체가 훨씬 적은 양의 스레드가 사용되기 때문에 context switching 비용이 적다. 그래서 Apahce보단 Nginx를 대부분 사용하는 추세이다.

Nginx 구조

Nginx의 메인 설정 파일 경로는 /etc/nginx/nginx.conf이다. 해당 파일에 있는 설정대로 Nginx는 작동이 된다. 다음은 Nginx PlayGround 실행시 나오는 기본적인 Nginx Conf의 모습이다.

worker_processes  1;
worker_rlimit_nofile 8192;

events {
  worker_connections  4096;
}

http {
  include    /etc/nginx/mime.types;
  index    index.html index.htm index.php;

  server { # reverse proxy for a local httpbin
    listen          80;
    server_name     http.bin;
    access_log      /dev/null;

    location = / {
      proxy_pass      http://localhost:7777;
    }
  }
}

 

이때, Nginx의 동작은 nginx.conf에 있는 directives(지시어)에 의해서 제어된다. 크게 두가지로 나눌 수 있다.

 

Simple Directive

worker_proecess auto;

위 처럼 이름과 값이 있는 경우이다. worker_process가 이름이고 auto가 값이다.

 

Block Directive

events {
  worker_connections  4096;
}

위 처럼 Simeple directive 구조를 감싸고 있는 {} 형태의 지시어이다. 블록안에 블록이 있을 수 있다. 또한, include  /etc/nginx/mime.types와 같이 다른 파일에서 가져올 수도 있다.

 

대표적인 Block으로는 events, upstream, server, location, http 등이 있다. 해당 글에서는 Nginx 기초를 다루기 때문에 events 블록은 생략하고, 바로 Server와 Location 블록으로 라우팅 해보는 예제를 보자.

 

Nginx 단순 라우팅 (Location 블록)

일단 제일 간단한 것 부터 시작해보자. 아마 학교에서 하는 개인 프로젝트에서는 아래 설정 정도만 쓸 것 이다. 왜냐하면, 위 사진처럼 단순히 웹페이지 서버와 api 서버를 구분 짓어서 라우팅 해주기 위해서 많이 쓸 것이기 때문이다. 예를 들어서, 우리가 학생이고 프리티어 EC2 인스턴스 한개 밖에 못 쓴다고 가정해보자. 

server {
    listen 80;
    server_name example.com;

    location / {
            proxy_pass http://react:3000;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /api {
        proxy_pass http://spring:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

 

listen은 요청을 받을 포트 번호를 의미한다. 80번 포트로 들어오는 모든 요청은 아래 location 규칙을 따른다는 뜻이다.

 

server_name은 도메인이다. example.com으로 들어오는 80번 포트 요청을 모두 받는다는 뜻이다.

 

location은 Request Pattern이라고 생각하면 된다. 우리가 example.com/help, example.com/api/help 이런식으로 요청을 날릴 때, 요청 경로를 보고 라우팅을 해주는 녀석이라고 생각하면 된다. 경로는 =, ~ 등 문법을 사용하여 다양한 조건을 정의하고 정규식도 사용이 가능하다. (이 부분에 대해서는 너무 길어져서 다음 글에 작성하겠다.)

 

proxy_pass : 모든 요청을 지정된 경로로 프록시하며, 요청이 전달될 최종 경로를 의미한다. spring, react는 Docker로 띄운 컨테이너 이름을 나타낸다. 프록시 서버나 로드 밸런서를 통과하는 요청이 서버에 도달할 때 원래의 목적지 경로를 유지하기 위해 사용한다.

 

proxy_set_header Host $host:$server_port : 프록시 요청의 Host 헤더를 클라이언트가 요청한 호스트명과 포트로 설정한다. 예를 들어, 클라이언트가 www.example.com:80으로 요청했다면 Host 헤더는 www.example.com:80이 된다. 가상 호스팅 환경에서 유용하며, 하나의 웹 서버가 여러 도메인을 호스팅할 때 서버가 Host 헤더를 보고 요청된 도메인을 판별할 수 있게 한다.

 

proxy_set_header X-Forwarded-Host $server_name : 프록시 요청의 X-Forwarded-Host 헤더를 서버 블록에 정의된 서버 이름으로 설정하여 원래의 호스트 정보를 전달한다. 예를 들어, 클라이언트가 www.example.com으로 요청했다면 X-Forwarded-Host 헤더는 www.example.com이 된다. 원래의 호스트 정보가 프록시를 통과하면서 유지되도록 한다.  

 

proxy_set_header X-Real-IP $remote_addr : 프록시 요청의 X-Real-IP 헤더에 클라이언트의 실제 IP 주소를 설정한다. 예를 들어, 클라이언트 IP가 192.0.2.1이라면 X-Real-IP는 192.0.2.1이 된다. 애플리케이션 로그에서 클라이언트의 실제 IP 주소를 기록할 때 유용하다.

 

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for : 프록시 요청의 X-Forwarded-For 헤더에 클라이언트의 IP와 이전 프록시 서버들의 IP 주소 목록을 설정한다. 예를 들어, 클라이언트 IP가 192.0.2.1이고 프록시 서버 IP가 203.0.113.1일 경우 X-Forwarded-For는 192.0.2.1, 203.0.113.1이 된다. 이를 통해 서버는 요청이 거쳐온 IP 목록을 파악할 수 있다.

 

그리고 Dockerfile을 이런식으로 작성해서 띄운다

FROM nginx

COPY default.conf /etc/nginx/conf.d/default.conf

CMD ["nginx", "-g", "daemon off;"]

 

이렇게하면, nginx.conf에서 자동으로 default.conf를 include하고, 이를 반영하는 것이다. Docker를 빌드하고 실행하면, /경로로 들어오는 모든 Request는 React로 라우팅 되고, /api로 시작하는 모든 Request는 다 Spring으로 갈 것 이다.

 

그런데 location Block을 작성할 때 몇가지 주의 사항이 있다. 그런데 해당 글에 적기에는 글이 너무 길어질 것 같에서 따로 적어보도록 하겠다.

 

어쨋든, 단순 라우팅을 원하는 경우 이 정도 설정이면 우리가 의도한대로 작동된다. 하지만, Nginx에 다른 여러 기능들이 굉장히 많은데 단순 라우팅만 쓰기에는 아쉽다. 그래서 다음 글에서는 퍼포먼스 향상을 위한 좀 더 세부적인 설정을 알아볼 것 이다. Nginx + HTTPS와 관련해서는 아래 글을 참고 바란다.

 

[배포] Route 53 + Nginx + Certbot으로 Https 적용하기

0. 시작하기전에도메인 관련 서비스는 좋은 것이 꽤 많다. 예를 들어, DNS로는 유명한 것이 AWS Route 53이 있고, ACM 같은 것을 이용하여 손쉽게 인증서를 구입하고 적용할 수 있을 것이다. 하지만, 이

mclub4.tistory.com