System Compleat.

'memcached'에 해당되는 글 5건

  1. Nginx + memcached, and balancing
  2. Session Clustering 2
  3. repcached on Ubuntu 9.0.4-server
  4. Extend your L2 Cache with Memcached! 4
  5. Memcached 의 사용.

Nginx + memcached, and balancing

Techs

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




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, 정윤진 ) 


Session Clustering

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

친구랑 술을 먹다가, WebLogic 과 Jboss 의 클러스터링 기능 때문에 매년 일정 라이센스 비용을 내고 서비스를 구축한다는 친구의 이야기를 들었다. 그녀석의 조직은 오픈소스를 워낙 겁내서, lighttpd 로 구현한 단순 static 파일 전송용 이미지 서버가 잘 돌아가지 않을까봐 걱정하는 조직에 몸담고 있다.

일이 이정도 되면, apache 를 왜 서비스에 사용하냐고 물어 볼 염두 조차 나지 않게 된다. 당연하겠지만 이런 대부분의 조직의 경우, 지원이 없는 제품을 굉장히 겁내곤 한다. 아... 생각해 보면 굳이 내 돈 들여서 라이센스 구매하는 것도 아닌데 왜 친구랑 박터지게 소맥을 마셔가며 언쟁을 했는지도 모를일이다.

아마도, 내가 웹 개발자가 아니어서 그런 듯.

세션 데이터의 특징을 보자.

1. 한번 쓰여지고, 자주 참조 된다.
2. 만료 시간이 되면 삭제해도 무방하다.
3. 여러대의 서버에서 참조될 가능성이 있다.
4. 데이터의 구조가 간단하다.
5. 전체 데이터를 잃어도 서비스에 크나큰 해악을 끼치는 경우가 드물다.

stateless 가 주가 되는 현대의 많은 웹 서비스들이 있지만, 그렇다고 세션 처리가 필요 없어지는 것은 아니다. 세션을 처리하는 방법에는 다음과 같은 것들이 있다.

1. L4 장치의 도입
2. Cluster 옵션 또는 라이센스가 제공되는 프레임웍 또는 WAS 의 사용
3. memcached / repcached 를 사용하여 각 페이지의 코드레벨에서 세션을 저장, 참조 하는 방법.

memcached

image from: http://wiki.eol.org/display/ITPUB/Memcached+System+Synchronization


대부분의 국내 웹 사이트에서는 L4 를 사용한다. 클라우드에서는 apache 의 모듈 또는 nginx 와 같은 서버가 제공하는 proxy 를 사용하는 경우가 대부분이다. 이런 1~3번 구조와 같은 것들은 이미 그 구성을 다이어그램으로 그리기 조차 민망한 지경이라고 할까.

다만 3번의 경우, 많은 업장에서 그 효용성을 모르는 듯 하다. 지난번의 오픈소스 관련 포스팅에도 썼지만, 3번과 같은 방법을 꺼리는 가장 큰 이유는 보안도 아니고 코딩의 어려움도 아니고 바로 고장나면 누가 고쳐줄건데 의 인식이 제일 큰 듯 하다.

묻고 싶다.

L4 에서 persistent 또는 sticky 로 클라이언트 별 session 을 이미 기억하고, 클라이언트가 접근했던 웹서버로 이미 던져주고 있는데, 왜 WAS 레벨의 session clustering 이 필요한가. 만약 동작중인 WAS 가 사망했을 경우, L4 의 healthcheck 에 의해서 지정한 retry 와 지정한 timeout 이후에 고객은 '다시 로그인' 외에 다른 행위가 없을 텐데. 만약 세션에 기반한 장바구니와 같은 기능을 구현하고 있었다면, 저렴한 WAS의 N 대 만큼의 고객만 다시 로그인 하면 될 일이다.

만약 3번을 통해 구현했다면, L4 에서 굳이 비용을 들여 session state 를 저장할 필요가 없지 않겠는가.

repcached 사용시 multi-master session cache 로서의 사용은 논의가 필요한 부분이라고 볼 수 있다. heartbeat 을 사용한 repcached 의 구현과 같은 것들이 있을 수 있다고 본다. 다만 안타까운 것은, 상용이 아니면 불가능하다고 생각하고, 만약 오픈소스로 구현 했을 때 문제가 발생한 경우 1빠로 '전에 오픈소스로 바꾼거 그게 문제 아냐?' 라고 생각하는 어간없는 상식이 문제가 아닐까.

