NGINX!
개요
트레일링 슬래시(trailing slash)
출처: https://djkeh.github.io/articles/Why-do-we-put-slash-at-the-end-of-URL-kor/
URL의 끝에 붙이는 슬래시(/)를 트레일링 슬래시(trailing slash)라고 부른다.
웹사이트에 들어가 보면 맨 끝에 이것이 어느새 알아서 붙어있다.
1 | https://www.google.com/example/ -> 디렉토리입니다. |
트레일링 슬래시가 없는 URL을 요청할 때, 서버는 해당 리소스를 우선 파일로 간주한다.
서버의 처리 동작은 다음과 같습니다
- 해당 이름의 파일이 존재하는지를 먼저 확인.
- 없을 경우, 해당 이름의 디렉토리를 확인.
- 디렉토리가 있으면, 그 안의 기본 파일을 확인.
nginx 기본파일은index.html
그렇다면 아래의 URL 은 어떤 자원을 요청하는 것일까… 디렉토리? 파일?
https://www.naver.com
파일일 것 같지만 디렉토리를 가리킨다. 해당 URL 의 루트 디렉토리.
해당 요청은 브라우저에 의해 자동으로 https://www.naver.com/ 변환된다.
CORS(Cross Origin Resource Sharing)
최근 서비스 사이트에서 한 페이지 안에 모든 데이터를 꽉꽉 담아 한번에 출력하는 페이지는 일반적으로 만들지 않는다.
네이버 메인페이지만 봐도 자체 HTML은 naver.com에, 쇼핑, 뉴스, 증권 등 각종 데이터를 각종 도메인에서 비동기 요청을 통해 json 형식으로 데이터를 수신받아 메인페이지에 출력한다.
naver.com 에서 받은 페이지에서 some-ews.com 에서 받은 뉴스정보 데이터를 받아 브라우저에서 랜더링하는데 SOP(Same Origin Policy) 보안 정책 에러가 발생한다.
CORS 에러는 SOP 보안정책으로 발생하는 문제로, 동일한 도메인이 아닌 서로 다른 도메인에서 받은 데이터 요청시 발생한다.
쿠키를 포함한 민감한 정보가 JS 를 통해 다른 도메인 서버에도 전달될 가능성이 있기 때문에 막는것이다.
Preflighted requests in CORS
OPTIONS 메소드를 통해 preflight(사전 전달) 보내 서버가 해당 parameters를 포함한 요청(GET, POST 등)을 보내도 되는지에 대한 응답을 줄 수 있게 한다.
1 | # request header |
Access-Control-Request-Headers - 실제 요청에서 어떤 HTTP 헤더를 사용할지 서버에게 알려줌Access-Control-Request-Method - 실제 요청에서 어떤 HTTP 메서드를 사용할지 서버에게 알려줌
1 | # response header |
Access-Control-Allow-Credentials - 요청에 대한 응답을 표시할 수 있는지를 나타냄, credentials을 사용하여 실제 요청을 수행할 수 있는지를 나타냅니다.Access-Control-Allow-Headers - 실제 요청시 사용할 수 있는 HTTP 헤더를 나타냅니다.Access-Control-Allow-Origin - 브라우저가 해당 출처가 리소스에 접근하도록 허용
{: .shadow}
그림과 같이 OPTIONS 요청에 200 OK 반환값과 특정 메서드와 헤더가 사용 가능함을 알게되고 데이터를 요청하게 된다.
이는 브라우저의 약속이기에 preflight를 발생시키지 않는 simple request 로 변경하여 CORS 이슈 없이 처리 가능하지만 보안상의 이유로 브라우저에서 별도의 설정을 통해서만 가능하다.
CGI, FastCGI, ASGI, WSGI
CGI(Common Gateway Interface)
HTTP request 가 발생하면 resource 반환하는 인터페이스
각 요청마다 프로세스를 생성하기에 많은 부하가 발생, 성능이 느리다
FastCGICGI 의 문제점을 개선한 것으로 요청마다 일어나는 서버의 부하를 줄이기 위해 만들어진 개선된 인터페이스FastCGI는 요청마다 프로세스가 만들어지는 것이 아닌, 기존에 만들어진 프로세스가 계속해서 새로운 요청들을 처리한다.
미리 만들어 놓은 프로세스와 캐시 데이터를 주고 받음으로서 CGI 보다 더 효율적이고 빠르게 작동한다
WSGI(Web Server Gateway Interface)
Python 웹서버에서 사용,
ASGI(Asynchronous Server Gateway Interface)
Python 웹서버에서 사용, 비동기 웹 애플리케이션을 지원하기 위해 WSGI의 비동기 확장판으로 개발된 인터페이스
정적 파일을 처리하는 HTTP 서버로서의 역할
단순 웹서버의 역할은 HTML, CSS, Javascript, 이미지와 같은 정보를 웹 브라우저(Chrome, Iexplore, Opera, Firefox 등)에 전송하는 역할을 한다. (HTTP 프로토콜을 준수)
리버스 프록시는 Event driven 형식으로 요청을 처리하며, 이런 정적파일을 호스팅 하는 역할을 쉽게 할 수 있다.
응용프로그램 서버에 요청을 보내는 리버스 프록시로서의 역할
두번째 역할은 리버스 프록시(reverse proxy)인데, 한마디로 말하면 클라이언트는 가짜 서버에 요청하면, 프록시 서버가 배후 서버(reverse server)로부터 데이터를 가져오는 역할을 한다.
1 | 프록시 서버: Nginx |
{: .shadow}
리버스 프록시를 두는 이유는 네트워크 대역이 다른 두 그룹 사이의 중개인 역할도 있지만,
요청에 대한 버퍼링, 캐싱, 요청을 분산시킬 수 있는 로드 밸런스기능 을 수행할 수 있기 때문.
Nginx 구조
{: .shadow}
엔진엑스의 실행이 막 시작된 순간에는 Master Process 라 부르는 특별한 프로세스 한 개만 존재한다.
마스터 프로세스 자신은 어떤 클라이언트 요청도 처리하지 않으며 단지 그 일을 대신 수행할 작업자 프로세스를 낳아서 증식(invoke)하는데, 이때 작업자 프로세스의 사용자/그룹은 바뀐다.
환경 설정에서 작업자 프로세스의 수, 작업자 프로세스당 최대 접속 수 등을 정할 수 있다.
Nginx 기본 명령어
1 | # 버전확인 |
1 | # /etc/nginx/nginx.conf 문법 체크 |
Nginx 지시어
nginx 서버를 사용하기위한 수많은 지시어 들이 있고 이를 컨피그 파일로 관리하며 서버실행시에 적용한다.
https://nginx.org/en/docs/
https://opentutorials.org/module/384/4526
ssl 처리와 basic auth 를 위한 파일생성은 아래 명령어 참고
1 | openssl req -x509 -nodes -days 36500 -newkey rsa:2048 \ |
아래와 같은 형태로 nginx.conf 를 구성할 수 있다.
1 | worker_processes auto; # 작업프로세스 개수, CPU 코어 만큼 자동설정, |
http, server, location 블록은 계층구조를 가지고 있다.http > server > location 범위로 작성되는것이 빌잔적,
상위블록의 지시어는 하위 블록의 기본값으로 설정된다.
많은 지시어가 각각의 블록에서 중복선언될 수 있는데 http 블록은 여러개를 사용할 수 있지만 관리상의 이슈로 한번만 사용하는 것을 권장한다.
mime
mime.types 에는 각종 통신에 사용되는 각종 문자열들이 key-value 형식으로 저장되어있다.
1 | # /etc/nginx/mime.types |
upstream 블록
nginx 만으로 클러스터링 구성을 지원하는 블록
nginx는 라운드 로빈 로드밸런싱 메커니즘으로 요청을 어플리케이션에 할당 한다.
아래 설정으로 메커니즘 변경이 가능하다.
ip_hash
같은 방문자로부터 도착한 요청은 항상 같은 업스트림 서버가 처리 할 수 있게 한다.session clustering이 구성되지 않은 경우 유용하다.least_conn
최소요청을 처리한 서버에 우선적으로 요청을 배치least_time
최소요청시간 서버에 우선적으로 요청을 배치weight=n
업스트림 서버의 비중을 나타낸다. 이 값을 2로 설정하면 두번의 요청이 해당 서버에서 먼저 처리된다.max_fails=nn으로 지정한 횟수만큼 실패가 일어나면 서버가 죽은 것으로 간주한다.fail_timeout=nmax_fails가 지정된 상태에서 이 값이 설정만큼 서버가 응답하지 않으면 죽은 것으로 간주한다.down
해당 서버를 사용하지 않게 지정한다.ip_hash;지시어가 설정된 상태에서만 유효하다.backup
모든 서버가 동작하지 않을 때backup으로 표시된 서버가 사용되고 그 전까지는 사용되지 않는다.least_conn
요청이 제일적은 서버로 요청 할당keepalive
서버 연결을 유지하기위한 캐시 할당, 연결 유지 수를 설정keepalive_requestkeepalive되는동안 요청 가능한 횟수, 모두 채우면 연결이 닫힘keepalive_timeoutkeepalive유지 시간
keepalive 가 worker_connections 최대 연결 개수를 넘지 말것
연결객체 유지때문에 새로운 연결을 받지 못할 수 있음
location 블록
location 블록은 server 블록 안에 등장하면서 특정 URL을 처리하는 방법을 정의한다.
이를테면 http://.../course/1, http://.../module/1 처럼 URL 뒤에 붙는 context 로 접근하는 요청을 다르게 처리하고 싶을 때 사용한다.
1 | location @rewrites { |
- 클라이언트가 요청한 경로가 실제 파일 또는 API가 아니고,
- Nginx 설정상 별도로 처리할 수 없다면,
- 그 요청을 /index.html로 리다이렉트하겠다는 의미.
Vue/React 앱에서 자주 사용
prefix match
1 | # prefix match |
위에서 작성한 /greet은 prefix match 이다.
http://localhost/greetAbracadabra 로 접근해도 /greet... 까지는 일치하기 때문에 위의 location 블록에 해당된다.
1 | # redirect |
http://localhost/logo 로 접근하면 http://localhost/thumb.png 로 리다이렉트 된다.(URI 도 자동으로 변환된다)
preferential prefix match
1 | # preferential prefix match |
약간 우선순위가 높은 prefix match 이다.
위의 prefix match 와 regex match 보다 높은 우선순위를 가진다.
exact match
1 | # exact match |
완전히 매칭되는 URI 대해서만 location을 지정하고 싶으면 위처럼 exact match를 사용
regex match
1 | # case sensitive regex match |
regex 를 사용해서 URL location을 지정이 가능하다. ~ 를 사용해 regex match를 사용
1 | # case insensitive regex match |
case insensitive(대소문자 구분x) 를 사용하면 ~* 를 사용.
priority 순서는 정의한 위치일것 같지만 아래와 같다.
exact > preferential > regex > prefix
root, alias 지시자
root: location 으로 넘어온 부분을 root로 설정한 경로에 추가한다.alias: location 으로 넘어온 부분 alias 로 설정한 경로에서 찾는다.
1 | location /static/ { |
/var/www/app/static/static 경로에서 index.html 을 찾는다.
1 | location /static/ { |
/var/www/app/static/ 에서 찾는다.`
autoindex: on 으로 설정시 디렉토리 내부의 목록을 보여주는 index 페이지를 출력, off 는index.html이 없으면 에러반환
Nginx variable
http://nginx.org/en/docs/varindex.html
https://opentutorials.org/module/384/4508
variable은 nginx.conf 에서 사용할 수 있는 변수를 의미.
NGINX 에서 어던 변수들을 지원하는지 알아보자.
http://localhost:8080/test?arg=123&name=kouzie
실제 variable 이 어떻게 출력되는지 확인
1 | location /test { |
1 | host: 192.168.100.101, |
$host:host명$uri:uri$http_{...}:http헤더값 출력, 케밥표기식 사용$args: 파라미터 문자열$scheme:http혹은https$remote_addr: 클라이언트 주소$proxy_host:proxy_pass지시자에 설정되어 있는host,port$proxy_port:proxy_pass지시자에 설정되어 있는port$proxy_add_x_forwarded_for: 클라이언트가 전달한X-Forwarded-For헤더, 헤더가 존재하지 않는다면$remote_addr값으로 대체|
configuration variable
configuration variable은 if와 같은 conditional state를 작성할때 주로 사용합니다.
1 | server { |
Nginx 모듈
rewrite
rewrite 는 입력받은 URI 을 정규표현식으로 찾아 URI을 변환하는 역할을 한다.
단 rewrite는 redirect와는 다르게 내부적으로 요청을 처리하고 URI는 기존에 호출했던 URI를 유지한다.
1 | # rewrite |
^ 는 문자열 시작을 알리는 메타문자\w 는 모든 영숫자를 표시하는 메타문자
/user 로 시작하고 뒤에 /영숫자가 오는 모든 URI 를 /greet 로 rewrite 하여 서버 리소스를 출력한다.
rewrite를 사용하면 URI의 특정 부분을 grap해 사용할 수 있다.
( ) 내부에 grap 하고싶은 URI의 특정 부분표기,$로 grap 할 수 있다.
proxy_pass
프록시 서버의 프로토콜과 매핑 할 URI를 설정. http또는 https
1 | location /name/ { |
1 | location /name/ { |
1 | location /other/ { |
proxy_http_version
proxy_set_header
1 | location /other/ { |
proxy_redirect
proxy_bind
proxy_bind address [transparent] | off;
proxy_bind $remote_addr transparent;
Nginx ssl 적용
CRL (Certificate Revocation List) - 기존에는 CRL 를 이용하여 인증서의 무결성 여부를 확인,
인증서에 기록된 CRL주소에서 CRL List를 다운로드 받아 인증서의 폐기여부를 확인,CRL은 클라이언트에서 매우 큰 인증서 폐기목록을 모두 다운로드 받아 확인해야 하는 단점을 가지고 있음.
OCSP - 인증서의 상태를 실시간으로 체크하기 위한 프로토콜.OCSP는 인증서의 시리얼을 통하여 실시간으로 인증서의 만료여부를 CA 인증서DB에 직접 요청.OCSP는 CRL과 같이 불필요한 목록을 모두 받아 볼 필요가 없어 그 속도가 빠름.
OCSP Stapling - OCSP 요청을 처리해주는 CA 인증서버에 걸리는 부하를 해결하기 위해 만든 기술
클라이언트가 직접 CA서버를 통하여 인증서 만료여부를 확인하는 것이 아니라 서비스를 제공하는 웹서버에서 만료여부를 중계.
1 | server { |
Lets Encrypt
DNS 와 공인IP 가 있을 경우 인증서를 생성하고 Lets Encrypt 를 통해 인증서
1 | $ apt-get update |
보안관련 메일을 수신받을 email 주소를 입력, 그리고 각종 동의를 진행하면
certbot 이 자동으로 Lets Encript 와 통신하며 도메인 인증과정을 진행한다.
- 기존 nginx 설정을 백업
- 기본 nginx 설정에다
/.well-known/acme-challenge등의 작업을 처리 - Lets Encrypt 인즌 진행
letsencrypt 를 통해 생성된 인증서를 확인
1 | $ sudo ls /etc/letsencrypt/live/gateway.mydomain.co.kr |
1 | sudo crontab -e |
1 | $ cd /etc/nginx/sites-avaliable |
1 | # nginx config test |