본문 바로가기

공부방/Nginx

Nginx vs apache

Nginx는 무엇일까?


Nginx는 웹서버이며, 리버스 프록시, 로드밸런서 그리고 http 캐시로도 쓰일 수 있는 소프트웨어이다.

요청에 응답하기 위해 이벤트 기반 구조를 채택했고,

덕분에 현재 웹 서버 분야에서 1등을 하고 있다.

 

이벤트 기반 구조가 어떤 점이 좋아서 nginx를 사용했는가


웹서버 분야에서 제일 잘 나가는게 아파치 서버라고 알고 있는 ngixn와의 차이점은 무엇인가

 

nginx게 벤치마킹 테스트를 했을때 가볍고 성능이 좋아서 선택

그리고 아파치 서버보다 최신 기술이라서 사용

 

설명이 부족하다.

Apache와 Nginx를 비교하면서 서로의 개념을 알아보자.

 

 

NCSA HTTPd


그 당시에는 유닉스 기반으로 만들어진 최초의 웹서버 NCSA HTTPd 가 있었어요.
그런데 이 프로그램은 버그가 굉장히 많아서 개발자들이 사용할 때 불편함을 겪었죠.

 

APACHE

 


그래서 몇몇 뛰어난 개발자들이 발 벗고 나서서 그 버그를 수정하기 시작했어요.
그러면서 구조를 변경하고 기능도 추가해서 만든 것이 바로 아파치 서버예요.

 

 

Apache의 구조


그러면 이 아파치 서버의 구조가 어떤지 간단하게 한 번 보도록 할게요.


아파치 서버는 요청이 들어오면 커넥션을 형성하기 위해 프로세스를 생성해요.
그래서 새로운 클라이언트의 요청 들어올 때마다 새로운 프로세스를 만들죠.
이는 UNIX 계열 OS가 네트워크 커넥션을 형성하는 모델을 그대로 적용한 것이었어요.

 


그런데 프로세스를 만드는 것이 시간이 걸리는 작업이다 보니 요청이 들어오기 전에 프로세스를 미리 만들어 놓는
prefork 방식을 사용했어요.
그래서 새로운 클라이언트로부터 요청이 들어오면 미리 만들어 놓은 프로세스를 가져다 사용했죠.
만약 만들어 놓은 프로세스가 모두 할당되었다면 추가로 프로세스를 만들었어요.

 


이런 구조는 개발하기 쉽다는 장점이 있었어요.
덕분에 개발자는 다양한 모듈을 만들어서 서버에 빠르게 기능을 추가할 수 있었죠.
이런 식으로 아파치 서버는 동적 콘텐츠를 처리할 수도 있게됐어요.
그리고 확장성이 좋다는 그 장점 덕분에 요청을 받고 응답을 처리하는 과정을 하나의 서버에서 해결하기 좋았죠.
이게 아파치 서버가 인기를 얻을 수 있는 비결 중 하나였어요. 

 


나온 지 1년도 되지 않은 시점에 아파치 서버는 벌써 세계 1위 웹 서버라는 타이틀을 가져갔어요.

 


자 이제 조금 빠르게 시간을 보내 1999년으로 가볼게요.
이 시기는 인터넷 트래픽이 계속해서 증가하는 상황이었어요.


이전에는 서버가 처리해야 할 요청 양이 그 당시 기술로 감당할 수 있는 정도였는데, 점점 컴퓨터가 더 많이 보급되고 요청이 많아지면서 서버에 이상한 문제가 생기기 시작합니다.
서버에 동시에 연결된 커넥션이 많아졌을 때 더 이상 커넥션을 형성하지 못하는 문제가 생겼어요.


이를 C10K 문제라고 하는데요.
Connection 10,000개의 문제라는 이름을 줄인 표현이에요.


사실 저희가 시간 여행을 온 진짜 이유는 바로 이 C10K 문제가 무엇인지 보기 위해서예요.
그래서 나아가기 전에 한번 짚고 넘어가야 할 부분이 있습니다.
동시에 연결된 커넥션이란 초당 요청 처리 수와는 달라요.

 


초당 요청 처리 수는 말 그대로 서버가 얼마나 빨리 요청을 처리할 수 있는지를 나타내는 지표예요.

 


그리고 동시에 연결된 커넥션 수란 요청을 처리하기 위해 서버가 한 시점에 얼마나 많은 클라이언트와
커넥션을 형성하고 있는지를 나타내고요.


한 클라이언트는 하나의 커넥션을 이용해서 여러 요청을 보낼 수 있어요.
그리고 커넥션은 긴 시간 동안 유지될 수도 있기 때문에 이 두 지표는 서로 같다고 볼 수 없죠.


