System Compleat.

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


Avconv, encoding your videos.

Techs

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



최근 스토리지 클라우드 관련 테스트를 위해 1~2 기가 바이트 정도의 동영상 파일들을 토런트를 사용하여 받고 있다. 단순히 받기만 하는 것이 아니라, 혹시 나중에 스트리밍을 위해 사용 할 지도 몰라서 RSTP / H.264 포멧, 그리고 애플용 모바일 기기를 위해 mp4 로 함께 인코딩을 하고 있다. 물론 파일의 크기와 종류가 다양해 지니만큼 소기의 목적이었던 풍족한 테스트를 위해서도 바람직한 일이다. 


문제는 이 인코딩 그리고 다운로드에 개인 노트북이나 PC를 사용하는 경우 쓸데없이 많은 리소스를 잡아먹어서 일에 제법 방해가 될 때가 많다. 단순히 일에 방해가 되는 것 뿐만 아니라, 효율이 떨어지는 2년된 맥북 프로가 고생하는 모습을 보니 안스러워서 새벽에 잠깐 검색질을 통해 조금 더 편리 한 방법을 구성 했다. 물론 아시는 분들은 다 아시는.



사진이_없으면_웬지_허전함.jpg

image from: http://www.muylinux.com/2009/05/19/convertir-videos-bajo-linux-con-ffmpeg/



간밤에 이미 횡설수설 포스팅을 길게 했기 때문에 짧게 쓰고싶다. 

핵심 키워드는, 


Ubuntu 

transmission-cli 

ffmpeg 

avconv 


그리고 뭐 굳이 더하자면 swift 이다. 


transmission-cli 는 토런트 클라이언트의 lightweight 버전을 다시 cli 전용으로 만든 것이다. apt-get install 로 쉽게 설치가 가능하다. ffmpeg 와 avconv 는 모두 영상/음성 관련 도구들이다. 많은 확장 기능들이 있지만, 핵심은 영상 또는 음성의 encoding 이다. 


transmission-cli 는 다음과 같이 간편하게 사용이 가능하다. 


transmission-cli -w /root/torrent/ [DOWNLOADNAME.TORRENT] 또는 [DOWNLOAD_MAGNET_ADDRESS] 


-w 뒤의 옵션은 다운로드 받은 파일을 저장하고자 하는 경로를 지정한다. 위의 커맨드로 인해 발생하는 아웃풋이 별로 마음에 들지 않는다면 stdout 의 리다이렉션 ( aka. > ) 을 사용하여 간단히 로그로 남겨도 된다. 


이런식으로 다운로드 받은 파일을 갖가지 용도의 다른 영상으로 인코딩이 가능한데, 여기에는 다음과 같은 스크립트를 뚝딱 만들어서 사용한다. 크론으로 돌리게 되는 경우에는 중복처리를 신경 써 주어야 할 것이다. 또한, 전용 인코딩 팜을 돌리려면 torque 와 같은 queue 시스템이 필요 할 지도 모르겠다. 다음의 스크립트는 /root/drama 디렉토리의 모든 영상을 변환하고, 변환이 완료되면 Swift 의 스토리지 클러스터로 업로드 한다. 


#!/bin/bash

PWD=/root/drama
cd $PWD
for input in `ls -1` 
do
echo "Process output name"
output=`echo $input | cut -d'.' -f1` 
echo "Source file: ${input}"
echo "Encoded file: ${output}.mp4" 
echo "Encoding $input to mp4"
avconv -y -i $input -strict experimental -qscale 2 ${output}.mp4
echo "Upload ${output}.mp4 to swift storage system" 
swift -V 2 -A http://127.0.0.1:35357/v2.0 -U account:userid -K userkey upload mp4 ${output}.mp4
echo "Upload compleated, delete local files"  
rm -f $input ${output}.mp4
done


avconv 도구의 -y 옵션은 사용자가 질의를 받지 않기 위함이며, -strict experimental 은 현재 apt 패키지 관리 도구로 설치되는 어플리케이션의 AAC 인코딩이 experimental 이라 그렇다. 그리고 -qscale 은, 이 옵션 없이 돌려 보면 실망을 금치 못할 것이다. 화질에 대한 옵션인데, 기본적으로 mp4 인코딩은 굉장히 구린 화질로 인코딩 되는데, 이 -qscale 옵션을 사용하게 되면 제법 볼만한 결과물이 나온다. 값은 2-5 를 지정 할 수 있는데, 낮을 수록 좋은 화질이다. 


달성하고자 하는 목적은, 

1. 인코딩에 서버 자원을 활용한다. 

2. 다운로드에 서버 자원을 활용한다. 

3. 클라우드로의 업로드도 서버 자원을 활용한다. 


노트북은 소중하니;;;; 


뭐 아무튼, 만약 여러분이 가상 서버를 사용중이거나 여분의 리눅스 자원이 있다면 전용 다운로드/인코딩 박스로 활용해도 좋지 않나 싶다. 모뎀 시절에는 참 이런 박스 많이 만들어 놀았는데 요새는 이런게 별로 어렵지도 않네... 


물론 여기에 보다 더 많은 기능을 넣을 수도 있겠지만, 현재로는 이정도면 충분하지 않나 싶다. 왜냐면 난 디게 바쁘니깐. +ㅁ+ 

궁금한 내용이 있다면 구글링!




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




RS232 to Bluetooth, BlueSnap-XP

Techs

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


한해 한해 갈 수록 머리가 점점 더 나빠지나 보다. 힘들게 설정한 장치나 설정파일을 며칠만 지나면 홀랑 까먹어 버린다. Evernote 를 사용하여 적어 두기는 하는데, 미처 다 적지 못한 부분은 꼭 이전에 했던 삽질을 다시해야만 하는 비효율 적인 짓을 최근 들어 자주한다. 이건 필시 이렇게 저렇게 들이부은 술과 꾸역꾸역 피워온 담배 때문인듯. 



Network device console port



