배경
MSA 프로젝트를 간단하게 해보면서 gateway쪽을 담당하게 되었는데, 이 부분이 nginx와 매우 유사하다는 생각이 들었습니다. 평소에도 nginx에 대해 궁금했던 부분이 많았는데 다음에 도입을 고려해보기 전 이번 기회에 nginx의 역사에 대해 조금 알아볼까 합니다.
내용
Nginx와 Apache: 웹 서버의 진화와 비교
안녕하세요! 오늘은 Nginx와 Apache 서버에 대해 깊이 있게 알아보겠습니다. 두 웹 서버는 각각의 장단점과 특징을 가지고 있으며, 서로 다른 상황에서 최적의 선택이 될 수 있습니다. 이 글에서는 두 서버의 역사적 배경, 구조, 장단점, 그리고 어떤 상황에서 각각을 선택해야 하는지에 대해 상세히 설명하겠습니다.
<Apache>
Apache서버는 최초의 web 서버 NCSA HTTPd를 대체하기 위해 1995년에 만들어졌습니다. NCSA HTTPd는 버그가 너무 많았기 때문에 대안이 필요했던거죠. apache서버의 기본 구조는 요청이 들어오면 커넥션을 형성하기 위해 프로세스를 생성합니다. 새로운 클라이언트의 요청 들어올 때마다 새로운 프로세스를 만들죠. 이는 UNIX 계열 OS가 네트워크 커넥션을 형성하는 모델을 그대로 적용한 것이었습니다. 그런데 프로세스를 만드는 것이 시간이 걸리는 작업이다 보니 요청이 들어오기 전에 프로세스를 미리 만들어 놓는 prefork 방식을 사용했습니다. 만약 만들어 놓은 프로세스가 모두 할당되었다면 추가로 프로세스를 만들어 사용했죠. 이런 구조는 개발하기 쉽다는 장점이 있었습니다.
덕분에 개발자는 다양한 모듈을 만들어서 서버에 빠르게 기능을 추가할 수 있었죠. 이런 식으로 아파치 서버는 동적 콘텐츠를 처리할 수도 있게 되었습니다.
하지만 시간이 조금씩 지나 인터넷 트래픽이 계속 증가하면서 서버가 처리해야 할 요청 양이 엄청나게 많아지게 됩니다. 이전에는 서버가 처리해야 할 요청 양이 그 당시 기술로 감당할 수 있는 정도였는데, 점점 컴퓨터가 더 많이 보급되고 요청이 많아지면서 서버에 이상한 문제가 생기기 시작한거죠. 서버에 동시에 연결된 커넥션이 많아졌을 때 더 이상 커넥션을 형성하지 못하는 문제가 생기게 됐는데 이를 C10K 문제라고 합니다.
커넥션은 긴 시간 동안 유지될 수도 있는데 그 이유는 커넥션을 형성하는데 여러 절차들이 있기 때문입니다. 이 당시에는 각 요청마다 매번 커넥션을 만들기엔 비효율적이고 속도도 매우 느렸죠. 그래서 이미 만들어진 커넥션이 있다면 그걸 재활용하자는 생각을 하게 되는데 이것이 ' keep alive' 헤입니다. 하지만 이 방법으로 클라이언트가 많아지면 그만큼 동시에 연결되어 있는 커넥션 수는 더더욱 많아지겠죠. 그렇게 동시 커넥션 수가 10000단위로 넘어가는 순간, 서버는 더 이상 커넥션을 형성하지 못하는 상황에 놓이게 됩니다.
동시에 처리하고 있는 커넥션이 많아지면 그만큼 형성된 프로세스가 많다는 거고 이는 곧 메모리 부족 현상으로 이어집니다. 요청이 많아질 수록 서버 CPU 코어는 컨텍스트 스위칭을 굉장히 많이 하면서 메모리가 감당하지 못하게 되는거죠. 이러한 문제를 해결하기 위해 Nginx가 2004년에 나오게 됩니다.
<Nginx>
초창기 nginx는 apache와 함께 사용하기 위해 만들어진겁니다.
그림을 보시는 것처럼 nginx를 apache서버 앞에 두게 되면 기존에 아파치 서버가 감당해야 했던 수많은 동시 커넥션을 Nginx가 대신해서 유지할 수 있게 됩니다. 구조적으로 동시 커넥션을 많이 유지 못 하는 아파치 서버의 부하를 Nginx를 이용해 크게 줄일 수가 있죠. 그리고 nginx는 그 자체로 웹서버이기 때문에 정적 파일에 대한 요청은 스스로 처리할 수 있어서 동적 요청에 대해서만 뒤에 있는 apache서버와 connection을 이어가면 됩니다. 이로써 서버의 리소스가 엄청 많이 줄어들게 된거죠.
그렇다면 Nginx는 어떤 구조로 되어있길래 그 많은 동시 커넥션을 유지할 수 있을까요? 비결은 만들어지는 프로세스의 수에 있습니다. 마스터 프로세스 밑에 워커 프로세스를 두고 이 워커 프로세스가 클라이언트로부터 요청이 들어오면 커넥션을 형성하고 요청을 처리하게 됩니다. 또한 커넥션이 형성되었다고 해서 하나의 워커 프로세스가 하나의 커넥션만 담당하는게 아니라 형성된 커넥션에 아무 요청이 없게 되면 새로운 커넥션을 형성하거나, 이미 만들어진 다른 커넥션으로부터 들어온 다른 요청을 처리하게 됩니다. Nginx에서는 이런 커넥션 형성, 커넥션 제거 그리고 새로운 요청을 처리하는 것을 이벤트라고 부릅니다.그리고 그 이벤트들은 OS 커널이 큐 형식으로 워커 프로세스에게 전달해줍니다. 이 이벤트는 큐에 담긴 상태에서 워커 프로세스가 처리할 때까지 비동기 방식으로 대기하죠. 그리고 워커 프로세스는 하나의 스레드로 이벤트를 꺼내서 처리해 나가게 됩니다.
또한 시간이 오래 걸리는 작업의 경우 따로 처리하는 Thread pool을 만들어 관리를 합니다. 이런 워커 프로세스를 보통 CPU 코어 개수만큼 생산하기 때문에 컨텍스트 스위칭에 소모되는 비용을 매우 크게 줄일 수 있게 됩니다.
이런 nginx는 장점만 있을까요? nginx는 개발자가 직접 모듈을 만들게 되면 워커 프로세스를 종료해버릴 수 있는 문제가 발생할 수도 있기 때문에 모듈 개발이 까다롭습니다. 또한 윈도우에서는 제대로된 성능을 발휘하기 어렵습니다. 하지만 장점이 너무 명확하죠. 가볍고, 커넥션 수를 엄청나게 많이 유지할 수 있으니까요. 또한 동적으로도 설정을 바꿀 수 있어서 너무나 편했습니다. 이외에도 SSL 터미네이션, 캐싱, HSTS, CORS처리, TCP/UDP 커넥션 분산 등 다양한 기능을 수행 가능합니다.
Apache와 Nginx는 대립관계가 아닙니다. 서로 보완해주는 관계죠.
뭐가 더 옳고 좋다 이런 것을 따지기 보다는 상황에 맞게 적절하게 배치해서 앞단에서는 nginx를, 뒷서버에서는 apache서버를 운용하는 것이 큰 이득이 될거라고 생각합니다.
오늘은 좋은 지식을 저도 배운 것 같아 성장한 느낌이 드네요. 감사합니다.
참고
'네트워크' 카테고리의 다른 글
RabbitMQ와 Kafka 차이에 대한 간단 설명 (0) | 2024.12.18 |
---|---|
네트워크와 인터넷 (0) | 2024.12.12 |