이렇게 커넥션이 길게 유지되는 이유는 그 커넥션을 형성하는데 여러 절차들이 있기 때문이에요.
그래서 이 당시에는 각 요청마다 매번 커넥션을 만들기엔 비효율적이고 속도도 느렸죠.


그래서 사람들은 요청을 보낼 때, 이미 만들어진 커넥션이 있다면 그것을 재활용하자는 생각을 합니다.

 


http 프로토콜을 보면 header의 종류 중에 'keep alive' 헤더가 있죠?
이 header에 적힌 시간만큼 클라이언트와 서버는 한번 형성한 커넥션을 계속 유지해요.

 

그런데 클라이언트 수가 많아지면 그만큼 동시에 연결되어 있는 커넥션 수는 더더욱 많아지겠죠.
그렇게 동시 커넥션 수가 10000단위로 넘어가는 순간, 서버는 더 이상 커넥션을 형성하지 못하는 상황에 놓여요.


그런데 C10K 문제에서 하드웨어는 문제 되는게 없었어요.
하드웨어는 이미 그 어느 때보다 발전 속도가 빠른 상황이었거든요.
그리고 이 당시 웹페이지의 컨텐츠의 용량을 감안했을 때 이미 컴퓨터의 성능은 충분히 좋았어요.


문제는 그 당시 서버 계의 원탑이었던 아파치 서버의 구조였어요.


물론 그 외 대다수 서버도 마찬가지였고요.
아파치 서버는 구조상 커넥션이 형성될 때마다 프로세스를 할당한다고 했죠.
그렇기 때문에 동시에 처리하고 있는 커넥션이 많아지면 그만큼 형성된 프로세스가 많다는 거고
이는 곧 메모리 부족 현상으로 이어져요.


설상가상으로 아파치 서버의 '여러 가지 기능을 쉽게 추가할 수 있는' 특징은 프로세스가 차지하는 리소스의 양을 늘렸어요.


그리고 많은 커넥션에서 요청이 들어오기 시작하면 CPU 코어는 계속해서 프로세스를 바꿔가며 일을 해야 했어요.
좀 어려운 말로 컨텍스트 스위칭을 굉장히 많이 한다는 건데, 그만큼 CPU가 감당해야 할 일이 지나치게 많았던 거죠.


쉽게 말해서 수많은 동시 커넥션을 감당하기엔 아파치 서버의 구조가 적합하지 않았어요.


물론 apache 진영과 서버 개발자들은 이런 구조적인 한계를 극복하기 위해 다양한 시도를 합니다.
아파치 서버의 구조를 완벽히 바꾸지는 못하지만, 성능 개선이 지금까지도 이루어지고 있어요.
그리고 새롭게 만들어지는 서버들은 아예 다른 구조를 채택하기도 했고요.

 

그렇게 시간이 흐르고 2004년에 새로운 구조를 채택하면서 아파치 서버를 보완하기 위한 소프트웨어가 나오는데요.


드디어 Nginx가 나온 거죠.초창기 Nginx는 아파치 서버와 함께 사용하기 위해 만들어졌어요.
웹 서버이긴 하지만, 아파치 서버를 완전히 대체할 목적은 아니었죠.
아파치 서버가 지닌 구조적 한계를 Nginx를 사용함으로써 극복하려고 했어요.


사용된 방식을 보면 이렇습니다.
아파치 서버 앞단에 Nginx를 둡니다. 간단하죠?
이렇게 하면 기존에 아파치 서버가 감당해야 했던 수많은 동시 커넥션을 Nginx가 대신해서 유지할 수 있어요.

 

이게 포인트예요.
구조적으로 동시 커넥션을 많이 유지 못 하는 아파치 서버의 부하를 Nginx를 이용해 크게 줄일 수가 있죠.


그리고 Nginx는 그 자체로 웹 서버예요.
그래서 정적 파일에 대한 요청은 스스로 처리할 수 있죠.
웹 서버 역할의 Nginx는 클라이언트로부터 동적 파일 요청을 받았을 때만 뒤에 있는 서버와 커넥션을 형성해요.
아파치 서버의 리소스를 커넥션 유지에 쓰지 않고, 개발자가 원하는 로직 처리에 쓰도록 도와주는 거죠.

 