네트워크 장비의 초기 설정을 주로 하시는 분들이라면, 또 주로 설정하는 네트워크 장비가 Arista 의 ZTP 와 같은 기능을 제공하지 않는 경우라면 콘솔 포트의 사용은 매일 접하는 업무의 한 부분일 것이다. 비단 네트워크 장비 뿐 아니라, 바코드리더와 연결하는 Serial 포트의 사용이라던가, 임베디드 장비의 콘솔 아웃을 serial 을 통해 확인해야 한다던가 하는 일을 할때에는 이 RS-232가 매우 유용하게 사용된다. 이전의 두께 4Cm 이상의 노트북, 또는 일반 데스크탑에서는 COM 포트가 기본으로 달려있어 별 무리없이 연결하여 HyperTerminal 이나 putty, 또는 minicom 등을 사용하여 연결했지만, 최근의 대부분의 노트북에는 이 포트가 달려서 나오는 경우가 거의 없다. 더군다나 엄청나게 창궐하고 있는 애플의 노트북들은 당연히 이런 구시대적인 인터페이스는 지원하지 않는다. 



보통 윈도우만을 지원한다.

image from: http://serialio.com/products/adaptors/usb_serial_WinDesk.php



물론 USB to RS-232 또는 FTDI 와 같은 방법들이 존재하기는 하지만, 개인적으로 맥에서 호환되는 USB to RS-232 를 찾는건 쉽지 않았던 것 같다. 뭐 물론 덥썩 사서 사용해 보는 무대뽀 정신도 필요할 지는 모르겠지만, 주변의 대대수의 엔지니어분들이 맥을 들고다니면서 윈도우 VM 또는 부트캠프를 올리고, 거기서 드라이버를 설정해 사용하는 모습을 보며 안타까움을 금할 길이 없었다. 많은 분들이 윈도우에 익숙 할 테지만, 그렇다고 콘솔 터미널 접근을 위해 맥에서 윈도우를 돌려 USB 케이블과 장비로 연결된 콘솔 케이블을 주렁주렁 매단 채로 시끄러운 데이터 센터 안에서 장비를 설정하는 모습은 분명 아름답지는 않은 것이었다. 


원래 네트워크 장비를 그다지 자주 설정하지는 않지만, 최근 들어 요구가 발생하는 바람에 가볍고 예쁜 맥북 에어에서 좀더 멋지게 작업하는 방법이 없을까 하고 고민 하던 중, RS232 를 Bluetooth 신호로 전달 해 주는 장치를 찾았다. 국내에서 판매하는 장치는 가장 많이 검색되는 것이 http://wirelessall.co.kr/goods_detail.php?goodsIdx=7395 인데, Palo Alto에서 지내시는 지인분께서 한국에 들어 올 일이 있다 하여 염치 불구하고 http://serialio.com/products/adaptors/BlueSnapXP.php 를 구매하여 셔틀을 부탁했다. 


BlueSanp-XP with internal battery




BlueSnapXP 는, RS-232를 Bluetooth 로 전달해 주는데 배터리가 내장 되어 있어 노트북에 주렁주렁 케이블을 연결 할 필요가 없다. 더군다나 Bluetooth 를 사용하기 때문에 별도의 드라이버를 설치하거나 할 필요도 없게 된다. 만약 데이터 센터에서 작업을 하게 된다면, 그저 BlueSnap-XP 에 콘솔 케이블을 연결 해 두고 대상 장비만 변경 해서 연결 해 주면 되고, 노트북은 전혀 움직일 필요가 없어진다. 귀찮은 윈도우 가상 머신을 사용하지 않아도 되니 이 얼마나 아름다운 세상인가. 하이퍼 터미널이여 안녕. minicom 설정이 복잡하다고 머리를 쥐어뜯던 지난 날이여 안녕. 



일단 나의 경우 별도의 전원장치가 필요하지 않다라고 판단 하여, 다음의 제품을 구매했다. serialio.com 의 온라인 스토어에서 찾아 볼 수 있다. 


BlueSnapXP Bluetooth Dongle DTR mode 


다만 충전은 해야 할 것이 아닌가. 다음의 링크에서 USB 충전 케이블을 함께 구매 해 준다. 

https://serialio.com//store/product_info.php?cPath=72&products_id=565&osCsid=ostjslmp70t5qcsd0uhbqtp5q4


컴퓨터나 노트북의 USB 전원을 사용하여 충전한다. 

밥 주는 중




이 생소한 장비가 배달 되었을때, 이걸 어떻게 사용해야 하나 하고 한참을 고민 했더랬다. 사실 맨 처음 가졌던 생각은, 그냥 노트북과 블루투스 페어링을 잡아 주면, 일반 콘솔 어플리케이션에서 접근 할 수 있지 않겠나 싶었다. 결론 부터 말하자면 매우 삽질했다. ㅠㅠ  언제나 그렇지만 알고 나면 다 써있는건데, 알게 되기 까지 참 고생이 심하다. 멍청해서 그런가보다. 

몇가지 장치의 동작에 대해서 간략하게 설명 하면, 다음과 같다. 

1. 장치의 기본 동작은 "설정 모드" 와 "데이터 통신 모드" 가 있다. 
    - 설정모드의 진입은 장치를 껐다가 켠 이후, 60초 이내에 콘솔 어플리케이션으로 접근하여 $$$ <cr> (aka. 엔터) 를 쳐 준다. 
    - 설정모드에서 나오려면 장치를 reset 하던지, 아니면 --- <cr> 로 빠져 나온다. 

2. 블루투스 기반의 장치들은 그저 Slave 로 동작하는 것이 아니라, 경우에 따라 Master 로도 사용 하여 같은 장비들끼리 연결이 가능하다. 하지만 이 기능은 원래 사용하고자 하는 목적과 부합되지 않으므로 패스. 

