System Compleat.

'load balance'에 해당되는 글 1건

  1. Nginx + memcached, and balancing

Nginx + memcached, and balancing

Techs

( younjin.jeong@gmail.com, 정윤진 )



English ver.


Nginx 는 언젠가부터 급 부상하기 시작한 다용도의-가볍고-사용하기쉬운 웹 서버다. lighttpd 와 같은 시스템 과는 약간 궤를 달리하는데, apache 에서 어플리케이션 서버의 기능을 배제하고, 부가기능에 보다 충실한 서버다 라고 할 수 있겠다. Nginx 에 대한 자세한 설명은 http://wiki.nginx.org 에서 얻을 수 있으므로 그게 무엇이다를 설명하는 것은 스킵하기로 한다. 


다음은 발로 그린 서비스 아키텍쳐이다. 여기에는 내부망과 외부망 사이에서 각 서비스로의 도메인 라우팅 ( aka. 리버스 프락싱 ) 및 로드 밸런싱, 그리고 SSL endpoint 로서의 기능을 하는 서버가 필요하다. 물론 여기에 상용의 밸런서를 넣을 수도 있으며, 아예 밸런서 클라우드로 구성해 버리는 방법도 있다. 하지만 양자 모두 돈이 많이 드니까, 그냥 Nginx 를 쓰기로 했다. 



Diagram #1, Entire system design


다시한번 그림은 발로 그렸음을 강조한다. ( 사실은 인튜오스4에 Sketchbook Express ;;; ) 


구성하고자 하는 것이 Compute cloud 건, Storage cloud 건, 하둡이건, 그도 아니면 태풍을 예보하는 기상용 HPC 클러스터이던 간에 서비스를 구성하는 구성 요소는 대부분 다 엇비슷하다. 멤버들의 스펙이나 연결 방법들은 조금씩 다르긴 하지만. 아무튼 요즘 유행하고 있는 여기건 저기건 무한대로 확장해야 한다 라는 요소를 가미하면, 모든 구간에 확장이 가능해야 하며 여기에는 똥색으로 그려진 밸런서들도 예외가 아니다. 만약 백본의 트래픽이 수십/수백 기가에 달하는 지경이 된다면 서비스의 종류에 따라 내부 트래픽은 훨씬 더 증가 할 가능성이 있으며, 이 트래픽들은 DSR 구조로 연결되어 있지 않다면 모두 밸런서를 통과하게 된다. 


행여 역시 개발로 쓴 영문을 보신 분들이라면 알겠지만, Nginx 는 만능이 아니다. 잘못사용하게 되면 사용하지 않느니만한 결과를 불러오며, 간결하게 만들어진 시스템에 이것저것 붙이다 보면 오히려 최근의 BMW 처럼 된다.  성능과 정비성이 떨어지게 되며, 장애 포인트가 늘어난다. 프락싱이나 밸런싱의 종류에 따라, Varnish / Pound / Apache 등이 좋은 선택이 될 수 있다. 테스트 테스트. 



Simple concept - 발그림 2탄



밸런싱은 기본적으로 Service rack #1 - 5 에 대해서 수행하게 되며, 추가적으로 Log 서버에서 제공하는 웹 페이지와 Redmine 을 운영에 필요한 Issue tracking 으로 사용한다고 치자. 그리고, 이 Nginx 는 SSL payload 를 처리하도록 한다. 물론 어차피 이런 구조라면 밸런서를 각 랙에 넣으면 되지 않냐고 할 수도 있겠지만, 그런 내용은 요기의 주제가 아니므로 패씅. 


요새는 멀티 코어 서버가 유행이므로, 일단 프로세서는 적당한 가격으로 1~2개 정도 넣어준다. 멀티코어에 하이퍼스레딩을 더하면  OS 에서 뻥튀기는 순식간이므로..;;  Nginx 용 서버는 Generic x86_64 로 하고, 편의상 우분투로 한다. 설정을 진행하기 전에, 아래의 내용을 보자. 


