프로그래밍/넥스터즈

[넥스터즈] 서버 개발 일지 - Nginx 적용기, Nginx 정규식 적용과 기타 주요 내용

Jay Tech 2019. 3. 15. 21:12
반응형

Nginx

더 적은 자원으로 빠르게 서비스할 수 있는 웹 서버로 우리 프로젝트에 적용할 웹서버이다.


엔진엑스는 하나의 마스터 프로세스(master process)와 워커 프로세스(worker process)를 가지고 있다. 마스터 프로세스는 설정들을 읽고 워커 프로세스들을 관리한다. 워커 프로세스는 실제로 요청들을 처리하는 프로세스이다. 워커 프로세스들의 숫자는 사용가능한 CPU 코어수에 맞춘다.

ps -ax | grep nginx

running 중인 모든 엔진엑스 프로세스를 보여준다.

정적 content 관리

/data/www 라는 디렉터리를 만들었다고 하면 그 곳에 index.html을 놓아보자.

http {
  server {
  }
}

이미 nginx를 설치를 한다면 디폴트로 주석이 달린 블럭들이 있는데 그것을 활용해도 된다. 일반적으로 이런 블럭들은 server name에 의한 포트를 수신하는 것으로 구분된다. 밑에 location 블럭을 보자.

location / {
  root /data/www;
}

이 location 블럭은 URI 리퀘스트에 / prefix로 매칭된다. 이 경로로 들어오면 root 에 명시된 곳으로 매핑되게 된다. 여러 location 블럭이 있으면 긴 prefix 로 매칭된다.

server {
  location / {
      root /data/www;
  }

  location /images/ {
      root /data;
  }
}

/images/로 오는 요청은 밑의 블럭을 타게 된다.

참고로 이런 설정들을 바꾸고 나면 reload를 해주어야 한다.

nginx -s reload

프록시 서버 설정

엔진엑스에서 자주 사용되는 기능은 프록시 서버 기능인데 이 기능은 클라이언트로 부터 요청을 받고 프록시 서버로 던진다. 그리고 응답이 오면 그것을 클라이언트에 전해준다.

server {
  listen 8080;
  root /data/up1;

  location / {
  }
}
server {
  location / {
      proxy_pass http://localhost:8080;
  }

  location /images/ {
      root /data;
  }
}

밑의 블럭은 포트 명시가 되어 있지 않는데 이것은 기본 80 포트가 쓰이기 때문이다. 저 쪽으로 요청이 오면 프록시 패스로 8080으로 던져준다. 그러면 /data/up1 이 root로 잡히게 된다.

혹은 권한 문제로 파일을 불러올 때 정규식을 쓰기도 한다.

location ~ \.(gif|jpg|png)$ {
  root /data/images;
}

정규식의 시작은 ~ 물결이다. gif, jpg, png로 끝나는 파일에 대한 요청이 올 때 /data/images 폴더에서 찾을 것이다.

프로젝트 적용기

위 내용을 바탕으로 우리 프로젝트에 적용을 시켰다. 골치아팠던 부분은 프런트엔드와의 연동이었는데 여태 서버와 프런트가 하나로 합쳐진 앱만 배포를 해보았었는데 처음으로 프런트를 따로 띄우는 작업을 했었다.

일단 우리는 비싼 서버를 샀다..! cpu가 무려 두우우개! 클라우드에서 더블 코어 서버를 처음 파보았다. 월 7만....

worker_processes 2;

워커 프로세스를 2개로 늘려주었다.

그리고 우리 프로젝트는 ssl 적용을 하였기 때문에 일반적인 80 으로 들어오는 요청을 전부 443 (https포트) 포트로 던졌다.

server {
  listen       80;
  server_name nexters.me *.nexters.me www.nexters.me nxt.rs;

  rewrite ^ https://www.nexters.me$request_uri? permanent;

  return 301 https://www.nexters.me$request_uri?;
}

그리고 나서는 서버 블럭을 443 listen 으로 만들었다. ssl 은 letsencrypt를 사용했는데 거기서 certbot을 사용하면 ssl에 대한 server 블럭도 자동으로 만들어 준다.

