Archive

Docker - 웹서버와 NginX

|

Docker - 웹서버와 NginX


1. 웹서버란?

  • 웹서버는 HTTP 요청을 읽어서 응답해주는 프로그램
  • 웹서버 프로그램을 서버상에 설치하여, 특정 HTTP 요청에 따라, 서비스를 제공해주는 방식으로 웹서비스를 구현
  • 상용으로 많이 쓰이는 웹서버 프로그램은 크게 apache, nginX가 있다

2. Apache와 NginX

  • Apache는 오픈 소스 프로젝트
  • Apache의 구동 방식
  • Prefork MPM(Multi Processing Module) 방식
    • HTTP 요청이 올 때마다, 프로세스를 복제하여, 각각 별도 프로세스에서 해당 HTTP 요청 처리
  • Worker MPM(Multi Processing Module) 방식
    • 하나의 HTTP 연결 후, 여러 요청 처리를 위해, 복제된 프로세스 내에서 쓰레드를 생성하여 여러 HTTP 요청을 처리하는 방식

  • NginX의 구동 방식

  • Event Driven 방식

    • 하나의 프로세스로 동작하며, HTTP 요청을 event로 비동기식으로 처리
      • 대부분의 HTTP 응답은 결국 html 파일을 서빙하므로 IO작업이다
      • 따라서, IO 작업으로 event를 포워딩하고 요청순이 아닌 요청이 끝난 순으로 처리
    • HTTP 요청마다 프로세스, 쓰레드 생성이 필요없으므로 리소스 관리에 장점이 있다
  • NginX 기본 사용법

  • AWS EC2 ubuntu 20.04, nginx 1.18.0 기준

  • 먼저 리눅스에 우분투 도커 컨테이너 생성 docker run -dit -p 80:8080 --name {이름} ubuntu:20.04

  • 해당 컨테이너로 접속 docker exec -it {이름} /bin/bash

  • 리소스 업데이트 apt-get update

  • 컨테이너에 nginx 설치 (버전 확인 필요) apt-get install nginx=1.18.0-0ubuntu1.2

  • 컨테이너 내에 설치된 nginx의 conf 파일을 찾는다 (컨테이너 접속 상태에서) find -name nginx.conf

  • vi 에디터로 실행 (vi가 없다면 vim 설치 필요) vi /etc/nginx/nginx.conf <— 경로 확인 필요

    • user : niginx를 돌리는 사람이 누구냐 (보통 www-data)

    • worker_process : 프로세스를 몇 개 만들꺼냐 (보통 auto)

    • pid : 시스템과 niginx 프로세스와의 연결고리 (기본으로 놔둠)

    • include : 설정이 여러 파일로 쪼개져있을 경우, 플러그인들 import시

    • events : worker_connections 이벤트를 몇 개까지 처리할꺼냐

    • http : 주로 여기 설정을 만힝 바꿈

      • nginx doc 참고

      • include /etc/nginx/conf.d/*.conf; conf.d 폴더에 내 사이트에 대한 conf 파일을 넣어서 넣는다

      • include /etc/nginx/sites-enabled/*; 기본으로 default 파일이 있는데 server 설정이 잡혀있다 sites-available의 default가 실제 파일 (심볼릭 링크가 걸려있는것)

        • default 파일의 server 설정 시 (sites-available의 default)

        • listen 8080 default_server

        • 연결할 포트 설정

        • default_server는 모든 웹서버 요청을 받겠다는 의미

        • server_name {도메인} 도메인이 없을 경우 _ (언더바)

        • index {index.html} 해당 웹서버 주소 요청 시, 디폴트로 응답할 index 파일명 설정

        • location / { try_files $uri $uri/ =404; }

        • ex)

          root /var/www/html;
                  
          location /blog {
              root /var/www
          }
                  
          # 123.0.0.1/blog/aaa.html로 접속시 root를 /var/www로 해서
          # /var/www/blog/aaa.html 파일을 찾으라는 소리 
                  
          location / {
              try_files $uri $uri/ =404;
          }
                  
          # 123.0.0.1/bbb/aaa.html로 접속시 /blog에 매칭이 안되므로
          # root인 /var/www/html/bbb/aaa.html을 찾는다
          
    • 설정을 바꿨다면 service nginx restart 명령어로 재시작


3. NginX Reverse Proxy

  • Proxy Server란 : 클라가 자신을 통해, 다른 네트워크 서비스에 접속하게 해줄 수 있는 서버
  • Forward Proxy란 : 클라가 외부 인터넷에 직접 접근하는 것이 아닌, 프록시 서버에 외부 인터넷 접근 요청을 하고, 요청에 대한 응답을 프록시 서버가 받은 후, 클라에게 전달
    • 클라가 자주 접속하는 외부 인터넷 서비가 있다면, 캐싱을 해서 성능 향상이 가능
  • Reverse Proxy란 : 클라가 리버스 프록시에 요청을 하면 관련 요청에 따라 적절한 내부 서버에 접속하여 결과를 받은 후, 클라에게 전달
    • 내부 DB 등에 직접 접속을 허용하지 않을 수 있으므로 보안에 유리
    • 요청 트래픽을 관리할 수 있는 로드 밸런싱 등에 유리

* NginX Reverse Proxy : 포트로 구분하기 예제

# docker-compose.yml

version: "3"

services:
	nginxproxy:
		image: nginx:1.18.0
		ports:
			- "8080:8080"
			- "8081:8081"
		restart: always
		volumes:
			- "./nginx/nginx.conf:/etc/nginx/nginx.conf"
			
	nginx:
		depends_on:
			- nginxproxy
		image: nginx:1.18.0
		restart: always
		
	apache:
		depends_on:
			-nginxproxy
		image: httpd:2.4.46
		restart: always

docker-compose로 프록시 서버와 2개의 웹서버를 준비

호스트 PC에 ./nginx/nginx.conf 파일을 준비

user nignx;
worker_processes auto;

# 에러 로그를 저장
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
    worker_connections 1024;
}

http {
    # 데이터에 대한 타입들을 기재
    include /etc/nginx/mime.types;
    # 없는 타입에 대해 디폴트 타입 설정
    default_type application/octet-stream;
    # 로그파일에 대한 포맷 설정
    log_foramt 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;
    # 운영체제에서 커널영역의 작업내용을 유저영역으로 보내지 않고 바로 전송
    sendfile on;
    # 클라의 요청에 대한 응답 제한시간 설정
    keepalive_timeout 65;
    
    upstream docker-nginx {
        server nginx:80;
    }
    
    upstream docker-apache {
        server apache:80;
    }
    
    server {
        listen 8080;
        
        location / {
            proxy_pass http://docker-nginx;
            proxy_redirect off;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_fpr;
            proxy_set_header X-Forwarded_Host $server_name;
        }
    }
    
    server {
        listen 8081;
        
        location / {
            proxy_pass http://docker-apache;
            # 서버 응답 헤더의 주소 변경
            proxy_redirect off;
            # 어느 프록시에서 요청했는지
            proxy_set_header Host $host;
            # 클라이언트 IP 주소
            proxy_set_header X-Real-IP $remote_addr;
            # 어떤 프록시들을 거쳐서 왔는지
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_fpr;
            # 클라이언트의 호스트 이름
            proxy_set_header X-Forwarded_Host $server_name;
        }
    }
}

다른 설정은 기본 설정과 유사하게 따라가나, upstream, server 설정이 중요

upstream의 docker-nginx, docker-apache는 임의로 지은 이름

위 docker-compose로 만든 nginx 컨테이너의 80 포트를 사용하겠다는 의미

default.conf 설정과 겹치지 않도록 include /etc/nginx/conf.d/*.conf는 제거

외부 PC의 8080 접속에 대한 요청을 proxy_pass로 docker-nginx로 보내고 nginx의 80으로 연결한다. apache도 마찬가지.


* NginX Reverse Proxy : 경로로 구분하기 예제

포트로 구분하기 예제의 설정에서 다음 부분을 변경

# docker-compose.yml

services:
	nginxproxy:
		ports:
			- "80:80"

포트를 80포트만 열어준다 (나머지 동일)

nginx/nginx.conf 파일을 수정한다 (나머지 동일)

# /nginx/nginx.conf

 server {
        listen 80;
        
        location /nginx/ {
            proxy_pass http://docker-nginx;
            proxy_redirect off;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_fpr;
            proxy_set_header X-Forwarded_Host $server_name;
        }
    
        location /apache/ {
            proxy_pass http://docker-apache;
            proxy_redirect off;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_fpr;
            proxy_set_header X-Forwarded_Host $server_name;
         }
    }

/nginx에 대한 접속은 docker-nginx가 처리한다

이를 위해서 docker-compose를 실행하고 실행된 nginx에 접속하여 location root에 nginx 폴더를 추가해준다. (/etc/nginx/conf.d/default.conf에서 location root 확인)

테스트를 위해 index.html을 만든다

{EC2인스턴스 IP}/nginx/index.html 로 접속하여 잘 나오는지 테스트 !

아파치도 비슷하게 기본 루트에 apache 폴더를 만들고 테스트 가능


* NginX Reverse Proxy : 경로로 구분하기 예제2 (내부 서버에 요청하는 경로 변경)

{IP}/nginx/index.html로 프록시에 요청했을 때, 내부 nginx에서 {IP}/index.html로 요청한것처럼 변경하기

이를 위해서는 nginx.conf의 server 설정에서 location 안에 rewrite 옵션을 추가해야함

rewrite regex URL [flag]

  • regex : 정규표현식으로 매칭되는 URL 패턴 설정 (Perl의 정규표현식)
  • URL : 변경할 URL 설정
  • flag : 여러 location이 설정되어 있을 때, 변경된 URL이 다시 다른 location에 매칭될 수 있으므로 이를 어떻게 처리할지 설정
    • break : 변경된 URL이 다시 다른 location에 매칭되지 않도록 끝냄
server {
    location /nginx/ {
        # /nginx/ 다음에 오는 문자를 $1이라 하고, $1로 바꿈
        rewrite ^/nginx(.*)$ $1 break;
        proxy_pass http://docker-nginx;
        # ...
    }
}



참고자료


인프런 - 풀스택을 위한 도커와 최신 서버 기술(리눅스, nginx, AWS, HTTPS, flask 배포) [풀스택 Part3]