---
# System prepended metadata

title: Hadoop入門
tags: [Hadoop]

---

[上課講義](https://hackmd.io/@yillkid/r1f7HrFHc/https%3A%2F%2Fhackmd.io%2F%40yillkid%2FHybR5uFr5)
### Hadoop架構:
map Readuce:資料處理
YARN:vm的管理
HDFS:資料儲存

安裝環境

- 我們將在 Ubuntu 22.04 Server 上安裝 Hadoop
 建議您安裝在 VirtualBox 上方便課堂上架設多個節點

VirtualBox 參數 (最低要求):
- Storage: 20 GB
- RAM: 2GB

SSH key pair 登入:
[使用教學](https://www.digitalocean.com/community/tutorials/how-to-configure-ssh-key-based-authentication-on-a-linux-server)
:::warning
說明:因為後面會創造多支線都協助作業，因此改用金鑰的方式做登入，否則每次都會向windos或master請求密碼，造成不便。

原理:先申請金鑰(公鑰與私鑰)，將公鑰交給伺服器端保管，當我對資料有需求時，會拿我的公鑰給伺服器確認，伺服器確認後會給予認證，伺服器會給予內容包，最後用私鑰打開內容包，取得資料。
:::


```
ssh-keygen
```
![image](https://hackmd.io/_uploads/Syw1EzEpa.png)



![image](https://hackmd.io/_uploads/BkCF5HNaa.png)
![image](https://hackmd.io/_uploads/HJ7ocrNTa.png)

Hadoop 由 Java 建構而成，因此要先安裝 Java 的執行環境
```
sudo apt update
sudo apt-get upgrade
sudo apt install default-jdk
```
確認你的 Java 版本
```
java -version
```
安裝SSH
```
sudo apt install ssh
```
新增 hadoop user:
```
sudo adduser hadoop
```
新增 hadoop 為 sudor:
```
sudo visudo
```
找到root ALL=(ALL) ALL
:::success
這一行指的是root這個使用者，
可以在所有的終端機下（第1個ALL），
使用所有人的（第2個ALL），
所有指令（第3個ALL）。
:::
照此邏輯，如法泡製，依序加上
`hadoop ALL=(ALL) ALL`

`%hadoop ALL=(ALL) ALL`
查看是否有開啟sudo權限
`id hadoop`
切換 hadoop user:
`su - hadoop`

設定 SSH 金鑰:
`ssh-keygen -t rsa`
:::success
`ssh-keygen` 是一個用於創建 SSH 密鑰的命令。在這個命令中，`-t` 選項用於指定密鑰的類型。在你提供的例子中，`-t rsa` 意味着你想要創建一對 RSA 類型的 SSH 密鑰。 RSA 是一種常見的密鑰類型，用於 SSH 連接和身份驗證。
:::
設定 SSH 登入權限:
`cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys`
:::info
這條命令用於將你的 SSH 公鑰添加到 `~/.ssh/authorized_keys` 文件中，這樣你就可以使用相應的私鑰進行 SSH 登錄，而無需輸入密碼。

這個命令的具體作用是將你的 SSH 公鑰文件（通常位於 `~/.ssh/id_rsa.pub`）的內容附加到 `~/.ssh/authorized_keys` 文件的末尾。這樣做的目的是將你的公鑰添加到授權的鍵列表中，以便你可以使用相應的私鑰進行 SSH 登錄。

請注意，如果 `~/.ssh/authorized_keys` 文件不存在，這個命令會創建一個新的文件。如果文件已經存在，則會將公鑰內容附加到現有文件的末尾。
:::
開啟authorized_keys (640權限)
```
chmod 640 ~/.ssh/authorized_keys
```
![image](https://hackmd.io/_uploads/Bk_P4jNTp.png)
PS.r:4 w:2 x:1
`ssh hadoop@192.168.255.141 (應該要免密碼)`
下載 Hadoop
`wget https://dlcdn.apache.org/hadoop/common/hadoop-3.3.6/hadoop-3.3.6.tar.gz`
解壓縮安裝包
`tar -xzvf hadoop-3.3.6.tar.gz`
將執行檔移動到特定資料夾上
`sudo mv hadoop-3.3.6 /usr/local/hadoop`
執行看看你下載的 Hadoop 是否正常
`/usr/local/hadoop/bin/hadoop version`
正常的output
```
Hadoop 3.3.6
Source code repository https://github.com/apache/hadoop -r abe5358143720085498613d399be3bbf01e0f131
Compiled by ubuntu on 2022-03-20T01:18Z
Compiled with protoc 2.5.0
From source with checksum 39bb14faec14b3aa25388a6d7c345fe8
This command was run using /usr/local/hadoop/share/hadoop/common/hadoop-common-3.3.6.jar
```
:::danger
出現以下錯誤:'ERROR: JAVA_HOME is not set and could not be found.'
你需要讓 Hadoop 知道你的 Java 執行環境

1.找出你的 Java 路徑:
`readlink -f /usr/bin/java | sed "s:bin/java::"`
2.開啟 ~/.bashrc
`vi ~/.bashrc`
在最後加上
```
export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64
export HADOOP_HOME=/usr/local/hadoop/
export HADOOP_INSTALL=$HADOOP_HOME
export HADOOP_MAPRED_HOME=$HADOOP_HOME
export HADOOP_COMMON_HOME=$HADOOP_HOME
export HADOOP_HDFS_HOME=$HADOOP_HOME
export HADOOP_YARN_HOME=$HADOOP_HOME
export HADOOP_COMMON_LIB_NATIVE_DIR=$HADOOP_HOME/lib/native
export PATH=$PATH:$HADOOP_HOME/sbin:$HADOOP_HOME/bin
export HADOOP_OPTS="-Djava.library.path=$HADOOP_HOME/lib/native"
```
3.載入環境變數
`source ~/.bashrc`

設定你的 Hadoop 設定檔:
`vi $HADOOP_HOME/etc/hadoop/hadoop-env.sh`
加上:`JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64`
:::
### Hadoop 運作模式
- 獨立模式 (Standalone)
只需要一台 host 即可
- 偽分佈模式 (Pseudo-Distributed)
只需要一台 host 即可
- 多機安裝模式 (Fully-Distributed)
需要多個 host 來達到分散 (Distributed) 的效果

### 獨立模式 (Standalone)
啟動:Standalone(預設)
`hadoop jar /usr/local/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar`

查詢pi的使用語法
`hadoop jar /usr/local/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar pi`
![image](https://hackmd.io/_uploads/Hkee2sNT6.png)


使用pi:
`hadoop jar /usr/local/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar pi 16 10000`
:::success
nMaps (任務數 -samples per task）
這是每個 Map 任務將要處理的樣本數。
增加這個值會提高估算的準確性，但同時也會增加計算的時間。
nSamples (樣本總數 - number of tasks）：
這是將要啟動的 Map 任務的總數，進而決定了總共要抽樣的次數。
舉例
如果 Map 任務的數量是 16，而每個任務的樣本數是 10000，則總共的抽樣次數是 160000 次。
:::
查詢grep用法
`hadoop jar /usr/local/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-<hadoop version>.jar grep`
使用grep
```
hadoop jar /usr/local/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar grep ~/input ~/output 'dfs[a-z.]+'
```

### 偽分佈模式 (Pseudo-Distributed)

1.建立 namenode 與 datanode 資料夾:
`mkdir -p ~/hadoopdata/hdfs/{namenode,datanode}`
:::success
Hadoop 依據功能不同，設定檔可分為以下部份：

core-site.xml
hdfs-site.xml
mapred-site.xml
yarn-site.xml
:::
:::warning
設定檔格式:
Property 屬性
name: 屬性名稱
value: 屬性質
description: 屬性描述

用以下格式寫入
```
<configuration>
  	<property>
    	<name>屬性名稱</name>
    	<value>屬性質</value>
	<description>形容描述(不會顯示)</description>
  	</property>
</configuration>
```
:::
:::info
1.core-site.xml
修改core-site.xml:
`vim $HADOOP_HOME/etc/hadoop/core-site.xml`
```
<configuration>
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://localhost:9000</value>
    </property>
</configuration>
```
2.hdfs-site.xml
修改hdfs-site.xmll:
`vim $HADOOP_HOME/etc/hadoop/hdfs-site.xml
`
```
<configuration>
  	<property>
    	<name>dfs.replication</name>
    	<value>1</value>
	<description>)</description>
  	</property>
</configuration>

<configuration>
  	<property>
    	<name>dfs.namenode.name.dir</name>
    	<value>file:///home/hadoop/hadoopdata/hdfs/namenode</value>
	<description>)</description>
  	</property>
</configuration>

<configuration>
  	<property>
    	<name>dfs.datanode.data.dir</name>
    	<value>file:///home/hadoop/hadoopdata/hdfs/datanode</value>
	<description>)</description>
  	</property>
</configuration>
```
3.mapred-site.xml
修改hdfs-site.xmll:
`vim $HADOOP_HOME/etc/hadoop/mapred-site.xml
`
```
<configuration>
  	<property>
    	<name>mapreduce.framework.name</name>
    	<value>yarn</value>
	<description>)</description>
  	</property>
</configuration>

<configuration>
  	<property>
    	<name>yarn.app.mapreduce.am.env</name>
    	<value>HADOOP_MAPRED_HOME=$HADOOP_HOME</value>
	<description>)</description>
  	</property>
</configuration>

<configuration>
  	<property>
    	<name>mapreduce.map.env</name>
    	<value>HADOOP_MAPRED_HOME=$HADOOP_HOME</value>
	<description>)</description>
  	</property>
</configuration>

<configuration>
  	<property>
    	<name>mapreduce.reduce.env</name>
    	<value>HADOOP_MAPRED_HOME=$HADOOP_HOME</value>
	<description>)</description>
  	</property>
</configuration>
```
4.yarn-site.xml
修改yarn-site.xml:
`vim $HADOOP_HOME/etc/hadoop/yarn-site.xml
`
```
<configuration>
  	<property>
    	<name>yarn.nodemanager.aux-services</name>
    	<value>mapreduce_shuffle</value>
	<description>)</description>
  	</property>
</configuration>
```
:::
### 啟動 Hadoop 相關 service

格式化 NameNode:
`/usr/local/hadoop/bin/hdfs namenode -format`
啟動 Hadoop cluster

```
cd  /usr/local/hadoop/sbin/
./start-all.sh
```

確認 Hadoop Cluster 服務正常啟動
JPS (Java process):
```
jps
```
安裝套件:
```
sudo apt install net-tools
```
確定 NameNode 正常:
```
sudo netstat -ntlp | grep 9870
```
確定 Resource Manage 正常啟動
```
sudo netstat -ntlp | grep 8088
```
確定 HDFS 正常啟動
```
sudo netstat -ntlp | grep 9000
```
![image](https://hackmd.io/_uploads/ryDiKINpp.png)
![image](https://hackmd.io/_uploads/BkSiL5H6T.png)


### 如何知道節點們的埠號？

1.使用 jps 確定 Hadoop 程序：
```
jps
```
2.使用 netstat 獲取監聽埠號：
```
netstat -tnlp | grep <PID>
```
![image](https://hackmd.io/_uploads/Hkhy6LB6p.png)

### NameNode web interface (overview)
![image](https://hackmd.io/_uploads/ryFvlwHTp.png)
![image](https://hackmd.io/_uploads/SyxZWPr6T.png)








將資料交給resourcemanerger處理
```
hadoop jar /usr/local/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar pi 4 10000
```
![image](https://hackmd.io/_uploads/H15qtFSpT.png)


### Workshop - HDFS(計算字數)
本地待處理的數據 (sample_text.txt):
```
Hadoop is an open-source framework that allows to store and process big data in a distributed environment.
Hadoop enables data processing over large clusters of computers.
```
1.創建2層資料夾project_wordcount/raw_data
```
hdfs dfs -mkdir -p project_wordcount/raw_data
```
2.將本地資料sample_text.txt送至偽分布project_wordcount/raw_data資料夾之中
```
hdfs dfs -put ~/hadoopdata/hdfs/sample_text.txt project_wordcount/raw_data
```
3.利用wordcount方法將sample_text.txt整理至 project_wordcount/processed_data
```
hadoop jar /usr/local/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar wordcount project_wordcount/raw_data/sample_text.txt project_wordcount/processed_data
```
YARN輸出畫面
![image](https://hackmd.io/_uploads/HysQGsBp6.png)
project_wordcount/processed_data看這邊有沒有產出的檔案
`hdfs dfs -ls project_wordcount/processed_data`
![image](https://hackmd.io/_uploads/rkE3zsBTp.png)
檢視輸出結果
`hdfs dfs -cat project_wordcount/processed_data/part-r-00000`
![image](https://hackmd.io/_uploads/BJhDzsr6a.png)

### 分析resource_manager_log結果的輸出文件，顯示 "ERROR" 日誌條目的統計數據。
1.創建project_log/resourcemanager_erroe_log兩層資料夾
```
hdfs dfs -mkdir -p project_log/resourcemanager_erroe_log
```
2.將本地端的log資料送到偽分布precessed_data
```
hdfs dfs -put /usr/local/hadoop/logs/hadoop-hadoop-resourcemanager-
chang.log project_log/resourcemanager_erroe_log/precessed_data
```
3.將資料文字"ERROE"過濾出來(資料會倒到result_log資料夾)
```
hadoop jar /usr/local/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar grep project_log/resourcemanager_erroe_log/precessed_data /project_log/resourcemanager_erroe_log/result_log "ERROR"
```
4.確認result_log資料夾的資料
```
hdfs dfs -ls /project_log/resourcemanager_erroe_log/result_log
```
5.檢視part-r-00000輸出資料
```
hdfs dfs -cat /project_log/resourcemanager_erroe_log/result_log/part-r-00000
```
![image](https://hackmd.io/_uploads/Bk9Ud2BTT.png)

## 多機安裝模式 (Fully-Distributed)

環境建置

1.架設 3 台節點

VirtualVM (slave only):
CPU: 1 core
RAM: 2GB RAM
硬碟空間: 20GB
請保持 3 台主機都有共同的 username
Eg. username: hadoop
![image](https://hackmd.io/_uploads/Bk5q69UTT.png)

#### 重新設定主機名稱(不然名稱會搞混):
 - Master (於 Master 主機):
 將主管機取名為Master
 ```
 hostnamectl set-hostname master
 ```
 
- Slave1 (於 Slave1 主機):
```
hostnamectl set-hostname slave1
````

- Slave2 (於 Slave2 主機):
```
hostnamectl set-hostname slave2
````

2.建立金鑰
```
ssh-keygen -t rsa
```
3.將公鑰寫入認證
```
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
```
4.到金鑰帳本將主管機與奴隸機的id_rsa.pub都寫入各自的.ssh/authorized_keys
```
cd /home/hadoop/.ssh
```
5.確定這 3 台節點，彼此可以使用 SSH 金鑰登入(不需要輸入密碼)

ps.請設定這 3 台節點的主機名稱 (hostname)，讓彼此可以使用 主機名稱 (hostname) & SSH 金鑰登入
:::success
master -> slave-1
slave-1 -> master
master -> slave-2
slave-2 -> master
slave-1 -> slave-2
slave-2 -> slave-1
:::

#### 綁定主機名稱與 IP 位址:
修改/etc/hosts
```
sudo vim /etc/hosts
```
將名稱與IP位置做連結(主管機與奴隸機都要貼)
```
10.167.218.124 master
10.167.218.176 slave1
10.167.218.201 slave2
```

6.關閉所有主機防火牆:
```
sudo ufw disable
```
示範 (Master to slave-1)
`ssh slave1`

7.全部的主機都需要事先安裝好 hadoop和java
- Master 的主機，請啟動 hadoop
- Slave-* 的主機，全部不啟動 hadoop
Master和slave確認hadoop執行版本
```
/usr/local/hadoop/bin/hadoop version
```
確認java版本
`java -version`


8.Master啟動 Hadoop 相關 service
- 停止Hadoop cluster
`./stop-all.sh`

- 格式化 NameNode:
`/usr/local/hadoop/bin/hdfs namenode -format`
- 啟動 Hadoop cluster
```
cd  /usr/local/hadoop/sbin/
./start-all.sh
```
#### 依照偽分布操作修改設定檔

- core-site.xml:
```
- name: fs.defaultFS
- value: hdfs://master:9000 
```
```
- name: fs.tmp.dir
- value: /usr/local/hadoop/tmp
```  
- hdfs-site.xml:
```
- name: dfs.replication
  - value: 2
```
```
- name: dfs.namenode.name.dir
- value: /usr/local/hadoop/hdfs/name
```
```
 - name: dfs.namenode.data.dir
 - value: /usr/local/hadoop/hdfs/data 
```
- mapred-site.xml:
```
- property
  - name: mapreduce.framework.name
  - value: yarn 
- property
  - name: yarn.app.mapreduce.am.env
  - value:HADOOP_MAPRED_HOME=/usr/local/hadoop
- property
  - name: mapreduce.map.env
  - value:HADOOP_MAPRED_HOME=/usr/local/hadoop
- property
  - name: mapreduce.reduce.env
  - value:HADOOP_MAPRED_HOME=/usr/local/hadoop
```
- yarn-site.xml:
```
- name: yarn.resourcemanager.hostname 
  - value: master
```
```
- name: yarn.nodemanager.aux-services
- value: mapreduce_shuffle 
```
```
- property
  - name: yarn.resourcemanager.resource-tracker.address
  - value: master:8031
- property
  - name: yarn.resourcemanager.scheduler.address
  - value: master:8030 
- propery
  - name: yarn.resourcemanager.address
  - value: master:8032 
- property
  - name: yarn.resourcemanager.admin.address
  - value: master:8033
- property
  - name: yarn.resourcemanager.webapp.address
  - value: master:8088
```
在本機端建立資料夾
```
mkdir -p /usr/local/hadoop/tmp
mkdir -p /usr/local/hadoop/hdfs/name
mkdir -p /usr/local/hadoop/hdfs/data
```
新增以下檔案，以指定 workers
`vim /usr/local/hadoop/etc/hadoop/workers`
內容:
```
slave1
slave2
```

#### 以 SSH 協定佈署到各個位置:
##### 在 Master 機器中，將設定檔複製到 slave1, slave2
將master複製到slave1
```
scp /usr/local/hadoop/etc/hadoop/* hadoop@slave1:/usr/local/hadoop/etc/hadoop/
```
將master複製到slave2
```
scp /usr/local/hadoop/etc/hadoop/* hadoop@slave2:/usr/local/hadoop/etc/hadoop/
```
在 Master 格式化 NodeNode
```
/usr/local/hadoop/bin/hdfs namenode -format
```
在 Master 機器中啟動 Master & Slave1, 2 的服務:
```
/usr/local/hadoop/sbin/start-all.sh
```
在master檢查成員
`jps`
![image](https://hackmd.io/_uploads/BJLllmDT6.png)
在slave1檢查成員
![image](https://hackmd.io/_uploads/BkuVeXP66.png)
在slave2檢查成員
![image](https://hackmd.io/_uploads/HJDvl7Ppp.png)


mkdir -p /usr/local/hadoop/hdfs/data
:::warning
環境變數提醒
env  | grep HOME
![image](https://hackmd.io/_uploads/BJd2RbDp6.png)

![image](https://hackmd.io/_uploads/HJz91fDpp.png)
:::

:::danger
缺少 DataNode:

`/usr/local/hadoop/sbin/stop-all.sh`
`cd  ~/hadoopdata/hdfs/`
`rm -rf *`
`mkdir -p ~/hadoopdata/hdfs/{namenode,datanode}`
`/usr/local/hadoop/bin/hdfs namenode -format`

(三台都要執行)
`rm -rf /tmp/hadoop-*`

(master)
`/usr/local/hadoop/sbin/start-all.sh`
:::

:::danger
缺少NodeManager:

停止運行後 > 格式化 > pkill -9 java >執行程式
:::

### 測試 HDFS
建立test_file_for_test_hdfs.txt
```
touch test_file_for_test_hdfs.txt
```
將test_file_for_test_hdfs.txt推至奴隸機
```
hdfs dfs -put test_file_for_test_hdfs.txt /
```
在 Master/Slave1/Slave2 中列出檔案:
- master
```
hdfs dfs -ls /
```
![image](https://hackmd.io/_uploads/BJla-QD6a.png)
- slave1 slave2
```
hdfs dfs -ls /
```
![image](https://hackmd.io/_uploads/SJQmfQDpT.png)

![image](https://hackmd.io/_uploads/SkwSzQwa6.png)

![image](https://hackmd.io/_uploads/B1w37Xvaa.png)
網址輸入:
`10.167.218.124:9870`
現在你有2個活的奴隸了!!!
![image](https://hackmd.io/_uploads/SJxYXXP6p.png)

ps.查詢hadoop指行檔
```
ll /usr/local/hadoop/sbin/
```
ps2.查看過作情形
```
watch jps
```
執行工作畫面
![image](https://hackmd.io/_uploads/HJO7Tm2a6.png)

### Workshop - Fully-Distributed Deploy (Multi-Host)
![image](https://hackmd.io/_uploads/SyRIpfnpp.png)
步驟一：關閉hadoop(同學A_master)
`/usr/local/hadoop/sbin/stop-all.sh`

步驟二：抓公鑰給別人(同學B_slave)
`cat ~/.ssh/id_rsa.pub`

步驟三：加入別人的公鑰(同學A_master和同學B_slave的公鑰都要互相寫入彼此的驗證裡)
`echo '別人的公鑰' >> ~/.ssh/authorized_keys`

步驟四：修改IP對應名稱，用一個好辨識的名稱即可(定義哪個ip是master，哪個是slave)
`sudo vim /etc/hosts`

步驟五：將別人的slave加入員工清單，把前一步驟好辨識的名稱加入
`vim /usr/local/hadoop/etc/hadoop/workers`

步驟六：移除暫存、設定，格式化hdfs
```
rm -rf ~/hadoopdata/hdfs/*
rm -rf /tmp/*
rm -rf /usr/local/hadoop/tmp/*
rm -rf /usr/local/hadoop/logs/*
rm -rf /usr/local/hadoop/hdfs/name/*
rm -rf /usr/local/hadoop/hdfs/data/*
```
`mkdir -p ~/hadoopdata/hdfs/{namenode,datanode}`

`/usr/local/hadoop/bin/hdfs namenode -format -y`

步驟七：將設定檔匯入對方電腦(以master傳到slave1為例)
`scp /usr/local/hadoop/etc/hadoop/* hadoop@slave1:/usr/local/hadoop/etc/hadoop/`
`scp  /etc/hostshadoop@slave1:/usr/local/hadoop/etc/hadoop/`

步驟八：開啟hadoop(master)
`/usr/local/hadoop/sbin/start-all.sh`
:::danger
當奴隸的主人有轉移時，要將暫時的紀錄清空，不然奴隸會不知道誰是主人

`echo " "">known_hosts`
`echo " "">known_hosts.old`

![image](https://hackmd.io/_uploads/ryszk5apa.png)

:::





