Posts nginx란 무엇인가
Post
Cancel

nginx란 무엇인가

Nginx

Nginx 는 웹 사이트의 서버(WAS)를 도와주는 비동기 이벤트 기반 구조의 경량화 웹 서버 프로그램이다.

클라이언트가 요청한 정적 파일을 응답하는 HTTP Web Server 로 활용하기도 하며, Reverse Proxy Server 로 활용하여 WAS 의 부하를 줄이는 로드 밸런서 역할을 하기도 한다. (참고: Forward Proxy와 Reverse Proxy)

탄생 배경

최초의 웹 서버 httpd 그리고 Apache server

1.png

출처: CERN httpd [위키백과]

1990년 초반, UNIX 기반 웹 서버인 CERN httpd 는 버그가 많아서 개발자들이 사용하기 불편했다.

그래서 몇몇 개발자들이 CERN httpd 를 개선하고자 했고, 그 결과로 나온 프로그램이 아파치 서버(Apache server) 이다.

2.png

출처: 아파치 HTTP 서버 [위키백과]

Apache server 의 작동 원리

아파치 서버는 클라이언트의 요청이 들어오면 커넥션(connection)을 생성하기 위해 프로세스를 만든다. 그래서 새로운 클라이언트의 요청이 들어올 때마다 새로운 프로세스를 생성한다.

3.png

출처: [10분 테코톡] 🤫 피케이의 Nginx [유튜브]

이는 UNIX 계열 운영체제가 네트워크 커넥션을 형성하는 모델을 그대로 적용한 것이었다.

하지만, 프로세스를 만드는 것은 시간이 오래 걸리다보니 요청이 들어오기 전에 미리 프로세스를 생성하는 prefork 방식을 사용했다.

4.png

출처: [10분 테코톡] 🤫 피케이의 Nginx [유튜브]

그래서 새로운 클라이언트로부터 요청이 들어오면 미리 만들어둔 프로세스를 가져와서 사용했다. 미리 만들어둔 프로세스가 모두 할당되면, 추가로 프로세스를 만들었다.

5.png

출처: [10분 테코톡] 🤫 피케이의 Nginx [유튜브]

아파치 서버는 멀티 프로세싱으로 작동하기 때문에 다양한 모듈을 사용하는 것이 가능하다. 여기서 말하는 모듈은 실행 파일(프로그램)이라 생각하면 된다.

멀티 프로세스는 컴파일 과정에서 정해진 루틴만 수행하는 멀티 스레딩과 달리 상황에 맞춰서 다양한 프로그램을 execve 함수로 실행하는 것이 가능하다.

그래서 아파치 서버는 모듈 방식을 이용해서 상황에 맞게 다양한 프로그램들을 추가하거나 제거해서 사용할 수 있었다.

또한, 모듈을 사용하면 컴파일 과정에서 웹 서버 자체의 용량에 모듈이 포함되지 않기 때문에 가벼우면서도 다양한 프로그램을 사용할 수 있게 되었다.

그래서 다양한 동적 컨텐츠를 하나의 서버에서 요청받고, 응답해줄 수 있다는 장점이 있었다.

C10K 문제의 등장

1999년에 들어서면서 컴퓨터의 보급이 활발히 이루어지면서 트래픽이 증가하기 시작했다. 그러자 웹 서버는 1만개 이상 커넥션이 생성되었을 때 동시에 처리할 수 없는 상황이 발생했다. 이를 C10K(Concurrent connection 10k) 문제라고 한다.

여기서 “동시에 연결된 커넥션 수”“초당 요청 처리 수” 는 다른 개념이다.

  • 동시에 연결된 커넥션 수 : 요청을 처리하기 위해 서버가 한 시점에 얼마나 많은 클라이언트와 커넥션을 형성하고 있는지 나타내는 지표
  • 초당 요청 처리 수 : 서버가 1초에 얼만큼의 요청을 처리할 수 있는지 나타내는 지표

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

커넥션을 형성하기 위해서는 handshake 과정을 거치게 되는데, 요청마다 커넥션을 생성하기엔 비효율적이고 속도도 느렸다.

그래서 어떤 클라이언트와 이미 만들어진 커넥션이 있다면 커넥션을 다시 사용하고자 했다. http 프로토콜의 keep-alive 헤더를 사용해서 해당 헤더에 적힌 시간만큼 클라이언트와 서버는 커넥션을 유지했다.