root@balancer01:/etc/nginx# openssl engine -t 
(aesni) Intel AES-NI engine
     [ available ]
(dynamic) Dynamic engine loading support
     [ unavailable ]


위의 커맨드는 현재 시스템에서 지원하는 SSL engine 을 확인하는 커맨드 된다. 이제 몇년전에 포스팅에 끄적이고 한번도 올리지 않았던 nginx.conf 의 샘플을 올려본다. 물론, 그대로 가져다 사용하면 문제가 될 수 있으므로 몇몇 포인트는 수정해야 할 수 있겠다. 물론 SSL 에 필요한 crt/key 파일 생성은 사전에 진행 되어야 하겠다. Nginx 설치 역시 여기서 다루지는 않는다. 


user www-data; worker_process 16; worker_cpu_affinity 0001 0001 0011 0111 1111 1100 1110 1001 1101 1011 1010 0010 0100 1000 1111 0110; worker_rlimit_nofile 8192; ssl_engine aesni; error_log /var/log/nginx/error.log; pid /var/run/nginx.pid; events { worker_connections 1024; multi_accept on; } http { upstream redmine { server 192.168.1.240:3000; } upstream splunk { server 192.168.1.241:8000; } upstream image { ip_hash; server 192.168.1.101:80 max_fails=3 fail_timeout=10s; server 192.168.1.102:80 max_fails=3 fail_timeout=10s; } upstream sharebox { least_conn; server 192.168.1.1:80 max_fails=3 fail_timeout=10s; server 192.168.1.2:80 max_fails=3 fail_timeout=10s; server 192.168.1.3:80 max_fails=3 fail_timeout=10s; server 192.168.1.4:80 max_fails=3 fail_timeout=10s; server 192.168.1.5:80 max_fails=3 fail_timeout=10s; } server { listen 0.0.0.0:443; ssl on; ssl_certificate /etc/nginx/ssl/cert.crt; ssl_certificate_key /etc/nginx/ssl/cert.key; server_name sharebox.mydomainname.com; sendfile on; client_max_body_size 4096m; client_body_buffer_size 128k; location / { set $memcached_key $request_url; default_type application/x-www-urlencoded; memcached_pass 127.0.0.1:11211; if ($request_method = POST) { proxy_pass http://sharebox; break; } proxy_intercept_errors on; error_page 404 502 = /fallback$uri; } location /fallback { internal; proxy_redirect off; proxy_pass http://sharebox; } } server { listen 0.0.0.0:80; server_name redmine.mydomainname.com; root /var/lib/redmine/; proxy_redirect off; sendfile_on; client_max_body_size 50m; client_body_buffer_size 128k; localtion / { proxy_pass http://redmine; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Photo $scheme; proxy_connect_timeout 90; proxy_send_timeout 90; proxy_buffer_size 4k; proxy_buffers 4 32k; proxy_busy_buffer_size 64k; proxy_temp_file_write_size 64k; } } server { listen 0.0.0.0:80; server_name image.mydomainname.com; location / { valid_referes none blocked *.mydomainname.com; if ($invalid_referer) { return 403; } proxy_pass http://image; } } } include tuning/*.conf;


서버는 단순 이미지 파일을 제공하는 image 서버, redmine 서버로의 프락싱, 그리고 무언가 서비스를 돌리고 있는 내부 시스템의 웹 서버로 밸런생+프락싱의 세가지 기능을 한다. 추가적으로 memcached 가 일부 서비스에서 엮여 있는데, 이는 테스트용이므로 참고 해도 좋고 아니어도 좋다. 


Nginx 서비스를 시작하고 다음의 커맨드를 넣으면 보다 디테일한 정보를 볼 수 있다. 


root@redmine:/etc/nginx# nginx -V
nginx: nginx version: nginx/1.0.5
nginx: TLS SNI support enabled
nginx: configure arguments: --prefix=/etc/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-log-path=/var/log/nginx/access.log --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --lock-path=/var/lock/nginx.lock --pid-path=/var/run/nginx.pid --with-debug --with-http_addition_module --with-http_dav_module --with-http_geoip_module --with-http_gzip_static_module --with-http_image_filter_module --with-http_realip_module --with-http_stub_status_module --with-http_ssl_module --with-http_sub_module --with-http_xslt_module --with-ipv6 --with-sha1=/usr/include/openssl --with-md5=/usr/include/openssl --with-mail --with-mail_ssl_module --add-module=/build/buildd/nginx-1.0.5/debian/modules/nginx-echo --add-module=/build/buildd/nginx-1.0.5/debian/modules/nginx-upstream-fair


로그 서버인 Splunk 는 upstream 에는 등록이 되었지만, 실제로 사용하지는 않았다. 다이어그램에는 Sumo Logic 인데 웬 Splunk 하시는 분들도 계실지 모르겠다. 그렇다. 아무 상관 없다. @_@  음, Sumo Logic 은 클라우드 로그 솔루션이다. 데모 어카운트를 받아서 해봤는데, 검색기능도 강력하고 무엇보다 골치아픈 로그 서버를 내부에서 관리 하지 않아도 된다는 특장점이 있다. 물론 이 특장점은 로그를 외부로 유출 할 수 없는 시스템에서는 고민거리로 탈바꿈 하지만. 


upstream 에 least_conn; 을 적용하려면 nginx 버전이 1.3.1 / 1.2.2 이 되어야 한다. 물론 keepalive 값을 주는 것도 가능. 

upstream 과 밸런싱에 대한 자세한 내용은 요기 http://nginx.org/en/docs/http/ngx_http_upstream_module.html


default_type 은 보통 text 를 할당하는데, 이미지 캐싱을 위해서 한번 바꿔봤다. 이미지가 로컬에 있지 않고 REST 기반의 시스템에 토큰이 포함된 주소를 던져야 뱉어주는데, 좀 빨라질까 싶어서 테스트 중. 


아, 그리고 memcached 최신의 버전에서는, -I (대문자임) 옵션을 사용하여 value 로 사용할 저장소의 크기를 1m 이상을 사용 할 수 있다. 물론 크게 잡으면 크게 잡을수록 키 하나가 먹는 메모리 사이즈가 증가하므로 별로 바람직하지 않을 수 있겠다. 위의 설정 파일에서는 키를 request_url 로 잡았음을 확인 할 수 있다. 또한 캐시 서버가 없거나 죽은 경우에는 바로 내부 서버로 던지므로 서비스 동작에는 문제가 없다. /etc/memcached.conf 를 서비스로 추가한다. 


# 2003 - Jay Bonci 
# This configuration file is read by the start-memcached script provided as
# part of the Debian GNU/Linux distribution. 

# Run memcached as a daemon. This command is implied, and is not needed for the
# daemon to run. See the README.Debian that comes with this package for more
# information.
-d

# Log memcached's output to /var/log/memcached
logfile /var/log/memcached.log

# Be verbose
# -v

# Be even more verbose (print client commands as well)
# -vv

# Start with a cap of 64 megs of memory. It's reasonable, and the daemon default
# Note that the daemon will grow to this size, but does not start out holding this much
# memory
-m 4096

# Default connection port is 11211
-p 11211 

# Run the daemon as root. The start-memcached will default to running as root if no
# -u command is present in this config file
-u memcache

# Specify which IP address to listen on. The default is to listen on all IP addresses
# This parameter is one of the only security measures that memcached has, so make sure
# it's listening on a firewalled interface.
-l 127.0.0.1

# Limit the number of simultaneous incoming connections. The daemon default is 1024
-c 10240

# Lock down all paged memory. Consult with the README and homepage before you do this
# -k

# Return error when memory is exhausted (rather than removing items)
# -M

# Maximize core file limit
# -r

# Increate allocation size limit of value per key to 5 Mega
-I 5m

무슨 메모리를 저리 무식하게 할당 했는가 할 수 있겠는데, 서버가 메모리가 48기가가 있어서 그랬다. 음.. 용서를. 


밸런서가 여러대 있는데 이건 또 어떻게 밸런싱 하나 뭐 그럴 수도 있겠는데, 그건 일단 그림에 PowerDNS 를 쑤셔박는걸로 회피했다. 또한, 당연히 성능에 대해서 이런 저런 의견이 있을 수 있겠지만, 어쨌든 서버만 있으면 오픈소스로 엮어서 돌릴 수는 있다. 가성비에 대해서는 추가적인 비교가 필요하지만, 이건 간단한 소개 정도이므로 여기서 종료. 


원래는 Quagga 까지 함께 엮어서 내부 망에 iBGP / eBGP 및 OSPF 등으로 L3 스위치와 엮는 설정을 함께 적으려 했는데, 아직 Quagaa 가 BGP에 대해 maximum-path ( ECMP ) 를 지원하지 않아서 스킵은 핑계. 아, OSPF 에 대해서는 지원한다. 



뭔가 거창하게 시작했는데 괴발개발 그림 그리다 날 샜다. 다시 업장으로 컴백 고고 하기 전에, naver.com 을 대상으로 reverse-proxy 를 수행 한 모습. 다음에도 해봤는데 아마도 피싱 방지 목적용인지 무조건 http://www.daum.net 으로 리다이렉션 시켜버린다. 네이버에는 몇년 전에도 해봤는데 이번에 해도 잘 나오네. 



user root;
worker_processes  16;
worker_cpu_affinity 0001 0001 0011 0111 1111 1100 1110 1001 1101 1011 1010 0010 0100 1000 1111 0110;
ssl_engine aesni;

error_log  /var/log/nginx/error.log;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
    multi_accept on;
}

http {
	upstream naver {
	server naver.com:80;
	}
	upstream daum {
	server daum.net:80;
	}

	server {
		listen 0.0.0.0:80;
		server_name naver.yourdomainname.com;
                sendfile on;
                client_max_body_size       4096m;
                client_body_buffer_size    512k;
		location / {
			set $memcached_key $request_uri;
			default_type application/octet-stream;
			memcached_pass	127.0.0.1:11211;
			if ($request_method = POST) {
				proxy_pass http://naver;
				break;
			}
			proxy_intercept_errors on;
			error_page 404 502 = /fallback$uri;
		}
		location /fallback {	
			internal;
			proxy_redirect off;
			proxy_pass http://naver;
		}
	}
	server {
		listen 0.0.0.0:80;
		server_name daum.yourdomainname.com;
                sendfile on;
                client_max_body_size       4096m;
                client_body_buffer_size    512k;
		location / {
			set $memcached_key $request_uri;
			default_type application/x-www-urlencoded;
			memcached_pass	127.0.0.1:11211;
			if ($request_method = POST) {
				proxy_pass http://daum;
				break;
			}
			proxy_intercept_errors on;
			error_page 404 502 = /fallback$uri;
		}
		location /fallback {	
			internal;
			proxy_redirect off;
			proxy_pass http://daum;
		}
	}
}


위와 같이 설정한 후, 브라우저에서 naver.yourdomainname.com 으로 접근하면 네이버 페이지가 나온다. 성실하게 작업하면 구멍 없이 리다이렉션을 걸어 줄 수 있을 듯. 뭐, 별로 할 필요는 없지만. 



Reverse-proxy to naver.com



네이버가 이뻐서 스크린샷까지 찍어 준 것은 아님. +ㅁ+ 


( younjin.jeong@gmail.com, 정윤진 )