그렇다면 Nginx는 어떤 구조로 되어있길래 그 많은 동시 커넥션을 유지할 수 있을까요?
비결은 만들어지는 프로세스의 수에 있습니다 
Nginx의 구조를 한 번 살펴보면 가장 먼저 마스터 프로세스라는 것을 볼 수 있어요. 
설정 파일을 읽고, 설정에 맞게 워커프로세스를 생성하는 프로세스죠.
이 워커 프로세스가 실제로 일을 하는 녀석인데요.
워커 프로세스가 만들어질 때 각자 지정된 listen 소켓을 배정받아요.
그리고 그 소켓에 새로운 클라이언트로부터 요청이 들어오면 커넥션을 형성하고 그 요청을 처리합니다.

그러고 나면 그 커넥션은 정해진 keep alive 시간만큼 유지되겠죠.

 



그런데 커넥션이 형성되었다고 해서 워커 프로세스가 해당 커넥션 하나만 한정적으로 담당하진 않아요.
형성된 커넥션에서 아무런 요청이 없으면 새로운 커넥션을 형성하거나 이미 만들어진 다른 커넥션으로부터 들어온 요청을 처리합니다.


Nginx에서는 이런 커넥션 형성, 커넥션 제거 그리고 새로운 요청을 처리하는 것을 이벤트라고 불러요.

 

커널이 어떻게 이벤트를 제공하는지는 멀티플렉싱, epoll에 대해 알아보면 된다.


그리고 그 이벤트들은 OS 커널이 큐 형식으로 워커 프로세스에게 전달해줍니다.
이 이벤트는 큐에 담긴 상태에서 워커 프로세스가 처리할 때까지 비동기 방식으로 대기해요.
그리고 워커 프로세스는 하나의 스레드로 이벤트를 꺼내서 처리해 나가죠.

 

저 네모 칸들이 큐인데요.
안에 보면 커넥션을 생성하거나 요청을 처리하는 이벤트로 채워져 있는 것을 볼 수 있어요.
이러면 워커 프로세스가 쉬지 않고 계속해서 일을 한다는 장점이 있어요.
아파치 서버의 구조와 비교했을 때, 요청이 없다면 방치되던 프로세스보다 서버 자원을 훨씬 효율적으로 쓰는 셈이죠.

 

 

그런데 여기서 이런 질문이 생길 수 있을 것 같아요.
만약 이 요청 중 하나가 시간이 오래 걸리는 작업이면 어떻게 할까요?
예를 들어 Disk에 읽고 써야 하는 작업을 해야 한다면, 그 뒤에 있는 이벤트는 요청을 처리하는 긴 시간 동안 블로킹 되겠죠.
Nginx는 이런 상황을 방지하기 위해 그렇게 시간이 오래 걸리는 작업을 따로 수행하는 스레드 풀을 만들어 놔요.
그리고 워커 프로세스는 지금 처리할 요청이 시간이 오래 걸릴 것 같다 싶으면 해당 스레드 풀에 그 이벤트를 위임하고,
큐 안에 있는 다른 이벤트를 처리하러 가죠.

 


이런 워커 프로세스는 보통 CPU의 코어 수 만큼 생성해요.
이러면 코어가 담당하는 프로세스를 바꾸는 횟수를 대폭 줄일 수 있어요.
CPU가 굳이 그런 부가적인 일을 하지 않아도 되는 거죠.


다시 말해 CPU의 컨텍스트 스위칭 사용을 줄이는 거예요.
이게 바로 Nginx가 채택한 event-driven model, 즉 이벤트 기반 구조이고, 아파치 서버와 가장 큰 차이점입니다.


Nginx의 이런 구조는 단점도 있어요.
개발자가 기능 추가를 시도했다가 돌아가고 있는 워커 프로세스를 종료하게 되는 상황이 생길 수 있는 건데요.
그러면 해당 워커 프로세스가 관리하고 있던 커넥션과 관련된 요청을 더 이상 처리할 수 없게 되는 문제가 발생하죠.
그래서 Nginx는 개발자가 직접 모듈을 만들기가 까다로워요.

 


하지만 단점에 비해 장점이 너무나도 명확했어요.
수많은 동시 커넥션을 빠르게 처리하는데, 프로세스를 적게 만들다 보니 가볍기까지 했죠.


그리고 프로세스를 적게 만드는 이 구조는 Nginx의 설정을 동적으로 바꾸는 것을 가능하게 해요.


개발자가 설정 파일을 변경하고 Nginx에 해당 설정을 적용하면
마스터 프로세스는 그 설정에 맞는 워커 프로세스를 따로 생성해요.
그리고 기존에 있던 워커 프로세스가 더 이상 커넥션을 형성하지 않도록 하죠.


