👀 Web Server와 WAS 차이
- Web Server : 정적인 컨텐츠(HTML, CSS, JS)를 제공하는 서버
- HTTP 프로토콜을 기반
- 정적 컨텐츠 제공
-> WAS를 거치지 않고 바로 제공 - 동적 컨텐츠 제공을 위한 요청 전달
-> WAS에 클라이언트 요청을 전달하고, WAS에서 처리한 결과를 클라이언트에게 전달 - ex) Apache, Nginx
- WAS(Web Application Server) : 동적인 컨텐츠를 처리하고 제공하는 서버
- HTTP 프로토콜 기반
- DB 조회 및 다양한 로직이 필요한 동적인 컨텐츠를 제공
- 프로그램 실행 환경 및 DB 접속 기능 제공
- ex) Tomcat
🤔 웹 서버의 기능은 WAS에서도 처리 가능한데, 둘을 구분하는 이유는?
사용하는 '목적'이 다르다.
정적인 컨텐츠는 웹 서버에서, 복잡한 동적인 컨텐츠는 WAS에서 처리한다.
기능을 분리하여 서버 부하를 방지하기 위함이다.
웹 서버를 앞단에 두고, 뒤에 WAS를 둔다.
만약 WAS에서 문제가 생긴다면, 웹 서버에서 WAS를 사용하지 못하도록 요청을 차단한다.
그 후, WAS를 재시작하면 사용자들은 WAS에 문제가 발생했다는 사실을 모른다.
이런 기능을 장애 극복 기능이라고 한다.
장애 극복 기능 외에도 Web Server를 통해 추가적인 기능들을 사용할 수 있다.
(리버스 프록시, 로드 밸런싱 등 .. )
이제 본격적으로 Web Server 중, Nginx에 대해 알아보자!
👀 Nginx가 만들어진 배경
🌟 1995년 : Apache 서버 등장
유닉스 기반으로 만들어진 최초의 웹 서버 NCSA HTTPd가 있었다.
하지만 버그가 너무 많아서 개발자들이 새로 만든 것이 Apache 서버이다.
Apache 서버는 새로운 클라이언트의 요청이 들어올 때마다 커넥션을 형성하기 위해 프로세스를 생성한다.
하지만 프로세스를 생성하는 과정은 시간이 오래걸리기 때문에, 프로세스를 미리 만들어두는 PREFORK라는 방식을 이용했다.
이런 구조는 다양한 모듈을 만들어서 서버에 빠르게 기능을 추가할 수 있었고, 동적 컨텐츠를 처리할 수도 있게 되었다.
확장성이 좋다는 장점 덕분에 요청을 받고 응답을 처리하는 과정을 하나의 서버에서 해결하기 좋았다.
🌟 1999년 : C10K 문제 발생
컴퓨터가 많이 보급되고 요청이 많아짐에 따라 서버에 동시에 연결된 커넥션이 많아졌을 때,
더 이상 커넥션을 생성하지 못하는 C10K 문제가 발생했다.
C10K 문제 : 동시 커넥션 수가 10000 단위로 넘어가는 순간, 더 이상 커넥션을 형성하지 못하는 문제이다.
컴퓨터 하드웨어에는 문제가 없었고, 원인은 아파치 서버 구조였다.
- 메모리 부족 : 동시 커넥션 수가 많다는 것은 그만큼 형성된 프로세스가 많다는 의미이고,
이는 메모리 부족으로 이어진다. - 무거운 프로그램 : 아파치 서버의 장점이였던 확장성은 프로세스가 차지하는 리소스 양을 늘렸다.
- CPU 부하 증가 : 많은 커넥션에서 요청이 들어오기 시작하면
CPU 코어는 계속해서 프로세스를 바꿔가면서 일을 하는데(=context switching) 그만큼 CPU 부하가 증가되었다.
🌟 2004 : Nginx의 등장
새로운 구조를 채택한 Nginx가 나오게 되는데, 초창기에 Nginx는 아파치 서버와 함께 사용하기 위해 만들어졌다.
(Apache 서버를 대체? -> NONO)
사용된 방식은 아파치 서버 앞단에 Nginx를 두는 것이였다.
이렇게 하면 기존에 아파치 서버가 감당해야 했던 수많은 동시 커넥션을 Nginx가 대신해서 유지할 수 있다.
구조적으로 동시 커넥션을 많이 유지 못하는 아파치 서버의 부하를 크게 줄일 수 있게 된 것이다!
또한 Nginx도 웹 서버이기 때문에 정적 파일에 대한 요청은 Nginx에서 처리하고
동적 파일 요청을 받았을 때만 아파치 서버와 커넥션을 형성한다.
=> 아파치 서버의 리소스를 커넥션 유지에 쓰지 않고, 로직 처리에 쓰도록 도와줌
🤔 Nginx는 어떻게 생겼길래 수많은 동시 커넥션이 가능한걸까?
👀 Nginx 구조
- Apache 서버 구조 : 멀티 프로세스
먼저 기존의 Apache 서버의 구조에 대해 알아보자.
멀티 프로세스의 구조이다.
클라이언트의 요청마다 별도의 프로세스 또는 스레드를 생성하여 처리하기 때문에,
CPU와 메모리 자원의 소모가 많았다.
- Nginx의 구조 : 이벤트 기반
이벤트 기반 구조이다.
한 개의 프로세스 또는 스레드에서 여러 클라이언트 요청을 비동기 방식으로 처리한다.
요청마다 프로세스를 생성했던 Apache와는 달리, 새로운 요청이 들어와도 프로세스를 생성하지 않기 때문에
적은 자원으로도 효율적인 운영이 가능한 것이다.
🌟 Nginx의 내부 구조
하나의 master process와 여러 개의 worker process로 구성되어 있다.
- maste process : 설정 파일을 읽고, 설정에 맞게 worker process를 생성
Nginx 서버 시작/중지, 네트워크 연결 관리, 신호처리, 워커 프로세스 간 통신 담당 - worker process : 실제로 요청을 처리하는 프로세스
worker process는 보통 CPU 코어 수 만큼 생성하기 때문에 컨텍스트 스위칭을 줄일 수 있음
🌟 동작 방식
- worker process가 생성될 때, master process로 부터 소켓을 배정받는다.
- 소켓에 새로운 클라이언트로부터 요청이 들어오면 커넥션을 형성하고 요청을 처리한다.
해당 커넥션은 정해진 keep-alive 시간 만큼 유지된다. - worker process가 하나의 커넥션만 담당하는 것이 아니기 때문에
형성된 커넥션으로부터 아무런 요청이 없다면 새로운 커넥션을 형성하거나
이미 만들어진 다른 커넥션으로부터 들어오는 요청을 처리한다.
이러한 커넥션 형성/제거 그리고 새로운 요청을 처리하는 것을 이벤트라고 한다.
이 이벤트들은 queue 형식으로 요청 큐에 저장되고, worker process는 큐에서 요청을 가져와 처리한다.
worker process는 하나의 스레드로 이벤트를 꺼내서 처리해 나간다.
이러면 worker process가 쉬지 않고 일을 한다는 장점이 있다.
🤔 하나의 요청이 오래걸린다면?
오래걸리는 요청 때문에 뒤의 요청들이 수행될 수 없는 블록킹 문제가 발생한다.
이런 문제를 해결하기 위해, Nginx에서는 스레드 풀을 사용한다.
오래 걸리는 작업은 스레드 풀에 이벤트를 위임하고, worker process 다음 이벤트를 처리한다.
👀 Nginx 활용
- 리버스 프록시
리버스 프록시란 클라이언트와 서버 간의 통신을 중계하는 서버이다.
포워드 프록시 : 클라이언트 앞에 놓여, 클라이언트의 요청을 대신 전달해줌
리버스 프록시 : 웹서버/WAS 앞에 놓여, 서버로 들어오는 요청을 대신 처리해줌
리버스 프록시로 사용하면 클라이언트 -> Nginx -> 웹 서버 형태가 된다.
사용자의 요청을 Nginx가 대신 웹 서버로 전달해주기 때문에 사용자로부터 서버의 주소를 숨길 수 있다.
사용자는 프록시 서버의 주소만 알게 된다.
따라서 DB 서버, 캐시 서버 등 중요한 정보를 숨기고 해킹을 방지할 수 있다.
리버스 프록시의 주요 기능으로는 아래에 나오는 것이 있다.
- 로드밸런싱
프록시 서버는 트래픽 분산 기능인 로드 밸런싱을 제공한다.
로드 밸런싱 : 다수의 서버에 트래픽을 분산시켜 부하를 분산시키는 작업
Nginx가 로드밸런서로써 요청을 여러 서버로 분산하는 작업을 수행한다.
각 서버의 상태에 따라 부하를 분산시켜 요청을 전달할 수 있다.
- 동적 설정 변경
Nginx는 보통 프로세스를 CPU 코어 수만큼 생성하기 때문에, Nginx의 설정을 동적으로 변경할 수 있다.
설정 파일을 변경하면, master process는 새로운 worker process를 만든다.
이전에 사용하던 worker process는 더이상 새로운 커넥션을 생성하지 않고,
현재 처리 중인 요청을 모두 처리하면, 해당 worker process를 종료시킨다.
ex) 서버를 한 대 더 추가하는 상황
- 캐싱
리버스 프록시는 요청에 대한 응답을 캐싱하여,
이후 동일한 요청에 대해 요청을 서버로 전달하지 않고 캐싱된 응답을 반환한다.
- ssl 터미네이션
클라이너트와 서버가 데이터를 주고 받을 때, SSL 암호화를 하는데, 원래는 서버에서 복호화를 진행한다.
리버스 프록시를 사용하면 리버스 프록시 서버가 SSL 복호화를 하고 웹 서버에는 복호화된 데이터를 전달한다.
이렇게 되면 웹 서버는 SSL 복호화 과정을 감당하지 않아도 되므로 웹 서버의 부하를 줄일 수 있다.
👀 Nginx의 장단점
- 장점
- 높은 동시성 처리 능력
비동기 이벤트 기반 구조를 사용하여 동시 연결을 효율적으로 처리 - 낮은 메모리 사용량
- 다양한 기능 제공
리버스 프록시, 로드 밸런서, 캐싱 등 여러 기능을 하나의 소프트웨어에서 제공
- 단점
- 동적 컨텐츠 처리 한계
Nginx는 정적 파일에 최적화 되어 있기 때문이다.
백엔드 어플리케이션 서버나 API 요청을 전달해 동적 컨첸츠를 처리하여 해결할 수 있다. - 연결 기반 처리 방식
개별 요청의 처리 속도가 느려질 수 있다.
로드 밸런싱이나 keep-alive 연결 시간에 제한을 줘서 해결할 수 있다. - 구성 파일 복잡도
Nginx의 구성 파일은 초보자에게 복잡하고 어렵게 느껴질 수 있다. - 모듈 설치
필요한 모듈을 컴파일 시점에 추가해야하므로 모듈 추가가 번거롭다.
👀 Apache, Nginx 정리
- Apache
- 요청 당 스레드 또는 프로세스가 처리하는 구조
- CPU/메모리 자원 낭비 심함
- Nginx보다 모듈이 다양함
- 안정성, 호환성, 확장성 우세
- 동적 컨텐츠 단독 처리 가능
- Nginx
- 비동기 이벤트 기반으로 요청
- CPU/메모리 자원 사용률 낮음
- Apache에 비해 다양한 모듈이 없음
- 성능 우세
- 동적 컨텐츠 단독 처리 불가능
성능과 가벼움이 중요한 서비스에는 Nginx를
다양하고 검증된 기능들을 필요로 하는 곳에는 오랜 기간 사용되며 안정성을 갖춰온 아파치를 사용하면 된다.
👀 Nginx의 config
Nginx의 기본 설정 파일은 nginx.conf이고, 파일 구성은 directive들로 이루어진다.
directive에는 두 가지 종류가 있다.
- simple directive - 이름과 인자가 공백으로 구분되고 ;으로 끝나는 단일 줄
- block directive - 구조는 같지만 { }로 둘러 쌓인 것
- simple directive
# nginx 서버 사용자 지정
user nginx;
# worker processes 개수
worker_processes auto;
# error log를 기록할 파일 위치
error_log /var/log/nginx/error.log warn;
# nginx 프로세스의 pid(process id)가 있는 파일의 경로
pid /run/nginx.pid;
- block directive
events {
worker_connections 1024; # 최대 연결 수
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# 로그 설정
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# Gzip 압축 설정
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
# SSL 기본 설정
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
# HTTP에서 HTTPS로 리디렉션
server {
listen 80;
server_name your_domain.com;
# 리디렉션 설정
return 301 https://$host$request_uri;
}
# HTTPS 설정
server {
listen 443 ssl;
server_name your_domain.com;
# SSL 인증서 경로
ssl_certificate /etc/nginx/ssl/certificate.crt;
ssl_certificate_key /etc/nginx/ssl/private.key;
# SSL 설정
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# 정적 파일 제공
location /static/ {
root /var/www/your_domain;
try_files $uri $uri/ =404;
}
# 리버스 프록시 설정
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 로드 밸런싱 설정
upstream backend_servers {
server backend1.your_domain.com;
server backend2.your_domain.com;
server backend3.your_domain.com;
}
location /api/ {
proxy_pass http://backend_servers;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}
👀 https 설정 방법
Nginx 프록시 서버에 SSL 인증을 적용하는 방법을 알아보자.
1. 도메인과 SSL 인증서 준비
무료로 SSL 인증서를 받을 수 있는 사이트이다.
2. nginx.conf 수정
nignx 설정 파일에 SSL 관련 설정을 추가해준다.
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name yourdomain.com www.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
root /var/www/yourdomain;
index index.html;
}
}
- 첫 번째 server 블록
return 301 https://$host$request_uri
기존에 80 포트(http)로 접근되는 요청을 443 포트(https)로 연결 시켜준다.
- 두 번째 server 블록
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
ssl certificate에 준비해둔 도메인과 인증서의 chain key, private key를
nginx가 설치된 서버의 특정 폴더에 저장하고 키가 위치한 경로를 작성해주면된다.
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
보안 프로토콜과 암호화를 지정해준다.
정리하자면 443 포트로 들어오는 요청을 SSL 인증서로 암호화하겠다는 의미이다.
위의 과정은 certbot을 사용하면 자동으로 설정된다.
'WEB' 카테고리의 다른 글
Virtual Dom (1) | 2024.09.04 |
---|---|
Polling/Long-Polling/SSE/Web Socket (0) | 2024.08.27 |
SEO 최적화 (0) | 2024.08.20 |
webpack vs vite (0) | 2024.08.14 |
Debounce와 Throttle (0) | 2024.08.02 |