You are on page 1of 9

[스페셜 리포트 | 2 부] 얼랭(Erlang)으로 다중서버 기반의 분산처리 플랫폼 만들기

얼랭(Erlang)으로 다중서버 기반의 분산처리 플랫폼 만들기

최근 대용량 데이터를 처리하는 플랫폼이 굉장한 관심을 끌고 있고 그 중심에는 Hadoop 이 있다.


또한 이 Hadoop 내부의 알고리즘인 Map/Reduce 알고리즘은 분산처리 플랫폼을 이야기 할 때
빠지지 않고 나오는 효율적인 분산 프로그래밍 모델이다.
이번 장에서는 얼랭이 Map/Reduce 분산 프로그래밍 모델과 얼마나 궁합이 잘 맞는지 이야기해
보고자 한다.

전희원(madjakarta@gmail.com) | 고려대학교 컴퓨터공학 석사 졸업, 학교에서는 정보검색 및


Machine Learning 을 공부했으며, 5 년간의 검색엔진 개발 및 데이터 마이닝 경험을 가지고 있다.
대용량 데이터 처리 및 데이터 마이닝에 많은 관심을 가지고 있고, 현재 Yahoo! Korea Search Eng
R&D 파트에서 방대한 양의 웹 데이터를 들여다 보며 재미있는 시간을 보내고 있다.
블로그 : http://www.freesearch.pe.kr

Map/Reduce 란 무엇인가?
최근 몇 년간 구글의 면접에서 가장 흔하게 나온 문제가 이랬다.

수백메가의 데이터를 소팅(Sorting)하려고 하는데 당신의 컴퓨터 사양은 아쉽게도 램이 64 메가에


디스크는 딱 데이터를 담을 정도인 수백메가 밖에 없습니다. 하지만 운 좋게도 당신에게는
네트워크로 연결된 비슷한 사양의 컴퓨터 N 대가 있습니다. 어떻게 하면 데이터 소팅을 효율적으로
할 수 있을까요?

구글이 이 문제를 내는 이유는 그쪽 엔지니어는 항상 대용량 데이터를 잘 다뤄야 된다는 전재가 깔려


있기 때문이다. 당장 해결 방법을 생각하기 힘든 문제일지 모르겠지만 다행스럽게도 위 문제의 답은
앞으로 설명한(할) Map/Reduce 와 크게 관련이 있다.
위 문제의 가장 큰 문제는 데이터가 있는 PC 에서 메모리나 디스크의 부족 때문에 단독으로
소팅하는건 어렵다.(는게 어렵다는 것이다.) 그래서 문제에 힌트가 있는데, 다수의 컴퓨터가
네트워크로 연결이 되어 있다는 게 바로 힌트다. (이 힌트를 무시하고 파일 기반 분산 소팅을 한다고
우긴다면 100 점을 받기는 힘들 것이다.)
그렇다! 데이터를 N 대의 컴퓨터에 적절하게 분산시켜(꽤 괜찮은 Hash 알고리즘을 써야겠지만) 각
PC 에서 소팅을 한 후 다시 내 PC 에 가져올 때 N 대의 PC 의 소팅된 데이터 중 가장 큰 값들을
비교해 순서대로 가져오면 되는 것이다.
Map/Reduce 알고리즘에 익숙하다면 이런 방법은 쉽게 풀 수가 있다. 왜냐면 이 프로그래밍 모델이
데이터를 잘게 잘라 분산을 시켜서 각자 프로세싱을 한 후 그 데이터를 다시 조합(merge)하는 과정을
거치기 때문이다.

