Hard skills/Data Engineering (5)

하둡과 응용 프레임워크 3) 하둡 기반 응용 프로그램 

http://ricksnursery.com/software-configuration-management-tools/

 

하둡기반 응용프로그램의 분류 

1) Databases/Stores

Avro : 하둡 맵리듀스 컨텍스트에서의 데이터 구조 => 효율적인 데이터 프로세싱 가능

HBase : 분산 비관계형 데이터베이스 (non-relational DB)

Cassandra : 분산 데이터 관리 시스템 (data management system)

 

2) Querying

Pig : HDFS 위의 큰 데이터셋을 분석하는 플랫폼

Hive : 큰 데이터셋 관리 및 querying

Impala : Hadoop file format 에 querying (고성능, low-latency) 

Spark : General processing engine fof streaming, SQL, machine learning, graph processing

 

3) Machine Learning, Graph Processing

Graph : Iterative graph processing using Hadoop framework

Mahout: Framework for machine learning applications using Hadoop, Spark

Spark : General processing engine fof streaming, SQL, machine learning, graph processing

 

응용 프로그램 예

1) Apache Pig

Apache Pig 는 data processing 을 위한 플랫폼으로 Pig Latin 이라는 high-level language 를 통해 HDFS 데이터에 접근한다. 이 때, execution environment 로 Map Reduce, Tez 등을 가질 수 있다. 또한 in built operation (minimum, average 와 같은) 을 다양하게 지원한다. 이러한 다양한 function 을 지원하면, 여러가지를 Pig 환경에서 실행할 수 있기 때문에 더욱 효율적이라고 할 수 있다. Pig 의 활용분야는 다음과 같다.

 

1) Extract, Transform, Load (ETL)

2) Manipulating, analyzing "raw" data 

 

예제 코드

hdfs dfs -put /etc/passwd /user/cloudera
pig -x mapreduce

cloudera VM 에서 다음과 같은 명령어를 입력하면 pig 의 interactive shell 인 grunt 로 접속하게 된다. 위 커맨드는 /etc/passwd 폴더를 hdfs 에 저장하고, map reduce 를 execution engine (back-end) 으로 해서 pig 를 실행한다는 뜻이다. 

grunt > A = load '/user/cloudera/passwd' using PigStorage(':');
grunt > B = foreach A generate $0, $4, $5 ; 
grunt > dump B;

load 명령어를 통해 해당 file 을 hdfs 에서 load gkrh, foreach 에서 subset 을 extract 한다 (각 line 마다, 1,5,6번째 컬럼을 가져옴). 

결과는 /user/cloudera/passwd 에 저장된 정보의 일부를 위 그림처럼 출력하게된다. 결과를 파일형태로 저장하기 위해서는 아래와 같은 명령어를 수행할 수 있다. 

grunt > store B into 'userinfo.out'

 

2) Apache Hive

Apache Hive 는 HDFS 에 대한 SQL interface 인 HiveQL 을 통해 데이터를 manage, query 하는 플랫폼이다. 이러한 과정을 beeline 이라는 command line interface 를 통해 interactive 하게 수행할 수 있다. Hive 는 기본적으로 Data warehouse software 이다. execution environment 로는 Map reduce, Tez, Spark 를 가질 수 있다. HDFS, HBase 에 저장된 데이터를 다룰 수 있고, mapper, reduce 를 customization 할 수 있다. 

 

Hive 의 활용분야는 아래와 같다.

 

1) Data mining, Analytics

2) Machine Learning

3) Ad hoc analysis

 

예제 코드

hdfs dfs -put /etc/passwd /tmp/
beeline -u jdbc:hive://

앞선 pig 예제와 마찬가지로 /etc/passwd 를 hdfs 에 저장하고 beeline 커맨드를 실행한다. 

CREATE Table userinfo (uname STRING, paswd STRING, uid INT, gid INT, fullname STRING, hdir STRING, shell STRING) ROW FORMAT DELIMITED FIELDS TERMINATED BY ':' STORED AS TEXTFILE;