하지만, 클라이언트의 수가 많아지는 만큼 동시에 연결되어 있는 커넥션 수는 함께 많아지는 문제가 발생한다. 그래서 동시 커넥션 수가 10,000 단위로 넘어가는 순간 서버는 더 이상 커넥션을 형성하지 못하게 되었다.

이러한 문제는 아파치 서버를 비롯한 그 당시 대부분의 웹 서버에서 마주하던 문제였다. 당시 웹 페이지 컨텐츠 용량은 그렇게 크지 않았고, 컴퓨터 성능도 빠르게 발전하고 있었기 때문에 하드웨어 성능 자체보다는 아파치 서버가 작동하는 방식이 원인이었다.

문제의 원인은 크게 3가지였다.

  1. 아파치 서버는 커넥션이 형성될 때마다 프로세스를 생성한다. 그래서 동시에 처리하고 있는 커넥션이 많아지면 프로세스도 많아지고, 곧 메모리 부족 현상으로 이어진다.
  2. 모듈을 사용해서 여러 프로그램을 함께 사용하다보니 프로세스가 차지하는 자원의 양이 늘어나게 되었다.
  3. 많은 커넥션에서 요청이 들어오면 CPU 코어는 프로세스를 바꾸어 일을 하는 컨텍스트 스위칭이 많이 발생하고, 이는 곧 CPU 의 부하가 늘어난다는 것을 의미한다.

따라서 수 많은 동시 커넥션을 감당하기에는 아파치 서버는 적절하지 않았다.

Nginx 의 등장

아파치 서버 개발자들은 위와 같은 문제점을 개선하기 위해 여러 시도를 했고, 2004년에 새로운 구조를 채택한 새로운 소프트웨어 프로그램을 출시한다. 이것이 바로 Nginx 이다.

초창기 Nginx 는 아파치 서버와 함께 사용하기 위해 만들어졌다. Nginx 도 웹 서버였지만, 완전히 아파치 서버를 대체할 목적은 아니었다.

6.png

출처: [10분 테코톡] 🤫 피케이의 Nginx [유튜브]

Nginx 는 아파치 서버 앞단에 위치했고, 기존에 아파치 서버가 감당해야 했던 수 많은 커넥션을 Nginx 가 대신 유지할 수 있었다. 구조적으로 많은 커넥션을 감당하지 못하는 아파치 서버의 부하를 Nginx 를 이용해서 크게 줄일 수 있었다.

Nginx 는 그 자체로 웹 서버이기 때문에 정적 파일(HTML, CSS, JS, 이미지 등)은 스스로 처리할 수 있었다. 웹 서버의 역할을 하는 Nginx 는 클라이언트가 요청한 동적 파일을 받았을 때만 뒤에 있는 서버와 커넥션을 형성한다. 아파치 서버의 리소스를 커넥션 유지에 쓰지 않고, 개발자가 원하는 로직 처리에 쓰도록 도울 수 있었다.

작동 구조

Nginx 가 수 많은 커넥션을 유지할 수 있었던 것은 아래와 같은 구조를 가지고 있었기 때문에 가능했다.

7.png

출처: [10분 테코톡] 🤫 피케이의 Nginx [유튜브]

Nginx 는 설정 파일을 읽고, 설정에 맞게 worker process 를 생성하는 master process 를 가지고 있다.

worker process 는 실제로 일을 수행하는 프로세스이며, worker process 가 만들어질 때 지정된 listen 소켓을 배정받는다.

그리고 그 소켓에 새로운 클라이언트의 요청이 들어오면 커넥션을 형성하고 처리한다.

커넥션은 정해진 Keep-Alive 시간동안 유지된다. 하지만 커넥션이 형성되었다고 해서 worker process 가 커넥션 하나만 감당하지 않는다.

형성된 커넥션으로부터 아무런 요청이 없다면 새로운 커넥션을 형성하거나, 이미 만들어진 다른 커넥션으로부터 들어온 요청을 처리한다.

Nginx 에서는 커넥션 형성 및 제거, 새로운 요청을 처리하는 것을 이벤트라고 부른다.

8.png

출처: [10분 테코톡] 🤫 피케이의 Nginx [유튜브]