3. 장치의 외부에 보면, 딥 스위치로 구성된 점퍼가 있다. 모두 이해하려면 머리 아프지만, 간단히 자주 쓰이는 설정은 다음과 같다. 
    - 1번 스위치 :  뭔가 잘못 설정 되었을때 초기 화 할때 사용한다. 
    - 초기화는 1번 점퍼 ON, 장치가 켜지면 Off / On / Off 2회 반복. 
    - 2번 스위치 : 일반적으로 많이 사용되는 기능. 장치의 자동 검색, 연결이 가능하도록 한다. 
    - 3번 스위치 : Master 모드로 다른 BlueSnap 장치의 마스터로서 설정하는 경우에 사용한다. 
                        점퍼를 올리기 전 설정 모드에서 SR 커맨드를 사용하여 리모트를 지정해 주어야 한다. 
                        또는, 2 번 스위치와 함께 사용하여 Auto discover 할 수도 있다. 
    - 4번 스위치 : 기본적으로 장치의 Baud rate 는 115200, 이 스위치를 올리면 9600 으로 동작. 


이 정도가 간단한 삽질의 핵심이다. 접근에 사용한 도구는 맥에 기본으로 포함된 터미널 어플리케이션이었는데, 설정 모드에서 종종 타이핑한 글자가 보이지 않아 따로 하나 구했다.  http://www.w7ay.net/site/Applications/Serial%20Tools/index.html  

먼저, 일반 네트워크 장비용으로 사용하고자 하는 경우에는 설정모드에서 커맨드를 통해 Baud Rate 를 9600 으로 세팅해 주어야 한다. 그 외에 장치의 이름이나 페어링에 사용할 패스워드의 변경, 기타 확장 명령도 사용이 가능하다. 설정은 다음의 순서. 

1. 장치를 켜고, OSX의 System preferences 의 Bluetooth 메뉴를 이용하여 페어링을 설정. 

2. 위 링크의 Serial tool 을 다운로드, 실행. 

3. 장치를 껐다가 다시 켠다. 그리고 Serial tool 에서 장치를 선택, Connect 버튼을 누른다. 
    - 페어링이 성공적으로 완료 되었다면, BlueSnap-XP-XXXX-SPP 와 같은 이름을 볼 수 있을 것이다.
    - 9600-8-none-1, cr/if 버튼에 체크. 이 외의 RTS/DTR 등의 체크박스는 uncheck 상태로 둔다. 

Serial tools




4. Connect 버튼을 누른다. 사실 이전에는 장치는 미리 페어링 되어 있지 않은 상태에서, 버튼을 누르면 장치를 찾고 블루투스를 연결하기 때문에 실패 할 수도 있다. 3번 까지 적당한 간격으로 연결이 될 때까지 눌러주면 된다. 

5. 연결이 완료 되었다면, 60초가 지나기 전에 $$$ 엔터 를 눌러 설정 모드로 진입한다. 성공적으로 진입 하였다면, CMD 의 문자열을 볼 수 있다. 

6. 기본 커맨드는 D ( 기본 설정 확인 ), E ( 확장 설정 확인 ), h ( 도움말 ) 등이 있다. 설정 명령의 형태는 기본적으로 2 캐릭터로된 문자열 뒤에 콤마 (,) 이후 공백없이 알맞은 값을 넣어주면 된다. 보다 디테일한 설명은 홈페이지의 표를 참조하면 된다. 

7. 설정을 마친 뒤에는 R,1 으로 장치를 재부팅한다. 재부팅이 완료되면 장치를 종료하고, 점퍼의 2번을 ON 상태로 변경한다. 

8. 스위치 등에 연결하여 정상 동작 확인. 



6번에서 설정의 내용은 다음과 같다. 


