티스토리 뷰

프로그래밍 공부/Ruby on Rails : 배포

Nginx 개념의 이해

마음 따뜻한 개발자, 나른한 하루 2020. 4. 17. 05:54

  • 글 시작 전

1. 해당 글은 단순 Nginx의 개념에 대한 설명글입니다.

 

2. Nginx를 활용한 자동배포(CD) 기능을 통해 편리하게 Ruby on Rails 프로젝트를 배포할 수 있습니다.

 부록  AWS EC2+Nginx+Capistrano 지속적(자동) 배포

 

 

  • 목차

  1. 글 시작 전(Intro)

  2. c10k Problem

  3. 그래서 Nginx을 왜 써야 하는가?

  4. Apache와 Nginx의 차이는?

  5. 자료 참고

 

 
  • Puma.. 이제 안녕...

Rails에서 기본으로 제공하는 Puma 엔진

Ruby on Rails은 기본적으로 PHP와 같이 서버엔진을 따로 깔지 않아도 된다는 장점이 있습니다.

하지만 이 장점이 미래를 바라보면 썩 좋은 장점은 아니었습니다.

 

왜 Puma 엔진이 미흡하다고 여겨졌는지 이번 글을 통해 이야기를 풀어보겠습니다.

 

 
  • c10k Problem

글을 시작하기전에 앞서, c10k 문제에 대해 언급을 하고 시작해보고자 합니다.

 

서버는 동시접속자가 많으면 많을수록 과부화가 되고, 치명적입니다.

이를테면 1월 1일 00시에 서로 새해 인사를 나눈다고 카카오톡을 사용하다보니 사용자가 급증하면서 카카오톡이 느리게 응답하거나, 죽어버리는 사례를 볼 수 있습니다.

* 카카오가 c10k 문제에 대해 대처를 안했다는건 절대 아닙니다.

 

잠시 옛날로 돌아가서, 초기에 네트워크 설계에 있어 현재보다 인터넷 문화가 발전하지 않았다보니 네트워크 소켓을 통해 최대로 커버 가능한 동시접속자의 작업 수는 1만개였습니다.

이 때 당시에는 사용자 수 = 프로세스 갯수 혹은 쓰레드 갯수 로 따져가며 서버 인프라가 설계되었다 보니 많은 CPU, 메모리 자원이 활용되던 시절이었습니다.

 

하지만 요즘은 인터넷 문화가 많이 발전했다보니, 동시접속자 수가 점점 늘어나고 있는 상황입니다.

덕분에 동시접속자가 늘어날 수록, 사용자들이 동시다발적으로 I/O 작업을 많이 하다보니 사용자들이 사용하는 서버의 자원은 점점 늘어나게 되고, 서버는 수많은 동시다발적 처리에 대한 I/O 처리를 견디다 못해 결국 죽어버리곤 하는데, 이러한 현상을 c10k Problem 이라고 합니다.

 

결국 c10k Problem 은 서버의 하드웨어적 문제, OS의 처리능력 한계, 내부 작업 프로세스 등 복합적인 원인으로 인해 탄생된 현상입니다.

 

 
  • 그래서 Nginx을 왜 써야 하는가?

효율적인 서버 자원(CPU, 메모리 등) 사용

1. Nginx 개요

다시 Nginx돌아와서, c10k Problem 문제의 해결법 중 하나로 제시된게 바로 Nginx를 활용하는 방법입니다.

과거 서버 통신방식 (bloking 방식)

과거 *멀티쓰레드를 사용하던 시절에는 위와같이 Bloking I/O 방식으로 처리가 이루어지곤 했었습니다.

Blocking I/O 방식은 뭔가 프로세스에 대한 처리를 하려고 하는데, 이미 다른 프로세스가 자리를 차지하고 있다면 이 전 프로세스가 작업이 끝날 때 까지 기다려야 함을 의미합니다.

* 멀티쓰레드 : 하나의 프로그램에 동시에 여러 작업을 돌리는 작업으로서, 프로세스 하나의 자원을 공유하다보니 처리속도가 빠르다는 장점이 있지만, 한정된 프로세스 자원을 쪼개서 사용하는 만큼, 스레드별로 자원을 효율적으로 분배해줘야 한다.

 

Apache의 요청/응답에 있어 서버 자원(Process, Thread) 활용방식 [Prefork, worker 방식; 후술에 내용 자세히 설명]

하지만 이러한 작업은 동시다발적으로 들어오는 작업에 있어서는 부적합했습니다.

