과거의 내가 미래의 나에게
웹 서버 학습 (4) - Nginx.conf 파일 본문
오늘은 nginx의 기본 설정을 할 수 있는 nginx.conf 파일에 대해서 뜯어보자.
nginx.conf 란?
nginx.conf 파일은 nginx의 주요 설정 파일로 전반적인 동작 방식을 제어한다. nginx를 설치하고 아무것도 건들지 않는 것을 기반으로 하면 보통 nginx.conf 파일은 "/etc/nginx/nginx.conf nginx" 이 경로에 위치한다.
구성은 블록 지시문과 단순 지시문으로 이루어져있는데, 단순 지시문은 공백으로 구분된 이름과 매개변수로 구성되어있고 세미콜론으로 끝난다. 블록 지시문은 세미콜론 대신 중관로로 둘러쌓인 추가 지시문 모듬으로 끝난다. 이렇게 중괄호 안에 다른 지시문들을 가질 수 있는 블록 지시문을 context라고도 한다. context에 둘러쌓이지 않은 지시문들은 main context에 있는 것으로 간주된다.
즉, 아래 nginx 기본 설정파일을 기반으로 다시 말하면 main context 안에 user, worker_processes, events, http... 등의 지시문들이 있는것이고, http context 안에 sendfile, tcp_nopush...와 같이 구성 되어 있다는 것이다.
nginx.conf 기본 설정 파일
user www-data;
worker_processes auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 768;
}
http {
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
##
# Logging Settings
##
access_log /var/log/nginx/access.log;
##
# Gzip Settings
##
gzip on;
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
이제부터는 각 context에 대해 설명할 것인데, main context 안에 event, http 같은 것이 포함되어있지만 context로 이루어진 지시어는 제외하고 별도로 적어두는 방식으로 하겠다.
main context
user www-data
프로그램이 실행될 때는 특정 권한을 빌려서 실행한다. 특정 프로그램이 모든 권한에 다 접근 가능하면 운영체제에 위협이 될 수 있기 때문에 그렇다 한다.
따라서 nginx도 프로그램의 일종이기 때문에 우분투 운영체제에서 특정 사용자 계정을 빌려서 하는데, 그 설정을 바로 위의 user에서 하는 것이다. www-data는 우분투의 사용자 계정 중 하나로, 웹 파일의 읽기/쓰기 권한, 로그 작성 권한, 운영체제의 중요한 파일에 접근 불가 같은 웹서버를 위해 만들어진 계정이라고 한다.
만약 nginx가 모든 파일을 다 접근하길 원한다면 user root 이런식으로 변경하면 되겠다.
worker_processes auto
worker는 말 그대로 일하는 사람이다.이 일꾼은 nginx에 들어오는 요청을 처리하는 사람이라고 보면 된다. nginx는 많은 사용자가 동시에 요청을 보내오는 경우가 다수 있어서 여러 worker를 활용하여 병렬로 해당 요청들을 처리하여 효율을 높일 수 있을 것이다.
일꾼이 많으면 빠르게 처리할 수 있지만 서로 일하는 공간이 비좁아져서 비효율적이 될 수도 있고 일꾼이 적으면 처리하는 것은 느리지만 공간이 널찍해서 효율적으로 운영할 수도 있으므로 상황에 따라 유연하게 설정해야한다.
보통 일반적으로 적어놓는 auto는 서버의 cpu 코어 수를 자동으로 감지해서 최적의 worker 수를 설정해준다.
< 참조 >
보통 worker는 CPU의 코어 수만큼 설정하는 것을 권장하는 데, 우분투에서 CPU 코어 수를 파악하려면 `nproc` 명령어로 알 수 있다.auto 외에도 만약 서버가 요청이 적고 리소스를 최대한 아끼고 싶다면 1로 설정해서 worker 수를 임의로 줄여도 될 것이다.
error_log /var/log/nginx/error.log;
에러 로그는 흔히 알다시피 에러나 경고 메시지를 기록하는 곳이다. 해당 줄은 에러 로그를 어디다가 저장할 것인지에 대한 주소를 지정해놓는 것이다. 로그 주소 뒤에는 에러의 심각도 수준을 레벨로 적어 어느 수준부터 기록할 지 결정할 수 있다.
수준은 debug(모든 걸 기록), info(시스템 상태 가볍게 확인하고 싶을 때), notice(오류는 아니지만 참고할 만한 것), warn(경고 수준), error(요청 자체를 처리할 수 없는 문제), crit(시스템이 제대로 동작 안하는 수준), alert(즉시 대응이 필요한 문제), emerg(서버가 멈춘 수준의 심각한 상황)이 있다.
에러로그는 main context에 적어 모든 요청에 대해 에러를 기록할 수도 있고 server context 안에 적어서 특정 서버의 에러만 기록할 수도 있고 또 location에 적어 특정 URL 경로의 에러를 기록하게 할 수도 있다.
로그 파일을 확인하려면 `sudo tail -f /var/log/nginx/error.log`로 하여 실시간으로 로그를 확인하거나 `sudo grep "404" /var/log/nginx/error.log`처럼 "grep 특정단어"만을 찾아서 검색할 수도 있다.
< 로그 로테이션 >
에러 로그 파일은 결국 일기와도 같은데 이것이 서버에 계속 쌓인다면 서버 공간을 차지하게 될 것이다. 그래서 로그는 주기적으로 관리해주고 삭제해줘야하는데, 이를 도와주는 것이 로그 로테이션이다.우분투에서는 logrotate라는 것이 있어서 이를 기본적으로 해주는데, 위치는 "etc/logrotate.d/nginx"에 있다.
nginx가 로그 파일을 계속 써내려가면 기존 로그 파일을 새 파일로 옮기거나 오래된 로그를 삭제 또는 압축한다.
로그 설정에 대해 더 자세히 알아보고 싶으면 ningx logrotate라 쳐서 살펴보면 될 것 같다.
pid /run/nginx.pid;
pid는 process ID의 줄임말로 nginx라는 프로그램을 식별할 수 있는 고유 번호이다. 운영체제가 프로그램이 실행되면 자동으로 고유 번호를 할당해주는 것이다. 운영체제는 이를 통해 현재 실행 중인 nginx을 쉽게 확인하고 재시작이나 중지 등 제어할 수 있게 된다.
그런데! 이 파일을 직접 보려고 해당 경로로 찾아가보니 없다! 이 파일이 없어도 nginx는 잘 돌아가는 것인가 알아보니 pid 파일은 단지 사람이 보기 편하게 만든 nginx의 이름표같은 역할이었다. 이름표가 없어도 nginx나 운영체제는 이미 알고 있어서 운영하는데는 무리가 없지만 사람들은 이를 알기 까다로우니 적어놓은 느낌?
참고로 없는 이유는 내가 우분투를 wsl에서 돌렸기 때문인 듯 하였다. wsl은 리눅스에서 시스템 관리 도구가 완벽히 지원되지 않고 제한적이라 제대로 생성 안되었을 수도 있다고 하는데...일단 이정도만 알아둘까 싶다.
event context
worker_connections 768
이것은 각 worker들이 동시에 처리할 수 있는 클라이언트-nginx 서버 연결의 최대 수를 지정하는 것이다.
연결 수를 너무 낮게 설정하면 설정된 요청보다 더 많은 요청을 처리하지 못하고 연결을 끊어버릴 수도 있고, 높게 설정한다면 서버 자원이 초과될 우려가 있다.
최적화된 수는 서버의 자원과 요청 패턴에 따라 다르기때문에 아마 좀 더 전문적인 영역에서 다뤄야하지 않나...싶다.
multi_accept off
nginx가 한 번에 여러 개의 연결을 수락할 지 한 번에 하나만 수락할 지 결정하는 지시어다. 기본적으로 off로 되어있는데, 한 번에 많은 연결을 받으면 서버 자원이 갑자기 소모될 수도 있고 또 많은 처리을 한 번에 하려하기에 응답 속도가 저하 될 수 있다. 따라서 off는 안정적으로 하나씩 연결을 처리하도록 설계된 값이다.
이를 on으로 해야할 때는 서버가 고성능이거나 많은 클라이언트가 동시에 접속하는 상황일 시, 이벤트 기반 모델이 고효율 이벤트 모델을 사용할 때 같은 경우 쓰면 더 나은 성능으로 활용할 수 있을 것이다.
< 이벤트 기반 모델 >
nginx는 요청을 처리할 때 이벤트 기반 모델을 사용하여 처리하는데, 리눅스에서는 보통 epoll을 사용한다고 한다. 나도 에러 로그를 debug 수준으로 설정한 후 재시작하여 로그를 띄워보니 epoll을 사용하고 있다고 나오더라.이벤트 모델을 강제로 설정할 수도 있는데 use epoll; 이런식으로 처리하면 된다. 다만 운영체제에서 지원하지 않거나 하면 nginx가 실행되지 않으니 많은 것을 알아보고 사용해야 할 문제인 것 같다.
debug_connection 특정IP;
debug 수준으로 설정해놓으면 모든 요청에 대해 로그를 남긴다. 그러나 해당 지시어를 사용하면 특정 IP에 대해서만 디버그 정보를 출력하도록 제한 할 수 있게 된다.
다만 이를 사용하려면 main context에 있는 error_log의 경고 수준을 debug로 해둬야한다. 모든 요청에 대한 디버그 로그를 기록 가능하게 설정한 후에 debug_connetion을 통해서 특정 IP만 할 것인지 모든 요청에 대해 다 기록할 것인지 추가 설정 가능한 것이다. 만약 특정 IP만 한다고 설정하는 경우 다른 요청들은 기본 수준인 error 수준으로 기록하게 된다
참고로, debug 모드는 모든 요청을 다 기록하기때문에 성능 저하의 우려가 있다. 따라서 뭘 하든 확인을 하고 나서는 해당 모드는 꺼놓는 것이 좋겠다.
http context
sendfile on;
nginx에서 파일 전송을 최적화하기 위한 설정이다. 기본설정은 off이며 off로 설정해 사용하지 않으면 아래와 같은 과정을 겪게 된다.
1. 클라이언트로 보내고자 하는 파일을 운영체제의 파일 I/O 시스템을 통해 읽어서 서버의 메모리로 가져옴
2. 서버의 메모리에 있는 파일 데이터를 다시 네트워크로 전송해서 클라이언트에 최종적으로 보냄
반면 on으로 설정하면 서버의 메모리로 읽어올 필요 없이 직접 파일을 읽어 클라이언트에 보내게 된다. 이를 통해 전송 소곧가 향상되고 메모리 자원 사용량이 줄어들게 된다.
on으로 하는게 무조건 좋지 않나 싶은데, 서버의 파일들이 내부에 있는게 아니라 네트워크를 통해 연결된 외부 저장소에 있거나 디버깅을 할 때 파일 입출력 과정을 더 상세히 보고 싶거나 할 때 쓰는 경우가 있는가 싶다.
tcp_nopush on;
모든 TCP 패킷을 하나의 큰 패킷으로 묶어서 보내라고 지시하여 한 번에 많은 데이터를 보낼 수 있게 된다. HTTP 헤더와 파일 내용을 한꺼번에 보낼 수 있어 전송 속도가 훨씬 빨라지게 된다. 위의 sendfile과 함께 사용하여 정적 파일을 빠르고 효율적으로 전달할 수 있게 한다.
데이터를 최대한 묶어서 보내는 기능이므로 혹시 빠른 응답이 중요하다면 꺼놓는 것이 나을 수도 있겠다.
< 빠른 전송을 하려면 tcp_nodelay 사용 >
반대로 패킷들을 최대한 빠르게 전송하려고 하면 해당 지시문이 도움 될 수도 있다. 패킷을 모으지 않고 바로바로 전송할 수 있는 방식을 사용하는 것으로 작은 데이터 전송이 잦거나 실시간 응답이 중요할 때는 해당 기능이 유리하다. 다만 작은 패킷을 끊임없이 전송하면 네트워크 부하가 커질 우려가 있으므로 이를 주의해야겠다.tcp_nopush와 같이 사용할 수도 있긴 하지만 아무래도 둘이 근본적으로 다른 방향이기에 같이 사용했을 때 정말 잘 돌아가고 있는지를 면밀히 살펴보고 테스트를 많이 거쳐야 할 것 같다.
types_hash_max_size 1024
웹 서버에 다양한 파일들이 요청될 것이고, nginx는 이러한 파일들의 MIME 타입을 찾아서 클라이언트에 보내준다. 이 때 nginx는 해시테이블을 이용해 MIME 타입을 빠르게 찾기 위해 정리해두는데, 해당 지시문은 이러한 해시 테이블의 최대 크기를 설정하는 것이다. 많은 종류의 파일을 다루고 있다면 이 테이블의 크기가 커야 더 효율적으로 찾을 수 있을 것이다.
기본값은 1024이고 파일 형식이 많으면 좀 더 크게 설정해야할 것이다(256, 512, 1024, 2048...) 보통 기본값으로 충분할 것 같고, 만약 해시 테이블이 충분치 않으면 로그에 "hash table is full"과 같은 로그가 뜬다고 한다. 직접 본 것은 아니라서 잘은 모르겠다!
default_type text/plain
이는 nginx에서 콘텐츠의 기본 MIME 타입을 설정하는 것이다. 요청된 파일의 MIME 타입을 알 수 없을 때 해당 타입으로 보내게 된다. 기본값은 text/plain이고 모르는 타입이 왔을 때 nginx는 이것을 일반 텍스트 파일로 간주하여 브라우저에 해당 파일을 렌더링하게 한다.
내가 설치한 nginx 파일에서는 application/octet-stream로 되어있었다. 이는 파일 다운로드를 유도하는 것으로, 브라우저가 파일을 직접 열지 않고 다운로드 창을 띄워서 사용자가 판단하게끔 하므로 브라우저가 알 수 없는 형식의 파일을 처리할 일을 방지한다.
include /etc/nginx/conf.d/*.conf;
nginx.conf 내에 모든 설정을 하는 것이 아닌 분리해서 설정 가능하게끔 해준다. 일종의 import 구문인 듯?
와일드카드 문자(*)를 사용하여 여러 파일을 한 번에 포함 할 수도 있다(*.conf와 같이)
ssl_protocols TLSv1, TLSv1.1, TLSv1.2, TLSv1.3;
nginx가 어떤 SSL 프로토콜을 사용할 지 설정하는 곳이다. 기본적으로 TLSv1, TLSv1.1, TLSv1.2, TLSv1.3이 사용된다.
ssl_prefer_server_cipher on;
SSL을 사용할 시, 사용할 암호화 알고리즘을 결정할 때 서버의 선택을 우선시 할 지 설정하는 지시문이다. 서버와 클라이언트 모두 자신이 사용하는 암호화 알고리즘을 가지고 있고, 이를 SSL 통신 시 협상한다고 한다. 이 때 해당 지시문이 on되어 있으면 서버의 것을 우선으로 사용하고 off로 하면 클라이언트의 것을 우선으로 사용한다.
gzip on;
gzip은 데이터를 압축해서 전송하는 방식으로 웹 서버가 클라이언트로 데이터를 보낼 때 압축하여 전송속도를 향상시키고 대역폭을 절약하게 된다. 보통 텍스트 기반의 데이터를 전송할 때 사용하고 이미지 파일이나 영상 파일은 이미 압축된 상태이기 때문에 추가로 압축을 진행할 필요가 없으니 off로 해두면 될 것이다.
압축관련하여 자세한 설정들을 공식홈페이지에서 참고하여 알아가면 더 좋을 것이다.
keepalive_timeout 75s
클라이언트와 서버 간의 연결을 얼마나 오래 유지할 지 설정하는 지시문이다. 첫 번째 인자는 클라이언트와의 유지 연결시간으로, 초 단위로 입력하며 입력된 시간동안 클라이언트와 서버의 연결을 유지하다가 클라이언트가 이 이상 새로운 요청을 보내지 않으면 연결이 끊어지게 된다.
두 번째 인자는 선택사항으로 Keep-Alive 응답 헤더에 설정된 값을 보내줌으로 클라이언트에게 해당 시간동안만 연결한다고 알려주는 역할이다. 첫 번째와 두 번째 인자 값은 서로 다르게 할 수 있는데 클라이언트한테는 일부러 짧게 알려줘서 더 안정적이 효과를 가져오려한다...라는데 잘 이해하진 못한 것 같다.
여하튼 이러한 기능을 통해 재 연결하는 비용을 줄여 로딩을 빠르게 할 수 있고 또 새로운 연결을 하는데 드는 비용을 줄일 수 있게 된다.
다만 너무 길게 유지하면 유지하는 동안 서버의 자원이 낭비될 것이고 너무 짧게하면 자주 새로운 연결을 생성해야하니 적절한 균형을 찾아야 한다.
log_format #name #format;
# 로그 포맷을 main이라는 이름으로 정의
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# main의 로그포맷 형태로 로그를 저장하겠다는 설정
access_log /var/log/nginx/access.log main;
nginx가 서버에서 발생한 요청을 어떤식으로 기록할 지에 대한 설정을 할 수 있는 지시문이다.
format은 어떤식으로 기록할 지 설정하는 곳으로 변수와 텍스트를 조합해서 작성한다. name은 위에서 정한 fotmat 방식으로 로그를 저장하고 싶을 때 여기서 지정한 name 값을 씀으로 지정할 수 있다.
로그 형식을 지정함으로써 좀 더 내가 원하는 세부적인 형식의 로그를 받아볼 수 있고, 이에 따라 서버 로그를 더 효율적으로 이용할 수 있게 된다.
< 참조 >
- $remote_addr: 클라이언트의 IP 주소.
- $remote_user: 요청을 보낸 사용자 이름.
- $time_local: 서버의 현지 시간을 기록.
- $request: 요청의 메소드, 경로, 프로토콜을 기록.
- $status: 응답한 HTTP 상태 코드.
- $body_bytes_sent: 클라이언트에게 전송된 응답 바디 크기(헤더는 제외).
- $http_referer: 요청을 보낸 참조 페이지 URL.
- $http_user_agent: 요청을 보낸 브라우저나 클라이언트 정보.
오늘은 main context와 event context정도만 알아보았다. 생각보다 꼬리질문이 많아서 시간 소요가 좀 있었지만 기타 짜잘 정보를 많이 알아서 좋았다. 다음 글에서는 http 컨텍스트 안에 속한 server(listen, server_name, root, index 등), location(root, proxy_pass, try_files 등)에 대해 추가로 알아보겠다.