웹서버(Web Server)란
브라우저가 웹 서버에서 불려진 파일을 필요로 할때, 브라우저는 HTTP를 통해 파일을 요청합니다. 요청이 올바른 웹 서버에 도달하였을때, HTTP 서버는 요쳥된 문서를 HTTP를 이용해 보내줍니다.
하드웨어 관점 | "웹 서버의 소프트웨어"와 웹 사이트의 "컴포넌트 파일"들을 저장하는 컴퓨터 (컴포넌트 파일 : HTML, CSS, JS, 이미지) 웹 서버는 인터넷에 연결되어 웹에 연결된 다른 기기들이 웹 서버의 데이터(컴포넌트 파일들)을 주고 받을 수 있도록 한다. |
소프트웨어 관점 | 웹 사용자가 어떻게 호스트 파일에 접근하는지를 관리 웹 서버는 HTTP 서버 형태로 존재(웹 구현 시)하며, HTTP서버는 HTTP의 소프트웨어 일부 |
사용자는 웹 브라우저에 접속한다. 웹 브라우저에서는 HTML, CSS, 자바스크립트 파일들이랑 각종 이미지, 기타 여러 데이터들을 갖다가 서버에서 사용자의 컴퓨터로 보내줄 수 있어야 한다.
이 서버의 특정 폴더, 디렉토리에 이것들을 넣어두면 이 폴더를 외부에서 접근 가능하도록 개방해서 서버에 지정된 웹사이트 주소로 접속하면 이것들을 받아갈 수 있도록 하는 것이 웹서버의 기본적인 역할 중 하나입니다.
WebServer 종류
웹 서버에는 대표적으로는 Apache, Nginx, Window IIS 등이 있다. IIS는 윈도우 GUI에 최적화된 것이기에 현업에서 대부분 Apache 또는 Nginx를 사용한다.
HTML, CSS, 자바스크립트 파일들이랑 각종 이미지, 기타 여러 데이터들을 갖다가 서버에서 사용자의 컴퓨터로 보내줄 수 있어야 한다. 즉 정적 웹페이지를 사용자에게 전달하는 역할을 한다.
정적 컨텐츠는 설계 아키텍쳐 구조성 Nginx가 적은 비용으로 효율적인 서비스를 제공한다.
그렇다면 webserver는 동적 컨텐츠는 제공하지 못하는것인가?
- Nginx : SCGI핸들러와 FastCGI 모듈을 사용해서 동적 컨텐츠를 제공
- Apache : mod_jk, mod_per 모듈 등을 통해 동적 컨텐츠를 제공하고 있다.
Apache
Apache 웹 서버는 Process-Driven 방식 즉 멀티 프로세스 + 멀티 스레드 방식이다.
항상 여유로운 수의 프로세스/스레드를 생성해두기 때문에 요청이 들어왔을 때 프로세스/스레드가 생성되는 것을 기다리지 않아도 된다. 서버를 최초 실행할 때 몇 개의 프로세스를 생성할지 전달받는다. 서버는 모든 프로세스에 속해있는 유휴 쓰레드 수를 파악하고, 이 값이 사전에 설정한 범위 내에 있도록 프로세스를 fork하거나 kill한다. 평소에는 요청 하나에 스레드 하나가 대응하는 구조. 하나의 프로세스에서 관리할 수 있는 스레드 수가 정해져있기 때문에 사용자 접속이 증가하면 프로세스를 fork() 한다.
하지만 fork 할때 마다 CPU와 메모리 사용량이 많이 증가하는 문제가 있다.
Apache MPM(Multi-Processing Modules)
클라이언트가 Http서버로 전송한 요청을 다중처리하기 위해 자식 프로세스들에게 분배하는 모듈
클라이언트에 요청이 많을 경우 그때마다 Process 또는 Thread을 생성하고 처리하기 때문에 동시 접속 요청이 10000개라면 그에 해당하는 Thread 생성 비용이 발생하게 되고, 시스템 자원이 고갈된다는 문제점이 있다
1) Prefork 방식
- 실행중인 프로세스를 복제하여 실행(1 process - 1 thread 방식, 자식 프로세스는 최대 1024개 가능.)
- 하나의 요청에 대해서 하나의 자식 프로세스가 생성되고 하나의 스레드를 사용해서 처리한다.
- 요청이 독립적인 프로세스로 처리되기 때문에, 프로세스 오류가 발생해도 다른 요청에 영향이 없다. ( 메모리 사용공간이 다름)
- 프로세스가 독립적으로 실행되므로 프로세스 수가 많아짐에 따라 cpu, memory 등 시스템 리소스에 부하가 심해짐
- 요청이 많이 없고 각각의 연결 안정성이 중요한 서비스에 사용
2) Worker 방식
- 각 요청을 프로세스 스레드로 받아 처리
- 1 process - multi thread 방식으로 프로세스 별 스레드 개수를 정할 수 있다.
- 자식 프로세스 안에 여러개의 스레드를 사용하고 있고 각 요청은 각 스레드에서 처리하게 된다.
- 프로세스 하나의 제한된 스레드 개수가 있는데 그 스레드 수를 넘어가면 새로운 자식 프로세스를 생성해서 처리하게 된다.
- 연결마다 메모리 공간을 공유하여 리소스 부하가 줄어든다는 장점이 있지만, 오류 발생시 한 프로세스 내 모든 스레드들이 영향을 받을 수 있다.
- Prefork 처럼 요청당 프로세스 하나가 아니라 메모리가 절약된다는 장점이 있어 부하 위험성이 덜하기 때문에 대량 연결이 필요한 서비스에서 사용된다.
- 프로세스가 죽으면 다른 프로세스들도 같이 죽을 수 있다.
Nginx
Nginx는 이러한 Apache가 가지고 있던 문제점을 보완하기 위해 등장하게 되었다.
문제점은 C10k(Concurrent 10,000 clients/connections) 문제인데 기존의 웹 서버를 통해 1만개의 동시 연결을 처리하기 어렵다는 것이다.
Nginx는 멀티 프로세스 + 싱글 스레드 방식으로 Event-Driven 방식이다. Node.js가 대표적으로 이 방식을 사용하고 있으며, 싱글 스레드의 Event Loop가 계속 돌아가면서 Event Queue에 요청이 들어오면 Thread Pool에 분배하여 비동기적으로 처리한다. 그렇기 때문에 많은 요청이 한꺼번에 오더라도 무리없이 서비스할 수 있다.
- Nginx는 하나의 master process가 다수의 worker proces를 관리합니다.
- Low memory usage와 high concurrency를 위해 asyn event-driven 방식을 사용
- master process는 worker process를 관리하는 일만 수행하고, worker process에서 사용자의 응답에 대응하는 방식을 취합니다. 각각의 worker process에 대해서 single thread방식으로 동작하기 때문에 각각의 worker는 초당 수천개의 동시 접속과 요청을 처리할 수 있다.
worker Process
- 네트워크와 스토리지의 상태 확인
- 새로운 연결을 초기화
- 런 루프에 추가
- 완료될 때까지 비동기적으로 처리
- 완료되면 연결이 할당 해제되고 런 루프에서 제거됨
WAS
'웹'이랑 '서버' 사이에 '어플리케이션'이란 말이 들어가서 WAS라고 불리운다.
웹서버처럼 단순히 뭔가를 갖다주는게 아니라 뭔가 프로그래밍된 걸 더 하는 역할이다. 이것은 동적 사이트를 전문적으로 처리해주는 역할을 한다.
아파치나 nginx 같은 웹서버도 php같은 것 처리할 수 있지만 spring 같은 경우 톰캣같은 WAS가 필요하다.
자바 바이트코드로 컴파일되는 언어들에 쓰이는걸로 가장 흔히 사용되는 톰캣이나 Jetty, Undertow등이 있다.
톰캣은 스프링으로 코딩된 웹앱을 war파일로 빌드하면 .class 파일들이랑 jsp, 이미지, css, 자바스크립트 파일 등이 압축되어있는데 톰캣을 다운받아보면 여러 폴더들과 파일들이 들어있는 하나의 폴더로 되어있는데 그 중 특정 폴더에 방금 말한 war 파일을 넣고 명령어를 실행하면 스프링 서비스가 톰캣을 사용해서 돌게 되는 것이다.
요즘은 반대로 스프링을 톰캣에 들어있는 jar 파일로 빌드해서 배포하곤 한다.
웹서버 -> WAS
웹서버로 들어오면 웹서버가 맨 앞에서 응접실에서 접대하도록 하고 그 뒤에서 WAS등 요리사가 요리를 하고 있는 것으로 보면 된다. WAS에서 요리가 완성되면 웹서버(nginx, apache)에서 서빙을 하는 것이다.
X -> WAS
WAS서버는 요리사라고 보면 되는데 직접 사용자에게 동적으로 요리해낸 웹을 서빙해서 갖다줄 수 있고 진열되어있는 정적 리소스들을 건내주는 일도 할 수 있다.
하지만 왜 웹서버에서 먼저 받아주는 역할을 하는것일까?
그 이유는 reverse proxy 역할 때문이다.
먼저 forward proxy는 사용자들이 사이트나 어디 접속할 때, 자기 아이피 주소 숨기려고 중간에 proxy라는거 둬서 그걸 통해서 데이터를 주고 받는 것이다. 즉 서버에 방문하는 손님들이 자기네 집 주소를 감추는 것
반대로 reverse proxy는 손님들에게서 서버의 정보를 감추는것이다.
손님들이 알고 접속하는데 감출 필요가 있을까?
서버도 보안상 내부 구조를 감출 필요가 있다. 예를 들면 정적 리소스들이 진열된 방은 어디인지 톰캣같은 was들이 동적 요소를 관리하는 곳은 어디인지 서비스가 몇 번 포트로 돌고 있는지 이런것들을 드러내지 않고 아파치나 nginx가 대신 손님들을 맞이하고 was에서 전달한 요리를 서빙하는 방식이다. 아파치나 nginx는 따라서 다양한 보안 기능들을 제공하고 있다.
Reverse Proxy의 특징
- 백앤드 infra를 숨김
- 서버는 어느 클라이언트에서 요청이 왔는지 모르고 클라이언트도 어느 서버로 요청을 보내야 하는지 모른다. 다만 reverse proxy를 매개로 요청과 응답을 주고 받을 뿐이죠. 따라서 백앤드가 어떻게 구성되어 있는지 클라이언트는 알 수가 없다.
- 캐싱
- reverse proxy에서 캐싱함으로서 웹사이트 엑세스 시간을 줄입니다. 단순히 웹사이트 엑세스 시간을 줄일 뿐 아니라 각 서버에 요청을 줄여 서버의 트래픽을 줄여준다.
- 보안
- 만약 reverse proxy가 없고 WAS가 직접 DMZ에서 서비스를 하고 있다면 혹여나 WAS가 털릴 경우 WAS와 연결된 DB 등 여러 자원들도 연이어 털리게 된다.
- 하지만 중간에 reverse proxy가 존재하고 WAS는 내부 네트워크에 존재하면서 혹여나 reverse proxy가 털려도 내부망으로 연결이 불가능하기 때문에 보안성이 높아진다.
이 외에도 웹서버는 로드밸런싱을 하고 있다.
손님들이 여럿 몰릴 때 여러 요리사들에게 분산해서 주문을 넣어주는 역할을 하는 것이다.
로드 밸런싱
부하 분산이라고 부르기도 하는 로드 밸런싱은 네트워크 기술의 일종이다.
하나의 웹 서비스에 클라이언트, 즉 접속 요청자들이 많아질 때 발생하는 문제를 해결하기 위해 사용된다. 로드 밸런싱은 L4 스위치에서 적용되어 클라이언트 분산처리 시킨다.
트래픽이 많을 때, 하나의 서버에서 이를 모두 처리하는 것이 아니라 여러대의 서버를 이용해서 요청을 처리하게 된다.
이때 서버의 로드율과 부하량, 속도저하 등을 고려하여 적절하게 서버들에게 분산처리 하는 서비스를 로드밸런싱이라고 한다.
그렇다면 로드밸런싱 Nginx가 많아지면 좋은건가?
여행가이드로 비유해보자.
현재 로비에서 가이드들(nginx)을 기다리고 있다. 한 가이드는 알고 있는 정보에 기초하여 각 여행객에에 대한 최선의 선택을 시도한다.
하지만 여러 가이드들이 있다면 가이드들은 각각의 대기열 길이와 대기 시간에 대한 독립적인 시각을 가지고 있으며, 각각의 대기열에 지시하는 여행자만 고려합니다.
이 경우 모든 가이드가 일시적으로 더 짧고 빠른 대기열 인지하고 모든 여행자를 해당 대기열로 보내는 불필요한 행동이 발생할 수 있습니다. "쏠림" 현상이 발생하는 것이다.
Power of Two Choices 로드 밸런싱 알고리즘
'Power of Two Choices' 알고리즘은 불완전한 데이터를 사용하여 절대적으로 최상의 선택을 하는 대신, 무작위로 두 개의 대기열을 선택하고 두 개 중 더 나은 선택을 하여 더 나쁜 선택을 피한다.
매번 최선의 선택을 하기 위해 모든 큐를 비교할 필요가 없으며 대신 두 개의 큐를 비교하기만 하면 된다. 아마 직관적이지는 않지만 최선의 선택 알고리즘보다 대규모로 더 잘 작동된다. 무작위성을 조금 더하여 최악의 대기열을 피하고 트래픽을 분산함으로써 원하지 않는 군집 동작을 방지한다.
지금은 알고리즘을 깊이 있게 공부하는 것은 후순위이기 때문에 링크를 첨부해두겠다.
AWS 로드 밸런싱
ELB
- ELB는 AWS에서 제공하는 로드밸런서를 통칭하는 의미하다.
- ELB의 종류로는 ALB, NLB, GLB, CLB가 있다.
ALB
- OSI 7 Layer중에서 애플리케이션 레이어에서 로드밸런싱 동작한다.
- HTTP/HTTPS 프로토콜의 URL의 PATH 기반으로 전송할 타겟을 지정할 수 있다.
- ALB는 SSL 적용이 가능하다.
- ALB는 IP주소 + 포트번호 + 패킷 내용을 보고 스위칭합니다.
- NLB보다 성능적으로 느릴수는 있어도 요청에 따른 패킷에 따라 다양한 전송 규칙을 지정할 수 있다.
NLB
- OSI 7 Layer중에서 전송계층 레이어(L4)에서 로드밸런싱 동작한다.
- TCP/UDP 프로토콜 기반으로 전송할 타겟을 지정할 수 있다.
- 데이터 안을 확인하지 않고 패킷레벨에서만 밸런싱하기 때문에 속도가 빠르지만 패킷을 확인하지 않기 때문에 섬세한 라우팅이 불가능하다.
- 사용자가 IP가 수시로 바뀌는 경우라면 연속적인 서비스를 제공하기 어려움.
- NLB는 프로토콜을 구분하지 못하기 때문에 SSL 적용이 불가능하기 때문에 애플리케이션에서 따로 적용해 주어야 합니다.
- 고성능을 요구하는 환경에서 부하분산에 적합하며, 낮은 레터런시로 초당 수백만 건의 요청을 처리할 수 있다.
ALB와 NLB의 속도 차이
NLB의 장점 중 하나는 클라이언트의 요청에 대해서 낮은 대기 시간으로 높은 처리가 가능하다는 것이다.
NLB는 network 계층까지만 확인하기 때문에 7계층인 ALB보다 빠릅니다.
마지막으로 단순한 라우팅이 필요하고, 트래픽이 극도로 많은 경우에는 ALB보다 NLB를 사용하는 것이 적합하다고 할 수 있다.
ALB & Nginx 왜 같이 쓰는걸까?
ELB의 주된 목적은 트래픽을 분산하는 것이지만, SSL 암호화 인증도 함께 지원하여 유용하게 사용할 수 있다.
대안으로 nginx만을 사용할 수 있지만 SSL 인증서 발급과 갱신에서 번거로움이 있을 수 있다.
ALB와 Nginx를 같이 사용하는 사례로
외부에서 http로 컨택하게 되면 ELB에서 http를 https로 리디렉션 해주는 작업을 수행한다. 하지만 Nginx까지 사용하는 경우
외부의 접속을 ELB가 받게 되면 https로 리디렉션되어 EC2에 전달해주지만, 혹시나 모를 케이스에서 Nginx가 EC2에서 https로 리디렉션 해주는 작업을 수행하게 된다.
먄약 로드밸런싱 환경을 만들었는데 여기에 reverse proxy를 하게 되는 경우 사용하게 된다.
결국 ALB에서 로드밸런싱 역할을 한다면 굳이 nginx 포함시킬 필요가 없다.
두 개를 같이 쓰는 글이 많이 보여 시스템 아키텍쳐에 포함시켰지만 이러한 경우라면 내가 하는 프로젝트에서는 하나만 쓰는게 맞는것 같다는 생각이 들었다.
그럼 둘 중 선택해야 하는데 Nginx선택해야 한다면
1. 효율적인 정적 콘텐츠 제공: Nginx는 정적 파일(예: HTML, CSS, 이미지 등)을 가장 빠르게 제공합니다. 이러한 점이 애플리케이션에 해당된다면, WAS 보다 Nginx에서 직접 콘텐츠를 서빙하는 것이 효율적일 수 있다.
2. 복잡한 라우팅 규칙: ALB는 URL 경로 또는 호스트 기반 라우팅과 같은 간단한 라우팅 규칙만 지원합니다. 좀더 복잡한 규칙(예: HTTP 메소드, 헤더 값 등으로 라우팅)이 필요하다면, Nginx가 필요하게 될 수 있다.
3. 캐싱: Nginx는 강력한 캐싱 기능을 제공합니다. 여기서의 캐싱은 서버 캐싱으로 서버로 찾아오는 손님들이 자주, 반복적으로 찾을 만한 리소스들을 대기실에서 쌓아뒀다가 바로 건내주는 역할을 하는 것이다. 이를 통해 애플리케이션 서버의 부하를 줄이고 응답 시간을 단축시킬 수 있다.
Reference
https://kbwplace.tistory.com/172
https://careerly.co.kr/qnas/4674
https://velog.io/@cjyooong/apache-nginx
https://niklasjang.tistory.com/56
https://no-easy-dev.tistory.com/entry/AWS-ALB%EC%99%80-NLB-%EC%B0%A8%EC%9D%B4%EC%A0%90
'CS' 카테고리의 다른 글
N+1문제의 발생이유와 해결방법 (0) | 2023.12.27 |
---|---|
SpringSecurity와 인증, 인가, JWT의 구조, 동작 과정 (1) | 2023.12.27 |
MVC 모델이란 (0) | 2023.12.21 |
[Spring] DI와 IoC (0) | 2023.12.19 |
[Java] Java Map의 내부 구현 파악 (1) | 2023.12.18 |