2021. 1. 19. 11:27ㆍ교육과정/KOSMO
키워드 : 하둡 설치 (2) / 하둡 사용자 추가 / 하둡 환경변수 설정 / 하둡과 자바 버전 확인하기 / 하둡 계정에서 자바 사용해보기 / 모든 파일에서 set nu 설정하기 / 노드 간 공개 키 공유하기 (SSH) / xml 파일로 하둡 환경설정 하기 / 네임노드와 데이터노드 생성하기 / 하둡 실행 하기 / 워드카운트 /
****
1. 하둡 설치
(1) 사용자 계정 추가 및 su 명령어로 계정 스위칭 가능하게 하기
① tmp 디렉토리에서 이어서 작업한다.
② 하둡 사용자를 추가하고 비밀번호를 등록한다.
[root@nn01 ~]# useradd hadoop
[root@nn01 ~]# passwd hadoop # 비밀번호 입력시 타이핑이 보이지 않음
③ 루트에서 만든 파일의 권한을 hadoop 계정으로 변경한다.
( -R 옵션을 사용하여 해당 디렉토리의 하위까지 모두 반영되도록 함 )
[root@nn01 tmp]# chown -R hadoop:hadoop /opt/hadoop/
[root@dn01 tmp]# ls -l /opt
total 4
drwxr-xr-x. 3 hadoop hadoop 34 Jan 18 09:37 hadoop
drwxr-xr-x. 3 root root 38 Jan 18 07:05 jdk
drwxr-xr-x. 10 109965 5000 4096 Jan 18 06:49 protobuf-2.5.0
④ hadoop 계정으로 전환한다. 하둡 계정의 홈 디렉토리는 /home/hadoop 이다.
( hadoop 계정을 벗어나려면 logout 또는 exit 를 입력하면 된다. )
[root@nn01 tmp]# su - hadoop
[hadoop@nn01 ~]$
[hadoop@nn01 ~]$ pwd
/home/hadoop
⑤ hadoop 계정에서는 root 계정으로 바로 su 할 수는 없다. ( 보안을 위한 권한상의 문제가 있음 )
[hadoop@nn01 ~]$ su - root
Password:
su: Authentication failure
⑥ hadoop 계정을 로그아웃하여 root 계정으로 전환한다.
[hadoop@nn01 ~]$ logout
[root@nn01 tmp]#
⑦ root 계정 스위치 권한을 변경하기 위해 vi 에디터로 설정파일을 연다.
[root@nn01 tmp]# vi /etc/pam.d/su
⑧ 10~12라인을 주석처리 후 정상적으로 수정되었는데 vi 에디터를 한 번 더 열어 확인한다.
⑨ hadoop 계정으로 su 했다가 다시 root 계정으로 su 가 가능함을 확인할 수 있다.
[root@nn01 tmp]# su - hadoop
Last login: Mon Jan 18 12:56:22 UTC 2021 on pts/0
[hadoop@nn01 ~]$ su - root
Password:
Last login: Mon Jan 18 09:31:33 UTC 2021 from 192.168.56.1 on pts/0
Last failed login: Mon Jan 18 13:03:53 UTC 2021 on pts/0
There was 1 failed login attempt since the last successful login.
(2) sudo 명령어 가능하도록 변경하기
※ sudo 명령어는 root 계정으로 변경하지 않더라도, root 에서 명령을 요청하는 것처럼 만들어준다.
① sudo 명령어가 되지 않는 경우가 있으므로 이에 대한 설정을 하기 위해 root 계정으로 전환한다.
[hadoop@nn01 ~]$ su - root
Password:
Last login: Mon Jan 18 17:40:02 UTC 2021 on pts/1
[root@nn01 ~]#
② visudo 명령어로 sudoers 파일을 열어서 수정한다.
- esc 를 눌러 명령모드에서 :100 을 입력하여 이동 후,
- 100 라인에서 yy 를 입력하여 복사 후, p 를 입력하여 101 라인으로 붙여넣기한다.
- 101 라인의 root 를 hadoop 으로 수정한다.
[root@nn01 ~]# visudo -f /etc/sudoers
③ 비밀번호를 묻지 않고 연결하고자 할 경우에는 추가로 기입한다. ( 비밀번호 매번 묻게 하려고 안 했음 )
hadoop ALL=(ALL) NOPASSWD: ALL ( 계정 등록 )
%hadoop ALL=(ALL) NOPASSWD: ALL ( 그룹 등록 )
(2) 자바 및 Hadoop 환경 변수 추가하기
① ls -al 명령어로 목록을 조회하면 설정과 관련된 . bash_profile 과 . bashrc 파일을 확인할 수 있다.
[hadoop@nn01 ~]$ ls -al
total 16
drwx------. 2 hadoop hadoop 83 Jan 18 09:18 .
drwxr-xr-x. 4 root root 35 Jan 18 07:39 ..
-rw-------. 1 hadoop hadoop 172 Jan 18 09:18 .bash_history
-rw-r--r--. 1 hadoop hadoop 18 Apr 1 2020 .bash_logout
-rw-r--r--. 1 hadoop hadoop 193 Apr 1 2020 .bash_profile
-rw-r--r--. 1 hadoop hadoop 231 Apr 1 2020 .bashrc
② 설정파일 수정시 에러를 방지하기 위해 Multi-Execution Mode 를 해제한다.
③ 첫 번째 노드(OS) 에서 . bash_profile 을 수정하기 위해 vi 에디터로 연다.
[hadoop@nn01 ~]$ vi ~/.bash_profile
④ export PATH 이후에 다음 내용을 각각 붙여넣고 저장한다.
#### HADOOP 2.7.7 start ############
PATH=$PATH:$HOME/bin
export HADOOP_HOME=/opt/hadoop/current
export PATH=$PATH:$HADOOP_HOME/bin
export PATH=$PATH:$HADOOP_HOME/sbin
#### HADOOP 2.7.7end############
#### JAVA 1.8.0 start#############
export JAVA_HOME=/opt/jdk/current
export PATH=$PATH:$JAVA_HOME/bin
#### JAVA 1.8.0 end##############
⑤ 나머지 노드 2곳에서도 동일하게 . bash_profile 을 수정하는 작업을 반복한다.
⑥ 다시 Multi-Execution Mode 로 전환 후,
세 노드 모두의 설정파일의 변경 내용을 반영하기 위해 source 명령어를 사용한다.
[hadoop@nn01 ~]$ source ~/.bash_profile
(3) 자바 및 hadoop 의 버전 확인
① 자바와 하둡의 심볼릭링크가 존재하는지 확인한다. ( 어제 생성했음 )
[root@nn01 ~]# ll /opt/jdk
total 0
drwxr-xr-x. 8 root root 255 Jan 18 06:58 1.8.0_131
lrwxrwxrwx. 1 root root 18 Jan 18 07:05 current -> /opt/jdk/1.8.0_131
[root@nn01 ~]# ll /opt/hadoop
total 0
drwxr-xr-x. 9 hadoop hadoop 149 Jan 18 09:37 2.7.7
lrwxrwxrwx. 1 hadoop hadoop 17 Jan 18 09:37 current -> /opt/hadoop/2.7.7
② 자바의 버전을 확인할 수 있다.
[hadoop@nn01 ~]$ java -version
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)
③ 하둡의 버전을 확인할 수 있다.
[hadoop@nn01 ~]$ hadoop version
Hadoop 2.7.7
Subversion Unknown -r c1aad84bd27cd79c3d1a7dd58202a8c3ee1ed3ac
Compiled by stevel on 2018-07-18T22:47Z
Compiled with protoc 2.5.0
From source with checksum 792e15d20b12c74bd6f19a1fb886490
This command was run using /opt/hadoop/2.7.7/share/hadoop/common/hadoop-common-2.7.7.jar
(4) 자바 사용해보기 ( 자바 설치 확인 )
① hadoop 계정에서 temp 디렉토리 생성 후 해당 디렉토리로 이동한다.
[hadoop@nn01 ~]$ mkdir temp
[hadoop@nn01 ~]$ ls
temp
[hadoop@nn01 ~]$ cd temp
[hadoop@nn01 temp]$
② Temp.java 생성 후 콘솔에 Hello World 를 출력할 수 있도록 작성한다.
[hadoop@nn01 temp]$ vi Temp.java
public class Temp{
public static void main(String [] args){
System.out.println("Hello World");
}
}
③ 자바 파일을 컴파일 한 뒤, 실행하여 Hello World 가 출력되는지 확인한다.
[hadoop@nn01 temp]$ javac Temp.java
[hadoop@nn01 temp]$ java Temp
Hello World
(5) vi 에디터로 연 파일에서 몇 번째 라인인지 항상 보이도록 설정하기
① root 계정으로 변경 후 vi 에디터로 /etc/vimrc 을 연다.
[hadoop@nn01 temp]$ su - root
Password:
Last login: Mon Jan 18 14:11:14 UTC 2021 on pts/0
[root@nn01 ~]# vi /etc/vimrc
② 명령모드에서 ]] 를 입력하여 파일 맨 아래쪽으로 이동 후 set nu 또는 set number 라고 입력 후 저장한다.
③ hadoop 계정으로 다시 돌아온 뒤 temp 디렉토리로 이동하여
vi 에디터로 Temp.java 파일을 열면 라인이 보이는 것을 확인할 수 있다.
[root@nn01 ~]# su - hadoop
Last login: Mon Jan 18 14:14:42 UTC 2
[hadoop@nn01 ~]$ cd temp
[hadoop@nn01 temp]$ vi Temp.java
(6) 비밀번호 없이 각 노드에 접속할 수 있도록 공개 키 공유 하기 ( SSH )
① 홈 디렉토리로 이동한다.
[hadoop@nn01 temp]$ cd ~
[hadoop@nn01 ~]$
② Virtual Box 에서 nn01 노드가 root 계정인지 확인한다.
[root@nn01 ~] #
③ vi 에디터를 사용하여 / etc / hosts 를 연다.
④ 명령모드에서 dd를 입력하여 3줄을 모두 지운 뒤, nn01 / dn01 / dn02 의 정보로 다시 입력 후 저장한다.
⑤ 나머지 dn01 과 dn02 노드에서도 동일하게 반복한다.
⑥ MobaXterm 에서 Multi-Execution Mode로 돌아온 뒤 세 노드에서 동시에 작업한다.
공개키를 만들기 위해 ssh-keygen 을 입력 후 엔터를 반복하면 각기 다른 SSH 키가 만들어짐을 확인할 수 있다.
[hadoop@nn01 ~]$ ssh-keygen
⑦ MobaXterm 에서 Multi-Execution Mode 해제 후
첫 번째 노드부터 자기 자신을 포함하여 총 세 곳에 키를 복사한다.
[hadoop@nn01 ~]$ ssh-copy-id hadoop@dn01
[hadoop@nn01 ~]$ ssh-copy-id hadoop@nn01
[hadoop@nn01 ~]$ ssh-copy-id hadoop@dn02
- 각 명령어를 입력할 때마다 다음과 같은 결과가 출력되고, yes 와 하둡 계정의 비밀번호를 입력해준다.
# 실행결과
[hadoop@nn01 ~]$ ssh-copy-id hadoop@dn01
/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/hadoop/.ssh/id_rsa.pub"
The authenticity of host 'dn01 (192.168.56.102)' can't be established.
ECDSA key fingerprint is SHA256:irl0go0TNAi1o4xBXeRFfWfpoECtcRVVRBtX7ZDiHuE.
ECDSA key fingerprint is MD5:d6:d0:a3:33:65:bc:8a:fe:ea:70:28:0d:5f:53:de:f9.
Are you sure you want to continue connecting (yes/no)? yes
/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
hadoop@dn01's password:
Number of key(s) added: 1
Now try logging into the machine, with: "ssh 'hadoop@dn01'"
and check to make sure that only the key(s) you wanted were added.
⑧ 나머지 노드 2곳에서도 ssh 키를 복사하는 작업을 동일하게 반복한다.
⑨ 각 노드에서 다른 노드로 비밀번호 입력 없이 넘어갈 수 있는지 확인한다.
[hadoop@nn01 ~]$ ssh dn01
Last login: Mon Jan 18 15:31:02 2021
[hadoop@dn01 ~]$ ssh dn02
Last login: Mon Jan 18 15:31:29 2021
[hadoop@dn02 ~]$ ssh nn01
Last login: Mon Jan 18 15:28:16 2021
(7) xml 파일로 하둡 환경 설정하기
core-site.xml | HDFS 와 맵리듀스에서 공통적으로 사용할 환경 정보를 설정한다. core-site.xml 에 설정값이 없을 경우 core-default.xml 에 있는 기본값을 사용한다. |
hdfs-site.xml | HDFS 에서 사용할 환경 정보를 설정한다. hdfs-site.xml 에 설정값이 없을 경우 hdfs-default.xml 에 있는 기본값을 사용한다. |
yarn-site.xml | Hadoop2 에서 자원관리하는 정보를 설정한다. |
mapred-site.xml | 맵리듀스에서 사용할 환경 정보를 설정한다. mapred-site.xml 에 설정값이 없을 경우 mapred-default.xml 에 있는 기본값을 사용한다. |
masters | 보조 네임 노드를 실행할 서버를 설정한다. ★ 마스터노드 (즉, 네임노드)를 설정하는 것이 아니다. |
slaves | 데이터 노드를 실행할 서버를 설정한다. |
hadoop-env.sh ( hadoop1 ) |
하둡을 실행하는 쉘 스크립트 파일에서 필요한 환경변수를 설정한다. JDK 경로, classpath, 데몬 실행 옵션 등 환경변수를 설정한다. |
yarn-env.sh ( hadoop2 에서 추가) |
① MobaXterm 에서 Multi-Execution Mode 해제 후 첫 번째 노드에서 작업을 시작한다.
아래 작업이 끝난 뒤 ⑭에서 나머지 노드에 작업 결과를 동일하게 반영할 수 있도록
hadoop 디렉토리 전체를 복사할 예정이다.
② vi 에디터로 /opt/hadoop/current/etc/hadoop/ 경로에 있는 core-site.xml 파일을 연다.
[hadoop@nn01 ~]$ vi /opt/hadoop/current/etc/hadoop/core-site.xml
③ <configuration> 태그 사이에 다음 내용을 붙여넣는다.
<property>
<name>fs.defaultFS</name>
<value>hdfs://nn01:9000</value>
</property>
④ vi 에디터로 /opt/hadoop/current/etc/hadoop/ 경로에 있는 hdfs-site.xml 파일을 연다.
[hadoop@nn01 ~]$ vi /opt/hadoop/current/etc/hadoop/hdfs-site.xml
⑤ <configuration> 태그 사이에 다음 내용을 붙여넣는다.
<property>
<name>dfs.replication</name>
<value>1</value>
</property>
<property>
<name>dfs.namenode.http-address</name>
<value>nn01:50070</value>
</property>
<property>
<name>dfs.namenode.secondary.http-address</name>
<value>nn01:50090</value>
</property>
<property>
<name>dfs.namenode.name.dir</name>
<value>file:/home/hadoop/hadoop_data/hdfs/namenode</value>
</property>
<property>
<name>dfs.datanode.data.dir</name>
<value>file:/home/hadoop/hadoop_data/hdfs/datanode</value>
</property>
<property>
<name>dfs.namenode.checkpoint.dir</name>
<value>file:/home/hadoop/hadoop_data/hdfs/namesecondary</value>
</property>
<property>
<name>dfs.webhdfs.enabled</name>
<value>true</value>
</property>
⑥ vi 에디터로 /opt/hadoop/current/etc/hadoop/ 경로에 있는 yarn-site.xml 파일을 연다.
[hadoop@nn01 ~]$ vi /opt/hadoop/current/etc/hadoop/yarn-site.xml
⑦ <configuration> 태그 사이에 다음 내용을 붙여넣는다.
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
<property>
<name>yarn.nodemanager.aux-services.mapreduce.shuffle.class</name>
<value>org.apache.hadoop.mapred.ShuffleHandler</value>
</property>
<property>
<name>yarn.resourcemanager.scheduler.address</name>
<value>nn01:8030</value>
</property>
<property>
<name>yarn.resourcemanager.resource-tracker.address</name>
<value>nn01:8031</value>
</property>
<property>
<name>yarn.resourcemanager.address</name>
<value>nn01:8032</value>
</property>
<property>
<name>yarn.resourcemanager.hostname</name>
<value>nn01</value>
</property>
⑧ 리눅스에서 만들어준 템플릿 파일을 사용하여 mapred-site.xml 이라는 파일을 동일한 디렉토리에 생성한다.
[hadoop@nn01 ~]$ cp /opt/hadoop/current/etc/hadoop/mapred-site.xml.template /opt/hadoop/current/etc/hadoop/mapred-site.xml
(+ 참고 ) ⑧의 명령어 입력 전에 ls 명령어를 사용하여 hadoop 디렉토리 내에 리눅스에서 만들어준
mapred-site.xml.template 파일의 존재여부를 사전에 확인할 수 있다.
[hadoop@nn01 ~]$ ls /opt/hadoop/current/etc/hadoop
capacity-scheduler.xml httpfs-env.sh mapred-env.sh
configuration.xsl httpfs-log4j.properties mapred-queues.xml.template
container-executor.cfg httpfs-signature.secret mapred-site.xml.template
core-site.xml httpfs-site.xml slaves
hadoop-env.cmd kms-acls.xml ssl-client.xml.example
hadoop-env.sh kms-env.sh ssl-server.xml.example
hadoop-metrics2.properties kms-log4j.properties yarn-env.cmd
hadoop-metrics.properties kms-site.xml yarn-env.sh
hadoop-policy.xml log4j.properties yarn-site.xml
hdfs-site.xml mapred-site.xml
⑨ vi 에디터로 /opt/hadoop/current/etc/hadoop/ 경로에 생성된 mapred-site.xml 파일을 연다.
[hadoop@nn01 ~]$ vi /opt/hadoop/current/etc/hadoop/mapred-site.xml
⑩ <configuration> 태그 사이에 다음 내용을 붙여넣는다.
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
<property>
<name>mapreduce.jobtracker.hosts.exclude.filename</name>
<value>$HADOOP_HOME/etc/hadoop/exclude</value>
</property>
<property>
<name>mapreduce.jobtracker.hosts.filename</name>
<value>$HADOOP_HOME/etc/hadoop/include</value>
</property>
⑪ vi 에디터를 사용하여 masters 파일에는 nn01 이라고 작성하고, slaves라는 파일에는 dn01 dn02 라고 작성한다.
( 기존에 적혀있던 loacalhost 는 명령모드에서 dd 를 입력하여 지운다. )
[hadoop@nn01 ~]$ vi /opt/hadoop/current/etc/hadoop/masters
[hadoop@nn01 ~]$ vi /opt/hadoop/current/etc/hadoop/slaves
⑫ vi 에디터로 hadoop-env.sh 파일을 연 뒤,
# The java implementation to use 부분에서 export JAVA_HOME=/opt/jdk/current 로 수정한다.
[hadoop@nn01 ~]$ vi /opt/hadoop/current/etc/hadoop/hadoop-env.sh
⑬ vi 에디터로 yarn-env.sh 파일을 연 뒤,
# some Java parameters 부분에서 export JAVA_HOME=/opt/jdk/current 로 수정한다.
[hadoop@nn01 ~]$ vi /opt/hadoop/current/etc/hadoop/yarn-env.sh
⑭ root 계정으로 접속 후 nn01 노드에서 나머지 노드로 hadoop 디렉토리 내의 모든 파일을 복사한다.
( 공개키를 공유했기 때문에 scp 명령어를 사용하거나 winscp 를 사용하여 복사할 수 있다. )
( 디렉토리 내 파일의 소유권 문제 때문에 root 계정으로 변경 후 복사한다. )
[hadoop@nn01 ~]$ su - root
Password:
Last login: Tue Jan 19 02:54:35 UTC 2021 on pts/0
[root@nn01 ~]# scp -r /opt/hadoop/* dn01:/opt/hadoop
[root@nn01 ~]# scp -r /opt/hadoop/* dn02:/opt/hadoop
⑮ dn01 노드와 dn02 노드에서,
심볼릭링크인 current 가 가리키는 기존의 inode 와 파일 복사한 파일의 inode 가 다를 수 있으므로
심볼릭링크의 정확한 연결을 위해 기존 심볼릭링크를 삭제 후 다시 생성한다.
[root@dn01 ~]# rm -rf /opt/hadoop/current
[root@dn01 ~]# ln -s /opt/hadoop/2.7.7 /opt/hadoop/current
⑯ nn01 노드와 dn01 노드와 dn02 노드, 모든 노드에서 각 디렉토리 내 파일의 소유자는 hadoop 계정이었으나,
nn01의 hadoop/ 디렉토리를 통째로 복사해오는 과정에서 root 계정으로 바뀐 상태이다.
따라서, chown 명령어를 사용하여 소유자를 hadoop 계정으로 변경한다.
[root@dn01 ~]# chown -R hadoop:hadoop /opt/hadoop/
(8) 네임노드와 데이터노드가 될 디렉토리 생성하기
① MobaXterm 에서 Multi-Execution Mode 를 해제한 상태에서 진행한다.
root 계정에서 hadoop 계정으로 변경한다.
[root@nn01 ~]# su - hadoop
Last login: Mon Jan 18 15:40:24 UTC 2021 from 192.168.56.101 on pts/3
② nn01 노드에서 namenode 와 namesecondary 디렉토리를 생성하면서
그 부모가 될 디렉토리도 함께 생성되도록 -p 옵션을 준다.
[hadoop@nn01 ~]$ mkdir -p ~/hadoop_data/hdfs/namenode
[hadoop@nn01 ~]$ mkdir -p ~/hadoop_data/hdfs/namesecondary
③ dn01 노드와 dn02 노드에서 datanode 디렉토리를 생성하면서
그 부모가 될 디렉토리도 함께 생성되도록 -p 옵션을 준다.
[hadoop@dn01 ~]$ mkdir -p ~/hadoop_data/hdfs/datanode
(9) Namenode 포맷 및 하둡 실행하기
① nn01 노드의 hadoop 계정에서 진행한다.
② namenode 의 포맷을 진행한다.
( 여기서의 포맷은 설정해둔 xml 의 내용에 따라 HDFS 의 네임스페이스 이미지 정보를 초기화하는 작업이다. )
[hadoop@nn01 ~]$ hadoop namenode -format
③ 포맷이 정상적으로 완료되면 start-all.sh 명령어로 하둡을 구동할 수 있다.
( start-dfs.sh 와 start-yarn.sh 를 두 줄로 입력하는게 정석이지만 번거로운 관계로 all 로 한 번에 실행시킴 )
[hadoop@nn01 ~]$ start-all.sh
This script is Deprecated. Instead use start-dfs.sh and start-yarn.sh
Starting namenodes on [nn01]
nn01: starting namenode, logging to /opt/hadoop/2.7.7/logs/hadoop-hadoop-namenode-nn01.out
dn01: starting datanode, logging to /opt/hadoop/2.7.7/logs/hadoop-hadoop-datanode-dn01.out
dn02: starting datanode, logging to /opt/hadoop/2.7.7/logs/hadoop-hadoop-datanode-dn02.out
Starting secondary namenodes [nn01]
nn01: starting secondarynamenode, logging to /opt/hadoop/2.7.7/logs/hadoop-hadoop-secondarynamenode-nn01.out
starting yarn daemons
starting resourcemanager, logging to /opt/hadoop/2.7.7/logs/yarn-hadoop-resourcemanager-nn01.out
dn02: starting nodemanager, logging to /opt/hadoop/2.7.7/logs/yarn-hadoop-nodemanager-dn02.out
dn01: starting nodemanager, logging to /opt/hadoop/2.7.7/logs/yarn-hadoop-nodemanager-dn01.out
[
④ 하둡이 정상적으로 구동되고 있는지 확인하려면 MobaXterm 에서 Multi-Execution Mode 에서
jps 명령어를 입력하여 네임노드 현황을 확인하면 된다.
( 4자리 숫자는 각 프로세스의 PID 이다. )
( 오류가 있을 경우 log 파일을 확인하여 문제사항을 찾아야 한다. )
[hadoop@nn01 current]$ ls -al /opt/hadoop/current/logs/
⑤ 윈도우 브라우저로 접속하는 GUI 를 통해서도 하둡이 정상적으로 구동중인지 확인할 수 있다.
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
⑥ 하둡을 종료할 때는 반드시 stop-all.sh 명령어를 입력해준다.
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
12. 하둡에서 사용할 자바 파일 작성하기 - 글자 수 세는 시스템
※ 자바에서 작성한 파일을 하둡 분산 처리 시스템을 통해 실행하고자 한다.
(1) 이클립스를 실행하여 hadoop 이라는 새 워크 스페이스를 생성, 실행한다.
Preferences 에서 enc 입력 후 Text file encoding 을 UTF-8 로 변경한다.
(2) Create a Maven project 클릭
(3) Create a simple project (skip archetype selection ) 체크 - Next
(4) Group Id와 Artifact Id (프로젝트 명) 지정 후 Finish 를 클릭
(5) 메이븐 프로젝트가 생성되었음을 확인할 수 있다.
(6) 제공된 xml 파일을 사용하여 pom.xml 파일의 내용을 덮어쓰기 해준다.
우측 하단의 업데이트 로딩이 모두 끝난 뒤 프로젝트에 x 표시가 있을 경우 Build Path 에 에러가 있는지 확인하고,
라이브러리 에러가 없을 경우 Maven Update 를 해주면 x 표시가 사라진다.
< 덮어쓰기 해줄 pom.xml 내용 >
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.kosmo</groupId>
<artifactId>lab1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>lab1</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-mapreduce-client-core -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-core</artifactId>
<version>2.7.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-common -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.7.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-mapreduce-client-jobclient -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-jobclient</artifactId>
<version>2.7.7</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jdk.tools</groupId>
<artifactId>jdk.tools</artifactId>
<scope>system</scope>
<version>1.8</version>
<systemPath>${JAVA_HOME}/lib/tools.jar</systemPath>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformer">
</transformer>
</transformers>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<!-- 메인 클래스 이름을 설정하여 jar로 실행할 때 메인 클래스명을 지정하지 않는다 -->
<!-- <plugin> -->
<!-- <groupId>org.apache.maven.plugins</groupId> -->
<!-- <artifactId>maven-jar-plugin</artifactId> -->
<!-- <version>3.0.2</version> -->
<!-- <configuration> -->
<!-- <archive> -->
<!-- <manifest> -->
<!-- <mainClass>sample.WordCount</mainClass> -->
<!-- </manifest> -->
<!-- </archive> -->
<!-- </configuration> -->
<!-- </plugin> -->
</plugins>
</build>
</project>
(7) 제공된 실습 파일에서 sample 폴더 전체를 복사하여 java 폴더 안에 붙여넣는다.
( 두 곳 모두 같은 곳이므로 아무데나 넣으면 된다. )
(8) 파일에서 단어를 읽고 그 갯수를 세는 프로그램을 만들기 위해 WordCount.java 파일을 연다.
(9) 지금은 간단한 프로그램이라 WordCount 라는 클래스 안에 이너클래스로 MyMapper 와 MyReducer가 존재하지만,
복잡한 프로그램일 경우에는 클래스를 완전히 분리해서 만들 수 있다.
(10) Mapper 와 Reducer 역할을 수행하기 위해 각각에 해당하는 클래스를 상속하고 있다.
public class WordCount {
public static class MyMapper
extends Mapper<LongWritable, Text, Text, LongWritable>{
...
public static class MyReducer
extends Reducer<Text, LongWritable, Text, LongWritable>{
...
(11) 메인함수에서는 설정과 관련된 객체 Configuration 을 얻어온 뒤
인자를 받으면 WordCount 라는 클래스를 수행하면서
첫 번째로 들어오는 단어를 입력 위치로, 두 번째로 들어오는 단어는 출력 위치로 사용하게 된다.
public static void main(String[] args) throws Exception{
Configuration conf = new Configuration();
if (args.length != 2) {
System.err.println("Usage: WordCount <input> <output>");
System.exit(2);
}
....
(12) 메인함수에서 Configuration 객체의 이름을 WordCount 라고 지칭하면서 Job 이라고 지정한다.
Job job = Job.getInstance(conf, "WordCount");
(13) 메인함수에서는 Job 을 각 클래스에 할당하여 작업을 수행하므로, 작업 수행과 관련된 포맷을 지정해준다.
출력시 Key는 Text 이고 Value 는 LongWritable 이다. (LongWritable 은 하둡에서 숫자 표현에 관한 스타일)
job.setJarByClass(WordCount.class);
job.setMapperClass(MyMapper.class);
job.setReducerClass(MyReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(LongWritable.class);
job.setInputFormatClass(TextInputFormat.class);
job.setOutputFormatClass(TextOutputFormat.class);
FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
(14) 메인함수 마지막에는 작업이 다 끝날 때까지 기다리게 하는 스크립트가 위치한다.
job.waitForCompletion(true);
< 메인함수 전체 >
public static void main(String[] args) throws Exception{
Configuration conf = new Configuration();
if (args.length != 2) {
System.err.println("Usage: WordCount <input> <output>");
System.exit(2);
}
Job job = Job.getInstance(conf, "WordCount");
job.setJarByClass(WordCount.class); // 나중에 jar로 실행해야 함
job.setMapperClass(MyMapper.class);
job.setReducerClass(MyReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(LongWritable.class);
job.setInputFormatClass(TextInputFormat.class);
job.setOutputFormatClass(TextOutputFormat.class);
FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
job.waitForCompletion(true);
}
(15) MyMapper 클래스에서 하둡의 자료형을 사용하여 각각의 변수를 선언한다.
LongWritable 은 자바에서의 long 타입, Text 는 자바에서의 String 타입에 해당한다.
public static class MyMapper
extends Mapper<LongWritable, Text, Text, LongWritable>{
// 출력 key, value
private final static LongWritable one = new LongWritable(1);
private Text word = new Text();
(16) Source - Override/Implement Methods 를 클릭하여 Mapper 클래스로부터 map( ) 함수를 오버라이딩한다.
@Override
protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, LongWritable>.Context context)
throws IOException, InterruptedException {
}
(17) MyMapper 클래스에서는 읽어온 파일에서 value 는 하둡의 Text 타입이므로
이후의 작업이 가능하게 만들기 위해, 자바의 String 타입으로 변경한다.
String line = value.toString();
(18) MyMapper 클래스에서는 StringTokenizer 를 사용하여 읽어온 글을 지정한 기준에 따라 잘라낸다.
이번에는 공백을 비롯하여 다음과 같은 특수문자를 만나면 단어로 선별해낸다.
StringTokenizer itr = new StringTokenizer(line, "\t\r\n\f :;,.()<>");
(19) MyMapper 클래스에서 while 반복문을 사용하여 itr 이 더 이상 쪼개지지 않을 때까지
StringTokenizer 를 수행하고 word 라는 변수에 하나씩 뽑아온 결과를 지정한다.
context 를 통해서 key 와 value 를 write 할 때 위에서 지정한 변수명 word와 one 을 사용한다.
while(itr.hasMoreTokens()) {
word.set(itr.nextToken());
context.write(word, one);
}
< MyMapper 클래스 전체 >
public static class MyMapper
extends Mapper<LongWritable, Text, Text, LongWritable>{
// 출력 key, value
private final static LongWritable one = new LongWritable(1);
private Text word = new Text();
protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, LongWritable>.Context context)
throws IOException, InterruptedException {
String line = value.toString();
StringTokenizer itr = new StringTokenizer(line, "\t\r\n\f :;,.()<>");
while(itr.hasMoreTokens()) {
word.set(itr.nextToken());
context.write(word, one);
}
}
}
(20) MyReducer 클래스에서 하둡의 자료형을 사용하여 결과값이 저장될 변수를 선언한다.
private LongWritable result = new LongWritable();
(21) Source - Override/Implement Methods 를 클릭하여 Reducer 클래스로부터 reduce( ) 함수를 오버라이딩한다.
@Override
protected void reduce(Text key, Iterable<LongWritable> values, Reducer<Text, LongWritable, Text, LongWritable>.Context context)
throws IOException, InterruptedException {
}
(22) 인자로 들어오는 values 로부터 데이터를 하나씩 꺼내어 연산을 하도록 변수 sum 과 for 반복문을 작성한다.
이 때, val 은 하둡의 자료형을 가지고 있으므로 get( ) 함수를 사용하여 값을 가져와야 한다.
int sum = 0;
for(LongWritable val : values) {
sum += val.get();
}
(23) 하둡의 자료형을 가진 변수 result 에 sum 의 값을 다시 넣는다.
마찬가지로 get( ) 함수를 사용한다.
result.set(sum);
(24) context 를 사용하여 key 와 result 값을 write 한다.
context.write(key, result);
< MyReduce 클래스 전체 >
public static class MyReducer
extends Reducer<Text, LongWritable, Text, LongWritable>{
private LongWritable result = new LongWritable();
protected void reduce(Text key, Iterable<LongWritable> values, Reducer<Text, LongWritable, Text, LongWritable>.Context context)
throws IOException, InterruptedException {
int sum = 0;
for(LongWritable val : values) {
sum += val.get();
}
result.set(sum);
context.write(key, result);
}
}
< WordCount.java 파일 전체 소스 코드 >
package sample;
import java.io.IOException;
import java.util.StringTokenizer;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
/**
* 파일을 읽어서 단어의 수를 계산하여 그 결과를 파일에 저장
*/
public class WordCount {
public static class MyMapper
extends Mapper<LongWritable, Text, Text, LongWritable>{
// 출력 key, value
private final static LongWritable one = new LongWritable(1);
private Text word = new Text();
protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, LongWritable>.Context context)
throws IOException, InterruptedException {
String line = value.toString();
StringTokenizer itr = new StringTokenizer(line, "\t\r\n\f :;,.()<>");
while(itr.hasMoreTokens()) {
word.set(itr.nextToken());
context.write(word, one);
}
}
}
public static class MyReducer
extends Reducer<Text, LongWritable, Text, LongWritable>{
private LongWritable result = new LongWritable();
protected void reduce(Text key, Iterable<LongWritable> values,
Reducer<Text, LongWritable, Text, LongWritable>.Context context) throws IOException, InterruptedException {
int sum = 0;
for(LongWritable val : values) {
sum += val.get();
}
result.set(sum);
context.write(key, result);
}
}
public static void main(String[] args) throws Exception{
Configuration conf = new Configuration();
if (args.length != 2) {
System.err.println("Usage: WordCount <input> <output>");
System.exit(2);
}
Job job = Job.getInstance(conf, "WordCount");
job.setJarByClass(WordCount.class); // 나중에 jar로 실행해야 함
job.setMapperClass(MyMapper.class);
job.setReducerClass(MyReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(LongWritable.class);
job.setInputFormatClass(TextInputFormat.class);
job.setOutputFormatClass(TextOutputFormat.class);
FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
job.waitForCompletion(true);
}
}
13. 하둡에서 자바 프로젝트 실행하기
(1) 메이븐 자바 프로젝트는 필요한 라이브러리가 윈도우 상에 폴더에 존재하고, 이를 끌어다 사용하기 때문에
하둡으로 프로젝트 파일만 옮기면 구동하지 않는다.
(2) 메이븐에서는 라이브러리 jar 파일까지 모두 프로젝트로 담아서 install 할 수 있다.
프로젝트명 우클릭 - Run As - 7 Maven install 클릭하면 콘솔에 로그가 올라오면서 install 이 실행되기 시작한다.
Maven install 은 import 한 라이브러리들을 모두 포함하여 jar 파일을 생성해준다.
(3) 콘솔 창에 BUILD SUCCESS 라는 메세지와 함께 실행이 성공적으로 이루어지면
좌측 Explorer 창에서 target 폴더 내에 lab1-0.0.1-SNAPSHOT.jar 파일이 생성되었음을 확인할 수 있다.
(4) 파일명을 lab1.jar 로 짧게 수정해준다.
(5) WinSCP 실행 후 nn01 의 세션을 만든 뒤, 로그인한다.
(6) WinSCP 에서 hadoop 계정의 홈 디렉토리에 source 라는 이름의 디렉토리를 생성한다.
마우스 우클릭 - 새로만들기 - 디렉터리
(7) WinSCP 에서 source 디렉토리로 이동 후, 윈도우의 lab1.jar 을 해당 디렉토리로 업로드한다.
(8) MobaXterm 의 nn01 노드 홈 디렉토리에서 source 디렉토리에 방금 업로드한 파일이 존재하는지 확인한다.
[hadoop@nn01 ~]$ ls /home/hadoop/source
lab1.jar
(9) data 라는 이름의 디렉토리 생성 후 해당 경로에 vi 에디터를 사용하여 mydata.txt 파일을 생성한다.
[hadoop@nn01 ~]$ mkdir data
[hadoop@nn01 ~]$ vi data/mydata.txt
(10) mydata.txt 파일 내용을 작성 후 저장한다.
good morning
i love coffee
coffee is good
people loves coffee
(11) HDFS 하둡 명령어는 리눅스 명령어와 거의 동일하되 앞에 hdfs dfs 가 붙는 것이 특징이다.
hadoop fs -cmd [args] |
hadoop dfs -cmd [args] |
hdfs dfs -cmd [args] |
< 하둡 명령어 정리 >
1. 파일목록보기 : ls, lsr
ls : 지정한 디렉토리에 있는 파일의 정보를 출력
lsr : 하위 디렉토리 정보까지 출력
hdfs dfs -ls [디렉토리|파일]
hdfs dfs -lsr [디렉토리|파일]
디렉토리나 파일을 지정하지 않을 경우 해당 계정의 홈 디렉토리를 조회한다.
2. 파일용량파일 : du, dus
du : 지정한 디렉토리나 파일의 사용량을 확인 ( 출력결과 바이트 단위 )
dus : 전체 합계 용량 출력결과
hdfs dfs -du [디렉토리|파일]
hdfs dfs -dus [디렉토리|파일]
디렉토리나 파일을 지정하지 않을 경우 해당 계정의 홈 디렉토리를 조회한다.
3. 파일내용보기 : cat, text
cat : 지정한 파일의 내용을 출력
text : cat은 텍스트파일만 출력하는데 text는 zip 파일 형태로 압축한 파일도 텍스트형태로 출력
hdfs dfs -cat [파일]
hdfs dfs -text [파일]
4. 디렉토리생성 : mkdir
hdfs dfs -mkdir [디렉토리]
이미 존재하는 디렉토리를 생성할 경우 에러 발생
5. 파일복사
(1) put / copyFromLocal : 로컬 파일 시스템의 파일 및 디렉토리를 HDFS의 경로로 복사
hdfs dfs -put [로컬디렉토리|파일] [목적지디렉토리|파일]
hdfs dfs -copyFromLocal [로컬디렉토리|파일] [목적지디렉토리|파일]
(2) get / copyToLocal : HDFS에 저장된 데이타를 로컬 파일 시스템으로 복사
hdfs dfs -get [소스디렉토리|파일] [로컬디렉토리|파일]
hdfs dfs -copyToLocal [소스디렉토리|파일] [로컬디렉토리|파일]
(3) getmerge : 모든 파일의 내용을 하나로 합친 후, 로컬파일 시스템에 단 하나의 파일로 복사
hdfs dfs -getmerge [소스디렉토리|파일] [로컬파일명]
(4) cp : HDFS에서 디텍토리나 파일 복사
hdfs dfs -cp [소스디렉토리|파일] [목적지디렉토리|파일]
6. 파일이동
(1) mv : 디렉토리나 파일을 목적지 경로로 이동
hdfs dfs -mv [소스디렉토리|파일] [목적지디렉토리|파일]
(2) moveFromLocal : put명령어와 동일한 로컬파일 시스템으로 복사된 후 소스 경로의 파일은 삭제된다.
hdfs dfs -moveFromLocal [소스디렉토리|파일] [목적지디렉토리|파일]
7. 삭제
(1) rm : 디렉토리나 파일 삭제된다. 디렉토리인 경우 반드시 비어 있어야 삭제된다.
(2) rm -r : 디렉토리나 파일 삭제된다. 디렉토리인 경우 비어 있지 않아도 삭제된다.
8. 카운트 조회
count : 지정한 경로에 대한 전체 디렉토리 개수, 전체 파일 개수, 전체 파일 크기 출력
hdfs dfs -count [디렉토리|파일]
9. 권한변경
(1) chmod : 지정한 경로에대한 권한 변경
hdfs dfs -chmod 777 sample.csv
(2) chown : 지정한 파일과 디렉토리에 대한 소유권을 변경
hdfs dfs -chown tester:testerGroup sample.csv
sample.csv 파일의 소유자를 tester, 소유그룹을 testerGroup으로 변경
(3) chgrp : 지정한 파일과 디렉토리에 대한 소유권 그룹만 변경
hdfs dfs -chgrp testerGroup sample.csv
sample.csv 파일의 소유그룹을 testerGroup으로 변경
-R : 하위 디렉토리의 정보도 모두 변경
10. 통계정보조회 : stat
hdfs dfs -stat [디렉토리|파일]
11. 휴지통비우기 : expunge
hdfs dfs -expunge
12. 0바이트 파일 생성 : touchz
hdfs dfs -touchz [파일]
(12) 리눅스 파일시스템과 하둡 파일시스템은 구분되어 있으므로,
하둡 명령어를 사용하여 data 디렉토리를 생성하되 부모가 되는 디렉토리도 만들어지도록 옵션을 준다.
하둡 파일시스템에 생성된 디렉토리는 리눅스 명령어로는 접근할 수 없다.
[hadoop@nn01 ~]$ hdfs dfs -ls /input/data
ls: `/input/data': No such file or directory
[hadoop@nn01 ~]$ hdfs dfs -mkdir -p /input/data
[hadoop@nn01 ~]$ hdfs dfs -ls /input/data
[hadoop@nn01 ~]$ ls /input/data
ls: cannot access /input/data: No such file or directory
(13) 리눅스가 가지고 있는 mydata.txt 를 하둡 파일시스템으로 옮기기 위해 하둡의 put 명령어를 사용한다.
[hadoop@nn01 ~]$ hdfs dfs -put ~/data/mydata.txt /input/data
[hadoop@nn01 ~]$ hdfs dfs -ls /input/data
Found 1 items
-rw-r--r-- 1 hadoop supergroup 13 2021-01-18 19:39 /input/data/mydata.txt
(14) yarn 명령어를 사용하여 source 디렉토리에 업로드했던 jar 파일을 통째로 실행한다.
실행시 패키지명과 클래스명, 입력 위치와 출력 위치를 함께 기술하되, 출력 위치는 없는 디렉토리여야 한다.
[hadoop@nn01 ~]$ yarn jar /home/hadoop/source/lab1.jar sample.WordCount /input/data /output/wordcount
(15) 정상적으로 수행되었을 경우, /output/wordcount 디렉토리가 생성되었음을 확인할 수 있다.
[hadoop@nn01 ~]$ hdfs dfs -ls /output/wordcount
Found 2 items
-rw-r--r-- 1 hadoop supergroup 0 2021-01-18 19:45 /output/wordcount/_SUCCESS
-rw-r--r-- 1 hadoop supergroup 16 2021-01-18 19:45 /output/wordcount/part-r-00000
(16) part-r-00000 파일에 jar 파일 실행결과가 저장되어 있으므로 하둡 명령어 중 cat 을 사용하여 확인한다.
[hadoop@nn01 ~]$ hdfs dfs -cat /output/wordcount/part-r-00000
coffee 3
good 1
i 1
is 1
love 1
loves 1
morning 1
people 1
(17) 하둡의 처리 프로세스
10. 워드카운트 실행 후 기존 폴더 삭제하여 초기화 하기
(1) 하둡의 input 디렉토리와 output 디렉토리를 삭제한다.
[hadoop@nn01 ~]$ hdfs dfs -rm -r /output
21/01/19 06:19:02 INFO fs.TrashPolicyDefault: Namenode trash configuration: Deletion interval = 0 minutes, Emptier interval = 0 minutes.
Deleted /output
[hadoop@nn01 ~]$ hdfs dfs -rm -r /input
21/01/19 06:19:15 INFO fs.TrashPolicyDefault: Namenode trash configuration: Deletion interval = 0 minutes, Emptier interval = 0 minutes.
Deleted /input
(2) 하둡에 input/data 디렉토리를 다시 생성한다.
[hadoop@nn01 ~]$ hdfs dfs -mkdir -p /input/data
[hadoop@nn01 ~]$ hdfs dfs -ls /input
Found 1 items
drwxr-xr-x - hadoop supergroup 0 2021-01-19 06:12 /input/data
(3) 워드카운트를 진행할 파일을 HDFS 의 put 명령어를 사용하여 하둡에 복사한다.
[hadoop@nn01 ~]$ hdfs dfs -put ~/data/파일명 /input/data
(4) 필요할 경우 자바 프로젝트를 수정 후 다시 Maven install 하고,
WinSCP를 사용하여 하둡의 source 디렉토리로 jar 파일을 업로드 하여 실행한다.