또한 위 사진과 같이 요청에 따라 쓰레드/프로세스가 무한대로 생겨나다보니 요청이 많을수록 작업 처리 방식이 무겁고, 오랜 시간이 소요되는 작업에도 역시 문제가 있었습니다.

Blocking에 있어선 딱 위 그림과 같이 좋은 비유가 아닐 수가 없다. (4번 박스를 요청하는 유저로 인해 그 뒤의 손님들은 아무것도 못하고 대기해야 한다.) [출처 : Nginx 공식사이트]

위와같이 길고 무거운 작업이 만약에 서버에 요청된다면 해당 작업이 처리될 때 까지 결국 다른 요청은 아무것도 못하고 대기해야 하는 문제점이 있었습니다. 

 

그래서 나오게 된 방법론이 Non-blocking 방법론입니다.

non-blocking 방식의 작업처리 [출처 : Nginx 공식사이트]

과거의 Blocking 방식과 다르게, Non-Blocking 방식은 앞의 프로세스가 작업을 수행중이더라도, 후의 프로세스는 기다리면서 다른 작업을 해낼 수 있습니다. (아주 기초적인 예시로 이를테면 카페에서 커피를 주문하고 진동벨이 울릴 때 까지 화장실을 다녀온다던지 등..)

 

Nginx은 이런 Non-Bloking 방식을 채택했습니다.

 

 

2. Nginx가 작동되는 과정

Nginx가 돌아가는 방식 [출처 : Nginx 공식사이트]

전체적으로 Nginx가 돌아가는 과정은 위와 같습니다.

Nginx가 돌아가는 원리를 담은 위의 과정을 아래에 나름대로 해석을 해봤습니다.

 

Nginx 작업 Step 1

 

Nginx은 위 사진과 같이 보이는 것과 같이 서버가 켜지면(start) 사전에 서버개발자가 정한 쓰레드 갯수를 생성하고(Thread  Pool), 해당 스레드 풀 내에 가진 한정된 자원(쓰레드풀)만을 가지고 HTTP 요청에 대한 작업을 처리합니다.

 

단, 쓰레드를 생성함에 있어 너무 적거나 너무 많이 생성하지 않는걸 추천합니다.

  • Thread가 너무 적을 경우, 작업을 처리함에 있어 Thread가 빈번하게 부족한 현상이 발생할 것입니다.
  • Thread가 너무 많을 경우, 놀고있는(IDLE) Thread가 발생하게 되고 이는 자원의 낭비입니다.
프로세스 : 실행 프로그램
쓰레드 : 프로세스를 running 시키는 작업 단위

 

Nginx 작업 Step 2

 

Nginx와 함께 돌아가고 있는 Worker Process

그리고 요청에 대한 응답은 Nginx 내부에서 실행되고 있는 Worker Process에서 진행됩니다.

여기서, Worker Process는 개발자가 따로 설정을 하지 않았다면, CPU 특성에 맞게 자동으로 Worker Process 갯수가 생성됩니다. (저는 1개가 생성되어 있었습니다.)

 

그런데 만약 이 설정을 변경함에 앞서 한 가지 유의사항이 있다면 단순히 '많으면 좋겠지' 하는 마음으로 변경을 생각한다면 이는 비추천합니다. 하드웨어 성능을 고려하지 않고 단순히 늘릴 경우 오히려 퍼포먼스가 떨어질 수 있다는 얘기가 있습니다.

 부록  [Nginx] Worker Process

 

Nginx 작업 Step 3

Nginx는 Event Driven의 특성을 가지고 있습니다. 그리고 Event Driven은 Event Loop 기반으로 요청에 대한 작업을 처리합니다. 하지만 이 요청을 처리함에 있어 한가지 특징이 있는데, 위 사진 속 요청에 있어 A→BCD 요청 순으로 작업이 진행된다 할 때, B 작업은 A 작업이 끝날 때 까지 기다리지 않는다 라는 특징이 존재합니다. 이는 Event Loop 에서 작업이 처리됨에 있어, 비동기 방식으로 작업이 돌아가기 때문입니다.


Nginx Event Driven 내에서 어떻게 작업이 되는지 제 나름대로 해석을 해봤습니다. :

  1. Nginx는 요청에 대한 작업을 처리할 때, 스레드가 직접적으로 작업을 하진 않고 이 작업들을 '이벤트 핸들러' 라는 곳으로 보냅니다.

  2. 이벤트 핸들러에서 작업을 마치게 되면 먼저 작업이 완료된 순서되로 Queue에 쌓이게 됩니다.

  3. 매번 Event Loop은 Queue를 체크하면서 완료된 작업이 있는지 체크합니다. 이를 통해 CPU는 최대한 IDLE 상태가 아닌채로 활동합니다. 일해라 CPU

  4. Queue에 완료된 작업이 있을 경우 이를 클라이언트에게 응답(Response 합니다.)

 부록 