LOAD DATA INPATH '/tmp/passwd' OVERWRITE INTO TABLE userinfo;

SELECT uname, fullname, hdir FROM userinfo ORDER BY uname;

위 커맨드는 userinfo 라는 table 을 생성하고, 파일로부터 데이터를 읽어들여 select 문을 통해 출력하는 예제이다. 

 

본 예제에서는 beeline command 를 사용했지만 이외에도 Hive command line interface, Hcatalog, WebHcat 도 있다. 

 

3) Apache HBASE

Hbase 는 scalable data store 이며 non-relational distributed databased 이다. HDFS 위에서 동작하며 In-memory operation 을 제공한다. 이는 cache 와 같은 개념이기 때문에 더 빠르게 같은 일을 처리할 수 있다. Hbase 의 특징은 1) replication 2) security 3) SQL like access 이다. 

 

예제 코드

- 테이블 생성

create 'usertableinfo', {NAME=>'username'}, {NAME=>'fullname'}, {NAME=>'homedir'}

- 데이터 삽입

put 'userinfotable', 'r1', 'username','vcsa'

- 데이터 조회 : 모든 컬럼, 특정 컬럼 조회

scan 'userinfotable'
scan 'userinfotable' , {COLUMNS=>'fullname'}

 이 예제는 Hbase Shell 을 이용할 때이지만, Hbase map reduce, Hbase api, Hbase external API 등을 이용할 수도 있다. 

하둡과 응용 프레임워크 2) 하둡 실행 환경 (YARN, Tez, Spark)

이전 포스팅에서 Map reduce 프레임워크의 한계에 대해 설명하고, 이를 보완하기 위하여 YARN, Tez, Spark 등이 사용된다는 것에 대해 간단하게 언급하였다. 하둡 아키텍쳐에 대해 다시 remind 를 해보자. 

 

Hadoop architecture

위 그림과 같이 데이터가 node 에 분산되어 저장되어있고, task 가 data node 에 할당되어 처리를 한 후에, 이 결과를 merge 하는 것이 하둡이 어떤 job 을 실행하는 방법이다. 이것이 이전 포스팅에서 언급한 '계산을 데이터로 보내는 것' (computation to data) 의 개념이라고 볼 수 있다. 이러한 방법은 불가능한 것을 가능할 수 있고, 무엇보다 더욱 효율적으로 (빠르게) job 을 처리할 수 있다. 병렬적으로 수행할 수 있고, 데이터의 이동이 최소화되기 때문이다. 하지만 문제는, MapReduce execution framwork 는 Map reduce paradigm 을 구현할 수 있는 application 에만 유용하다. 어떤 application 이 map reduce 를 통해 구현할 수 없는 경우에 문제가 생긴다. 이를 보완하기 위하여 YARN, Tez, Spark 등이 사용된다. 

 

Next Generation Execution Framworks

 

 

YARN 은 기본적인 Hadoop 의 execution engine 이다. 이 위에 YARN 위에서 동작하는 HBase 와 같은 application 이 있다. 또한 YARN 위에서는 Tez 와 Spark 와 같은 다른 framework가 올라갈 수도 있다. Pig 나 Hive 같은 application 은 TEZ 와 Spark (Hive 의 경우 TEZ와 Spark 모두 이용) 위에서 동작한다. 또한 Spark 는 YARN 없이도 동작할 수 있는 execution framework 이다. 

 

Tez 의 특징은 dataflow graph 를 지원하고, custom data type 을 지원한다. 따라서 맵 리듀스 프레임워크에서 처럼 모든 데이터의 입출력이 key-value pair 로 이루어져야하는 제약이 없다. Tez 를 활용하는 것의 장점은 자원을 효율적으로 관리하고 복잡한 태스크를 DAG (directed acyclic graph) 를 활용해서 실행할 수 있다는 것이다.  

 

Hive on Tez example 

Hive 는 backend execution engine 으로 Tez 를 이용하는 것을 지원한다. 예를 들어 아래 코드를 보자.