Fig 1. Map/Reduce (출처 : http://hadoop.apache.org )


이걸 컴퓨터 클러스터의 관점에서 보면 Fig1 과 같다.
데이터를 컴퓨터 클러스터에 있는 서버에 분산을 시키고 Map 함수를 이용해 잘게 자른 데이터를
<Key, Value> 쌍으로 만든다. 그리고 그 처리된 데이터들에서 같은 Key 를 가지고 있는 데이터를
누적해서 value 에 대한 작업을 해주면 된다. 여기서 누적해서 Value 값들에 대한 작업을 해주는
역할을 Reduce 가 해준다.
독자 분들은 이게 소팅하고 무슨 관계가 있는지 하실지 모르겠지만 관계가 많다.
Map 작업을 하고 나온 데이터들이 수많은 노드에 분산이 되어 있는데, 이들을 같은 Key 값으로
누적시켜 주는 Reduce 를 태우기 전 각 노드에서 같은 Key 를 가지는 데이터를 모두 가져와야 한다.
이 Key 를 찾기 위해서는 각 노드가 소팅되어 있어야 시간이 덜 걸린다. 그래서 각 노드별로 Key 를
기반으로 소팅 작업이 이루어 진다. 만일 소팅이 되어 있지 않다면 각 노드별로 최악의 상황에는
데이터 개수만큼의 서치(Search)가 이루어 질 것이다.
예를 들어 각 노드가 내림차순 정렬로 소팅되어 있다고 하면 각 노드에서 가장 큰 값을 팝(Pop)을
해와 뽑아온 데이터들간에 소팅한 후 가장 큰 키를 찾아서 Reduce 를 태우는 과정을 거치면 여타
다른 노드에서 해당 Key 의 데이터가 없다는 걸 보장 받게 된다.
만일 위 구글 문제의 답을 Map/Reduce 로 구한다면 Map 에서 <Key(소팅할 데이터 단위), NULL>
쌍으로 만들어 주고 내부 노드에서 소팅을 할 때 원하는 소팅 방법으로 소팅 후 그대로 Reduce
함수에서 Map 에서 출력된 <Key, NULL> 값을 출력해 주기만 하면 된다. (Hadoop 에서 이런
방식으로 소팅이 가능하다.)
위 원리는 후에 소개할 예제 프로그램에서도 중요한 원리로 작용하니 꼭 이해하기 바란다.

Map/Reduce 프로그래밍 모델 설명이 나올 때 가장 단골 메뉴로 나오는 word count 예제를


소개한다. word count 예제는 필자가 후에 소개할 프로그램의 테스트로도 쓰인다.

Fig 2. word count 예제 (출처 : http://wiki.apache.org/hadoop/)

굉장히 쉽게 도식화 되어 있어서 이해하기 쉬우리라 생각된다. Map/Reduce 프로그래밍 모델의


효율성은 위의 작업을 수 Terabyte 의 데이터를 가지고 카운팅 한다고 가정 했을 때 어떻게 해야 할지
고민해 보면 이 알고리즘의 효율성을 간접적으로나마 이해할 수 있을 것이다.
하지만 상당수 복잡도가 큰 알고리즘들이 Map/Reduce 모델로 쉽게 변환 가능하지 않은 단점이
있다. 따라서 알고리즘에 따라 위와 같이 1 번씩의 Map/Reduce 에 끝날 수도 있고, 다수의
Map/Reduce 를 거치고 나서 결과가 나올 수도 있다.

분산 얼랭 (Distributed Erlang)
필자가 얼랭(Erlang)을 공부하고 이 언어가 Map/Reduce 같은 분산 알고리즘을 이용한 어플리케이션
개발에 최적화된 언어라고 언젠가 언급한 적이 있었다. 그것의 중심에는 spawn()함수의 편리성과
spawn()함수를 통해 나온 Process ID 값이면 어느 노드에 있든 프로세스건 핸들링이 가능한 분산얼랭
(Distributed Erlang)환경에 있었다.
이 분산얼랭 환경은 최적화가 되어 있어서 같은 머신 에서는 물론이고 개방된 네트워크간의
노드에서도 쉽게 운영이 가능한 장점이 있다. 그래서 이 분산얼랭을 이용하면 다수의 머신 에서
운영하나 마치 한대의 머신 에서 돌리는 것 같은 착각마저 불러 일으킨다.
Map/Reduce 모델하고 어울리는 점이 바로 이거다.
다수의 노드를 관리하기 편하고 수십 개 수백 개의 map 프로세스와 reduce 프로세스를 쉽게
생성하고 죽일 수 있기 때문이다. 또한 얼랭의 경량 프로세스는 OS 레벨의 프로세스하고의 비교를
불허할 정도로 가볍고 빠르다.
메시지 전달을 기반으로 한 함수형 언어의 특징을 가지고 있어 입력 값이 같으면 결과값은 항상
같다는 것을 보장하게 된다. 중간에 쓰레드 같은 게 들어와서 변수를 몰래 고치거나 그런 일이 절대
일어나지 않는다는 것인데, 이로서 얼랭 프로세스 한 개가 정상적으로 잘 돈다면 수백 개의 같은
얼랭 프로세스도 잘 돌게 된다는 것을 보장하게 된다..
마지막으로는 에러에 강건한(fault tolerant) 시스템을 만들 수 있다는 장점이 있다. 생성된
프로세스는 다른 프로세스와 쉽게 링크(link)가 되어 서로 프로세스 작동 여부를 감시할 수 있다.
따라서 어디서든 충돌이 일어나 프로세스가 죽으면 바로 프로세스를 되살릴 수 있다.

얼랭을 이용한 다중 서버 기반 Map/Reduce 설계


그럼 본격적으로 구현을 해보겠다. 아래는 구현하게 될 Map/Reduce 프레임웍이다.

Fig3. 프로세스 구조도

물론 Broker 를 제외한 모든 프로세스 및 노드의 개수는 가변적이다. 각 노드에 Reducer 프로세스가


위치할 수 있으나 일단 편의상 위와 같이 구성했다.
그럼 각 프로세스가 하는 일에 대해서 간략하게 설명해 보겠다.

Broker Process
Map/Reduce 전체 프로세스를 스케줄링 하기 위한 프로세스. 실제 각 노드 프로세스 정보를 가지고
있고 Reducer 프로세스 정보도 가지고 있다. 모든 노드에 Map 프로세싱 작업의 진행 여부를
체크하고 각 노드에 Job 을 할당하며, Map 단계가 끝나고 나서는 각 Reduce 프로세스에 데이터를
할당한다.
Node Process
Broker 프로세스로부터 생성이 되며, Broker 프로세스로부터 데이터를 받아 휘하 Map
프로세스들에게 Job 을 할당한다. 데이터 처리 진행 여부를 체크하며 이를 Broker 프로세스에 리포팅
한다. Map 프로세싱 이외에 combine 이라는 최적화 작업이 각 Node 프로세스에서 수행되어 진다.

Map Processes
Node 프로세스로부터 생성이 되며 실제 map 작업을 하는 프로세스이다. Map 프로세스의 생성시
할당되는 map 함수에 따라 다양한 작업이 가능하다.

Reduce Processes
Broker 프로세스로부터 생성/파괴가 되고 각 노드에서 결과로 나오는 데이터를 받아서 최종 Reduce
프로세싱을 하게 된다. 역시 Reduce 함수를 교체해줌으로 인해 다양한 작업이 가능하다.

얼랭의 프로세스 자체가 메시지 전달(message passing)에 의해서 이루어 지기 때문에 이해를 위해
MSD(message sequence diagram)을 그려봤다.
사실 전체적인 메커니즘은 아래 MSD 에 다 나와 있다. 예제 소스코드와 메시지를 잘 매칭해서 보면
전체적인 윤곽을 파악하는데 큰 도움이 될 것이다.
따라서 지면이 좁은 관계로 세세한 설명 보다는 반드시 이해해야 되는 구현 원리를 설명해 보도록
하겠다.

Fig3. 전체 Map/Reduce 프로세싱 과정 (메시지의 흐름 관점에서)


Broker Process 에서 각 노드에 모듈 분산하기
위에서부터 시간 순으로 보자면 Broker 프로세스가 main 프로세스로부터 생성이 되고 Node
프로세스 및 Reduce 프로세스를 생성시킨다. 그리고 init 명령을 각 Node 프로세스에 날리면 노드는
데이터를 프로세싱 할 준비와 약정된 연산을 하는 Map 프로세스들을 생성한다.
이 부분 Broker 프로세스에 대한 코드는 아래와 같다.

broker_process(ParentId, NodeInfo, ReduceProcesses, mappable) ->


receive
{init, {NodeList, MapFunc, MapperN, ReduceFun, ReducerN}} ->
%각 노드를 초기화 한다.
ActiveNode = lists:filter(fun(X) -> net_adm:ping(X) == pong end, NodeList),
io:format("~B of ~B is avalible.~n", [length(NodeList), length(ActiveNode)]),
c:nl(?MODULE),
Pid = self(),
NewNodeInfo = map(fun(Node) ->
NodePid = spawn(Node, ?MODULE, node_process, [Pid, [],[], [], false]),
NodePid ! {init_node, {MapFunc, MapperN}},
{NodePid, [Node, init, 0]}
end, ActiveNode),
%reducer 들을 만든다.
NewReduceProcesses = map(fun(_) ->
spawn(fun() -> reducer(Pid, ReduceFun) end)
end, lists:seq(0, ReducerN-1)),
broker_process(ParentId, NewNodeInfo, NewReduceProcesses, mappable);
...............................
end.
Code1. broker_process() 함수의 init 부분
위 코드에서 {init, .. } 명령으로 들어가는 부분을 보면 내가 node 프로세스를 만들고 싶은 노드에
대한 정보들이 NodeList 에 들어 있는데 이들 중에 가용한 노드가 어떤 것인지 net_adm:ping()함수로
확인을 하게 된다. 그리고 nl()명령을 수행하게 되는데, 만일 이 함수가 없다면 각 노드에 들어가서
소스 컴파일 하고 동일한 모듈이 각 노드에 로딩이 되었다는 걸 보장할 수 있는 어떤 작업을 해줘야
할 것이다. 하지만 단지 nl()만으로 백 개의 노드이건 되었던 천 개의 노드이건 모듈 로딩이 간편하게
수행된다.
위 코드에서 주의해야 될 부분은 Broker 프로세스의 Pid 를 먼저 구하고 이를 spawn 인자로 넘겨야
된다는 것이다. 만일 spawn 의 인자에 self()를 직접 넣어버리면 spawn 으로 생성된 프로세스의 Pid 가
들어가게 된다. 이점 주의하길 바란다.

얼랭 프로세스에서 꼬리재귀(tail recursion)란?


앞에서 얼랭은 메시지 전달에 의해서 프로세스가 동작한다고 했다. 프로세스 내에서 전역적으로
쓰이는 변수는 반드시 꼬리재귀(tail recursion)를 통해서 전달이 된다. 데이터의 누적 역시 이를
통해서 하게 된다.
만일 receive 패턴에 맞지 않거나 (맞더라도) 꼬리재귀에 들어가지 않을 경우 프로세스는 종료되고
만다.
그리고 얼랭의 모든 프로세스는 메시지 드리븐(message driven)방식이어서 메시지가 오지 않고서는
자동으로 진행이 되기 힘들다. 따라서 Fig3 의 MSD 처럼 간단하게 메시지 전달에 대한 디자인을
세심하게 해야 될 필요가 있다.
간단하게 Broker 프로세스의 receive 패턴과 꼬리 재귀의 얼개를 살펴보자.

broker_process(ParentId, NodeInfo, ReduceProcesses, mappable) ->


receive
{init, {NodeList, MapFunc, MapperN, ReduceFun, ReducerN}} ->
broker_process(ParentId, NewNodeInfo, NewReduceProcesses, mappable);
{clear_all_process} ->
_;
{distribute_data_to_node, {Chunk}} ->
broker_process(ParentId, NodeInfo, ReduceProcesses, mappable);
{end_of_distribute_data} ->
broker_process(ParentId, NewNodeInfo, ReduceProcesses, mappable);
{statuses, {NodePid, map_completed}} ->
broker_process(ParentId, NewNodeInfo, ReduceProcesses, mappable);
{statuses, {NodePid, combine_completed, DataLen}} ->
case is_all_of_status(NewNodeInfo, combine_completed) of
true ->
broker_process(ParentId, NewNodeInfo, ReduceProcesses, PidList, [{in,0}, {out,0}] ,reduceable);
false ->
broker_process(ParentId, NewNodeInfo, ReduceProcesses, mappable)
end
end;
Code2. broker_process() 함수에서 map 단계를 처리하는 코드 얼개
보시다시피 이 부분 함수는 mappable 애텀(atom)을 가지고 있어서 반드시 map 단계에 실행이 되는
부분이다. {statuses, {NodePid, combine_completed, DataLen}} 패턴의 메시지를 받을 때마다 모든
Node 의 상태가 combine_completed 로 되어 있는지 확인하게 되는데 이때 모든 Node 의 상태가
combine_completed 로 되었다는 게 확인이 되면 reduceable 단계로 넘어 가게 된다.(라인 16) 이
reduceable 패턴이 매칭되는 위 함수 이후의 나머지 부분이 Reduce 단계를 수행하게 된다.
보시다시피 각 패턴 매칭 단계의 마지막에는 꼬리 재귀를 위한 broker_process 함수 호출이 있다.
그리고 유일하게 {clear_all_process}부분에서는 꼬리재귀를 하지 않는다. 왜냐면 모든 Node
프로세스들을 종료시키고 자신도 종료하는 부분이기 때문이다.
그럼 꼭 꼬리재귀를 해야 하나?
반드시 그런 건 아니다. 재귀를 모듈 중간에 했다고 하더라도 그 모듈이 무한으로 실행되는 것이
아니라는 보장만 있으면 사용해도 되지만 위와 같이 언제 종료가 될지 모르는 프로세스에서 쓰는
것은 스택 오버플로우(stack overflow)를 일으킬 위험이 많으니 삼가야 한다. 그래서 얼랭에서는
꼬리재귀를 사용하는 것이 정석이다.

각 노드에서의 Map 과 Combine


각 노드에서 Map 프로세싱과 Combine 프로세싱이 어떻게 이루어 지는지 알아보겠다.
Map 프로세싱은 Map 프로세스에 데이터를 던지고 받는 과정으로 끝난다.
처음 Map 프로세스가 생성될때 Map 프로세스에 쓰일 래퍼런스 함수를 하나 받아 가는데 이를
사용해서 들어온 데이터를 파싱한다.
예제로 추가된 word count map 함수를 사용하면 아래와 같은 리스트 객체가 리턴 된다.
“I am a boy.” -> [ {“I”, 1}, {“am”, 1}, {“a”, 1}, {“boy.”, 1} ]
그리고 Map 작업 후에 효율적으로 Map 프로세싱된 결과물을 보내기 위해 같은 해당 노드에서 Key
를 가지는 데이터 단위에 대해서 묶어주는(merge)작업을 하게 된다. 바로 이러한 작업이 Combine
프로세싱이다.

map process 1 : (the,1), (weather, 1), (is, 1), (good, 1)


map
map process
process 2
2 :: (today,
(today, 1),
1), (is,1),
(is,1), (good, 1)
(good, 1)
map
map process
process 3
3 :: (good,
(good, 1),
1), (weather,
(weather, 1),
1), (is,
(is, 1),
1), (good,
(good, 1)
1)

node
node process:
process: (good,1),(good,1),(good,1),(good,1),(is,1),(is,1),
(good,1),(good,1),(good,1),(good,1),(is,1),(is,1),
(is,1),
(is,1), (the,1),
(the,1), (today,1),(weather,1),
(today,1),(weather,1), (weather,
(weather, 1)
1)

node
node process
process :: (good,4),
(good,4), (is,3),(the,1),(today,1),(weather,2)
(is,3),(the,1),(today,1),(weather,2)

Fig4. combine 프로세싱


이렇게 Combine 프로세싱을 하는 이유는 Reduce 프로세싱을 하기 전에 같은 Key 의 데이터에 대해
모든 노드에서 데이터를 효율적으로 가져오기 위한 준비이기 때문이다. 또한 여러 번 보내야 되는
데이터들에 대한 압축 프로세싱의 의미도 있다. 이렇게 압축이 되어 있으면 데이터 전송 효율도
좋아진다.
간단하게는 각 노드에서 일어나는 일종의 Reduce 프로세싱이라고 생각해도 큰 무리는 없을 것이다.
이 부분에 대한 코드를 보자!

node_process(BrokerPID, MapProcesses, KeyValueDict, Status, IsLastData) ->


receive
.............
{map_result, {KeyValues}} ->
...........
NewKeyValueDict = lists:foldl(fun(X, Accu) -> insert(Accu, X) end, KeyValueDict, KeyValues),
...........
node_process(BrokerPID, MapProcesses, NewKeyValueDict, NewStatus, IsLastData);
...........
end.

insert(Dict, {Key, Value}) ->


case dict:is_key(Key, Dict) of
true ->
dict:append(Key, Value, Dict);
false ->
dict:store(Key, [Value], Dict)
end.
Code3. node_process() 함수에서 map 결과물을 가지고 Combine 작업을 하는 부분
사실 필자가 만든 프로그램은 KeyValueDict 라는 사전(dict)객체를 만들어서 node 프로세스로
들어오는 Map 프로세싱 된 데이터를 누적 시킨다. 따라서 모듈에서는 6 번 라인과 insert()함수로
설명이 가능하다.
위의 과정을 거친 데이터를 뽑아갈 때는 큐(Queue)에 있는 데이터를 팝(Pop)해가듯이 리스트 객체의
첫 데이터를 순서대로 가져가면 될 것이다..

Broker 에서 각 Reduce 프로세스에 중복 없는 <Key, Value> 데이터를 보내는 방법


우여곡절 끝에 각 노드에 map/combine 작업을 수행해서 데이터를 잘 쌓아 두었다. 이제는 각 노드에
흩어져 있는 데이터를 Reduce 프로세싱에 할당하는 마지막 작업이 남았다. 이를 위해서 우리의
Broker 프로세스는 아주 중요한 일을 하게 된다.
그 과정을 설명해보자면.

1. 전체 노드에서 첫 번째 데이터를 하나씩 가져온다. (이는 Key 로 소팅되고 Combine 된


데이터들이다.)
2. 이렇게 가져온 데이터들을 Key 로 소팅하고 가장 큰 Key 값을 가지는 것들만 조합(merge)
한다.
3. 그 조합된 데이터를 Key 로 해슁한 결과로 선택된 Reduce 프로세스에 할당한다.
4. Reduce 프로세스로 보내진 Key 를 가지고 있던 노드에서만 새로운 데이터를 받아와서 2 ~ 4
번 과정을 반복한다.

위 1~4 과정은 모든 Node 프로세스에 데이터가 남아 있지 않을 때까지 수행해야 한다.


위의 과정을 반복하면 Reduce 프로세스에 중복 없는 <Key, Value> 데이터들이 빠르게 공급되어
진다.
관련 함수가 바로 아래 두 함수이다.
preprocess_data_before_reduce_reduce(ReduceProcesses, PidData).
broker_process(ParentId, NodeInfo, ReduceProcesses, PidData,[{in,In}, {out,Out}] , reduceable).

예제 코드 테스트
예제로 쓰일 것은 Map/Reduce 프로그래밍의 “Hello world!” 격인 word count 예제이다.
주어진 문서에서 TAB 이나 공백 단위로 쪼개진 단어를 카운팅 하는 게 예제의 목적이다.
이를 위해 map 함수와 reduce 함수를 제작해보면 아래와 같다.

map_function(Line) ->
TokenList = string:tokens(Line, "\t\s"),
[ {Tok, 1} || Tok <- TokenList].

reduce_function(ValueList) ->
lists:foldl(fun(X, Accu) -> X + Accu end, 0, ValueList).
Code3. word count 예제의 map 함수와 reduce 함수
보시다시피 map 함수는 파일의 라인을 받고 이를 [ {Key, 1}, {Key2, 1} ] 리스트로 반환한다.
그리고 reduce 함수는 값들의 리스트만을 받아서 리스트 내부의 값들을 순회하면서 더한 결과를
리턴 한다.
이를 실험 하기 위해 필자가 미리 main()함수를 만들어 두었다.
가장 먼저 해야 될 것이 대상이 되는 머신 에서 분산 얼랭 쉘을 띄워 두는 것이다. 필자는 동일
서버에서 3 개의 터미널을 띄우고 테스트를 수행했다. 아마 서로 각기 다른 서버에 띄운다고 해도
크게 다르지 않을 것이다. .
우선 Node 프로세스와 Map 프로세스가 띄워질 2 개의 얼랭 쉘을 띄운다.

gogamza@freesearch:~$ erl –sname gamza


gogamza@freesearch:~$ erl –sname goguma

마지막으로 컴파일 된 코드가 존재하는 디렉터리로 이동해 Broker 프로세스와 Reduce 프로세스들이
띄워질 1 개의 얼랭 쉘을 띄운다.

gogamza@freesearch:~$ erl –sname gogamza

위 쉘에서 모듈을 로딩하고 main 함수를 실행 시킨다. 입력 파일은 예제 소스 파일 자체를


사용하겠다.

(gogamza@freesearch)1> c(distmapreduce).
(gogamza@freesearch)1> distmapreduce:main(["distmapreduce.erl", "goguma@freesearch",
"gamza@freesearch”]).

main 함수에 들어가는 첫번째 인자는 프로세싱한 파일명이고, 나머지는 쉘을 띄운 node 정보이다.
이렇게 실행 시키면 distmapreduce.erl_word_count 라는 파일이 생성이 되는데 이게 바로 word
count 된 결과 파일이다.
만일 동일 네트웍상의 다른 머신에서 돌린다면 얼랭 쉘을 띄울시 아래와 같이 하면 된다.
gogamza@gogamza.freesearch:~$ erl –name gogamza –setcookie abc
필자가 3 대의 서로 다른 머신에서 돌려봤는데 정상적으로 처리가 된다면 아래와 유사한 화면이
출력될 것이다. (아래는 필자가 3 대의 서로 다른 머신에서 돌려봤을 때 출력된 로그이다. 독자 분들이
직접 돌려본다면 아래와 유사한 로그를 볼 수 있을 것이다.)
Now number of 2 nodes is avalible.
<8278.113.0> node init : 5 mapper processes is aviable!
<0.44.0> broker init : 3 reducer processes is  aviable!
<8279.112.0> node init : 5 mapper processes is aviable!
<8279.112.0> node map process is done!
<8279.112.0> node combine process is completed
Map processing on <8279.112.0> node was completed
<8278.113.0> node map process is done!
<8278.113.0> node combine process is completed
Map processing on <8278.113.0> node was completed
reduce completed!
Cleaning Process!

All process was completed!


<0.44.0> broker was killed

        check distmapreduce.erl_word_count file!

<0.54.0> reducer was killed!


...............
<0.56.0> reducer was killed!
ok
<8278.113.0> node process was killed
....................................
<8278.117.0> map process was killed!
3 개의 서버에서 돌린 word count 예제의 출력 로그
실제 성능 비교를 위해 비교 대상이 되는 프로세싱 작업이 있었으면 좋았을 텐데 이 부분에 대해서는
독자 여러분에게 숙제로 남긴다.
또 다른 숙제는 각 노드에 있는 Map 프로세스에 일을 할당하는 함수를 좀더 효율적인 것으로 짜볼
수 있는데 이걸 한번 생각해 보라고 요청하고 싶다. 단순히 랜덤 하게 선택해서 일을 할당하기
보다는 놀고 있는 프로세스에 할당을 하는 게 가장 좋은 방법이겠고 이를 위해서 각 프로세스의
메시지 큐를 확인 할 수 있으면 될 것이다. 한번 찾아보고 바꿔보고 테스트해보길 바란다.
또한 예제와 같이 메모리 기반으로도 충분히 처리할 수 있는 데이터가 아닌 반드시 디스크를
사용해서 해야 될 정도로 큰 데이터를 처리 해야 된다면 얼랭에 포함되어 있는 디스크 기반
해쉬테이블인 DETS 나 Mnesia 를 사용하는 것도 좋은 방법이 될 것이다. 재미있게도 Mnesia 는 복제
(replication)가 가능하다.(Hadoop 과 같은 데이터 복제(replication)이(가) 간편하게 가능할지도
모르겠다.)

마치며...
사실 짧은 지면에 Map/Reduce 및 분산 얼랭을 설명한다는 거 자체가 불가능 이였을지도 모르겠다.
하지만 둘 중에 하나만 독자들이 이해를 한다면 다른 하나를 다시 찾기에는 그리 오랜 시간이 걸리지
않을 거라 생각한다.
만일 이 섹션을 이해하기 힘든 독자 분들에게는 프로그래밍 얼랭(Programming Erlang)책의 10 장과
20 장을 참고하시는걸 추천한다.
필자는 이런 분산얼랭의 특징 때문에 기계학습(Machine Learning) 어플리케이션을 구현함에 있어서
얼랭을 사용했었다. 물론 지금까지 수십 대의 노드를 사용해야 할 정도로 큰 데이터를 학습시켜 보지
않았지만 얼랭은 잠재력 있는 최적의 선택이었다고 생각한다.
멀티코어, 대용량 데이터의 시대를 맞이하여 앞으로도 얼랭은 아마도 최적의 선택이 되지 않을
듯싶다. [이달의 디스크 : dist_mapreduce.zip]

You might also like