세션이 은행의 DB 트랜젝션 만큼 중요하다면 돈을 아낌없이 투자해도 할 말은 없다만, 단지 세션때문에 L4 구매하고, WAS의 클러스터 라이센스를 구매하고 있다면 해 줄 말이 없다. 1인 블로그 만들려고 오라클 엔터프라이즈 구매하는 경우랄까..

Cache 를 쓰는 방법에는 여러가지가 있지만, session 이라는 데이터 특성을 잘 고려해 보는 것이 필요할 수 있다. 물론 내가 틀렸을 수도 있지만, 국내의 잘 나가는 쇼핑몰의 세션 공유 처리에 잘 들 사용하는 것을 보면 많이 잘못 되지도 않은 것 같다.

JSP 는 많이 다를까?

음...
 

repcached

image from: http://wiki.eol.org/display/ITPUB/Memcached+System+Synchronization

위와 같은 repcached 의 사용은 애플리케이션 별 검증이 필요하다.

한가지 더, memcached, memcachedb, repcached 등은 단순 세션 처리에만 사용되는 것은 아니다.
그리고, nodejs 짱이더라... 

NCache
http://cgeers.com/2010/07/11/ncache-distributed-in-memory-object-cache/

Nginx Subsystem, NCache
http://code.google.com/p/ncache/
http://code.google.com/p/ncache/wiki/HowToNcacheV3
http://code.google.com/p/ncache/wiki/HowToNcacheV2

Repcached
http://repcached.lab.klab.org/

Memcached
http://memcached.org/

Consistent Hashing
http://weblogs.java.net/blog/tomwhite/archive/2007/11/consistent_hash.html

Terracotta
http://www.terracotta.org/

Java EE scaling article
http://www.theserverside.com/news/1320914/Scaling-Your-Java-EE-Applications-Part-2

Hazelcast ( In Memory Grid )
http://www.hazelcast.com/documentation.jsp

memcached-session-manager
http://code.google.com/p/memcached-session-manager/

PHP session memcached
http://www.ducea.com/2009/06/02/php-sessions-in-memcached/

EHCache
http://ehcache.org/



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


repcached on Ubuntu 9.0.4-server

Techs
( 정윤진, yjjeong@rsupport.com )

사용자 삽입 이미지

이름에서 쉽게 알 수 있듯이, memcached 를 복제하여 안정성을 높여주는 도구이다.
최근 이런 저런 이유에서 memcached 에 대해서 관심이 좀 생겨서 알아보다가 의외로 쉽게 얻어 걸린 괜찮은 툴인듯 해서 정리해 본다.

용도는, memcached 의 multi-master 로서의 사용이다.
항상 그렇지만, 뭔가 단일화된 자원을 분산하여 처리할때는 여러가지의 경우를 생각해야 하는데
Multi-Master 구조가 Multi-Slave 구조보다 훨씬 힘든 이유는 각각의 분산된 자원이 서로 동일한 정보를 가지고 있어야 하고, 이 정보는 장애 극복이 가능하여야 하며 가능한한 최단 시간내의 sync 를 통해 동기화 되어야 하는 등, 비교적 그 구현이 쉽지 않아 제대로 지원하는 툴 또는 3rd-party 의 무언가를 찾기 쉽지 않은게 사실이다.

일단, 이 repcached 의 성능도 어떨지는 잘 모르겠지만, memcached 의 동작 자체가 DBMS 의 그것과 같지 않게 매우 단순하여 구조적 취약점이 크게 발생할 일이 없으므로, 로컬에서는 일단 잘 동작할 것이라 믿어 의심치 않는다. ( 웹 서버간 세션 공유를 위한 memcached 의 사용 등 )

다만, 일종의 글로벌 서비스를 계획함에 따라 일부 웹 어플리케이션에서 가공되고, 사용되어지는 캐싱 가능한 모든 데이터에 대한 캐쉬 클라우드를 memcached 로 구현함에 있어서,
repcached 가 글로벌한 환경에서 multi-master 로서 동작 할 수 있는가 하는 테스트는 나름 의미가 있지 않을까 한다.

뭐, 남의 프로젝트 페이지에서 이런 설명까지 긁어오는건 별로 좋아라 하지 않지만,
특별히 장애 대응 부분과 replication 동작 부분만 이미지를 가져다 붙이도록 한다.

사용자 삽입 이미지