시간이 지나 기존 워커 프로세스가 담당하던 이벤트 처리가 모두 끝나면, 해당 프로세스를 종료합니다.

 

그런데 이런 동적 설정 변경을 언제 쓸까요?
아주 대표적인 경우로, Nginx가 여러 동시 커넥션을 관리하는 도중에 뒷단에 서버가 추가되는 상황이 있어요.
그땐 Nginx가 로드 밸런서의 역할을 담당하게 되는 건데요.
로드밸런서는 요청을 여러 서버로 분산하는 작업을 수행하죠.
Nginx 뒤에 새로운 서버를 추가해야 하는데, Nginx는 수많은 동시 커넥션을 담당하고 있다면
설정을 바꾸기 위해 Nginx를 종료하기는 좀 어려워요.
그런데 동적으로 설정을 변경할 수 있다면 어떨까요?
동시 커넥션을 유지한 채, 그리고 기존 요청을 계속해서 처리하면서 뒷단에 서버를 추가 할 수 있죠.
Nginx는 이런 설정 변경을 초당 수십번을 해도 무리 없이 커넥션을 관리하고 요청을 서버에 전달해요.

이벤트 기반 구조라서 가능한 방식이죠.


그런데 신기하게도 Nginx가 처음 출시되었을 때는

사용자가 그리 많지 않았어요.
2007년까지만 해도 아파치 서버는 여전히 웹 서버 1위 자리를 차지하고 있었고 Nginx는 순위권에도 없었죠.
그런데 2008년부터 아파치 서버는 점점 점유율을 잃기 시작하고 Nginx는 빠른 속도로 치고 올라오기 시작해요.
도대체 2008년에 무슨 일이 있었던 걸까요? 
2008년에는 스마트폰이 인터넷 환경을 바꾸기 시작했어요.


스마트폰은 사람들이 인터넷을 더 많이 사용하게 된 계기이기도 했지만,
동시 커넥션을 훨씬 더 많이 생성하는 계기이기도 했거든요.
사람들은 자신의 최애 아이돌의 무대 영상이라든지, 짝사랑이 방금 올린 사진을 알림으로 받고 싶어 했어요.

다양한 정보를 실시간으로 제공받고 싶어하는거죠.
그리고 웹에 담기는 콘텐츠가 다양해지고, 그 용량이 커지면서 브라우저도 리소스를 빨리 가져오기 위해
여러 TCP 커넥션을 동시에 형성하기 시작했어요.
그리고 각각의 커넥션은 모두 keep alive 설정으로 유지되었고요.
결국 동시 커넥션 문제를 처리해야 할 서버가 날이 갈수록 많아졌죠.

 


회사들은 빠르게 Nginx라는 대체재에 눈을 돌리기 시작했어요.
특히 Nginx는 대규모 사이트를 운영하고 있는 큰 회사들이 좋아할 만한 솔루션이죠.
덕분에 Nginx가 인터넷 트래픽에 관여하는 비중은 멈추지 않고 계속 증가해요.
아파치 진영도 시대에 맞게 MPM이라는 모듈을 추가해서 성능을 개선해요

 


MPM이란 Multi Processing Modules의 약자로, 아파치 서버를 어떤 방식으로 운영할지 선택할 수 있게끔 해주는 모듈이었어요.
안정성이나 하위호환이 필요하다면 기존의 prefork 방식을 사용하고, 성능 향상을 원한다면, 워커라고 불리는 스레드를 만들어서 워커가 요청을 처리하도록 했죠.
워커 어디서 많이 들어봤죠?
개발자들은 이렇게 구조적인 한계를 그나마 줄이기 위한 시도를 계속해서 해요.

 



그런데 성능 테스트 결과를 보면 아직까지 동시커넥션 관련 지표에선 Nginx가 아파치를 크게 앞서요.
이 지표는 동시 커넥션 수당 메모리 사용률인데요. Nginx는 동시커넥션이 많아져도
메모리 사용률이 낮고 일정하게 나오는 방면 아파치 서버는 굉장히 많이 사용하죠.


동시커넥션 수가 많아졌을 때 처리하는 초당 요청 수는 오히려 Nginx가 훨씬 많고 아파치는 낮다는걸 볼 수 있어요.
최소 두 배 이상 차이 나는 것을 볼 수 있죠. 이는 Nginx가 커넥션 관리를 얼마나 잘하는지 알려줘요.


