[프로젝트 일기] Nginx - reverse proxy 적용하여 Tomcat과 연동
Updated:
이번 스프린트에서 저는 서버 구축이라는 중대한? 테스크를 담당하게 되었습니다.
Nginx를 설치하고 리버스 프록시를 적용하여 스프링 부트 프로젝트의 내장 톰캣에 연동하는 작업을 하게 되었는데, 이 과정을 글로 남겨볼까 합니다.
웹 서버와 WAS
웹 서버는 정적 리소스를 처리하고, WAS는 애플리케이션 로직만을 처리하던 과거와 달리, 현재는 둘 다 정적 리소스, 애플리케이션 로직 모두 처리가 가능합니다.
그렇기 때문에 WAS만으로 웹 시스템을 구성하는 것이 가능하나 이 경우에 WAS가 너무 많은 역할을 담당하게되고 서버 과부화가 발생할 우려가 있습니다.
또한 가장 비싼 애플리케이션 로직이 정적 리소스 때문에 수행애 어려움을 겪을수 있습니다. WAS 장애시 오류 화면 노출도 불가능하다는 문제가 있습니다.
때문에 저는 웹 서버인 Nginx를 설치하여 정적 리소스는 웹 서버가 처리하고 WAS는 애플리케이션 로직 처리를 전담할 수 있도록 설계하였습니다.
이렇게 하면 정적 리소스만 제공하는 웹 서버는 잘 죽지 않고 WAS는 잘 죽기 때문에 WAS, DB 장애시 웹 서버가 오류화면을 제공하는 것이 가능하게 됩니다.
웹 서버를 사용한다면 Nginx? Apache?
대표적인 웹 서버에는 크게 Nginx와 Apache 두개가 있습니다.
프로젝트에 적용할 웹 서버를 선택하기에 앞서서, 각각의 웹 서버의 탄생 배경과 장단점에 대해서 알아보았습니다.
Apache
아파치 서버는 요청이 들어오면 커넥션 형성을 위해서 프로세스를 생성합니다.
때문에 새로운 클라이언트의 요청이 들어올때 마다 새로운 프로세스를 생성하게 됩니다.
하지만 프로세스를 만드는데는 상당한 비용과 시간이 필요하기 때문에 요청이 들어오기
전에 정해진 수의 프로세스를 미리 만들어 놓는 PreFork
방식을 사용합니다.
새로운 요청이 들어오면 미리 만들어놓은 프로세스를 할당해주고, 더 이상 할당할 프로세스가 없다면 추가로 프로세스를 만들어서 사용하게 됩니다.
이러한 아파치의 구조 덕분에 확장성이 좋으며 다양한 모듈을 만들어서 서버에 빠르게 기능을 추가하는것이 가능했습니다. 뿐만 아니라 요청을 받고, 응답을 처리하는 과정을 하나의 서버에서 해결할 수 있게 되었습니다.
하지만 스마트폰이 보급되고 서버에 동시 커넥션 수가 많아지게 되면 서버가 더이상 커넥션을 생성하지 못하는 문제가 있습니다.
이를 C10K
문제라고 부릅니다.
Apache의 문제점 - 동시 커넥션을 처리하기엔 부적합 하다.
동시 커넥션 수가 많아지면 프로세스 생성 수가 많아지고 이는 곧 메모리 부족 문제로 이어지게 됩니다.
게다가 여러 기능을 쉽게 추가할 수 있다는 확장성 때문에 프로세스가 차지할 리소스의 양도 많아집니다.
많은 커넥션에서 요청이 들어오기 시작하면, CPU는 컨텍스트 스위칭을 해가며 프로세스마다 요청을 처리해야 하기 때문에 CPU에 부하가 발생하게 됩니다.
Nginx
앞서 언급한 Apache 서버의 구조적 한계를 극복하고자 등장한게 바로 Nginx
입니다.
Nginx의 master 프로세스는 설정 파일을 읽고, 설정에 맞게 worker 프로세스를 생성합니다.
worker 프로세스는 만들어 지면서 listen socket을 배정받으며, 해당 socket에 새로운 사용자로 부터 요청이 들어오면, 커넥션을 형성하고 요청을 처리합니다.
커넥션은 헤더에 지정된 keep-alive 시간만큼 유지됩니다.
하나의 worker 프로세스는 형성된 커넥션에 아무런 요청이 없으면 새로운 커넥션을
형성하거나 이미 만들어진 다른 커넥션으로 부터 요청을 처리합니다. 이러한 커넥션
관련된 작업을
Event
라고 합니다.
Nginx의 Event 기반 구조
이 Event 들은 OS 커널이 queue 형식으로 worker 프로세스에게 전달해 줍니다.
Event는 큐에 담긴 상태에서 worker process 가 처리할 때 까지 비동기 방식으로 대기합니다.
worker 프로세스는 하나의 쓰레드로 이벤트를 꺼내서 처리해 나갑니다.
만약 이벤트중 하나가 Disk I/O 와 같이 오래 걸리는 작업일 경우에는 뒤의
요청들이 Blocking 되는걸 방지하고자 Thread Pool
이라는 별도의 공간에서
오래 걸리는 작업을
처리합니다.
즉, worker 프로세스는 수행하던 작업이 오래걸릴것 같으면 해당 작업을 Thread Pool에 위임하고 큐안의 다른 이벤트를 처리하게 됩니다.
이러한 worker 프로세스는 보통 CPU의 코어 갯수만큼 생성되는데, 이 덕분에 CPU의 컨텍스트 스위칭 비용을 줄일 수 있으며, 요청이 없다면 프로세스를 방치하던 아파치와 달리, worker 프로세스가 쉬지않고 일을 하기 때문에 서버 자원을 효율적으로 사용할 수 있게 됩니다.
이처럼 수많은 동시 커넥션을 빠르게 처리하면서 프로세스는 적게 생산하기 때문에 가볍기 까지한 장점이 있습니다.
Nginx의 로드 밸런서 역할
프로세스를 적게 만들기 때문에 nginx의 설정을 동적으로 변경하는것이 가능합니다.
개발자가 설정 파일을 변경하고, nginx의 설정 파일을 적용하면 master 프로세스는 해당 설정에 맞는 worker 프로세스를 새로 생성합니다.
그리고 기존에 있던 worker 프로세스가 더 이상 커넥션을 생성하지 않도록 합니다.
기존 worker 프로세스가 담당하던 이벤트 처리가 끝나면 해당 프로세스를 종료하게 됩니다.
이러한 동적 설정 변경은 nginx 뒷단에 새로운 서버를 추가해야 하더라도 nginx를 종료할 필요가 없게됩니다.
기존 요청은 계속해서 기존 worker 프로세스가 처리하면서, 뒷단에 서버를 추가하는게 가능하기 때문입니다.
Reverse Proxy란?
웹 서버의 역할중 하나는 WAS에 요청을 보내는 것이며 이를 리버스 프록시라고 합니다.
클라이언트는 가짜(proxy) 서버에 요청하면, 프록시 서버가 배후(reverse server)로 부터 데이터를 가져오는 역할을 합니다. 여기서 proxy가 nginx이고 reverse server가 WAS를 의미합니다.
nginx.conf 파일의 location 지시어를 사용하여 요청을 배분합니다.
SSL 터미네이션
nginx가 클라이언트와는 https 통신을 하고 서버와는 http 통신을 하는것을 말합니다.
서버는 복호화 과정을 담당할 필요가 없어지기 때문에 비즈니스 로직 처리에 리소스를 더 할당할 수 있게 됩니다.
보통의 nginx와 서버는 같은 네트워크 안에 존재하기 때문에 http 통신을 하더라도 보안적인 위험이 비교적 적습니다.
이 외에도 nginx를 사용하여 CORS 처리, HSTS, TCP/UDP 커넥션 부하 분산 등의 이점을 얻을 수 있습니다.
Nginx의 동시 커넥션 처리 이점이 현재 진행중인 프로젝트에 더 적합하다고 느껴졌기 때문에 Nginx를 프로젝트에 적용하기로 하였습니다.
Nginx를 서버내에 설치하는 과정을 다음과 같이 진행하였습니다.
ssh연결 통한 Nginx 설치
OS : Ubuntu 18.04
설치
nginx 설치 : sudo apt-get install nginx
nginx 설치(버전) 확인 : nginx -v
nginx -v를 했을때 버전 정보가 출력된다면 성공적으로 설치된것이다.
우리는 apt-get을 이용한 패키지 설치를 하였기 때문에 nginx가 /etc/nginx
폴더에 설치되었습니다.
(직접 compile한 경우에는 /usr/local/nginx/conf
또는 /usr/local/
etc/nginx
에 설치됩니다.
80 port → WAS
80번 포트로 들어온 요청을 톰캣 서버로 리버스 프록시하도록 설정했습니다.
vim /etc/nginx/sites-enabled/default
server_name
리버스 프록시를 적용하고자 하는 도메인을 입력해 줍니다. ex)
naver.com
location
root(/)로 요청이 들어오면 지정해놓은 tomcat 8080포트로
요청을 넘겨줍니다.
Nginx 실행
/ root 경로로 가서 systemctl start nginx
Nginx 실행 확인 systemctl status nginx
웹 브라우저에 도메인 IP 주소를 입력하면 톰캣에 잘 연동된것을 확인할 수 있습니다.
참고 자료
https://jjeongil.tistory.com/1490
Leave a comment