memcached replication


보면, 클라이언트는 각 memcached 서버에서 적절히 데이터를 받아온다.
적절히 분산해서 받아 오다가, 장애가 발생하면 요렇게 된다.

사용자 삽입 이미지

memcached Crash!


장애가 발생하면, 살아있는 캐쉬 서버로 부터만 데이터를 가져온다.

요러다가 장애난 서버가 되살아 나면~
사용자 삽입 이미지

recover memcached data set


클라이언트로의 접근을 막는 동시에, 동기화를 수행한다.
아... 똑똑하기도 하여라.

각설하고 직접 설치하고 써보는게 장땡이다.


일단 뭐 설치 방법은 간단하다.
최신 버전의 memcached 를 받고, repcached 의 patch 파일을 받은 후에 패치 적용하고
컴파일 하면 된다.

뭐, 실제 구현을 위한 잡다한건 잘 안쓰는 편이지만, 나도 점점 멍청해져 가고 있다는게 최근 확실해 졌으므로 큰맘 먹고 적어본다.

서버는 제목과 같이 우분투 9.0.4 서버 버전을 사용하였다.

# 다운로드
wget http://memcached.googlecode.com/files/memcached-1.2.8.tar.gz
wget http://jaist.dl.sourceforge.net/sourceforge/repcached/repcached-2.2-1.2.8.patch.gz

# 패치가 적용된 다운로드
wget http://jaist.dl.sourceforge.net/sourceforge/repcached/memcached-1.2.8-repcached-2.2.tar.gz

# 뭐 굳이 쓸 필요는 없지만, 문제 추적의 과정을 설명하자는 취지에서,
# 다운 받은 패치를 memcached 의 압축을 풀어놓은 디렉토리로 이동시키고,  적용한다.

gzip -d repcached-2.2-1.2.8.patch.gz | patch -p1

./configure --prefix=/opt/memcached --enable-replication

# i686 어쩌고 찡얼댄다.  알고보면 build-essential 을 설치해 달라는 말이다.

apt-get install build-essential

# 설치가 완료되면, 걸렸던 부분이 넘어간다.
# 잘 넘어가다가,
# libevent 라이브러리가 설치되어있지 않다면, 설치하고 있다면 path 를 알려달라고 또 징징댄다. 귀찮은 녀석.

wget http://www.monkey.org/~provos/libevent-1.4.11-stable.tar.gz

# ./configure , make, make install 한다.

# 다시 memcached 를 configure 한다.
# 잘 된다.

make && make install

# 설치를 완료하고, memcached 를 굴려보면,

# 또 징징댄다. 또 libevent 관련 메세지다. 
# libevent* 를 찾아봤더니, /usr/lib 에 있단다.
# ldd 해보면, libevent 부분만 빵꾸다.
# 훗, ln -s 해준다.  Cent 64bit 짬밥.

ln -s /usr/lib/libevent-1.3e.so.1.0.3 /lib/libevent-1.4.so.2

# 자 이제 사용하면 된다.

물론 잘 동작 하는지는 좀더 봐야겠지만, Amazon EC2 의 EU-west 에서 한국의 서버와 동기화 해 테스트를 해 볼 예정.

많은 Unix 또는 Linux 의 바이너리들 처럼, memcached는 간단, 심플하게 원하는 기능을 수행하는데 군더더기 없이 충실하다.  그 안정성 역시 전세계의 많은  웹서버에 들러붙어서 동작하고 있으므로, 충분히 검증 되었다 싶고.

단순 웹서버의 url contents 캐슁만을 위해 memcached 를 사용하는건, 웬지 아깝고
작게는 세션부터 MySQL 의 result caching 의 엄한 영역 까지 사용 될 수 있으니 만큼,
클라이언트 라이브러리를 조금만 연구 하면 많은 소득을 얻을 수 있지 않나 싶다.

사용 방법은, 뭐 많이 나와있으니깐. 
아, 여기 나와있는 모든 이지미는 repcached 의 wiki 에서 퍼온것임을 강조한다.

repcached
http://repcached.lab.klab.org/

Extend your L2 Cache with Memcached!

Techs
( 정윤진, yjjeong@rsupport.com )

사용자 삽입 이미지A Waffle Image from Waffle Grid Project.



데이트도 없고 약속도 없는 토요일 네티즌 수사대로서 이런 저런 떡밥을 덥썩 무는 것보다 오늘은 맘먹고 구글링이나 해 봐야지 하는 생각에 이런저런 그리드 관련 문서들을 보다가, 희한한 프로젝트가 있어서 소개한다.

