Profile picture

[Docker] 트래픽 관리를 위한 nginx cache 서버 구축하기

JaehyoJJAng2023년 04월 20일

▶︎ 개요

홈서버로 블로그나 기타 이미지, 영상이 포함되어 있는 서비스를 운영하게 되면

트래픽에 대한 부담이 커질 수 밖에 없다.

그렇다고 호스팅을 하거나 클라우드 서비스를 이용하자니 트래픽당 과금되는 요금 때문에 섣불리 도입하는 것도 쉬운 일은 아닐 것이다.

이 때, Nginx cache 서버를 구축해서 사이트의 특정 URL의 특정 파일들에 대해 캐싱 하게되면,

캐시된 리소스로 인해 메인 서버로 들어오는 요청을 분산시킬 수 있게되는 장점이 존재한다.

또한, 클라이언트가 반복해서 요청하는 정적 콘텐츠의 경우 캐시 서버에서 제공되므로 네트워크 대역폭도 최대한 절약할 수 있게 되는 셈이다.


▶︎ 구축 환경

캐시 서버의 구축 환경은 매우 중요하다.

예를 들어, 홈서버에 메인 서버와 캐시 서버를 구축한다고 하면 어떨까?

보통 가정 집은 계약한 통신사 1회선만 집 단자로 넘어오기 때문에

캐시 서버와, 메인 서버를 동일한 네트워크 상에서 운영하게 되는 경우

캐시 서버를 운영함으로써 얻게 되는 이득이 없을 확률이 높다.

그래서 본인은 AWS의 EC2 서비스의 t2.small 인스턴스를 임대하여 테스트 해볼 것이다.

🔸 이번 실습에서는 nginx cache 서버를 NPM(Nginx Proxy Manager) 뒷단에 둔다는 가정하에 진행한다.


▶︎ 도메인 설정

시작하기에 앞서, DNS 설정이 필요하다.

본인은 가비아에서 가장 저렴한 .shop 도메인을 구매하였다.

레코드 설정은 아래와 같다.
image
A 레코드로 만들어주고, cnd.example.com에 서버 공인 IP를 부여해주면 된다.

레코드 호스트
A cdn 10.1.1.1(서버 공인 IP)

▶︎ Cache 서버 구축

docker-compose.yaml 파일과

캐시 서버 및 nginx.conf 파일에 대한 설정을 해보자.


먼저 메인 프로젝트 폴더를 생성하고 nginx 설정이 들어갈 config/nginx 폴더 및 로그 폴더를 생성해주자.

mkdir -p ~/docker-nginx-cache/{data/log/nginx,config/nginx}
cd ~/docker-nginx-cache

‣ 서버 설정

config/nginx/nginx.conf

user nginx;

pid /var/run/nginx.pid;

##################################################################################
# nginx.conf Performance Tuning: https://github.com/denji/nginx-tuning
##################################################################################

# you must set worker processes based on your CPU cores, nginx does not benefit from setting more than that
worker_processes auto; #some last versions calculate it automatically

# number of file descriptors used for nginx
# the limit for the maximum FDs on the server is usually set by the OS.
# if you don't set FD's then OS settings will be used which is by default 2000
worker_rlimit_nofile 100000;

# only log critical errors
error_log /var/log/nginx/error.log crit;