server {
    listen 443 ssl;

    server_name nexters.me

    charset utf-8;

    root /usr/share/nginx/html/dist;
    index index.html index.htm

여기서 프런트 엔드 개발자들이 작업한 프런트 파일들의 위치를 root로 설정한다. 그곳의 index.html 을 등록하여 첫 페이지가 그곳으로 뜨게 한다. 그런데 프런트는 Single Page App 이었다. 각각 페이지마다 html 파일들이 따로 있는 것이 아니라 하나에서 각기 렌더링이 되는 방식이었다. (대충 프런트를 여기까지 파악...) 그래서 location 블럭을 설정할 때 매우 골머리를 앓았다. 그 날 밤은 서버 3명이서 잠을 못잤다. 우리 서비스는 URL 단축인데, 형식이 [우리도메인]/[단축문자열] (예를들어, nexters.me/Yb) 이런식인데 nexters.me 루트는 프런트엔드가 띄워주는 화면이 나와야하고 저 형식으로 들어가게 되면 스프링부트 앱의 응답을 바로 받아야 하고, 프런트엔드의 상세 페이지 경로 (nexters.me/statistics/[url명]) 로 갈때에는 프런트엔드가 띄워주는 화면이 나와야한다. 이건 정말 처음 접했을 때 헬이었다.

어쨌거나 root를 잡아주고 location을 설정한다.

location ~ ^/docs/(.*) {
proxy_pass http://127.0.0.1:8080;
}

우리 앱은 8080에서 톰캣이 대기를 하기 때문에 (Spring boot App) 프록시패스는 로컬호스트 8080을 바라보게 한다. 저 정규식은 (docs) swagger를 보여주기 위함이다. 기억에는 경로가 단축url로 인식되는걸 막기위해 swagger 기본 경로까지 수정했던거 같다...

그리고 프런트 엔드 페이지가 처음에 나올 때 모든 static한 리소스들의 권한이 없어서 forbidden이 폭발했다.

location ~* \.(?:css|js|map|jpe?g|gif|png|svg|ico)$ {
  expires max;
  add_header Pragma public;
  add_header Cache-Control "public, must-revalidate, proxy-revalidate";
}

그래서 이런식으로 static 리소스들에 대한 정규식을 허용해주었다.

그리고 단축url로 들어왔을 때의 설정이다.

location ~ ^/[0-9a-zA-Z]+ {
  proxy_intercept_errors on;
  access_log /nexters/logs/nginx/access.log;
  error_log /nexters/logs/nginx/error.log;
  proxy_pass http://127.0.0.1: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;
  proxy_set_header   Referer $http_referer;
}

base62 인코딩한 문자열에 대한 정규식을 설정해주고 proxy_set_header에 referer를 추가해줌으로써 서버 단에서 레퍼러를 잡을 수 있도록 하였다. (통계 기능에 레퍼러활용해야 하기 때문에)

마지막으로 대망의 SPA 앱의 경로 설정... 결국 우리 팀장님이 구원투수로 해결을 해주었다.

재작성 (rewrite)

엔진엑스에서 요청이 들어오는 URL의 규칙을 변경하는 설정이다.

location ~ ^/statistics/[0-9a-zA-Z]+ {
rewrite ^(.+)$ /index.html last;
}

이 부분은 확실히 이해를 하지 못했다. last를 써줌으로써 재작성 이후 프로세싱을 종료하고 만들어준 다음번 URI를 매핑을 시킨다라는데 좀 더 살펴봐야할듯 하다.

결과적으로는 Nginx를 사용해봄으로써 여러 특징들을 알게 되었다.

Event Driven 방식을 사용함으로써 요청이 들어오면 그 다음 동작만 알려주고 바로 다음 요청을 처리하는 방식으로 흐름이 끊기지 않고 빠르게 응답을 할 수 있다. 그렇기 때문에 1개의 프로세스로 더 빠른 작업이 가능하게 된다. 각 커넥션마다 프로세스와 쓰레드를 fork 하지 않는다. 이와 대조되는 Apache를 보면

Apahce: 쓰레드/프로세스 구조로 요청 하나 당 하나의 쓰레드가 할당, 사용자가 많을 수록 쓰레드가 많이 생성되어 CPU 낭비가 심해진다.

이런 문제를 해결할 수 있는 좋은 웹서버인것 같다. 공부할게 많아 행복하다.

반응형