본 프로젝트는, 궁극적으로 컴퓨팅 성능을 memcached 를 사용해 프로세서에 들러붙어있는 L2 Cache를 뻥튀기 함으로서 확장하려는 야심찬, 어떻게 보면 약간의 또라이적( 좋은 의미다 ) 발상으로 시작된 '놀라운 그 무언가' 이다.

요지는, LRU에 무언가의 데이터가 쓰여지기 전에 이 데이터를 memcached 에 먼저 쓰고, 읽기 작업을 할떄도 먼저 읽어서 구현하자는 내용이다.

실제 전체 운영체제에 반영하기에는 매우 힘든 부분이 없지 않으며, 이를 위해서는 리눅스 커널을 만져야 할 필요가 있지만 중요한건 커스터마이징 가능한 어떠한 오픈소스라도 이런 형태의 확장이 가능하지 않겠나 하는것이다.

이 와플그리드 프로젝트는 어떤 DBA의 저녁식사에서 시작되었으며, 개발자가 와플을 좋아해서, 또 본인이 그려놓은 다이어그램이 와플같아서 그렇게 이름 지었다 한다. DBA가 시작한 프로젝트라 그런지 MySQL 의 InnoDB 성능 확장을 프로젝트에서 구현하고 있다.
MySQL 에 InnoDB를 사용하는 분들은 봐 두시면 매우 많은 도움이 되지 않을까 싶다.

이후의 내용은  http://www.bigdbahead.com/?p=73 의 내용을 번역한 것 임을 미리 알려둔다. 번역이 맘에 들지 않더라도 알아서 보시면 되겠고, 못알아 보겠으면 링크의 원문을 보셔도 좋다.

이 프로젝트가 어떤 부분에서는 참 기가막힌 성능을 나타낼 것이고, 다른 부분에 반영하기에 위험이 따르는 것도 사실이므로, 아직은 테스트 정도로 또는 다른 어떤 솔루션을 구현할때의 아이디어 정도로 쓰면 되겠다.  사실 뭐 memcached 의 확장적 사용영역에서 엄청 크게 벗어난 것은 아니니까.



--- Memcached as a L2 Cache for InnoDB - The Waffle Grid Project  ---

번역 ( 정윤진, yjjeong@rsupport.com )

몇 달전 힘든 일과 뒤에 목요일 Yves와의  저녁식사에서 어떻게 하면 성능과 확장성을 증가 시킬 수 있을까 하는 논의를 했다. 이 논의에서 나는 그에게 InnoDB를 위한 L2캐쉬로서 memcached 를 사용하면 어떻겠냐는 아이디어를 주었는데, 이 간단한 아이디어는, 뭔가가 LRU 를 참조 할 때마다 memcached 에 데이터셋을 박는 것에서 출발한다.

버퍼에 저장되지 않은 데이터를 디스크에서 읽기전에, memcached 에서 긁어오는 것.

해서 Yves는 메일로 질문질을 하더니, 기어이 골때리게도 실제 0.1 버전의 동작 가능한 프로그램을 보내왔다.  아이디어의 실제 프로젝트화 된 시발점이었달까.

해서 우리는 Waffle Gird 프로젝트라 명명하기로 했다.  왜? 아래의 다이어그램을 보면 와플같이 생기지 않았는가.  게다가 난 와플을 좋아하니까 ( 주: 니가 좋아하니까 그렇게 보이는게 아니냐? ) 또 디게 맛나니까.  뭐 암튼 몇일 밤낮을 홀라당 샌 뒤에, 테스트 가능한 패치코드를 생성 해 냈고, 벤치마크를 시작했다.  동작 했을까?  당근.  게다가 꽤 괜찮았다.  아래의 벤치마크 결과를 참조하시라.

기본적으로, 이 패치를 Central Node 에 적용하고 몇몇의 서버를 remote L cache 로 롤 분배 한다.  예로, 128G 의 메모리를 가진 MySQL 서버 한대와, 각각 64GB 를 가진 4대의 remote 서버를 둔다.  이러한 구성은 전체 256G의 L2 Cache 를 가지게 되겠다.  ( 주: 번역을 끝낸뒤에 재검토 해 보니, 이 글에서 말하는 L2 캐쉬는 Remote 노드의 메모리 총합을 말하는 듯 하다. -ㅁ-;  L1 캐쉬를 128G 라 하는 것을 보니, Central Node 의 Localhost 의 memcached 영역의 128G 로 잡은 모양인가 보다.  쳇, 내가 구현해서 테스트해봐야지. )