SELECT a.vendor, COUNT(*), AVG(c.cost) FROM a 
JOIN b ON (a.id = b.id)
JOIN c ON (a.itemid = c.itemid)
GROUP BY a.vendor

1) Original Hadoop MapReduce 를 사용한 해법

위의 쿼리는 여러개의 map reduce job 으로 나뉘어진다. 하나의 map reduce job 이 다른 job 의 인풋이 되고, 이것들이 조합이 되어 최종적인 쿼리의 결과를 가져올 수 있다. 

 

2) Tez 를 사용한 해법

Tez 를 사용하면 조금 더 간단하게 같은 작업을 수행할 수 있다. original map reduce 만을 사용했을 때와의 차이점은 intermediate map task 가 없다는 것이다. map reduce 의 경우 원래는 결과를 hdfs 에 저장하도록 되어있는데, 그러지 않고 데이터를 재사용함으로써 graph 를 간편화할 수 있고 이를 통해 효율적으로 같은 job을 수행할 수 있는 것이다. 

 

Spark 의 경우 advanced DAG execution engine 이며 cyclic data flow 를 지원한다. 또한 in-memory computing 을 지원한다. 우선 데이터 처리를 memory 위에서 할 수 있기 때문에 매우 빠르고, DAG 사이에 데이터 공유도 가능하다는 장점이 있다. 또한 Java, Scala, Python, R 언어를 지원하기 때문에 범용성이 높은 execution engine 이라고할 수 있다. 예를 들어 Spark python interface 를 통해 Logistic regression 을 수행하는 코드를 확인해보자. 

points = spark.textFile(...).map(parsePoint).cache()
w = numpy.random.ranf(size = D) # current separating plane 
for i in range(ITERATIONS):
  gradient = points.map(
  	lambda p: (1 / (1 + exp(-p.y*(w.dot(p.x)))) - 1) * p.y * p.x 
    ).reduce(lambda a, b: a + b)
  w -= gradient
print "Final separating plane: %s" % w