[Youtube] Evnet Driven은 어떠한 원리로 동작이 될까?
비동기 방식을 통해 Single Thread를 처리하는 과정 (이벤트 루프에 대한 설명)
[JavaScript] EventLoop와 비동기 동작

 

Nginx 작업 Step 4

Queue에 있는 완료된 작업을 받아내어 이를 클라이언트에게 응답(Response) 합니다.

 

Nginx 작업 Step 마무리

나름대로 최대한 이해하고 써봤는데, 미숙한 부분이 있을수도 있습니다. 혹시 내용이 부족하거나 잘못 쓴 부분이 있다 싶으면 댓글로 제보주시면 감사하겠습니다.

 

 

3. Nginx와 Puma간 메모리 퍼포먼스의 차이

 

[좌측] Nginx를 이용한 서버 오픈 / [우측] Puma 엔진을 이용한 서버 오픈

아주 기본적인 Rails 프로젝트 상태에서 Nginx와 Puma를 통해 서버를 켰다 했을 때, 메모리 사용량을 비교해 봐도 메모리 사용량이 2배 가량 차이가 납니다.

 


Nginx의 Thread, CPU/메모리 활용에 대해 일상생활 비유로 정리

 

비행기에는 기본적으로 승객과 승무원이 존재합니다. 여기서 승객은 비행 중, 필요한 사항(간식 요청, 면세품 구입, 담요 요청 등)이 있을 시 승무원을 호출합니다.

  • 승무원을 호출하는 승객  클라이언트에서 서버에게 보내는 요청

  • 승무원  요청을 처리하는 프로세스 혹은 쓰레드

위와같은 개념으로 나름의 Apache와 Nginx의 설명을 이어보겠습니다.

 

1) Apache

 실제 서버 내에서 작업 

하나의 요청 당 서버에서는 하나의 프로세스(혹은 쓰레드)를 다룹니다. 하지만 서버 내 CPU/메모리는 한정적임에 반해 프로세스(쓰레드)는 요청이 들어온 만큼 계속적으로 생성/작업을 하다보니 무리한 작업으로 인해 서버가 뻗어나갈 수 있습니다.

 

 일상생활 비유(항공) 

정규 비행 예약량을 기준으로 볼 때 승객 한명 당, 승무원 한명이 배치된다고 보면 됩니다. 하지만 이는 항공사에서도 비효율적인 운영방식이고, 또한 항공사 입장에서도 승무원 고용에 있어 수많은 비용이 들 것입니다.

어찌어찌해서 정규 비행 예약 기준으로 손님 1명 당 승무원 1명이 배치되었다고 치지만, 오버부킹으로 인해 손님 >= 승무원이 되어버린다면?...

 

2) Nginx

 실제 서버 내에서 작업 

서버가 실행되면 일단 사전에 쓰레드를 생성해냅니다. (몇개를 설정할 지는 서버개발자 마음)

그리고 클라이언트로 부터 요청이 들어오면 일단 서버에서는 사전에 만들어진 한정된 쓰레드 갯수를 기반으로 요청에 대해 처리합니다.

그러나 요청 >= 쓰레드일 경우, 뒤늦게 들어온 요청은 잠시 큐에 대기 및 앞의 요청에 대해 처리를 끝낸 쓰레드는 큐에 대기하고 있는 요청이 있는지 확인 후, 아직 처리되지않은 요청이 있을 경우 이를 또 처리합니다. (남은 요청이 있는지에 대해서는 Event Loop가 항상 체크합니다.)

 

 일상생활 비유(항공) 

사전에 승무원 수를 5명으로 지정해서 비행기에 탑승을 시킵니다. 그리고 승무원들이 일을 하는 스타일은 센스를 발휘해서 FIFO 순서로 일을 진행하지는 않고, 빨리 끝날 것 같은 일 부터 먼저 진행을 합니다.

 

비행기에서 승객으로부터 호출이 하나 둘 들어오고, 승무원은 이를 해결합니다.

5명의 승무원이 사전에 승객으로부터 받은 부탁을 처리를 위해 비행기 내부를 이리저리 도라다니는 도중, 다른 승객이 '저기요~' 하면서 승무원을 부르면서 추가적인 부탁을 합니다. 그런데 중간에 호출 및 추가적으로 받은 부탁이 이전에 받은 요청보다 일찍 끝날 것 같다 싶으면 빨리 끝나는 일을 능동적으로 먼저 해결합니다.

 

 