$$$CMD
h
*** SET COMMANDS ***
SA,<1,0>   - Authentication
SB,   - Send Break
SC,   - Service Class
SD,   - Device Class
SE,<1,0>   - Encryption
SF,1       - Factory Defaults
SI,   - Inquiry Scan Window
SJ,   - Page Scan Window
SL, - Parity
SM,<0-5>   - Mode (0=slav,1=mstr,2=trig,3=auto,4=DTR,5=Any)
SN,  - Name
SO,  - conn/discon Status
SP,  - Pin Code
SR,   - Remote Address
SS,  - Service Name
ST,   - Config Timer
SU,  - Baudrate
SW,   - Sniff Rate
SX,<1,0>   - Bonding
SY,   - TX power
SZ,   - Raw Baudrate
S7,<0-1>   - 7bit data
S~,<0-3>   - Profile (0=SPP,1=DCE,2=DTE,3=MDM,4=D&S
S?,<0-1>   - role switch
S$,  - CMD mode char
S@,   - io port dir
S&,   - io port val
S%,   - io boot dir
S^,   - io boot val
S*,   - pio(8-11) set
S|,   - low power timers
*** DISPLAY      ***
D     - Basic Settings
E     - Extended Settings
G  - Stored setting
GB    - BT Address
GK    - Connect Status
G&    - I/O Ports
V     - Firmare version
*** OTHER        ***
C,    - Connect
F,1        - Fast Mode
I,


어떤 부분이 커맨드고 어떤 부분이 아웃풋인지 구분하기가 힘들것이다. XX,0 이런 스타일로 된 부분이 커맨드이며, 설정값이 정상인 경우 AOK 를 보여준다. 만약 설정 값에 문제가 있거나 정해진 문자열의 길이를 넘는다면, ERR 메세지를 볼 수 있을 것이다. 


위와 같이 설정이 완료 되었다면, 이제 장치를 끄고, 스위치에 연결된 콘솔 포트의 RS-232에 연결 한 다음, 장치를 켜자. 그리고 맥에 기본 내장 되어있는 터미널을 실행 하고, 다음의 커맨드를 날려준다. 


screen /dev/tty.YZ-SnapXP-SPP



모든 설정이 정상적으로 완료 되었다면, 예전 하이퍼 터미널에서 보던 것과 같은 콘솔 메세지를 확인 할 수 있다. 지금까지의 과정에서 무언가 잘못 되었거나, 실수로 마스터 모드등으로 설정한 경우라면 위에 소개된 점퍼 1번을 통해 공장 초기화를 진행하면 된다. 


네트워크 가상화가 유행이기는 하지만, 어떠한 형태로든 망의 물리적인 연결은 필요하다. 이러한 망 설정의 첫 단계인 콘솔의 사용 에 있어 인터페이스의 제한을 핑계로 윈도우에 묶여 있지 말고, 간지나는 맥북 에어를 들고 센터에 가자. 



Connexion



별 내용도 아닌데 한참 삽질 한게 억울해 쓰다보니 괜시리 길어진듯. 


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




Openstack Swift with keystone on multiple nodes.

Techs


 

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

 

 

I’ll try to explain with this post about how to make swift storage cluster works with keystone service. If you have been worked with this open-source based project, you might get struggled, or suffered to configure because there’re not many information about this brand-new cluster. It’s because not only less information, there’re so many old versions and instructions, which means there’re not many certified working set exists. The Launchpad site is some kind of chaos. If you goolge about it, there’re only questions without right answers, and closed issues without comments. One another cause is that this project is so young, and things get changed very fast. All of those reasons will make you get angry to use this, and you may throw your keyboards during setup this brand-new cluster. The hardest thing is that understanding about how the keystone works, and how it can be plugged into swift-proxy’s middle ware. I’ll do not explain about what the keystone is, and what is the swift is as well. I’ll try to give you the instructions what is the result of my research about it. I also made swift + swauth system before, but it’s not that hard, and the swauth is not welcome in nowadays.

 

One more thing to let you know is, this research is based on multi-node cluster use. All components are installed on physically different server, and it’s network is combined with Arista 10G network with iBGP and eBGP with Quagga. But network is not major issue on this post, so I’ll keep it for next time.


The storage cloud install on multiple nodes went like this. 


1. Install and connect every node physically. Power cord, TwinAx, UTP, etc.

2. Setup the server BIOS and IPMI. 

3. Configure all switches. 

4. Install Ubuntu 12.04 on management server. 

5. Write Chef code for automated install. 

6. Prepare all nodes with PXE boot and network install. 

7. Update each configurations to every node by using chef. 

8. Check its functionalities. 



Quanta servers


 

Working environment as follows :

 

1.    Keystone server which is running with 192.168.1.200

2.    Swift proxy server running with 192.168.1.2/24 for admin, and it has 10G interface for services. I made a simple rule about network expansion, so the 10G network has similar IP structure, such as 10.0.1.2/24. And it also have ipmi network for physical control, such as boot order, power managements, its IP is 192.168.1.102. Every cabinet is designed to use /24 network.

3.    Swift storage servers running with 192.168.1.3 – 10 , also has 10G for 10.0.1.3 – 10.0.1.10

4.    Swift version is 1.6.1, you can get it from openstack github.

5.    Keystone is also available on openstack github.

6.    Each storage server has 12 disks for store, and 2 ssd disks for OS.

7.    Ubuntu version is 12.04 LTS.

8.    Quanta servers were used. ( X22-RQ, X12 series )

9.    Arista 7124SX per cabinet.

10. Cisco 2960 per cabinet.



Bluesnap-XP, RS-232 to Bluetooth

 



Every servers ( a.k.a bare-metals )  are installed by automation tool, chef and pxe boot. I’ve downloaded all packages from github.com, and you can also find the url easily.  To install swift successfully, there’re some python modules are needed. If there are no modules exists, then the python will show you error message and you’ll be get which python modules is needed easily. If you don’t know the exact name of the package, then you can search it by typing “apt-get update ; apt-cache search <string>”.

 

I have installed additional python modules packages as below.

 

 

root@allnew-quanta:/root# apt-get install python-eventlet python-netifaces \ 
python-pastedeploy python-webob openssl libssl-dev \
python-setuptools python-lxml python-libxslt1 python-dev

 

After install the swift, you’ll need to configure storage server, such as xfs file system, mount all of it, and build rings.  This configurations are well documented in swift multiple-node install, so I’ll not describe about it. More important thing is, setup the swift proxy server. As you may know, the proxy server should have keystone middle ware on it. So, you need to install keystone with swift on proxy server.  It also can be easily done with python setup.py install. After install it, you need to setup the proxy-server.conf. Here’s the recommended ( I mean the basic ) configuration for it.

 

[DEFAULT]
cert_file = /etc/swift/cert.crt
key_file = /etc/swift/cert.key
bind_port = 8080
user = swift
log_facility = LOG_LOCAL1
workers = 5

[pipeline:main]
pipeline = catch_errors healthcheck cache authtoken keystone proxy-server

[app:proxy-server]
use = egg:swift#proxy
account_autocreate = true

[filter:keystone]
paste.filter_factory = keystone.middleware.swift_auth:filter_factory
operator_roles = admin, swiftoperator

[filter:authtoken]
paste.filter_factory = keystone.middleware.auth_token:filter_factory

# Delaying the auth decision is required to support token-less
# usage for anonymous referrers ('.r:*') or for tempurl/formpost
# middleware.

delay_auth_decision = 0

auth_port = 35357
auth_protocol = http
auth_host = 192.168.1.200
auth_token = ADMIN
admin_token = ADMIN

[filter:cache]
use = egg:swift#memcache
set log_name = cache

[filter:catch_errors]
use = egg:swift#catch_errors

[filter:healthcheck]
use = egg:swift#healthcheck



 

You may install additional python module needed to run swift proxy. It’s same story. Install the additional packages by referencing error messages.

 

After finish swift cluster, you’ll need to configure keystone server. You may consider high-availability for keystone service. Keystone can be sit on sqlite and mysql both, so you can find some way from goole search.

 

Here’s the keystone.conf configuration which is running with database. As you may know, there’s a way to use static file to setup, but if you want to use a feature, such like sharing, then you’ll need database to manage keystone.

 

[DEFAULT]
# A "shared secret" between keystone and other openstack services
admin_token = ADMIN

# The IP address of the network interface to listen on
bind_host = 0.0.0.0

# The port number which the public service listens on
public_port = 5000

# The port number which the public admin listens on
admin_port = 35357

# The port number which the OpenStack Compute service listens on
# compute_port = 8774

# === Logging Options ===
# Print debugging output
verbose = True

# Print more verbose output
# (includes plaintext request logging, potentially including passwords)
debug = True

# Name of log file to output to. If not set, logging will go to stdout.
log_file = keystone.log

# The directory to keep log files in (will be prepended to --logfile)
log_dir = /var/log/keystone

# Use syslog for logging.
# use_syslog = False

# syslog facility to receive log lines
# syslog_log_facility = LOG_USER

# If this option is specified, the logging configuration file specified is
# used and overrides any other logging options specified. Please see the
# Python logging module documentation for details on logging configuration
# files.
# log_config = logging.conf

# A logging.Formatter log message format string which may use any of the
# available logging.LogRecord attributes.
#log_format = %(asctime)s %(levelname)8s [%(name)s] %(message)s

# Format string for %(asctime)s in log records.
log_date_format = %Y-%m-%d %H:%M:%S

# onready allows you to send a notification when the process is ready to serve
# For example, to have it notify using systemd, one could set shell command:
# onready = systemd-notify --ready
# or a module with notify() method:
onready = keystone.common.systemd

[sql]
# The SQLAlchemy connection string used to connect to the database
#connection = sqlite:////var/lib/keystone/keystone.db
connection = mysql://keystone:XXXXX@localhost/keystone
# the timeout before idle sql connections are reaped
# idle_timeout = 200

[identity]
driver = keystone.identity.backends.sql.Identity

[catalog]
# dynamic, sql-based backend (supports API/CLI-based management commands)
driver = keystone.catalog.backends.sql.Catalog

# static, file-based backend (does *NOT* support any management commands)
#driver = keystone.catalog.backends.templated.TemplatedCatalog

template_file = /etc/keystone/default_catalog.templates 

[token]
driver = keystone.token.backends.kvs.Token

# Amount of time a token should remain valid (in seconds)
expiration = 86400

[policy]
driver = keystone.policy.backends.rules.Policy

[ec2]
# driver = keystone.contrib.ec2.backends.kvs.Ec2

[ssl]
#enable = True
#certfile = /etc/keystone/ssl/certs/keystone.pem
#certfile = /etc/keystone/cert.crt
#keyfile = /etc/keystone/ssl/private/keystonekey.pem
#keyfile = /etc/keystone/cert.key
#ca_certs = /etc/keystone/ssl/certs/ca.pem
#cert_required = True

[signing]
certfile = /etc/keystone/ssl/certs/signing_cert.pem
keyfile = /etc/keystone/ssl/private/signing_key.pem
#ca_certs = /etc/keystone/ssl/certs/ca.pem
#key_size = 1024
#valid_days = 3650
#ca_password = None

[ldap]
# url = ldap://localhost
# user = dc=Manager,dc=example,dc=com
# password = None
# suffix = cn=example,cn=com
# use_dumb_member = False

# user_tree_dn = ou=Users,dc=example,dc=com
# user_objectclass = inetOrgPerson
# user_id_attribute = cn
# user_name_attribute = sn

# tenant_tree_dn = ou=Groups,dc=example,dc=com
# tenant_objectclass = groupOfNames
# tenant_id_attribute = cn
# tenant_member_attribute = member
# tenant_name_attribute = ou

# role_tree_dn = ou=Roles,dc=example,dc=com
# role_objectclass = organizationalRole
# role_id_attribute = cn
# role_member_attribute = roleOccupant

[filter:debug]
paste.filter_factory = keystone.common.wsgi:Debug.factory

[filter:token_auth]
paste.filter_factory = keystone.middleware:TokenAuthMiddleware.factory

[filter:admin_token_auth]
paste.filter_factory = keystone.middleware:AdminTokenAuthMiddleware.factory

[filter:xml_body]
paste.filter_factory = keystone.middleware:XmlBodyMiddleware.factory

[filter:json_body]
paste.filter_factory = keystone.middleware:JsonBodyMiddleware.factory

[filter:user_crud_extension]
paste.filter_factory = keystone.contrib.user_crud:CrudExtension.factory

[filter:crud_extension]
paste.filter_factory = keystone.contrib.admin_crud:CrudExtension.factory

[filter:ec2_extension]
paste.filter_factory = keystone.contrib.ec2:Ec2Extension.factory

[filter:s3_extension]
paste.filter_factory = keystone.contrib.s3:S3Extension.factory

[filter:url_normalize]
paste.filter_factory = keystone.middleware:NormalizingFilter.factory

[filter:stats_monitoring]
paste.filter_factory = keystone.contrib.stats:StatsMiddleware.factory

[filter:stats_reporting]
paste.filter_factory = keystone.contrib.stats:StatsExtension.factory

[app:public_service]
paste.app_factory = keystone.service:public_app_factory

[app:admin_service]
paste.app_factory = keystone.service:admin_app_factory

[pipeline:public_api]
pipeline = stats_monitoring url_normalize token_auth admin_token_auth xml_body json_body debug ec2_extension user_crud_extension public_service

[pipeline:admin_api]
pipeline = stats_monitoring url_normalize token_auth admin_token_auth xml_body json_body debug stats_reporting ec2_extension s3_extension crud_extension admin_service

[app:public_version_service]
paste.app_factory = keystone.service:public_version_app_factory

[app:admin_version_service]
paste.app_factory = keystone.service:admin_version_app_factory

[pipeline:public_version_api]
pipeline = stats_monitoring url_normalize xml_body public_version_service

[pipeline:admin_version_api]
pipeline = stats_monitoring url_normalize xml_body admin_version_service

[composite:main]
use = egg:Paste#urlmap
/v2.0 = public_api
/ = public_version_api

[composite:admin]
use = egg:Paste#urlmap
/v2.0 = admin_api
/ = admin_version_api

 

 

Now, you can start keystone service by using keystone-all. If you need service management for keystone, then you can make some script for chkconfig.  There are good documents already exists.

 

Oh, before you start your keystone service with mysql, you need to configure mysql service. Create database and user, then give some proper privileges to it. You can see the database connection string in above configuration.

 

Before you setup the keystone, you may need to understand about how the tenant/user/role/key work with account/user/key in swift. If you already experienced how to use swift api, then you know the credential goes like account:user and key pair. The tenant matches with account, and user is user.

 

Here’s the instructions about how to setup the keystone, and please remember that our proxy-server.conf has allowed the role “admin” and “swiftoperators”, and the keystone.conf has default admin key, which is “ADMIN”.

 

 

First of all, install the python-keystoneclient for your setup system. There’re no mac version. Install it on your keystone server. Openstack keystone package does not contain the “keystone” tool, so you’ll need it if you didn’t install keystone with apt-get.

 

a. Create a tenant.

 

root@allnew-quanta: ~# keystone --username admin --token ADMIN --endpoint http://192.168.1.200:35357/v2.0 tenant-create --name=service 

 

Note that the “endpoint” assigned in command line. You can specify it as OS environment variable.

 

 

b. Then, create an user. Type “keystone –token ADMIN –endpint YOURENDPOINT tenant-list” to see the ID.

 

root@allnew-quanta: ~#  keystone --token ADMIN --endpoint http://192.168.1.200:35357/v2.0 user-create --name=swift --pass=swiftadmin --tenant_id=ID

 

 

c. Create services. Keystone is usually assigned as identity service, and Swift is object-store.

 

root@allnew-quanta: ~# keystone --token ADMIN --endpoint http://192.168.1.200:35357/v2.0 service-create --name=swift --type=object-store --description="Swift Service"

root@allnew-quanta: ~# keystone --token ADMIN --endpoint http://192.168.1.200:35357/v2.0 service-create --name=keystone --type=identity --description="Keystone Identity Service"

 

 

d. Attach the service with endpoint.

 

root@allnew-quanta: ~#  keystone --token ADMIN --endpoint http://172.17.17.76:35357/v2.0 endpoint-create --region RegionOne --service_id=SWIFT_SERVICE_GUID --publicurl 'https://192.168.1.2:8080/v1/AUTH_%(tenant_id)s' --adminurl 'https://192.168.1.2:8080/' --internalurl ' https://192.168.1.2:8080/v1/AUTH_%(tenant_id)s'

root@allnew-quanta: ~#  keystone --token ADMIN --endpoint http://192.168.1.200:35357/v2.0 endpoint-create --region RegionOne --service_id=KEYSTONE_SERVICE_GUID  --publicurl 'http://192.168.1.200:5000/v2.0' --adminurl 'http://192.168.1.200:35357/v2.0' --internalurl 'http://192.168.1.200:5000/v2.0'

 

 

e. Crete roles.

 

root@allnew-quanta: ~#  keystone --token ADMIN --endpoint http://192.168.1.200:35357/v2.0 role-create --name=admin 

root@allnew-quanta: ~#  keystone --token ADMIN --endpoint http://192.168.1.200:35357/v2.0 role-create --name=swiftoperator

 

 

 

f. Attach users to a role.

 

root@allnew-quanta: ~#  keystone --token ADMIN --endpoint http://192.168.1.200:35357/v2.0 user-role-add --tenant_id=TENANT_ID --user=USER_ID --role=ROLE_ID

 

 

 

With swift tool  ( the swift python client tool ), you can check it’s work or not. If it works fine, then you can see the results as below.

 

root@allnew-quanta:/etc/keystone# swift -V 2 -A http://192.168.1.200:35357/v2.0 -U service:swift -K swiftadmin stat

Account: AUTH_6049fcdd4c3a46909a9dbaad04f1636a

Containers: 0 Objects: 0 Bytes: 0 Accept-Ranges: bytes X-Timestamp: 1345122033.16266 X-Trans-Id: tx166227c25e604e4db4c5bdc9039041a4

 

This means, now you can do CRUD to swift storage cluster with swift tool.

 

root@allnew-quanta: ~#  swift -V 2 -A http://192.168.1.200:35357/v2.0 -U service:swift -K swiftadmin upload test keystone.conf
keystone.conf

root@allnew-quanta: ~# swift -V 2 -A http://192.168.1.200:35357/v2.0 -U service:swift -K swiftadmin list
test

 

Basically, the keystone controls authentications for users. More than that, it can be used to control container sharing by using tenants. Currently, I’m focusing on how to implement it as an enterprise service and how it can be installed with Chef. So, will not explain about it more.

 

As I mentioned at top of this post, install swift and keystone on multiple nodes is not simple work for now. I have no doubt that there will be good methods to install this clusters will come out later by many contributors. But for now, well until now, it was not an easy work to do. And its why I wrote this post at this moment.

 

If you’re a web developer, you may interested in how to implement it with Keystone and Swift API for your applications. There’s good explains about how to do this with curl. To understand its action more, then visit this blog.

 

http://blog.chmouel.com/2011/11/24/swift-and-keystone-middleware-part1/

 

Since the cluster works, you can test many things about to running cloud storage and S3 like infrastructure. And that’s exactly what I’m working for.

 

Hope you’ll have successfully working set of private storage cloud on your own.

 


( Younjin.jeong@gmail.com, 정윤진

Github with Xcode4

Techs

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


인프라 기술자가 Xcode 를 사용한다는 것은 사실 엄청나게 흔한 일은 아니라고 생각한다. 나 역시도 맥북을 구매한지 벌써 2년이 넘어가지만, brew 를 사용한 맥용 패키지 관리에 필요하다는 이유로 Xcode 를 설치만 해 두고 있었다. 우분투라면 build-essential 패키지면 될 것을 어마어마한 덩치를 자랑하는 Xcode 를 왜 굳이 컴파일러와 분리해 두지 않았나 하는 생각이었달까. 


하지만 언제나 어플리케이션 디렉토리 한 구석에 자리잡은 망치는 vi 와 터미널이 슬슬 버거워 지는 '자동화 코드' 를 쓰기 시작하면서 점점 흥미를 일으켜 왔다. 뭐 물론 훨씬 가볍고 좋은 도구들이 많지만, 가끔씩 구동해서 텍스트를 넣어 보면 그 예쁘디 예쁜 syntax highlighting 이라던가, 자동 완성 등의 팬시한 모습이 '언젠간 쓰고 말꺼야' 하는 마음을 가지게 만들었다. 역시, 애플의 가장 큰 매력의 원천은 디자인으로 인한 호기심 유발, 그리고 사용해 보니 단순히 예쁜것이 아니라 기능도 좋다 라는 믿음이 발생하게끔 제품을 만드는 것이 아닌가 싶다. 



그대는 너무 아름답소



아무튼 최근의 대부분의 오픈소스 프로젝트들이 sourceforge 에서 github 로 옮겨 가면서, 이제 github 는 개발자건 관리자에게건 아주 중요한 업무의 장이 되어가고 있다. 우리 회사의 경우에도 github 에 private 계정을 만들어 사용중인데, 사내에 서버를 두고 별도로 관리 하는 것 보다 훨씬 낫다. 더군다나 웹 Front-end, Back-end 와 인프라 기술자가 각각 관장하는 코드를 하나의 저장소에서 작업하기 때문에, 또 나중에 장사 안되는 프로토타입은 open source 로 전향 할 가능성이 많기 때문에, 이 github 의 사용성은 우리에게는 축복이다. 


하여, Xcode 4 와 github 를 좀 같이 사용하면 참 좋겠다 싶은 생각에 그동안 미뤄왔던 Xcode 삽질을 오늘 시작 해 보았는데, 이 좋은 도구를 사용함에 있어 말도 안되는 부분에서 난관에 봉착하여 포스팅을 해 볼 까 한다. 그 난관이란 다름아닌 바로, 


"Xcode 의 Project 와 github 에서 받아온 repository 가 연동이 안된다." 


는 초딩스러운 문제다. 그렇다. 웃겨도 할 수 없다. 

대충 설명 하자면, 난관의 순서는 다음과 같았다. 


1. XCode 의 프로젝트는 반드시 신규 생성만 되며, 신규 생성할 때 "꼭" 디렉토리를 생성한다. 

2. XCode 에서 신규 프로젝트를 생성 할 때에는 기본으로 로컬 repository 를 생성한다. 

3. 따라서, 기존에 git clone 으로 받아온 working repository 를 직접 Xcode 에 추가 하려면 /PATH/TO/REPO/${PROJECT_NAME} 과 같은 거지 같은 형태가 된다. 

4. 해서 프로젝트를 먼저 생성 한 후에 .git 디렉토리를 init 시도를 해 보아도 연동이 되지 않았다. 



아무리 개발자가 아니라도 내 시스템 통밥이 몇년인데 이런거로 헤메자니 자괴감 마저 들었다는... 

일단 본인이 해결 한 방법은 다음과 같다. 



1. 원하는 디렉토리에 먼저 Xcode 로 File -> New -> Project 로 프로젝트를 생성한다. 본인의 경우 Xcode 에서 제공하는 컴파일러 및 라이브러리를 사용하지 않기 때문에 Empty 프로젝트를 선택 했다. 



음.. 이런 스크린샷까지 찍어버리다니!




2. Next 를 누르면, Project 이름을 묻는다. 아무리 테스트라도 나쁜 단어로 지지 하지 말자. 





3. 다음을 누르면 프로젝트가 사용 할 디렉토리를 선택하라고 한다. 



여기서 주의 할 것은, "Create local git repository for this project" 버튼의 체크를 해제 해야 한다는 것이다. 

이게 은근히 불편한 것이, Xcode 를 무시하고 터미널 작업과 같은 CLI 에 의존하는 경우 낭패를 보는 경우가 있다. 하지만 더 웃긴건, 어떤 부분에서는 CLI 로 처리를 해 주어야만 동작한다는 것. 



4. Create 버튼을 누르면 프로젝트는 생성이 완료된다. 해당 디렉토리로 가 보면, Xcode 에서 관리를 위한 무언가 디렉토리를 하나 생성해 두었다. 아마도 프로젝트 이름.xcodeproj 라는 이름의 디렉토리 일 것이다. 


기존에 있는 git repository 를 clone 해서 디렉토리를 사용 할 수 없기 때문에, git remote add origin 을 사용하여 코드를 가져온다. git 에 대해서는 별도의 설명은 하지 않는다. 한가지 더, 맥에서 git 를 설치하는 방법은 다루지 않는다. 궁금하신 분은 os x brew 로 구글링. 


Younjins-MacBook-Pro:Velox younjinjeong$ pwd
/Users/younjinjeong/Xcode/Velox
Younjins-MacBook-Pro:Velox younjinjeong$ git init 
Initialized empty Git repository in /Users/younjinjeong/Xcode/Velox/.git/
Younjins-MacBook-Pro:Velox younjinjeong$ git remote add origin https://github.com/XXXXXXXXXX/velox.git
Younjins-MacBook-Pro:Velox younjinjeong$ git pull 
remote: Counting objects: 251, done.
remote: Compressing objects: 100% (208/208), done.
remote: Total 251 (delta 38), reused 237 (delta 24)
Receiving objects: 100% (251/251), 622.49 KiB | 116 KiB/s, done.
Resolving deltas: 100% (38/38), done.
From https://github.com/XXXXXXXXXX/velox
 * [new branch]      master     -> origin/master
You asked me to pull without telling me which branch you
want to merge with, and 'branch.master.merge' in
your configuration file does not tell me, either. Please
specify which branch you want to use on the command line and
try again (e.g. 'git pull  ').
See git-pull(1) for details.

If you often merge with the same branch, you may want to
use something like the following in your configuration file:
    [branch "master"]
    remote = 
    merge = 

    [remote ""]
    url = 
    fetch = 

See git-config(1) for details.
Younjins-MacBook-Pro:Velox younjinjeong$ git branch -a 
  remotes/origin/master
Younjins-MacBook-Pro:Velox younjinjeong$ git checkout master 
Branch master set up to track remote branch master from origin.
Already on 'master'
Younjins-MacBook-Pro:Velox younjinjeong$ git branch -a 
* master
  remotes/origin/master
  remotes/origin/velox-Railway
Younjins-MacBook-Pro:Velox younjinjeong$ ls 
Velox.xcodeproj	app.js		dropbox.js	models		package.json	public		services	swauth		swift		views
Younjins-MacBook-Pro:Velox younjinjeong$ git pull 
Already up-to-date.


5. .gitignore 파일에 Xcode 에서 생성한 디렉토리를 추가한다. 


Younjins-MacBook-Pro:Velox younjinjeong$ echo "Velox.xcodeproj/*" >> .gitignore 




6. 이상 없이 모든게 완료 되었다면, 이제 다시 Xcode 로 돌아온다. Shift-CMD-2 를 누르면 Organizer 가 나타나는데, Repository Tab 에서 방금 작업한 디렉토리의 경로로 Repository 를 추가 해 준다. 



Type 이 Subversion 으로 걸리는 경우도 있는데, git repo 가 확실 하다면 git 로 선택 해 준다. 



7. Repository 의 추가가 완료 되면, 다음과 같은 화면을 볼 수 있다. Xcode 에서 Repository 를 체크하는 동안은 노랑색 아이콘이 표시되며, 확인이 완료되면 녹색으로 변경된다. 계속 주황색으로 머물러 있다면 경로나 github 의 계정을 체크 해 보도록 한다. 한가지 더, 간혹 Xcode 에서 github 사이트의 인증서가 확인이 안된다며 징징대는 경우가 있는데, 이때는 Certificate 버튼을 누르면 이 사이트는 항상 신뢰 와 비슷한 의미의 버튼을 찾아 볼 수 있다. 이 체크버튼을 활성화 하면 귀찮게 하지 않는다. 


일반적으로 github 를 커맨드 라인에서 사용할 때는 맥에 git 에 사용할 key chain 을 연동 할 수 있는데, 이는 매번 패스워드를 입력 할 필요가 없으므로 매우 유용하다. 단, key chain 에 시스템 부팅 후 처음 access 할 때에는 시스템의 패스워드를 묻는다. 






8. 이제 프로젝트 메인 화면으로 돌아오면, 처음 프로젝트를 생성 했을때와 마찬가지로 아무것도 보이지 않는다. 사실 일반적인 사용성을 가진다면 프로젝트의 디렉토리 정보를 가져다가 파일 및 디렉토리등으로 이루어진 코드들이 계층형으로 보여야 할 것 같은데, 아무것도 안보인다. 왼쪽 아래의 + 버튼을 이용하여 파일 및 디렉토리 추가를 선택하고, repository 내의 필요한 파일과 디렉토리를 모두 선택하여 추가 하도록 하자. 



하위의 디렉토리와 파일을 선택하여 모두 추가 하게 되면, 왼쪽의 프로젝트 브라우저에 계층형 구조로 추가된 디렉토리들이 리스팅된다. 이제 파일 중 하나를 살짝 변경하고 File -> Repository -> 탭으로 이동하면, 뭔가 Commit, Add, Push, Pull 과 같은 동작이 되어야 할 것 같은데, 아무것도 발생하지 않는다. 


만약 버튼이 활성화 되어 눌러보더라도, 다음과 같은 메세지가 발생한다. 



초저녁부터 자자는 얘기여 뭐여 - 타짜 광열씨 -



이것때문에 무던한 삽질을 했더랬다. 대체 왜 안되는지 모를 일이었다. 


결론은, Xcode 버그란다...


Xcode 한번 껐다 켜면 된단다.... 


윈도우냐... 



이 해법을 접하고 나서 살짝 멘붕이 올 뻔 했다. 하지만 역시 모든게 올바르게 동작하는 Xcode 는 세살배기 웃음마냥 사람 흐뭇하게 만드는 뭔가가 있다. 



이 화면은 commit 을 진행하면 나타나는데, 프로젝트 단위로 commit 이 필요한 파일의 목록을 보여준다. M 은 수정됨 의 의미이며, 신규 추가되는 경우에는 A 로 나타난다. 가운데의 코드는 당연히 diff 의 내용을 표시해 주는 것이며, 아래에는 commit 에 적용 될 메세지를 쓰는 부분이다. 


커밋하고 보면 Xcode 에서 이슈 관리 부분이 있는 것 같은데, Target 과 이슈 등을 적절히 사용하면 별다른 도구가 필요 없을 정도로 편리 할 것 같다. 


다 해 놓고 보니 별것도 아닌걸 가지고 한참을 헤멨다. 이영도님이 그랬던가. 혼돈의 신 헬카네스는 열쇠를 항상 가까운 곳에 숨겨둔다며... 


사실 그냥 몇 문장으로 그랬다 저랬다 해도 되는 내용이긴 하지만, 웬지 몇시간이나 날려버린게 서운해서 뻘포스팅 해 봤..;;; 


아참, 사용한 Xcode 버전은 4.4.1, 2012년 8월 7일 업데이트 버전인듯. 

이번 주말 및 다음주 초는 개 달려야지... 



덧1. 

왼쪽의 파일 브라우저와 같이 보이는 구조에서 실제 파일을 만들거나 하지 말아야 한다. 신규 생성하는 파일이 있다면 터미널에서 작업 해 주는 것이 안전하다. 이유인 즉슨, 실제 파일 및 디렉토리를 관리 하는 것이 아니라 논리적인 일종의 메타 브라우저인 것 같다. 이것때문에 또 한번 낭패를 볼 뻔. 


덧2. 

.gitignore 를 잘 설정 하지 않으면 엄한 파일들이 올라가므로 주의 할 것. 


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