뭐 당연하지만, 보다 빠른 네트워크를 구성하는 경우에  더 빠른 응답을 기대 할 수 있다. 디스크에서 데이터를 가져오는 것보다 훨씬 더-

그림을 까보면 ( 와플같이 생겼다는 ) 이렇다.

사용자 삽입 이미지Example Waffle Grid Setup

왜 이런 짓거리를 할까? 몇몇 이유는 다음과 같다.

- 우리는 맨날 어떤 클라이언트가 낮은 디스크 성능으로 인해 충분한 역할을 해 내지 못하는지 확인하여야 하며, 이애대한 해결책은 보다 빠른 디스크를 넣거나 또는 메모리를 추가하는 것이다. 하지만 자금상의 문제에 봉착한다거나  서버 한대에 박을 수 있는 메모리 공간이 쫑난다면 어떻게 할 것인가?

- 32비트 제약으로 인한 성능향상. ( 64bit 로의 마이그레이션을 위한 법률 서비스를 제공하는 회사가 있기도 하다 )

- DBRD를 Active/Passive 로 구현하는 경우, Passive 서버에 뭔가 더 역할을 주고 싶어서.

- 친구들이 말하곤 한다. "난 10대의 와플 그리드로 5TB의 메모리 굴린다~"

전술 하였듯이, 동작한다.  근데 니들은 물어보겠지?  "쫌 만들었냐? 이거 퍼지는거 아냐?"
벤치마크 결과를 보자.

사용자 삽입 이미지Waffle Bench

로컬 메모리 ( Central Node 의 ) 를 추가한다면, 더 좋은 성능을 기대 할 수 있다. 하지만 로컬에 더 많은 메모리를 추가 할 수 없는 경우, remote 의 buffer pool 이 답이다. 아무것도 하지 않은 것 보다, Remote Buffer 를 추가 했을 때 38%의 성능 향상이 발생했다. 또 계속 최적화 할 것이므로, 성능은 더 나아질 것으로 기대한다.

해서, Waffle Grid 가 세계의 기아에 대한 솔루션이 될 수 있을 것인가?  아니다. 

모든 어플리케이션에 적용 가능 할까?  역시 아니다.  

우리는 데이터를 network 에서 읽어오기 때문에, cache miss 나  데이터를 리모트에 넣는데 시간이 걸린다. 근데 만약, 디스크에서 반드시 읽어와야만 하는 데이터가 있는경우, 이에 따른 오버헤드가 매우 커지게 된다.  로컬 캐쉬에서 번저 찾아야 하고 ( OS 로직상 ), 이후 로컬의 memcached 에서 찾고, 다시 remote 의 memcached 에서 찾은 이후에 비로소 디스크를 보게 된다.  이러한 불가결한 로직은 우리가 모르는 동작에 의해 더 늘어 날 수도 있다.  역설하면,  이런 마법같은 상황 ( read-ahead, data accessed from disk sequentially, 아마 빠르겠지 ) 도 발생하지만, 우리는 아직 구현하지 못하고 있다. 

추가적인 지연시간은, full table scan 또는 대량의 데이터가 바로 즉시 읽어져야 하는 경우에 발생할 수도 있다.  사실 결과는 일부 커다란 양의 데이터셋에 대한 쿼리의 실행 속도가 5~6배 느려지기도 했다.  이러한 부분의 개선을 위해 계속 작업하고 있으며, 원하는 데이터를 보다 빨리 찾아내기 위해 cache miss 를 줄이는 알고리즘을 개선중이다. 뭐 말하자면, 아직은 개발중인 실험적 코드임에 분명하고, 무결점으로 동작하지 않는다. 이 부분을 피력하려 노력중이며, 이러한 노력이 약속의 반증이라 생각한다. ( 이러지 말자)

SSD는 어떠냐고 물어보시겠지들.  SSD로 맛나는 와플을 더 빠르게 할 수 없냐?  대답은, NO 다.  뭐 사실은 나도 SSD로 이런 저런 테스트 해 보았지만, 기대만큼 커다란 향상은 없다. 일반 디스크에 비하면, 약 20% 정도의 향상은 있었다.