그리고 이벤트들은 운영체제 커널이 queue 형식으로 worker process 에 전달해준다.

이벤트들은 queue 에 담긴 상태에서 worker process 가 처리할 때까지 비동기 상태로 대기한다.

worker process 는 하나의 스레드로 이벤트를 꺼내서 처리한다.

이러한 구조 덕분에 worker process 는 쉬지 않고 일을 계속할 수 있다는 장점이 생긴다.

아파치 서버의 구조와 비교했을 때, 요청이 없으면 방치되던 프로세스보다 서버 자원을 훨씬 효율적으로 사용한다.

그런데, 만약 어떤 이벤트가 시간이 오래 걸리는 작업(디스크 I/O)이라면 어떻게 해야 할까?

시간이 오래 걸리는 이벤트 뒤에 있는 이벤트는 요청을 처리하는 동안 오래 블로킹이 될 것이다.

그래서 Nginx 는 이러한 상황을 방지하기 위해 시간이 오래 걸리는 작업은 따로 수행하는 thread pool 을 생성한다.

그리고 worker process 는 지금 처리할 요청이 시간이 오래 걸릴 것 같으면, 해당 thread pool 에 이벤트를 위임하고, queue 안에 있는 다른 이벤트를 처리하러 간다.

9.png

아파치 서버의 스레드 방식 - 출처: Nginx란 무엇인가? [티스토리]

10.png

Nginx 의 Event-Driven 방식 - 출처: Nginx란 무엇인가? [티스토리]

worker process 는 보통 CPU 의 코어 개수만큼 생성한다.

이러면 코어가 담당하는 프로세스를 바꾸는 횟수(컨텍스트 스위칭)를 대폭 줄일 수 있다.

하지만 Nginx 의 구조에는 단점도 존재한다.

개발자가 기능 추가를 위해 작동하고 있던 worker process 종료하는 상황에서는 해당 worker process 가 관리하고 있던 커넥션과 관련된 요청을 더 이상 처리할 수 없는 문제가 발생한다.

그래서 Nginx 는 개발자가 직접 모듈을 만들기 까다롭다는 단점이 있다.

그럼에도 불구하고 프로세스를 적게 생성하면서 빠르게 처리할 수 있다는 장점은 유효했다.

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

개발자가 설정 파일을 변경하고 Nginx 에 해당 설정을 적용하면 master process 는 그 설정에 맞는 worker process 를 따로 생성한다.

그리고 기존에 있던 worker process 가 더 이상 커넥션을 형성하지 않도록 한다.

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

이처럼 동적으로 설정을 변경하는 경우는 Nginx 가 여러 동시 커넥션을 관리하는 도중에 뒷단에 서버를 추가하는 상황이 대표적이다.

그때는 Nginx 가 로드 밸런서의 역할을 담당하게 된다.

로드 밸런서는 요청을 여러 서버로 분산하는 작업을 수행한다.

Nginx 는 이런 설정 변경을 초당 수십번을 해도 무리없이 커넥션을 관리하고 요청을 서버에 전달한다. 이것이 가능한 이유는 이벤트 기반 구조이기 때문이다.

특징

SSL Termination

Nginx 는 클라이언트와는 https 통신을 하고, 서버와는 http 통신을 하는 것을 SSL Termination 이라고 한다.

이러한 구조 덕분에 서버가 복호화 과정을 감당하지 않도록 만들 수 있고, 비즈니스 로직 처리에 리소스를 사용할 수 있도록 부하를 줄여준다.

보통 Nginx 와 서버는 같은 네트워크 안에 있는 경우가 많기 때문에 이 둘은 http 통신을 해도 보안상 위험하지 않다.

캐싱 (Caching)

Nginx 는 http 프로토콜을 사용해서 전달하는 콘텐츠를 캐싱할 수 있다.

SSL Termination 을 하는 경우엔는 Nginx 를 서버와 가깝게 배치하지만, 캐싱을 하는 경우에는 클라이언트와 가깝게 배치한다.

그리고 한번 서버로부터 받은 응답을 스스로 보관하고 클라이언트에게 전달한다.

참고자료

This post is licensed under CC BY 4.0 by the author.

HTTP와 HTTPS, 그리고 SSL/TLS

프로그래머스 Level 1 - 기사단원의 무기 (JavaScript)