撰寫 Hadoop MapReduce Program 紀錄
講真的,我不喜歡那隻黃色大象,所以請容許我把它碼掉。
(好像碼的還不錯 ...)
這是一個練習寫在 Hadoop 中執行 MapReduce 程式的紀錄。
關於 Hadoop
這邊不詳細說明與介紹,只提及有關此文章有關之事。
- Hadoop 是一個大型的檔案儲存系統,分散式 DB。
- Hadoop 提供 HDFS 的分散式儲存功能,以及叫做 MapReduce 的數據處理方式。
- 你可以把 MapReduce 的過程想成 multi thread,他開很多分支去做 Map (處理與計算),再把結果全部統整再一起。
如果你用過 BT 或是 IDM 這種下載軟體,那應該可以很好理解 (分支處理再統合起來) - 舉例 : 我想要數整個圖書館的書,Mapper 負責叫一群人數不同書架,Reducer 負責把大家回報的加起來。|
實際你寫 mapper 和 reducer 時,也是要以此方針來考量 input 和 output。
關於本次作業
雖然您可能是因為正在做其他 project 或是遇到其他問題而找到這篇,
但是 ... 不太好意思的是,本篇只記錄完成作業之遇到問題與注意事項
像是架設 Hadoop 這部分,我就沒有實作到,因為我們是用 Docker (直接提取別人建好的 image)。
- 要求 :
- 用 Docker 去跑 Hadoop。
- Write a map reduce application using Python to do the log analysis of apache2 web server access log.
- The output of the application: visits per hour.
- 使用 Python 完成。
- 因為要在 VPS 裡面搞 Hadoop,如果沒有足夠的 Ram 和空間,會直接炸開。
所以我使用本地端的 Docker 去完成,也就是 Windows (with wsl)- WSL : Windows Subsystem for Linux
準備環境過程
這篇是一篇很不錯的例子,在講解 Word Count 使用 MapReduce 去實作。
Writing An Hadoop MapReduce Program In Python
不過我想大部分人都應該看過上面那篇 XD
再來是一些準備 :
- 除非你跟我做同一個作業,不然大可不必用這個 repo ... 恩。
- 作者不是我,這個 Repo 內有什麼 Bug 我也無法修改 ...。
- Docker Desktop Version。
- WSL (不確定是不是必須,但根據官方說法,使用 WSL2 引擎效能會提升。)
- 你常用的 Terminal (在 Windwos 上)
準備環境 : Docker image
如果不是跟我同個作業,這段不用看。
Windows 的 Docker 安裝也沒什麼特別的,就一直按確認就好。
不過,如果沒開啟虛擬化技術,好像會不能跑。
關於這點,請去根據提示開啟 Hyper-V (安裝時其實就會幫你開了)
然後大部分的 CPU 都需要去開起虛擬化技術。
Intel 的就叫 Intel Virtualization Technology,AMD 叫 SVM Mode
例如我的 R7 3700X 就要去 BIOS 開啟 SVM Mode (Enable it)。
跟這篇文章借個圖,大概長這樣。
如何在我的電腦上啟用虛擬化技術(VT)?
裝完 Docker 之後,你可以在任意目錄開啟你的 Terminal,開始下一些關於 docker image 的命令。
關於這個作業,有個特別的點是 ... 在上面的 Repo 內的 Readme 先寫了 Build 某個 image
但其實,作者也有將該 image 上傳至 DockerHub,不用自己 Build。
不過我也有自己 Build 過一次,這邊提一下可能會遇到的問題。
(記得 Build 時要在這個 repo dir ! 該先下載的東西也請照 repo 寫的去找 !)
- hue-4.3.0.tgz 我載不到阿 ?
- 這個你可以找到 Hue 的 repo,同個版本但是是 zip,可以解壓縮後再打包成 .tgz。
- 但過程甚至還有權限問題 ... 有 Link 的關係吧,總之用管理員權限啟動壓縮程式就好。
- 在 Run
docker build -t hadoop3hbase-spark-hive .
時,Hue 會出現很多錯誤。
- 沒錯,在
RUN make apps
會出現很多問題 - 多裝了 java 8 (之前在 VPS 會出現這個問題)
- 但是又會發現少了某個 Compiler。
- 後來直接把跟 Hue 有關的 command 都註解掉了,因為我們也用不到 GUI。
- 會發現缺少
scala-2.11.12.deb
。
wget www.scala-lang.org/files/archive/scala-2.11.12.deb
這行原本是註解掉的,先跑這個再跑一次吧。
這邊放上我自己改過的 Dockerfile,可以參考一下再 build。
1 | # HUE |
建了老半天發現 DockerHub 就能 pull 了,辛酸滿點。
Pull 完之後,執行 docker network create -d overlay --attachable my-attachable-network
會出現
1 | $ docker network create -d overlay --attachable my-attachable-network |
解法 : docker swarm init
再來是按照順序把 image run 起來。
- docker run --hostname=mysql --name mysql --network my-attachable-network -d sdwangntu/hive-metastore-db
- docker run --hostname=hadoop-master --name hadoop-master --network my-attachable-network -d sdwangntu/hadoop3hbase-spark-hive
- docker run --hostname=hadoop-worker --name hadoop-worker --network my-attachable-network -d sdwangntu/hadoop3hbase-spark-hive
如果遇到這樣的 error
1 | $ docker run --hostname=mysql --name mysql --network my-attachable-network -d sdwangntu/hive-metastore-db |
那代表你已經 create 過了,請使用 start 的指令去喚醒他,或是直接用 Docker GUI 介面點擊 start。
小技巧 :
1 | $ docker ps -l |
建立完環境,network 弄好了,container 也都在 run 了 (一個 DB,一個 master,一個 worker)
如果我想要去做一些事情,這時再開一個 container 去搞。
docker run --hostname=hadoop-dev --name hadoop-dev -v $(pwd):/home --network my-attachable-network -d sdwangntu/hadoop3hbase-spark-hive
這個叫 hadoop-dev 的 container 就是我們要拿來和整個 Hadoop 系統溝通的地方。
值得注意的是,這指令有做 volumn 的連接,但是我在 windows 不起作用,也許有某些地方出問題。
現在有個問題,我們怎麼"進去" 這個 container ?
用過 Docker 的應該知道就是 exec。
但在 windows 上會遇到一些問題,看 Log 說故事。
1 | $ docker container exec -it hadoop-dev /bin/bash |
進來之後做一些測試
1 | root@hadoop-dev:/# yarn node -list |
也順便跑了 hdfs dfsadmin -report
和 yarn jar /opt/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.2.jar pi 4 10000
妥。
MapReduce 撰寫範例
這兩個程式我都有寫 read_from_file 的 function,用於 Local 測試。
不過,善用指令也能直接免寫檔測試 :
cat access.log | python mapper.py | python reducer.py
|
的功用就是會把 stdout 給 pipe 到下一個階段之中 (作為 stdin )。
請注意,檔案內最上面一定要寫這樣 :
1 | #!/usr/bin/env python |
一個是告訴 mapreduce 你用的語言,一個是 Linux 編碼老問題。
1 | #!/usr/bin/env python |
1 | #!/usr/bin/env python |
Log 範例 :
1 | 64.242.88.10 - - [07/Mar/2004:16:05:49 -0800] "GET /twiki/bin/edit/Main/Double_bounce_sender?topicparent=Main.ConfigurationVariables HTTP/1.1" 401 12846 |
單看其中一句 :
1 | 64.242.88.10 - - [07/Mar/2004:16:10:02 -0800] "GET /mailman/listinfo/hsdivision HTTP/1.1" 200 6291 |
每句格式都差不多,仔細觀察一下直接暴力切,最暴力無腦那種 ...
然後別從中間的 -0800
下手,apache 在那邊會因為時區不一樣輸出不一樣
請從 [ ]
中括號去切。
Mapper 的輸出會是 :
1 | 某某時間點 1 |
Reducer 就是去讀他們然後統計就好了。
讓 MapReduce 在 Hadoop 上執行
這邊分成幾個步驟 :
- 放好 input
- 放好 Code
- 執行。
說實話,很簡單很單純,但是在指令不確定的時候,我的好幾個小時就噴了。
放 input
首先在 home 底下先把你的 Log (測資) 放進去吧。
我是直接開 vim 硬貼上 ... 不過建議不要這樣做。
後面也會提到其他可行的上傳方式。
放好之後,要上傳 input 到 HDFS 的中心
我參考 Writing An Hadoop MapReduce Program In Python
但是某些目錄根本不存在於這個環境之中,這篇教學用不上了。
姑且這樣做試試看 :
1 | $dfs -copyFromLocal log.txt /user/hduser/worker |
這個 command 有兩個問題,一是後面目錄參數是指想要在 HDFS 上面放的位置
以及翻了一些講義,發現 dfs 不是一個程式,這是搭配在 hadoop 或是 hdfs 後面做使用的。
所以正確的流程應該是這樣 :
1 | root@hadoop-dev:/home# hdfs dfs -mkdir /workdir |
跑 MapReduce
這時候又有幾個問題 :
- Python 檔案怎麼上傳
- 找不到
hadoop-*streaming*.jar
在哪裡啊 - 指令到底怎麼打
以下依序解決。
Python 檔案怎麼上傳
其實 docker 直接用 volumn 連接就好了,ubuntu 上很好用
但是 Windows 我沒成功
不過其實可以用 docker 內建的 cp 方式複製就好
docker cp log.txt hadoop-dev:/home/
這樣就會出現在 hadoop-dev 這個 Container 裡面的 /home 底下。
我自己當時做的時候繞了個遠路,上傳到了我個人架網站的 VPS
(有 Server 可以讓網頁大家都能訪問到的地方)
然後直接 wget 下來。
以下一些鬼點子 :
如果有工作站可以用,可以先傳到工作站,再用 scp 指令從工作站拉到本地。
或是上傳到 github,然後再把 repo 拉下來。
找不到 hadoop-*streaming*.jar
在哪裡啊
不管是網路上文章或是講義內的,目錄都不對
於是自己找 ..
(以下不用照做,單純紀錄)
- 先 exec 進去 hadoop-master
winpty docker container exec -it hadoop-master bash
- 然後
find / -iname 'hadoop' -type d
- type d 是 dictionary 的意思。
- 然後發現大概是在 /opt/hadoop/share/hadoop 那邊,後來也挖到 compiler 了。
但其實關於這點,可以從 readme 的範例執行看到端睨 :yarn jar /opt/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.2.jar pi 4 10000
當時想說這是 yarn 的指令就沒特別在意,
但其實這邊可以看到 hadoop 的位置位於 /opt/hadoop/share
就能進去挖挖看。
但總之結論就是這個 image 的 hadoop-*streaming*.jar
位置是在/opt/hadoop/share/hadoop/tools/lib
裡面。
指令到底怎麼打
遇到下面這類 error,如果程式只是小程式,有很大問題還是指令打錯。
1 | java.lang.RuntimeException: PipeMapRed.waitOutputThreads(): subprocess failed with code 127 |
以下是我各方搜尋嘗試過的
1 | hadoop jar /opt/hadoop/share/hadoop/tools/lib/hadoop-*streaming*.jar -mapper "python $PWD/mapper.py" -reducer "python $PWD/reducer.py" -input "/workdir/log.txt" -output "/workdir_output" |
這個過程花了非常多時間,也看過助教的範例但是不知怎地也不能 work
總之,在這個 image 底下,我是這樣成功的,給你參考。
1 | hadoop jar /opt/hadoop/share/hadoop/tools/lib/hadoop-*streaming*.jar -files mapper.py,reducer.py -input /workdir/log.txt -output /workdir_output -mapper "python mapper.py" -reducer "python reducer.py" |
國父革命 11 次成功,我這第 13 次才成功。
Thanks to Greg Ory
Solve Reference
hdfs workdir_output already exists Streaming Command Failed!
類似範例 :
1 | root@hadoop-dev:/home# hadoop jar (一長串指令) |
如同他所寫,output dir 已經存在,所以要手動去刪掉。
這樣設計的原因是怕使用者沒有去備份 output 吧。
但不得不說手動刪挺麻煩的,我寫成 script 去跑。
或是你可以用指令 hdfs dfs -rm -r -f /workdir_output
去刪除。
1 | #/bin/sh |
- 使用方式 :
- vim del.sh
- 然後上面那些打進去
- chmod 0755 del.sh
- 想跑的時候輸入
./del.sh
- vim del.sh
將 Output 拉到本地端
從 HDFS 拉下來 Container 內 :
1 | rootdev:/home# hdfs dfs -copyToLocal /workdir_output/part-00000 . - |
從 Container 拉到自己電腦內 :
1 | docker cp hadoop-dev:/home/part-00000 . |
成果展示
執行 Log 截圖
執行 Output 片段
心得
寫 mapper 和 reducer 的時間與小於弄環境以及搞指令,崩潰。
嗚嗚。