더 알고 싶으면, wiki 를 참조하셍. ( http://www.wafflegrid.com/wiki/index.php?title=Main_Page )
---


30분 동안 날림 번역했더니 담배를 피워야 겠다는 사실을 잊고 있었다.  아! 억울해 ㅠ

어떠한 버전의 MySQL 에서 동작하는지는 wiki 에서 찾아보기도 하고 그러시라.
나만 뺑이 칠 순 없잖아.

아, 라이센스는 GNU로 보인다.


Memcached 의 사용.

Techs

정윤진 ( yjjeong@rsupport.co.kr )


시스템을 하면서 느끼는 부분은, 개발에서 어떤 시스템 자원을 필요로 하는지 모르면 최상의 퍼포먼스를 구현하기 어렵다는 점이다. 뭐 해보신 분들에게는 당연한 이야기겠지만, 다르게 말하면 어플리케이션 ( 웹이던 웹이 아니던 ) 에서 사용하는 라이브러리나 함수를 모르고는 극한의 튜닝은 힘들고, 이 마저도 제대로 건드리지 못하면 사용자 몇백명에도 버벅대는 시스템이 나올 수 있다.   또는 문제가 발생한 경우, 실제 개발자가 나는 잘못 없어 라고 두손 두발 들어버리면 각종 장애에 대한 부담은 시스템 하는 사람이 떠안기 때문에, 괜히 문제도 없는 시스템에서 패킷 덤프도 뜨고 메모리 덤프도 떠 가면서 GC는 정상 동작 하는지, 각 프로세스나 스레드의 상태등을 체크하면서 상욕을 하는 경우도 많이 있다.

따라서 개발자 만큼은 아니더라도 적어도 우리가 서비스 하고 있는데 사용되는 핵심적인 라이브러리와, 그 라이브러리가 어떠한 방식으로 시스템 자원을 사용하는지 정도에 대한 이해는 항상 필요하지만 요즘과 같이 윈도우, 유닉스등 멀티 플랫폼으로 구성된 서비스의 경우엔 많이 헤메기 마련이다. 그래서, 사실 쉽지 않은 직업이라고도 생각한다.

유닉스의 경우엔, 최악의 경우 strace 를 사용해 보는 방법 ( 극악 무도한 방법이면서 시간이 걸리긴 하지만 ) 을 통해 추적의 실마리 정도는 잡아 낼 수 있다.  농담처럼 아파치에 strace 거는 소리 하고 있네  라고 떠들기도 하지만, 실제 이렇게 잡아내는건 거의 노가다란 사실.
그나마 Open Solaris 등 최신의 SunOS 들은 dtrace 라는 막강한 툴을 제공하기도 하지만.

뭐 서론이 너무나도 길어져 버렸지만, 오늘 이야기 하고자 하는 Memcached 는 이름에서와 같이 Cache 처리를 하는 어플리케이션이다.  PHP 에도 붙이고, 아파치에도 붙이고, Nginx 및 원하면 Perl 에도 가져다 붙일 수 있는 이 강력한 캐시관련 툴은, 경우에 따라 비약적인 서버성능을 가져다 줄 수 있다.

PHP의 extension 으로 제공되는 eAccelerator 나, xcache 와는 다르다.
( 덧붙이자면, eAccelerator 는 실행 환경에 사용될 수 없기 때문에 최근에는 xcache 를 더 선호하는 편이다. )

모든 Cache 가 그렇지만, 적용을 잘 해야한다.  이 잘 해야 한다는 말의 의미는, 데이터가 자주 변경될 가능성이 있는곳에 가져다 붙이면 쓸데 없이 데이터를 cache 에 넣거나, 넣은 데이터가 맞는 데이터인지 검증해야 하는 오버헤드가 붙게 된다.  하지만,  데이터가 자주 변할 가능성이 낮은 구간에 적용한다면 비약적인 성능의 향상을 가져 올 수 있다.


더군다나 풍부한 클라이언트 API의 지원으로, php, perl, python, ruby, java 뿐 아니라
MySQL, PostgreSQL 에도 적용 가능한 UDF 도 제공하기 때문에 그것이 웹 - DB 의 중간자이든, 웹에서 사용되는 세션이건, 각 서비스에 필요한 어떠한 형태의 무엇이건 간에 그 사용의 단순성으로  어느 서비스에나 쉽게 적용 될 수 있겠다.


memcached 는 기본적으로 객체 ( Object ) 를 저장하는데 사용되지만, string 과 같은 직렬화 될 수 있는 어떤 변수라도 저장 할 수 있다.  memcached 에 데이터를 넣는데는 다음 4개의 인자만 주어지면 된다.

유일 키 - 캐시 내부의 데이터 검색용. 레코드 자체가 고유 아이디인 경우 ( 이를테면 세션 ) 그 자체로 키로서 사용 가능하다.

저장 변수 - 직렬화 되어 저장되고 비 직렬화 되어 검색될 수 있다면 어떤 유형이든 가능하다.

Boolean -  압축 여부  ( 압축에 의한 오버헤드와 메모리 저장공간에 대해 생각한다 )

Cache Expire time -  캐쉬가 종료될 시간을 지정한다. 0으로 지정된 경우 캐시에서 절대 지워지지 않는다.

뭐 예제의 경우, 나보다 더 잘 써 놓은 사람들이 있으니 내가 새로 짤 필요는 없다고 생각한다.
따라서 danga 페이지의 Perl 에 적용한 예를 퍼다 담아 본다.


Perl Example

sub get_foo_object {
   my $foo_id = int(shift);
   my $obj = $::MemCache->get("foo:$foo_id");
   return $obj if $obj;

   $obj = $::db->selectrow_hashref("SELECT .... FROM foo f, bar b ".
                                   "WHERE ... AND f.fooid=$foo_id");
   $::MemCache->set("foo:$foo_id", $obj);
   return $obj;
}


리눅스 및 유닉스 등에서 각종 시스템 관리 에 사용하는 툴에도 물론 사용 가능하겠다.
뭐 조악한 예를 들자면, DB 에 저장된 각 시스템의 리소스들에 대한 접근 정보를 퍼담아서
캐쉬에 넣고 사용 할 수도 있겠지.

아무튼 memcached 에 넣은 데이터에 대한 검색, 생성, 삭제 등 모든 액션이 가능하고,
TCP/IP 를 지원하기 때문에 실 시스템의 위치에 관계 없이 구성 가능 하다.

실제 성능에 대한 분석은, 이것 역시 굳이 직접 하지 않아도 될 일이다.  본인이 일하고 있는
사이트에서 이러한 로직을 적용 가능한 구간을 색출하여 한번 비교 분석해 보면 될 일이다.


시스템 관리자가 할 일은, 코드레벨에서 로직의 검증이 아니다. 성능을 분석하고, 사용 가능한 각종 퍼포먼스 튜닝의 방법을 로직에 어떻게 적용할지에 대한 사용가능한 가장 안정적인 해법과 실제 구현을 위한 개발자와의 대화 능력이다.  실 서비스에 적용하기 전에 베타시스템이 존재한다면 베타에 적용하여 두고, 각 API 에 대한 링크를 개발자에게 주는 것만으로 큰 도움이 될 수 있다.


요새는 뭐 항상 당연한 이야기만 쓰고 있는것 같은데, 암튼 2년전에 사용했던 memcached 는
아주 유용 했기에, 요새 nginx 때문에 다시금 보게 되어 반가운 마음에 한번 정리한다.

설치나 기타 뭐 APM 과 같은 환경에서의 운용은, 다른 분들이 잘 설명해 놓은게 많지만
나는 IBM의 링크를 추천한다. ( 다른 PHP 관련 튜닝도 볼 수 있으니까 )


우리 서비스 ( Rsupport 의 서비스 ) 에 적용 할 수 있는 부분은, 아마도 닷넷용 클라이언트 라이브러리를 사용하여 서비스에 녹이는 방법이 있을 수 있겠다.  닷넷쪽에는 이번에 MS MVP이신 webgenie 님이 ( 장현희 과장님이 ㅋ ) 새로 오셨기 때문에  만약 필요 하다면 언제는 지원이 가능할 것이라 생각 된다.  후훗, 앞으로 만들어질 서비스가 기대 된다.


관련 페이지:
memcached Home page
http://www.danga.com/memcached/

IBM 의 memcached 를 사용한 PHP 성능 향상에 관한 링크
http://www.ibm.com/developerworks/kr/library/os-php-fastapps3/index.html

sf.net 의 memcached client library
http://sourceforge.net/projects/memcacheddotnet/

윈도 서버 사용자라고 울지 마시라!   memcached for win32
http://jehiah.cz/projects/memcached-win32/