보안 취약점 대비 가능

클라이언트에서 서버에 접근함에 있어, Rails Project에 접근하기 전, 그 앞단에 존재하는 Nginx을 바라보게 됩니다.

 

이를 통해 Nginx에서 보안적으로도 다양한 대비를 할 수 있습니다.

서버에서 발생할 수 있는 다양한 취약점(XSS을 이용한 세션 하이잭킹 등)에 대한 보안 대비를 할 수 있습니다. (IP Blacklist 추가 등등)

 

 

  • Apache와 Nginx의 차이는?

웹 서버 엔진의 양대산맥이라 불리는 Apach와 Nginx에 대해 알아보겠습니다.

 

1. Apache

1995년 부터 웹서버 기반으로서 사용되고 있는 서버엔진입니다.

아파치는 Prefork와 Worker 방식으로 돌아가고 있는데 각각의 방식에 대해 설명해보도록 하겠습니다.

 

1) Prefork

Apache : Prefork

프로세스를 아예 다중적으로 복사하여 사용하는 방식입니다. 하나의 요청 당 하나의 프로세스로 처리가 이루어지며, 요청량이 많아질수록 프로세스는 증가하지만 복제시 메모리영역까지 복제되어 동작하므로 메모리를 많이 사용하게 된다는 단점이 존재합니다.

 

2) Worker

Apache : worker

과거 Prefork 방식과 다르게 Request >= Process 구조로 이루어져 있는 Worker 방식입니다. 

쓰레드 특성 상 프로세스 내의 Memory를 공유한다는 장점 덕분에 Preworker보다 메모리 자원을 덜 쓰면서도 작업이 수행된다는 장점이 존재합니다.

 

 

  Apache 정리

 

  1. 1995년에 나온 서버엔진으로서, 지금까지도 많은 인기를 누리고 있음.

  2. Process가 Blocking 시, 프로세스 작업이 끝날 때 까지 대기해야 한다.

  3. 주로 요청 하나당 프로세스(혹은 쓰레드) 하나가 쓰이는 방식이다.

  4. 자원(CPU, 메모리 등) 사용이 유동적 (요청에 따라 쓰레드, 프로세스 할당갯수가 증감됨.)

  5. 서버 내 자원(CPU, 메모리 등) 활용이 비효율적이다.

 

 

2. Nginx

2007년에 오픈소스로 공개되어 점점 많은 인기를 얻어가고있는 엔진입니다.

초반에 언급했다 싶이 Apache의 무리한 자원 활용을 완화하여 탄생하게 된 엔진입니다.

Nginx와 일반적인 서버의 구동 방식

위 사진과 같이 프로세스를 효율적으로 활용함으로서 서버 자원을 최대로 활용해나갔고, Event Driven을 활용한 비동기 Non-Blocking 방식을 선호함으로서 프로세스 작업이 끝날 때 까지 대기하지 않아도 된다는 장점도 있습니다.  

 

 

  Nginx 정리

 

  1. 2007년에 탄생

  2. Event Driven방식의 웹 서버 : 이 덕분에 비동기 Non-Blocking 방식 특징을 활용 가능

  3. 싱글스레드의 활용 → 적은 메모리 사용

  4. 자원(CPU, 메모리 등) 사용이 고정적 (싱글스레드)

  5. 대용량 트래픽(수많은 동시접속자) 처리에 있어 요즘 떠오르는 대안

 

 

 

이것으로 AWS EC2에서 Nginx 엔진 개념 및 활용해야 하는 법에 대해 알아봤습니다.

다음에 이어질 Nginx를 활용한 Rails 프로젝트 배포법에서 다시뵙겠습니다.

 

긴 글 봐주셔서 감사합니다.

 

 
  • 자료 참고

1. 위키백과 : C10k problem

2. [오픈위키] C10k Problem

3. Apache냐 Nginx냐, 그것이 알고싶다.

4. Apache httpd – Prefork MPM 과 Worker MPM 의 비교

5. Nginx 공식페이지 : Thread Pools in NGINX Boost Performance 9x!

6. 아파치 2.4와 Nginx 특징 및 비교

7. Nginx Architecture

8. [씨엔텍시스템즈] 웹서버 소프트웨어 Apache 와 NginX 비교

9. apt-get update에서 hit 및 get 개념

10. 보안등급 A+를 받을 수 있는 Nginx SSL 설정

 

 

 

댓글
댓글쓰기 폼
공지사항
Total
156,742
Today
42
Yesterday
130
링크
«   2021/11   »
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30        
글 보관함