1. Apache
아파치 서버는 요청이 들어오면 커넥션을 형성하기 위해 프로세스를 생성합니다.
그래서 새로운 클라이언트의 요청 들어올 때마다 새로운 프로세스를 만드는데, 이는 UNIX 계열 OS가 네트워크 커넥션을 형성하는 모델을 그대로 적용한 것이라고 합니다.
그런데 프로세스를 만드는 것이 시간이 걸리는 작업이다 보니 요청이 들어오기 전에 프로세스를 미리 만들어 놓는
prefork 방식을 사용합니다.
그래서 새로운 클라이언트로부터 요청이 들어오면 미리 만들어 놓은 프로세스를 가져다 사용합니다.
만약 만들어 놓은 프로세스가 모두 할당되었다면 추가로 프로세스를 만드는 작업을 실행합니다.
그러나 시간이 지남에 따라 인터넷 트래픽이 계속해서 증가하게 되었고 이전에는 서버가 처리해야 할 요청 양이 그 당시 기술로 감당할 수 있는 정도였는데, 점점 컴퓨터가 더 많이 보급되고 요청이 많아지면서 서버에 동시에 연결된 커넥션이 많아졌을 때 더 이상 커넥션을 형성하지 못하는 문제가 생기기도 하였습니다.
이를 C10K 문제라고 하는데, Connection 10,000개의 문제라는 이름을 줄인 표현입니다.
2. NGINX
그렇게 시간이 흐르고 2004년에 새로운 구조를 채택하면서 아파치 서버를 보완하기 위한 소프트웨어가 나오는데 그것이 NGINX 입니다.
초창기 Nginx는 아파치 서버와 함께 사용하기 위해 만들어진 것입니다.
웹 서버이긴 하지만, 아파치 서버를 완전히 대체할 목적은 아니었으며 , 아파치 서버가 지닌 구조적 한계를 Nginx를 사용함으로써 극복하려고 한 것입니다.
사용된 방식을 보면 아파치 서버 앞단에 Nginx를 두는데 이렇게 하면 기존에 아파치 서버가 감당해야 했던 수많은 동시 커넥션을 Nginx가 대신해서 유지할 수 있는 것입니다.
구조적으로 동시 커넥션을 많이 유지 못 하는 아파치 서버의 부하를 Nginx를 이용해 크게 줄일 수가 있습니다.
그렇다면 Nginx는 어떤 구조로 되어있길래 그 많은 동시 커넥션을 유지할 수 있을까?
비결은 만들어지는 프로세스의 수에 있습니다.
Nginx의 구조를 한 번 살펴보면 가장 먼저 마스터 프로세스가 있으며, 설정 파일을 읽고, 설정에 맞게 워커프로세스를 생성합니다. 이 워커 프로세스가 실제로 일을 하는 녀석인데, 워커 프로세스가 만들어질 때 각자 지정된 listen 소켓을 배정받고 그 소켓에 새로운 클라이언트로부터 요청이 들어오면 커넥션을 형성하고 그 요청을 처리합니다.
그러고 나면 그 커넥션은 정해진 keep alive 시간만큼 유지되겠죠.
그런데 커넥션이 형성되었다고 해서 워커 프로세스가 해당 커넥션 하나만 한정적으로 담당하진 않으며, 형성된 커넥션에서 아무런 요청이 없으면 새로운 커넥션을 형성하거나 이미 만들어진 다른 커넥션으로부터 들어온 요청을 처리합니다.
Nginx에서는 이런 커넥션 형성, 커넥션 제거 그리고 새로운 요청을 처리하는 것을 이벤트라고 합니다.
그리고 그 이벤트들은 OS 커널이 큐 형식으로 워커 프로세스에게 전달해줍니다.
이 이벤트는 큐에 담긴 상태에서 워커 프로세스가 처리할 때까지 비동기 방식으로 대기합니다.
그리고 워커 프로세스는 하나의 스레드로 이벤트를 꺼내서 처리해 나갑니다.
저 네모 칸들이 큐입니다.
안에 보면 커넥션을 생성하거나 요청을 처리하는 이벤트로 채워져 있는 것을 볼 수 있습니다.
이러면 워커 프로세스가 쉬지 않고 계속해서 일을 한다는 장점이 있습니다.
아파치 서버의 구조와 비교했을 때, 요청이 없다면 방치되던 프로세스보다 서버 자원을 훨씬 효율적으로 쓰는 셈입니다.
만약 이 요청 중 하나가 시간이 오래 걸리는 작업이면 어떻게 할까요?
예를 들어 Disk에 읽고 써야 하는 작업을 해야 한다면, 그 뒤에 있는 이벤트는 요청을 처리하는 긴 시간 동안 블로킹 되는 문제가 발생합니다.
Nginx는 이런 상황을 방지하기 위해 그렇게 시간이 오래 걸리는 작업을 따로 수행하는 스레드 풀을 만들어 놓습니다.
그리고 워커 프로세스는 지금 처리할 요청이 시간이 오래 걸릴 것 같다 싶으면 해당 스레드 풀에 그 이벤트를 위임하고,
큐 안에 있는 다른 이벤트를 처리하러 갑니다.
이런 워커 프로세스는 보통 CPU의 코어 수 만큼 생성합니다.
이러면 코어가 담당하는 프로세스를 바꾸는 횟수를 대폭 줄일 수 있습니다.
CPU가 굳이 그런 부가적인 일을 하지 않아도 됩니다.
다시 말해 CPU의 컨텍스트 스위칭 사용을 줄입니다.
이게 바로 Nginx가 채택한 event-driven model, 즉 이벤트 기반 구조이고, 아파치 서버와 가장 큰 차이점입니다.
3. Nginx 설정 (nginx.conf)
nginx를 설치하면 초기에 설정된 nginx.conf와 default.conf 파일을 확인할 수 있습니다.
nginx의 기본 설정 파일 ( /etc/nginx/nginx.conf ) 옵션을 확인해보겠습니다.
user nginx; ## NGINX 프로세스가 실행되는 권한, root 권한은 보안상 위험함
worker_processes auto; ## CPU 코어 하나에 최소한 한 개의 프로세스가 배정되도록 변경 권장
# 로그레벨 [ debug | info | notice | warn | error | crit ]
error_log /var/log/nginx/error.log notice; ## 로그레벨을 notice -> warn로 변경함
pid /var/run/nginx.pid;
events {
# 작업자 프로세스에서 열 수 있는 최대 동시 연결 수를 설정, cpu 코어수 * 1024 권장,
# 하나의 cpu코어가 처리할 수 있는 양을 고려하여 적절한 숫자를 입력하면 됨
worker_connections 2048; ## Default: 1024 -> 2048로 변경, comms-search는 10240으로 되어있음
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
# 클라이언트에서 해당 서버로 keepalive 커넥션을 유지할 수 있는 시간을 설정합니다. 너무 길면 요청이 없는 클라이언트와의 커넥션을 연결하고 있기 때문에 자원의 낭비가 발생한다.
keepalive_timeout 65; ## 접속 시 커넥션 유지 시간, comms-search는 10초로 되어있음
open_file_cache max=200000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
# 하드디스크io처리와 socket-io처리의 밸런스를 얻기위해 on
sendfile on;
# 하나의 데이터 패키지에 모든 헤더 정보가 포함
tcp_nopush on;
# 데이터를 캐쉬하지 않고 계속 송신시킨다.
tcp_nodelay on;
gzip on; ## 전송 내용을 gzip으로 압축, 텍스트 컨텐츠는 gzip 압축으로 전송해야 성능이 좋다
# 압축을 적용할 컨텐츠의 최소 사이즈를 지정하며 이보다 작은 파일은 압축하지 않습니다.
gzip_min_length 10240;
# 1~9까지 설정할 수 있으며, 숫자가 클 수록 압축율은 올라가지만 압축 속도는 느려진다.
gzip_comp_level 1;
# gzip_vary 옵션을 키면 response headers에 Vary: Accept-Encoding 이 추가적으로 나온다.
gzip_vary on;
# gzip_disable 에는 압축을 적용하지 않을 브라우저를 지정하며 예전 IE 는 압축을 지원하지 않으므로 msie6 는 예외 항목으로 설정합니다.
gzip_disable msie6;
gzip_proxied expired no-cache no-store private auth;
gzip_types
# text/html is always compressed by HttpGzipModule
text/css
text/javascript
text/xml
text/plain
text/x-component
application/javascript
application/x-javascript
application/json
application/xml
application/rss+xml
application/atom+xml
font/truetype
font/opentype
application/vnd.ms-fontobject
image/svg+xml;
# request에 대한 버퍼크기를 지정
# 디폴트 값은 1KB이다.
# 이 버퍼의 사이즈를 초과하는 요청이 들어왔을 경우에는 아래에서 다룰, 조금 더 큰 버퍼로 처리하게 된다.
client_header_buffer_size 128k;
include /etc/nginx/conf.d/*.conf;
}
4. Nginx 설정 (default.conf)
default.conf는 nginx.conf를 통해 include된 서버 설정관련 파일이다. default2.conf, default3.conf 등 여러 개의 파일을 추가하여 서버관련 설정을 추가로 만들어 포함시킬 수 있습니다.
upstream search {
server 127.0.0.1:8080 fail_timeout=20s;
}
server {
listen 80;
server_name dev-search.coolmessenger.com;
access_log /var/log/nginx/search.coolmessenger.com.log main;
error_log /var/log/nginx/search.coolmessenger.com.log debug;
location / {
proxy_pass http://search;
# X-Real-IP 요청한 클라이언트의 실제 IP
# $remote_addr: 요청한 클라이언트 주소
proxy_set_header X-Real-IP $remote_addr;
# X-Forwarded-For 클라이언트의 IP 주소, 이전에 프록시 서버가 또 있었다면 그 IP 를 의미한다는 것이 Real-IP 와의 차이
# $proxy_add_x_forwarded_for: 요청 헤더와 그 뒤에 따라오는 클라이언트의 원격 주소를 포함
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# $http_host: http 요청이 들어 왔을 시 호스트 명
proxy_set_header Host $http_host;
# nginx proxy 를 썼다는 뜻. 안써도 무방. 표준 X
proxy_set_header X-NginX-Proxy true;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
proxy_read_timeout 300;
proxy_connect_timeout 300;
proxy_redirect off;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
upstream search {
server 127.0.0.1:8080 fail_timeout=20s;
}
server {
listen 443 ssl;
server_name search.coolmessenger.com;
access_log /var/log/nginx/search.coolmessenger.com.ssl.log main;
error_log /var/log/nginx/search.coolmessenger.com.ssl.log debug;
# t서버인증서+체인+루트 통합 unified 파일 지정
ssl_certificate /etc/nginx/conf.d/ssl/2022/Wildcard.coolmessenger.com_pem.pem;
# 개인키 파일 지정
ssl_certificate_key /etc/nginx/conf.d/ssl/2022/KeyFile_Wildcard.coolmessenger.com_pem.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
# 보안 통신 과정에서 사용할 암호화 알고리즘을 지정
ssl_ciphers EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA256:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EDH+aRSA+AESGCM:EDH+aRSA+SHA256:EDH+aRSA:EECDH:!aNULL:!eNULL:!MEDIUM:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS:!RC4:!SEED;
location / {
proxy_pass http://search;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
# 백엔드 서버로부터 데이터를 읽을 때의 제한시간으로,
# 이 제한시간은 전체 응답 지연 시간에 적용되는 것이 아니라 두 개의 읽기 작업 사이에 적용됩니다.
proxy_read_timeout 300;
# 백엔드 서버 접속 제한시간을 정의합니다.
# 이 값은 읽기/쓰기 시간 초과와는 다릅니다.
# 즉, 엔진엑스가 이미 백엔드 서버에 접속된 상태라면 proxy_connect_timeout은 적용될 수 없습니다.
proxy_connect_timeout 300;
# proxy_redirect : 백엔드 서버에 의해 촉발된 리다이렉션에 대해 로케이션 HTTP 헤더에 나타나는 URL을 재작성합니다.
# off: 리다이렉션은 설정된 그대로 전달된다.
proxy_redirect off;
}
# redirect server error pages to the static page /50x.html
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
'공부방 > Nginx' 카테고리의 다른 글
Nginx vs apache (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 |