Nginx 소개
동기와 비동기:
동기와 비동기의 차이점은 메시지 알림 방식에 있습니다. 즉, 호출 결과 알림 방식이 다릅니다.
동기: 동기 호출이 발생하면 호출자는 호출 결과 알림을 받을 때까지 후속 작업을 수행할 수 없습니다.
비동기: 비동기 호출이 발생하면 호출자는 호출 결과가 반환될 때까지 계속 기다릴 필요가 없습니다. 비동기 호출로 결과를 얻는 방법은 일반적으로 두 가지가 있습니다:
- 비동기 호출 결과를 주기적으로 확인하는 폴링(polling)
- 호출된 측이 콜백(callback)을 통해 호출된 측에 결과를 알리는 방식
예시 설명:
동기 방식으로 택배 받기: 소식이 왔을 때 계속 기다리다가 택배가 도착하면 받습니다.
비동기 방식으로 택배 받기: 택배가 도착하면 나중에 내려가 받습니다.
비동기 방식으로 택배 받기: 택배가 도착했음을 알아내는 두 가지 방법:
- 계속해서 전화하여 택배 도착 여부를 확인 (폴링)
- 택배가 도착하면 전화로 알려주고 그때 내려가 받음 (콜백)
블로킹과 논블로킹:
블로킹과 논블로킹의 차이점은 프로세스/스레드가 메시지를 기다리는 동안의 행동에 있습니다. 즉, 메시지를 기다리는 동안 현재 프로세스/스레드가 중단(suspend) 상태인지 아닌지에 있습니다.
블로킹: 호출이 발생한 후 메시지가 반환되기 전까지 현재 프로세스/스레드는 중단되며, 메시지가 반환되어야 활성화됩니다.
논블로킹: 호출이 발생한 후 현재 프로세스/스레드를 차단하지 않고 즉시 반환됩니다.
예시 설명:
블로킹 방식으로 택배 받기: 택배가 도착할 것이라는 소식을 듣고 다른 일을 하지 않고 오직 택배만 기다립니다.
논블로킹 방식으로 택배 받기: 택배가 도착할 것이라는 소식을 듣고 기다리는 동안 코딩과 카톡을 병행합니다.
동기와 비동기는 메시지 알림 방식에 초점을 맞추고, 블로킹과 논블로킹은 메시지를 기다리는 동안의 행동에 초점을 맞춥니다.
이에 따라 다음과 같은 4가지 조합 방식이 있습니다:
동기 블로킹: 소식을 듣고 아무것도 하지 않고 택배만 기다림 동기 논블로킹: 소식을 듣고 트위터를 보면서 택배를 기다림 비동기 블로킹: 소식을 듣고 아무것도 하지 않고 택배 배달원의 연락만 기다림 비동기 논블로킹: 소식을 듣고 트위터를 보면서 택배 배달원의 연락을 기다림
대부분 프로그램의 I/O 모델은 동기 블로킹 방식이며, 단일 프로세스는 파일 디스크립터당 하나의 I/O 작업만 수행합니다. 각 I/O 시스템 호출은 데이터 전송이 완료될 때까지 블로킹됩니다. 전통적인 서버는 동기 블로킹 다중 프로세스 모델을 사용합니다. 서버는 각 요청(request)에 하나의 프로세스를 할당하며 세션이 끝날 때까지 해당 프로세스가 요청을 처리합니다. 프로세스 수가 곧 동시 처리 수가 되며, 운영체제가 지원하는 프로세스 수는 제한적이며 프로세스가 많을수록 스케줄링 오버헤드가 커져서 고부하(high concurrency) 상황을 대처하기 어렵습니다.
Nginx는 비동기 논블로킹 방식으로 작동합니다. I/O 다중화(multiplexing) 중 epoll 모델에 대해 알아보겠습니다.
epoll 모델:
I/O 이벤트가 발생하면 epoll은 어떤 연결에 I/O 이벤트가 발생했는지 프로세스에 알려주고, 프로세스는 해당 이벤트를 처리합니다.
예시: 집 아래에 우체국이 있고, 택배가 오면 관리인이 먼저 받아 표시를 합니다. 그런 다음 주인에게 택배가 도착했음을 알립니다.
왜 Nginx는 다른 웹 서버보다 동시 처리량이 높은가 (Nginx 작동 원리)
Nginx가 epoll을 설정하여 비동기 논블킹 방식으로 작동하면 백만 단위의 동시 연결을 쉽게 처리할 수 있습니다.
처리 과정: 새로운 요청이 들어올 때마다 워커 프로세스가 요청을 처리합니다. 하지만 전체 과정을 처리하는 것이 아니라, 차단(block)이 발생할 수 있는 부분까지 처리합니다. 예를 들어 백엔드 서버로 요청을 전달하고 응답을 기다리는 경우, 처리 중인 워커 프로세스는 계속 기다리지 않습니다. 요청을 보낸 후 "백엔드 서버가 응답하면 알려주고 그때 다음 작업을 계속하자"는 이벤트를 등록하고 휴식을 취합니다. 이때 새로운 요청이 들어오면 이 방식으로 빠르게 처리할 수 있습니다. 그러나 백엔드 서버가 응답하면 이 이벤트가 트리거되고 워커가 다시 작업을 이어받아 요청이 계속 처리됩니다. 이러한 빠른 처리와 요청 해제 방식을 통해 동일한 구성으로 더 많은 동시 요청을 처리할 수 있습니다.
Nginx 상세 정보
1. 개요
Nginx(엔진엑스)는 고성능 HTTP 및 리버스 프록시 웹 서버이며, IMAP/POP3/SMTP 서비스도 제공합니다. Nginx는 러시아의 두 번째로 많은 트래픽을 가진 Rambler.ru 사이트를 위해 이고르 세소예프(Igor Sysoev)가 개발했으며, 첫 번째 공개 버전 0.1.0은 2004년 10월 4일에 출시되었습니다.
Nginx는 경량 웹 서버/리버스 프록시 서버 및 이메일(IMAP/POP3) 프록시 서버이며, BSD 계열 프로토콜로 실행됩니다. 메모리 사용량이 적고 동시 처리 능력이 뛰어나다는 특징이 있습니다.
2. 작동 모드
Nginx는 마스터-워커(master-worker) 모드와 단일 프로세스 모드 두 가지 작동 모드를 가지고 있습니다. 마스터-워커 모드에서는 마스터 프로세스와 최소 하나 이상의 워커 프로세스가 있으며, 단일 프로세스 모드는 이름 그대로 하나의 프로세스만 가집니다. 두 모드는 각각의 특징과 사용 시나리오를 가집니다.
마스터-워커:
이 모드에서 Nginx가 성공적으로 시작되면 마스터 프로세스와 최소 하나 이상의 워커 프로세스가 생성됩니다. 마스터 프로세스는 시스템 신호 처리, 구성 파일 로드, 워커 프로세스 관리(시작, 종료, 모니터링, 정보/신호 전송 등)를 담당합니다. 워커 프로세스는 실제 비즈니스 로직을 처리하며, 외부에 서비스를 제공하는 것은 워커 프로세스입니다. 프로덕션 환경에서 일반적으로 이 모드를 사용하며, 다음과 같은 장점이 있습니다:
a. 안정성이 높으며, 워커 프로세스가 하나라도 살아있으면 서비스를 제공할 수 있습니다. 또한 하나의 워커 프로세스가 중단되면 마스터 프로세스가 즉시 새로운 워커 프로세스를 시작하여 워커 프로세스 수를 유지하고 서비스 중단 가능성을 줄입니다.
b. 리눅스 CPU 친화성 설정과 함께 사용하면 다중 코어 CPU의 장점을 최대한 활용하여 성능을 향상시킬 수 있습니다.
c. 신호 처리/구성 재로드/업그레이드 시 서비스 중단을 최소화하거나 없앨 수 있습니다(핫 리스타트)
단일 프로세스 모드:
단일 프로세스 모드에서 Nginx가 시작되면 하나의 프로세스만 있으며, Nginx의 모든 작업은 이 프로세스가 담당합니다. 하나의 프로세스만 있기 때문에 gdb와 같은 도구를 사용하여 디버깅하기 쉽습니다. 이 모드는 Nginx의 부드러운 업그레이드 기능을 지원하지 않으며, 어떤 신호 처리라도 서비스 중단을 유발할 수 있습니다. 또한 단일 프로세스이므로 프로세스가 중단된 후 외부 모니터링이 없으면 서비스를 재시작할 수 없습니다. 따라서 이 모드는 일반적으로 개발 및 디버깅 단계에서만 사용하며, 프로덕션 환경에서는 사용하지 않습니다.
3. 구성 파일 구조
user webuser webgroup;
# 프로그램 실행 사용자와 그룹
worker_processes auto;
# 시작할 프로세스 수, CPU 코어 수와 일치시키는 것이 좋음
error_log /var/log/nginx/error.log crit;
# 전역 오류 로그
pid /var/run/nginx.pid;
# 마스터 프로세스 PID 저장 파일
worker_rlimit_nofile 51200;
# 파일 디스크립터 수
events {
use epoll;
# epoll 모델 사용, 2.6 이상 커널에서는 성능 향상을 위해 epoll 모델 권장
worker_connections 51200;
# 워커 프로세스의 최대 연결 수
}
http {
# 웹사이트 최적화 매개변수
server { # 특정 웹사이트의 구성 정보
listen 80; # 리스닝 포트
root /var/www/html; # 웹 루트 디렉토리
server_name www.example.com; # 서버 도메인
index index.html; # 기본 로드 페이지
access_log /var/log/nginx/access.log; # 접속 로그 저장 위치
......;
location (.*)\.php$ {
정규식으로 특정 접속 객체 매칭;
}
location {
리디렉션 등 규칙;
}
}
server {
가상 호스트;
}
}
4. Nginx 실습
주의:
- 구성 파일 끝에는 세미콜론(;)이 종료 기호로 사용됩니다.
- 구성 파일을 수정한 후에는 Nginx를 재시작해야 변경 사항이 적용됩니다.
4.1 Nginx 상태 통계
a. Nginx 설치 시 --with-http_stub_status_module 모듈을 활성화합니다
b. Nginx 구성 파일 수정 (접속할 server 블록에 추가)
location /server_status {
stub_status on;
access_log off;
}
c. 클라이언트에서 URL 접속: http://서버_IP/server_status
"Active connections"는 현재 활성 연결 수를 나타냅니다; "server accepts handled requests"는 처리된 연결 정보를 나타내며, 세 숫자는 순서대로 처리된 연결 수, 성공한 TCP 핸드셰이크 횟수, 처리된 요청 수를 의미합니다
4.2 디렉토리 보호
a. 원리는 Apache의 디렉토리 보호 원리와 같음 (이전 실습 이어서 진행)
b. 상태 통계 location에 추가:
auth_basic "Nginx 상태 페이지 접근 인증 필요!";
auth_basic_user_file /etc/nginx/htpasswd;
c. htpasswd 명령으로 사용자 비밀번호 파일 생성 (지정된 위치에 생성)
htpasswd -c /etc/nginx/htpasswd adminuser
d. Nginx 서비스 재시작 후 다시 통계 페이지 접속
4.3 IP 기반 인증 (접근 제어)
a. 이전 실습 이어서 진행
b. 상태 통계 location에 추가:
allow 192.168.1.100;
deny 192.168.1.0/24;
192.168.1.100 IP만 서버 접속 허용
4.4 Nginx 가상 호스트 (도메인 기반)
a. 두 개의 웹사이트 도메인을 미리 준비하고 각 웹사이트 페이지 저장 디렉토리 계획
b. Nginx 메인 구성 파일에 두 개의 server 블록을 나란히 작성하고 각각의 정보 기입
server {
listen 80;
server_name blog.example.com;
index index.html;
root /var/www/blog;
access_log /var/log/nginx/blog-access.log main;
}
server {
listen 80;
server_name forum.example.com;
index index.html;
root /var/www/forum;
access_log /var/log/nginx/forum-access.log main;
}
c. 각각 다른 도메인으로 접속하여 결과 확인 (hosts 파일 수정 필요)
4.5 Nginx 리버스 프록시
프록시와 리버스 프록시
프록시: 클라이언트 대신 클라이언트가 할 수 없는 일을 대신 처리 (대리 구매), 프록시의 대상은 클라이언트입니다. 리버스 프록시: 제조업체의 제품을 대신 파는 사람 (담배/주류 대리점), 프록시의 대상은 서버입니다.
a. 다른 머신에 Apache 설치하고 시작 후 테스트 페이지 작성
b. Nginx 서버 구성 파일에 추가 (특정 웹사이트의 server 블록 내부)
server {
listen 80;
server_name blog.example.com;
index index.html;
root /var/www/blog;
access_log /var/log/nginx/blog-access.log main;
location / {
proxy_pass http://192.168.1.101:80;
}
}
c. Nginx 재시작 후 클라이언트로 접속 테스트
4.6 부하 분산
부하 분산(Load Balance)은 작업을 여러 작업 단위에 분산시켜 실행하는 것을 의미합니다. 예를 들어 웹 서버, FTP 서버, 기업 관련 애플리케이션 서버 및 기타 중요 작업 서버 등에서 공동으로 작업을 완성하는 것입니다.
a. 기본 rr(라운드 로빈) 알고리즘 사용, Nginx 구성 파일 수정
upstream blog_servers { # 이 블록은 server 블록 전에 추가
server 192.168.1.102:80;
server 192.168.1.101:80;
}
server {
listen 80;
server_name blog.example.com;
index index.html;
root /var/www/blog;
access_log /var/log/nginx/blog-access.log main;
# 기존 location / 블록 수정, 기존 내용 삭제 후 다음 두 항목 추가
location / {
proxy_pass http://blog_servers; # 리버스 프록시 추가, 주소는 upstream에서 선언한 이름
proxy_set_header Host $host; # 요청 헤더 재작성, 웹사이트 모든 페이지 접속 가능하게 보장
}
}
b. 192.168.1.101 & 192.168.1.102 두 호스트 실행 및 설정 Apache 설치하고 각기 다른 index.html 페이지 내용 설정
c. Nginx 재시작 후 클라이언트로 접속 테스트
확장: rr 알고리즘을 사용한 가중치 라운드 로빈 구현
upstream blog_servers {
server 192.168.1.102:80 weight=1;
server 192.168.1.101:80 weight=2;
}
4.7 Nginx로 HTTPS 구현 {인증서 + 리라이트}
a. Nginx 설치 시 --with_http_ssl_module 모듈을 활성화해야 함
b. 암호화할 server 블록에 다음 내용 추가하여 SSL 활성화
server {
listen 443 ssl;
server_name secure.example.com;
index index.html;
root /var/www/secure;
access_log /var/log/nginx/secure-access.log main;
#ssl on;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES256-GCM-SHA384";
}
c. 인증서와 비밀키 파일 생성
주의: 실험 환경에서는 명령으로 생성한 테스트 인증서를 사용할 수 있지만, 실제 운영 환경에서는 반드시 HTTPS 인증서 업체에서 등록해야 함
[root@webserver /etc/nginx/ssl]# openssl genrsa -out server.key 2048
[root@webserver /etc/nginx/ssl]# openssl req -new -key server.key -out server.csr
[root@webserver /etc/nginx/ssl]# openssl x509 -req -days 365 -sha256 -in server.csr -signkey server.key -out server.crt
d. 다음 server 블록 추가 (가상 호스트 + 리라이트 기능 활용)
server {
listen 80;
server_name secure.example.com;
rewrite ^(.*)$ https://secure.example.com permanent;
root /var/www/secure;
index index.html index.htm;
}
e. Nginx 재시작 후 테스트