# provides the configuration file context in which the directives that affect connection processing are specified.
events {
# determines how much clients will be served per worker
# max clients = worker_connections * worker_processes
# max clients is also limited by the number of socket connections available on the system (~64k)
worker_connections 4000;

# optmized to serve many clients with each thread, essential for linux -- for testing environment
use epoll;

# accept as many connections as possible, may flood worker connections if set too low -- for testing environment
multi_accept on;
}
http {
  set_real_ip_from 10.0.0.0/8;
  set_real_ip_from 172.16.0.0/12;
  set_real_ip_from 192.168.0.0/16;
  real_ip_header X-Forwarded-For;
  client_max_body_size 64M;
  
  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"';
  
  # cache informations about FDs, frequently accessed files
  # can boost performance, but you need to test those values
  open_file_cache max=200000 inactive=20s;
  open_file_cache_valid 30s;
  open_file_cache_min_uses 2;
  open_file_cache_errors on;
  
  # to boost I/O on HDD we can disable access logs
  access_log off;
  
  # copies data between one FD and other from within the kernel
  # faster then read() + write()
  sendfile on;
  
  # send headers in one peace, its better then sending them one by one
  tcp_nopush on;
  
  # don't buffer data sent, good for small data bursts in real time
  tcp_nodelay on;
  
  # reduce the data that needs to be sent over network -- for testing environment
  gzip on;
  gzip_min_length 10240;
  gzip_proxied expired no-cache no-store private auth;
  gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/json application/xml;
  gzip_disable msie6;
  
  # allow the server to close connection on non responding client, this will free up memory
  reset_timedout_connection on;
  
  # request timed out -- default 60
  client_body_timeout 10;
  
  # if client stop responding, free up memory -- default 60
  send_timeout 2;
  
  # server will close connection after this time -- default 75
  keepalive_timeout 30;
  
  # number of requests client can make over keep-alive -- for testing environment
  keepalive_requests 100000;
  
  #########################################
  # Just For Security Reason
  #########################################
  
  # Security reasons, turn off nginx versions
  server_tokens off;
  
  # #########################################
  # # NGINX Simple DDoS Defense
  # #########################################
  
  # limit the number of connections per single IP
  limit_conn_zone $binary_remote_addr zone=conn_limit_per_ip:10m;
  
  # limit the number of requests for a given session
  limit_req_zone $binary_remote_addr zone=req_limit_per_ip:10m rate=5r/s;
  # zone which we want to limit by upper values, we want limit whole server
  server {
    limit_conn conn_limit_per_ip 10;
    limit_req zone=req_limit_per_ip burst=10 nodelay;
  }

  # if the request body size is more than the buffer size, then the entire (or partial)
  # request body is written into a temporary file
  client_body_buffer_size 128k;

  # headerbuffer size for the request header from client -- for testing environment
  client_header_buffer_size 3m;

  # maximum number and size of buffers for large headers to read from client request
  large_client_header_buffers 4 256k;

  # read timeout for the request body from client -- for testing environment
  # client_body_timeout   3m;

  # how long to wait for the client to send a request header -- for testing environment
  client_header_timeout 3m;

  include /etc/nginx/conf.d/*.conf;
}

config/nginx/nginx.conf

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=static:100m max_size=10g inactive=30d;
proxy_cache_key "$scheme$request_method$host$request_uri";

server {
        listen 80 default_server;
        listen [::]:80 default_server;

        server_name cdn.example.com;

        access_log /var/log/nginx/access.log;
        error_log /var/log/nginx/error.log;

        location ~* \.(?:css|js|gif|png|jpg|jpeg|mp4|webm)$ {
                valid_referers none blocked example.com; # 본서버 외 불펌금지
                if ($invalid_referer) {
                    return   403;
                }
                proxy_pass https://example.com; # 본서버 도메인
                proxy_cache_valid 200 301 302 600m;
                proxy_cache static;
                proxy_cache_use_stale  error timeout updating http_500 http_502 http_503 http_504;
                proxy_cache_revalidate on;
                proxy_cache_lock       on;
                proxy_ignore_headers Set-Cookie;
                access_log off;
                add_header My-Cache-Status $upstream_cache_status;
                add_header my-ray  "KR";
                sendfile on;
                tcp_nopush on;
                tcp_nodelay on;
                keepalive_timeout 65;
                }

        location / {
                return   403;
        }


        location ~ /\.ht {
                                deny all;
        }
}
  • example.com: 사용중인 블로그/사이트의 도메인 주소
  • cdn.example.com: 도메인 설정에서 지정한 cdn 도메인 주소
  • 여기서는 cdn.palworldgall.shop

‣ YAML 설정

docker-compose.yaml

services:
  cache:
    #image: dalso/ds-nginx:1.25.3-alpine
    image: nginx:latest
    restart: always
    environment:
      - TZ=Asia/Seoul
    ports:
      - 88:80
    volumes:
      - ./config/nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./config/nginx/default.conf:/etc/nginx/conf.d/default.conf
      - ./data/log/nginx:/var/log/nginx
    container_name: nginx-cache
    networks:
      - "cache-net"

networks:
  cache-net:
    driver: bridge
    external: false

설정 값 작성 후, docker-compose up -d 명령어로 컨테이너를 실행시켜주자.

docker-compose up -d --build

domain:88 or ip:88번 포트로 접속했을 때 403이 나온다면 정상!
image


‣ npm 설정

nginx proxy manager로 들어가서 cdn용 도메인을 구성해보자.
![사진]
여기서 forward hostname에는 nginx cache 서버의 IP를 적어주면 된다.

또는, 도메인 A 레코드에 @, www 레코드 지정을 해준 상태라면 도메인 이름을 넣어도 상관 없다.

포트는 nginx cache 컨테이너를 띄울 때 매핑한 호스트 포트 88번을 그대로 적어준다.
image


▶︎ 메인서버 설정

이제 마지막으로 메인 서버의 nginx 설정 방법이다.

default.conf에 가서 아래 내용을 추가해주자.

sub_filter [기존도메인경로] [cdn도메인경로] 처럼 캐싱할 경로를 지정해주면 되는데

워드프레스 기준 설정 방법은 다음과 같다.

sub_filter_once off;
sub_filter 'https://exmaple.com/wp-content/uploads/' 'https://cdn.example.com/wp-content/uploads/';
sub_filter 'https://example.com/wp-content/themes/' 'https://cdn.example.com/wp-content/themes/';

이걸 본인의 설정으로 바꾼다면 아래와 같이 되겠다.

sub_filter_once off;
sub_filter 'https://palworldgall.shop/wp-content/uploads/' 'https://cdn.palworldgall.shop/wp-content/uploads/';
sub_filter 'https://palworldgall.shop/wp-content/themes/' 'https://cdn.palworldgall.shop/wp-content/themes/';

Loading script...