original map reduce 는 iterative data process 에 적용하기 매우 힘들다는 단점이 있었다. 바로 위 코드에서 iteration 이 등장하는데, 같은 데이터셋을 이용해서 gradient 를 여러번 반복해서 구해서 weight 를 업데이트하는 logistic regression 의 training 과정이다. 우선 같은 데이터셋을 여러번 반복해서 사용하기 때문에 .cache() 함수를 통해 데이터를 RAM 에 저장시킨다.  위 코드는 spark 공식 홈페이지의 예제 (http://spark.apache.org/examples.html)인데 original map reduce 와 100배 정도의 속도차이가 난다고 한다. 이러한 이유로 in-memory computing 이 가능한 경우 선호된다. 이 링크에서는 다양한 Spark 예제를 확인할 수 있다. 또한 spakr 는 Machine learning 을 위한 라이브러리도 제공하기 때문에 이를 직접 구현하지 않고도, high-level interface 를 사용할 수도 있다 (https://spark.apache.org/docs/2.1.0/ml-classification-regression.html).

 

References

코세라 - Hadoop Platform and Application Framework 강의를 참고하였습니다. 

Docker 를 통한 Hive 실습환경 구축 및 간단 예제

 

본 포스팅에서는 Hive 핵심정리 (다융 두 저, 에이콘 출판사) 의 3장 예제를 Docker 를 이용해 실습해 보았다. 

 

1. Docker 설치 

2. Apache Hive 2.3.2 docker container 다운로드 (https://github.com/big-data-europe/docker-hive)

3. "docker-compose.yml" 파일에서 코드 샘플 경로 마운팅 (C:\workspace\HiveBook\HiveEssentials-master\scripts 을 container 의 /HiveBook 폴더로 마운트) 샘플 코드는 Hive 핵심정리 (에이콘 출판사) 샘플코드 (https://github.com/willddy/HiveEssentials)를 활용하였음.

 

- 샘플코드를 container 에 mount 하기 위해서는 아래 hive-server 에서 volumes 으로 경로를지정하면됨

version: "3" 

services: 
  namenode: 
    image: bde2020/hadoop-namenode:2.0.0-hadoop2.7.4-java8 
    volumes: 
      - namenode:/hadoop/dfs/name 
    environment: 
      - CLUSTER_NAME=test 
    env_file: 
      - ./hadoop-hive.env 
    ports: 
      - "50070:50070" 
  datanode: 
    image: bde2020/hadoop-datanode:2.0.0-hadoop2.7.4-java8 
    volumes: 
      - datanode:/hadoop/dfs/data 
    env_file: 
      - ./hadoop-hive.env 
    environment: 
      SERVICE_PRECONDITION: "namenode:50070" 
    ports: 
      - "50075:50075" 
  hive-server: 
    image: bde2020/hive:2.3.2-postgresql-metastore 
    env_file: 
      - ./hadoop-hive.env 
    environment: 
      HIVE_CORE_CONF_javax_jdo_option_ConnectionURL: "jdbc:postgresql://hive-metastore/metastore" 
      SERVICE_PRECONDITION: "hive-metastore:9083" 
    ports: 
      - "10000:10000" 
    volumes: 
      - C:\workspace\HiveBook\HiveEssentials-master\scripts:/HiveBook 
  hive-metastore: 
    image: bde2020/hive:2.3.2-postgresql-metastore 
    env_file: 
      - ./hadoop-hive.env 
    command: /opt/hive/bin/hive --service metastore 
    environment: 
      SERVICE_PRECONDITION: "namenode:50070 datanode:50075 hive-metastore-postgresql:5432" 
    ports: 
      - "9083:9083" 
  hive-metastore-postgresql: 
    image: bde2020/hive-metastore-postgresql:2.3.0 
  presto-coordinator: 
    image: shawnzhu/prestodb:0.181 
    ports: 
      - "8080:8080" 

volumes: 
  namenode: 
  datanode:

 

4. hive-server 실행

docker-compose up -d
docker-compose exec hive-server bash

 

docker-compose up -d 는 각각의 docker-compose.yml 에 위치한 container 를 background 실행하는 명령어

 

The docker-compose up command aggregates the output of each container (essentially running docker-compose logs -f). When the command exits, all containers are stopped. Running docker-compose up -d starts the containers in the background and leaves them running.

 

Command Line Interface (cli) 에서 실행

 

1. beeline으로 실행하기

 

/opt/hive/bin/beeline -u jdbc:hive2://localhost:10000

 

2. hive 로 실행하기

hive

 

Hive cli 의 경우 하이브 클라이언트는 하이브가 설치된 동일한 장비여야한다. 하지만 비라인의 경우 하이브 서버를 JDBC 커넥션으로 연결하고 클라이언트와 동일 장비에 하이브 라이브러리를 설치할 필요가 없다. 따라서 비라인은 하둡 클러스터 바깥에서 원격으로 실행할 수 있다. 이외의 다양한 hive client 들에 대한 설명은 https://cwiki.apache.org/confluence/display/Hive/HiveServer2+Clients에서 찾아볼 수 있다. 

 

예를 들어, beeline 으로 실행하면 아래와 같은 명령어와 함께 beeline 커맨드가 실행된다.

 

Connecting to jdbc:hive2://localhost:10000 
Connected to: Apache Hive (version 2.3.2) 
Driver: Hive JDBC (version 2.3.2) 
Transaction isolation: TRANSACTION_REPEATABLE_READ 
Beeline version 2.3.2 by Apache Hive 
0: jdbc:hive2://localhost:10000>

 

sample code 를 mounting 했기 때문에 아래 경로에서 파일을 확인할 수 있다.

 

/HiveBook/Chapter_03/employee.txt

더보기

Michael|Montreal,Toronto|Male,30|DB:80|Product:DeveloperLead
Will|Montreal|Male,35|Perl:85|Product:Lead,Test:Lead
Shelley|New York|Female,27|Python:80|Test:Lead,COE:Architect
Lucy|Vancouver|Female,57|Sales:89,HR:94|Sales:Lead

Table 생성 및 데이터 삽입

CREATE TABLE employee
(
  name string,
  work_place ARRAY<string>,
  sex_age STRUCT<sex:string,age:int>,
  skills_score MAP<string,int>,
  depart_title MAP<STRING,ARRAY<STRING>>
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '|'
COLLECTION ITEMS TERMINATED BY ','
MAP KEYS TERMINATED BY ':';

--Verify tables creations run in Beeline
!table employee

--Load data
LOAD DATA LOCAL INPATH '/HiveBook/Chapter_03/employee.txt' OVERWRITE INTO TABLE employee;

--Query the whole table
SELECT * FROM employee;

하둡  플랫폼과 응용 프레임워크 - Hadoop Basic module


하둡은 Doug Cutting and Mike Cafarella 에 의해 2005 년에 만들어졌다. 하둡의 핵심 아이디어는 '데이터를 계산하는 것' (data to computation)이 아니라 '계산을 데이터로 보내는 것' (computation to data) 이다.  Hadoop 의 핵심은 다음과 같다. 


Scalability : 어떠한 클러스터 환경에서도 적용 가능하다. 

Reliability : 분산 환경에서의 근본적인 가정은 Hardware 의 고장이 자주 생긴다는 것이다.  이것은 당연한 것이고, Hadoop 은 이를 핸들링하는 방법을 제공한다. (이를 Resilence 라고 부르기도한다.)


Hadoop distributed file system (HDFS) 와 Map reduce framework 는 기존의 google 의 computing system 과 file system 을 기반으로 만들어졌다. 


Hadoop 의 기본 모듈


1. Hadoop Common : 다른 module 이필요한 library, utility 를 갖고 있다. 

2. Hadoop Distributed File system : big data 를 클러스터에 분산해서 저장할 수 있는 파일 시스템이다. 

3. Hadoop YARN : 클러스의 resource management platform 이다. 

4. Hadoop MapReduce :  hadoop 의 분산 컴퓨팅 프레임워크다. 


HDFS

: Distributed, scalable, and portable file system written in Java for the Hadoop framework. 


일반적인 window, linux file system 은 C나 C++ 로 쓰여져있는데, HDFS 는 hadoop framework 를 위해 Java 로 만들어진 file system 이다. 각각의 HDFS 는 GB 에서 PB 에 이르는 매우 큰 파일을 저장한다. Data node 를 Replication 함으로써 reliability 를 확보한다. 


Hadoop 1.0 vs Hadoop 2.0



Hadoop 2.0 에서 바뀐 점은 YARN 이라는 cluster resource management 모듈이 생긴 것이다. 또한 Global manager 가 아니라 per application manager 로 바뀌었다.YARN 은 Map reduce 에 한정되지 않게 hadoop cluster 을 강화시키기 위한 목적으로 scalability 와 관련이 있다. YARN 은 cluster의 resource 를 capacity, guarantees, fairness 와를 기준으로 활용도를 향상시키도록 만든다. Hadoop 2.0에서는 Map reduce 뿐 아니라 graph process 나 iterative modeling 과 같은 다양한 프로그래밍 방법을 적용할 수 있다. 




Hadoop "Zoo" 



HDFS 와 Map reduce 에 대해서는 어떤것인지 다루었다. 그런데 그 위에서 작동하는 Oozie, Pig, Hive 는 무엇인가? 이러한 Hadoop eco system 은 Google 의 기술 스택에서 출발했다. Chubby, MapREduce, MySQL Gateway, Bigtable 과 같은 구글의 original 기술 스택을 기반으로 hadoop eco system 이 발전했다. 아래는 구글과 페이스북의 데이터 관련 기술 Stack 의 예시이다. 




맵리듀스와 하둡 execution environment


맵리듀스는 Job tracker, task tracker 로 나뉘며, Job tracker 는 master node (name node) 에 task tracker 는 data node 에서 실행된다. HDFS 환경에서 맵리듀스의 한 가지 장점은 실제 computation 이 각각의 data node 에서 그 node 에 있는 data 를 통해서 처리되기 때문에 data 이동을 최소화할 수 있다는 것이다. 또한 하나의 노드에 fitting 이 되지 않는 데이터를 처리할 수 있다. (맵리듀스 관련 이전 포스팅 보기) 하지만 맵리듀스는 Java 를 기반으로 규약에 맞게 프로그래밍을 한 후에 job 을 submission 하는 방식으로 구동되기 때문에 매번 이러한 프로그램을 만들어야한다는 부담이 있다. 따라서 맵 리듀스를 내부적으로 구현한 어플리케이션이 등장했다. 하지만 또 문제는 맵리듀스 만으로 해결할 수 없는 문제가 있을 수 있다는 것이다. 대부분의 데이터 프로세싱이 맵리듀스 방법을 통해 해결 가능했으나, 아닌 문제도 많았다. 


예를 들어 다음과 같다. 


1) Interactive data exploration : 탐험적 데이터 분석을 하는 경우 데이터로부터 결과를 구하는 작업을 여러분 수행한다. 그때마다 Map reduce 방법을 이용해서 데이터를 불러오는 것은 비효율적이다.  

2) Iterative data processing : 데이터 프로세싱을 반복적으로 수행하는 경우도 마찬가지로 맵리듀스 방법이 적절하지 못하다. 이와 같은 상황에서 메모리에 데이터를 불러온 후에 이를 통해 프로세싱을 하는 편이 더욱 효율적일 것이다. 


이러한 문제점을 해결하고자 나온 것이 YARN, Tez, Spark 와 같은 next generation execution framework 이다. 이러한 프레임워크에서는 task 의 복잡한 Directed acyclic graph (DAG) 를 지원하고, data 의 in memory processing 을 지원한다. 다음 포스팅에서는 YARN, Tez, Spark 를 다루어보려고한다.


References

코세라 - Hadoop Platform and Application Framework 강의를 참고하였습니다. 

Hadoop MapReduce 파이썬 구현 (단어 빈도수 세기)

 

Map-Reduce 란?

 

본 포스팅에서는 하둡이 분산처리를 하는 방법인 맵리듀스를 파이썬으로 구현하는 방법을 간단하게 다루겠습니다. 하둡은 자바로 코딩되어 있지만 파이썬, C++ 등의 언어를 이용해서 하둡을 이용할 수 있습니다. 가장 간단하게는 파이썬 코드를 Jython 을 이용해 자바의 jar 파일로 만드는 것인데, 이것은 매우 불편합니다. 특히, Jython 에서 제공하지 않는 파이썬의 기능, 패키지를 이용할 때 문제가 됩니다. 그래서 하둡에서는 standard input, standard output 을 인터페이스로해서 파이썬과 같은 다른 언어에서도 하둡을 이용하는 방법을 제공하고 있습니다. 이번 포스팅에서는 파이썬으로 맵리듀스 코드를 구현하고 테스트 해보는 것을 정리하였습니다. 맵리듀스의 프로세스는 아래와 같습니다. 

 

출처 -  http://www.admin-magazine.com/HPC/Articles/MapReduce-and-Hadoop

 

맵 리듀스는 기본적으로 Split -> Map -> Shuffle -> Reduce 의 절차를 갖습니다. 여기서 Split-Map을 합쳐서 맵 태스크, Shuffle-Reduce 를 합쳐서 리듀스 태스크라고도 부릅니다. Split 은 인풋데이터를 쪼개서 인풋을 키-쌍 값으로 만들어주는 작업이고, Map 은 키-쌍 값을 인풋으로 받아 list(키-값 쌍)을 내보냅니다. Shuffle 에서는 list(키-값 쌍) 을 인풋으로 받아 키-값 쌍을 내보내고 Reduce 에서는 Shuffle 의 결과인 키-값 쌍 입력으로 받아 마지막으로 list(키-값 쌍) 을 내보냅니다. 

 

Map-Reduce 의 인풋/아웃풋

 

 

 Input

Output 

 Split

 텍스트

 <k1, v1>

 Map

 <k1, v1>

 list (<k2, v2>)

 Shuffle

 list (<k2, v2>)  <k2, list(v2)>

 Reduce

 <k2, list(v2)>

 list (<k3, v3>)

 

단어 빈도수 세기 예제

 

맵 리듀스는 왜 필요할까요? 예를 들어, 100개의 문서가 있을 때, 이 문서들에서 단어의 빈도수를 세는 프로그램을 작성한다고 해봅시다. 그런데 문서의 크기가 각각 1TB 라서 500GB 램을 갖는 하나의 컴퓨터에서 실행할 수 없다고 해봅시다. 만약, 분산 컴퓨팅에 대한 학습이 안 되어있는 프로그래머라면, 우선 메모리가 충분하지 않으므로 문서의 일부만 불러와서 단어수를 세고, 결과를 어딘가에 저장하고, 메모리에서 지우는 것을 반복하는 것을 생각해볼 수 있을 것입니다. 물론 이것도 좋은 해법이겠지만, 단점은 시간이 오래걸린다는 것입니다. 만약 충분한 수의 컴퓨터가 있다면 문서의 총 크기는 100TB 이므로, 예를 들어, 각각 500GB 의 메모리를 갖는 200개의 컴퓨터를 활용해서 문제를 해결하고자 하는 것이 분산 컴퓨팅이고 분산컴퓨팅에 사용되는 유명한 방법이 바로 맵리듀스라고 할 수 있습니다.

 

단어 빈도수 세기 예제에서의 자료 구조 흐름

 

인풋데이터

We hold these truths to be self-evident. Governments long established should not. Such has been the patient. .....

 

Splitting 결과 (<k1, v1>)

(0, We hold these truths to be self-evident, ..)

(138, Governments long established should not ...)

(256, Such has been the patient ...) 

 

이렇게 나눠진 문서는 각 노드에 정해진 양만큼 할당이 됩니다. 맵 태스크가 하는 일은 이 키-값 쌍을 인풋으로 받아 다음과 같은 리스트를 만들어주는 것입니다. 총 100개의 문장이라면 다음과 같은 100개의 리스트가 생깁니다. 

 

Map 결과 (list (<k2, v2>))

('We' : 1, 'hold' : 1 ...) 

('Government' : 1, 'long' : 1 ...)

('Such' : 1, 'has', 1 ...)

 

맵 태스크의 결과는 문장별 단어의 빈도수를 갖고 있는 리스트입니다 (여러번 단어가 나오더라도 각각 1을 같습니다.). 이를 키-값 쌍으로 변화하기 위해 셔플링 (Shuffling) 을 수행합니다. 셔플링은 각 단어별로 문장 내에서 찾은 빈도수의 리스트를 갖고 있습니다. 

 

Shuffle 결과 (<k2, list(v2)>)

('We' : [1,1,1])

('Government : [1,1,1])

('Such' : [1,1,1])

 

리듀스 태스크는 셔플링의 결과 키-값 쌍을 입력으로 받아 최종 결과를 출력합니다. 

 

Reduce 결과 (list (<k3, v3>))

('We' : 100)

('Government' : 10)

 

파이썬 Mapper 

 

Map Task 는 인풋 데이터를 적절히 쪼갠 후, 여러 개의 키-값 쌍 (key-value pair) 으로 만드는 과정입니다. 단어 수를 세는 문제에서는 먼저 텍스트를 라인 단위로 나눈 후 (스플릿), 각 라인별로 단어를 쪼개서 출력합니다. Map task 를 실습해보기 위해 hadoop.txt 라는 샘플 텍스트 파일을 만든 후 아래와 같이 실행하였습니다. 

#!/usr/bin/env python
"""mapper.py"""

import sys

# input comes from STDIN (standard input)
for line in sys.stdin:
    # remove leading and trailing whitespace
    line = line.strip()
    # split the line into words
    words = line.split()
    # increase counters
    for word in words:
        # write the results to STDOUT (standard output);
        # what we output here will be the input for the
        # Reduce step, i.e. the input for reducer.py
        #
        # tab-delimited; the trivial word count is 1
        print ('%s\t%s' % (word, 1))

hadoop.txt
0.00MB

cat hadoop.txt | python mapper.py

MapReduce       1
framework.      1
The     1
idea    1
is      1
based   1
on      1
the     1

 

위와 같이 (단어, 1) 의 키-값 쌍이 아웃풋으로 나오게 됩니다. 이제 이 아웃풋을 리듀서에 전달하면 됩니다. 참고로 이 Mapper 가 실제 hadoop 에서 실행될 때, 아웃풋이 셔플 단계에 의해 sorting 되고 적절한 수의 노드에 나누어서 전달 됩니다. 나누어서 전달할 때도 랜덤하게 나누는 것이 아니라 sorting 된 채로 나누어지기 때문에 효율을 최대화할 수 있는 방법으로 데이터를 전달합니다. 하지만 본 포스팅에서 이 부분은 다루지 않습니다. 이 부분은 하둡에 의해 제어되며, 프로그래머는 코드를 통해 맵리듀스의 각 프로세스의 데이터 구조 프로토콜에 맞게 아웃풋을 내주기만 하면됩니다.  

 

파이썬 Reducer 작성

 

Reduce task 는 map task 의 output 을 input 으로 받아 원하는 결과를 집계해주는 작업입니다. 실제로 hadoop 에서는 reduce task 는 shuffle 과 reduce 로 나뉩니다. reduce 작업에 사용되는 노드는 하나일 수도 있지만, 여러개의 노드를 사용하기도 합니다. 이러한 작업이 복잡해보이지만 분산해서 처리하는 일은 hadoop 에서 제어하는 부분에 속합니다. 즉, 코드를 작성하는 사람은 코드의 로직에만 집중하면됩니다. 

#!/usr/bin/env python
"""reducer.py"""

from operator import itemgetter
import sys

current_word = None
current_count = 0
word = None

# input comes from STDIN
for line in sys.stdin:
    # remove leading and trailing whitespace
    line = line.strip()

    # parse the input we got from mapper.py
    word, count = line.split('\t', 1)

    # convert count (currently a string) to int
    try:
        count = int(count)
    except ValueError:
        # count was not a number, so silently
        # ignore/discard this line
        continue

    # this IF-switch only works because Hadoop sorts map output
    # by key (here: word) before it is passed to the reducer
    if current_word == word:
        current_count += count
    else:
        if current_word:
            # write result to STDOUT
            print ('%s\t%s' % (current_word, current_count))
        current_count = count
        current_word = word

# do not forget to output the last word if needed!
if current_word == word:
    print ('%s\t%s' % (current_word, current_count))

 

우선 코드가 잘 작동하는지 알아보기 위해 mapper 의 아웃풋을 '키' 인 단어를 기준으로 아래와 같이 정렬합니다.  

cat hadoop.txt | python mapper.py | sort -k 1

(Figure 1
(typically      1
1).     1
Adobe,  1
Amazon  1
Amazon, 1
An      1
An      1
Apache  1
Automatic       1
Because 1
Elastic 1
Google  1
Google  1
Hadoop  1

 

다음으로 위 명령어의 아웃풋을 piping 을 통해 reducer 에 전달한 후, '값' 인 빈도수를 기준으로 내림차순 정렬하면 최종 결과를 얻게 됩니다. 

cat hadoop.txt | python mapper.py | sort -k1,1 | python reducer.py | sort -k 2 -r

to      8
MapReduce       8
is      7
that    6
in      6
and     6
a       6
on      5
reduce  4
can     4
be      4
are     4
map     3
by      3
The     3
which   2

 

본 포스팅에서는 파이썬을 통해 맵리듀스 코드를 작성하는 방법을 포스팅하였습니다. 하지만 이는 분산 컴퓨팅은 아닙니다. 다음 포스팅에서는 이러한 파이썬 맵리듀스 코드를 하둡으로 실행해서 분산 처리하는 방법 대해 다루어보겠습니다. 

 

References