사실 저희가 지금 동시 커넥션이라는 포인트에만 집중해서 그렇지 아파치가 아직 Nginx와 세계 1~2위를 다투는 데에는
다 그만한 이유가 있어요.
아파치 서버 이전의 웹 서버는 버그가 굉장히 많았다는 사실 기억하시나요?
버그 수정을 계기로 시작된 게 아파치 서버이기에, 그리고 지금까지 오랜 기간 업데이트를 계속해왔기 때문에 서버 자체가 다양한 OS에서 안정적이라는 장점이 있어요.


Nginx는 그렇지 않아서, 윈도우에서 제대로 된 성능을 발휘하지 못하거든요.
그리고 모듈을 추가해서 그 기능을 확장하기 쉽다는 장점 기억나시나요?
아파치 서버로 만들어진 오래된 서비스라고 해도 현재 잘 돌아가 있고 모듈로 기능을 계속 추가할 수 있다면 굳이 다른 서버로 옮길 이유가 없겠죠.


그리고 모듈의 종류는 아파치 서버가 Nginx보다 훨씬 많아요.
자 제가 이렇게 구구절절하게 설명을 한 건 각 웹 서버가 어떠한 이유로 나왔는지를 알면, 그 서버를 왜 선택해야 하는지 알 수 있다는 것을 말하고 싶었어요.
이 두 서버가 탄생한 이유를 보면 사실 대립 관계가 아니거든요.
애초에 만들어진 목적 자체가 달랐던 거죠.

 

 

아파치 서버가 서버계의 1등을 할 때에는 안정성과 확장성이 무엇보다 중요했고,
Nginx가 치고 올라올 때는 동시 커넥션 문제가 너무나도 중요한 상황이었어요.

 

그럼 이제 마지막으로 2021년에 와서 과연 우리는 Nginx를 어떻게 사용해야 하는지를
간단하게 키워드 중심으로 살펴볼게요.

앞서 저희는 Nginx의 웹 서버로서의 기능, 그리고 로드 밸런서로서의 기능을 살펴봤어요.
그런데 Nginx는 동시 커넥션을 여러 개 유지할 수 있고, 그 자체가 가볍다는 장점을 살려서 이 외에도 다양한 방법으로
웹 서버 가속기 역할을 해요.

 



먼저 Nginx는 SSL 터미네이션을 수행할 수 있어요.
SSL 터미네이션이란 Nginx가 클라이언트와는 https 통신을 하고, 서버와는 http 통신을 하는 것을 말해요.
이 구조를 만들어서 서버가 복호화 과정을 감당하지 않도록 할 수 있어요.
비즈니스 로직 처리에 리소스를 사용할 수 있도록 부하를 줄여줘요.

 

보통 Nginx와 서버는 같은 네트워크 안에 있는 경우가 많기 때문에, 이 둘은 http 통신을 해도 보안적인 위험이 비교적 적어요.

 


두 번째로, Nginx는 http 프로토콜을 사용하여 전달하는 콘텐츠를 캐싱할 수 있어요.

 

앞서 SSL 터미네이션을 설명할 때는 서버와 Nginx가 같은 네트워크에 있다고 말했는데,
캐싱을 하는 경우는 Nginx를 반대로 클라이언트 쪽에 가깝게 배치해요.
그리고 한 번 서버로부터 받은 응답을 스스로 보관하고 클라이언트에게 전달하죠.

 


Nginx는 이 외에도 HSTS, CORS 처리, TCP/UDP 커넥션 부하 분산, 그리고 HTTP/2 지원 등 정말 많은 방식으로 서버를 지원해요.

 



그러니 Nginx를 아키텍처에 도입하신다면, 어떤 기능이 있는지 알아보고, 적극적으로 적용하시면 좋을것 같아요.
오늘 발표한 내용은 다음과 같은 곳에서 더 구체적으로 확인할 수 있고, 제가 설명하지 않은 부분도 더 볼 수 있습니다.


마지막으로 꿀 정보 하나 제공하고 마무리하도록 하겠습니다.
현업에서 일하는 개발자들이 모은 Nginx 설정 템플릿인데요.
일반적인 상황에서 이 템플릿을 이용하면 Nginx를 더욱 빠르게, 편하게, 강력하게 사용할 수 있어요.
물론 제대로 된 아키텍처를 구축하려고 한다면 템플릿 말고 Nginx를 실제로 튜닝해서 써야겠죠.
이상 발표 마치도록 할게요. 
저는 피케이였고요. 참여해주셔서 감사합니다.

 

'공부방 > Nginx' 카테고리의 다른 글

발표자료  (0) 2023.04.02
노드 설치  (0) 2023.03.21
Tomcat 설정  (0) 2023.01.17
Nginx config 설정  (0) 2023.01.17
Nginx, Tomcat, War  (0) 2023.01.12