本環境使用的單個宿主主機,而不是跨主機叢集,本spark叢集環境存在的意義可能在于便于本地開發測試使用,非常輕量級和便捷。這個部署過程,最好在之前有過一定的hadoop,spark叢集部署經驗的基礎,本文重點在于docker相關的操作,至于hadoop和spark叢集的部署,極力推薦這兩個網頁:
hadoop叢集:http://blog.csdn.net/stark_sum ... 24279。
spark叢集:http://blog.csdn.net/stark_sum ... 58081
主機系統:ubuntu14.04,64位,記憶體4g,主機名docker。(實際上是在虛拟機上安裝的)
軟體版本:hadoop-2.6.0,jdk1.7.0_79,scala-2.10.5,spark-1.2.0-bin-hadoop2.4,docker版本:1.9.1,鏡像:ubuntu14.04。
搭建環境前調研結果描述:
目前網上在docker上部署spark的介紹比較簡單和沒有相關啟動使用的操作,部署大緻分為兩類情況:
1. 直接在docker倉庫pull下來。這個方法我嘗試了一下,不建議使用,首先下載下傳鏡像比較大,2g多,其次下載下傳之後貌似隻能單機啟動,也就是僞分布式,并不是叢集(我自己沒有實際使用過,看到的相關資料是這樣說的)。如下sequenceiq/spark:1.2.0這個鏡像:
repository tag image id created virtual size
sequenceiq/spark 1.2.0 334aabfef5f1 10 months ago 2.115 gb
2. 自己使用基礎鏡像搭建環境。本文采用這種方式,由于自己也是剛接觸docker一個多月,還不會使用dockerfile,是以使用的是commit方式制作的叢集。
具體部署過程
第一步,相關軟體準備。
通過對spark源碼當中docker檔案夾的閱讀得出的思路,利用資料卷共享資料。相關的叢集軟體都放在/opt目錄下,目的是為後面啟動叢集的時候使用docker資料卷共享和永久儲存資料,不會随着容器的删除而丢失。spark源碼docker檔案夾解讀參考網頁:http://blog.csdn.net/yunlong34 ... 33731
操作說明,直接把java等軟體解壓到/opt下,總共是四個,java,hadoop,scala,spark。不需要在宿主主機做任何修改,包括/etc/hosts,/etc/profile添加變量等,因為是在容器當中使用,宿主主機并不會用到。解壓之後如下:
root@docker:/opt# ll
total 32
drwxr-xr-x 7 root root 4096 12月 22 22:12 ./
drwxr-xr-x 23 root root 4096 11月 30 19:35 ../
drwxr-xr-x 12 root root 4096 12月 22 22:07 hadoop-2.6.0/
drwxr-xr-x 8 root root 4096 4月 11 2015 jdk1.7.0_79/
drwxr-xr-x 9 root root 4096 12月 22 13:54 scala-2.10.5/
drwxrwxr-x 12 root root 4096 12月 22 22:19 spark-1.2.0-bin-hadoop2.4/
然後把hadoop和spark 的配置檔案修改,這一步主要是靠之前的相關基礎操作了,可以參考上面給出的兩個網站修改配置檔案,我是直接拷貝我之前叢集的配置檔案替換的,然後再結合後面的主機名,ip等行稍作修改就行了。如果之前沒有部署過叢集,這一步的工作量是相當大的。
需要特别注意的一點是hadoop的配置檔案中的hdfs-sit.xml中的dfs.datanode.data.dir,這個也就是hdfs的datanode的檔案夾,也是通過小技巧修改為file:/root/data,為什麼這麼修改後面有講解,最終的想要的目的是通過連結檔案,連結到資料卷/opt的hadoop目錄裡面,這樣資料就能儲存在容器之外了,不會随着容器的删除而丢失。修改如下:
dfs.datanode.data.dir
file:/root/data
第二步,制作基礎鏡像。(主要工作)
本叢集的思路是盡可能的減少額外的工作量,使用的是固定網絡環境,這可能和docker本身的網絡不固定性相悖,是以使用了一點小技巧修改的網絡,這也是這個方法不能大規模使用的原因,也算是一個弊端吧。我看到有人使用動态的ip注冊,我還沒有了解到哪個地步,在後期的學習中再慢慢完善吧。節點容器主機名和ip規劃如下:
主節點容器主機名hostname:node0,ip:172.17.0.150。
從節點容器主機名hostname:node1,ip:172.17.0.151。
從節點容器主機名hostname:node2,ip:172.17.0.152。
下面就開始一步一步的來設定:
1.檢視鏡像,使用ubuntu:14.04做為基礎鏡像,如果沒有就pull一個吧。
root@docker:/opt# docker images
repository tag image id created virtual size
ubuntu 14.04 ca4d7b1b9a51 8 weeks ago 187.9 mb
2.啟動一個容器,安裝vim和ssh。
root@docker:/opt# docker run -it ubuntu:14.04 /bin/bash
root@67f272584448:/# apt-get -y install vim openssh-server
3.修改ssh配置檔案,允許root登陸。
root@67f272584448:/# vim /etc/ssh/sshd_config
找到:permitrootlogin without-password
修改為:permitrootlogin yes
4.生成ssh公鑰,輸入ssh-keygen,一直回車就行了。着裡需要說明的是,三個節點的公鑰都是一樣的,為了簡單起見而利用了小技巧。如果比較了解ssh的話,我說的這些相當于廢話了,後面還會有涉及的。
root@67f272584448:/# ssh-keygen
此時/root/.ssh檔案夾裡如下:
root@67f272584448:/# ls /root/.ssh/
id_rsa id_rsa.pub
root@67f272584448:/# cat /root/.ssh/id_rsa.pub >> /root/.ssh/authorized_keys
authorized_keys id_rsa id_rsa.pub
5.下面開始關鍵步驟了。
把需要的變量寫入/root/.bashrc,為什麼不寫入/etc/profile呢,因為我試了一下,寫入/etc/proflie生成鏡像啟動容器的時候變量不能生效。
看到這裡,相信下面的變量都是很熟悉吧:
export java_home=/opt/jdk1.7.0_79
export classpath=.:/opt/jdk1.7.0_79/lib/dt.jar:/opt/jdk1.7.0_79/lib/tools.jar
export hadoop_home=/opt/hadoop-2.6.0
export scala_home=/opt/scala-2.10.5
export spark_home=/opt/spark-1.2.0-bin-hadoop2.4
export path=$java_home/bin:$path:$scala_home/bin:$spark_home/bin
6.這個是最後一步了,在/root下建立一個run.sh腳本,對容器所做的修改,全部寫入這個腳本了,先把腳本貼出來,再解釋吧。
1 #!/bin/bash
2
3 echo "172.17.0.150 node0" > /etc/hosts
4 echo "172.17.0.151 node1" >> /etc/hosts
5 echo "172.17.0.152 node2" >> /etc/hosts
6
7 case $hostname in
8 "node0")
9 ifconfig eth0 172.17.0.150
10 sed -i 's/root@.*$/root@node0/g' /root/.ssh/authorized_keys
11 ;;
12 "node1")
13 ifconfig eth0 172.17.0.151
14 sed -i 's/root@.*$/root@node0/g' /root/.ssh/authorized_keys
15 ln -s /opt/hadoop-2.6.0/dfs/node1 /root/data
16 ;;
17 "node2")
18 ifconfig eth0 172.17.0.152
19 sed -i 's/root@.*$/root@node0/g' /root/.ssh/authorized_keys
20 ln -s /opt/hadoop-2.6.0/dfs/node2 /root/data
21 ;;
22 *)
23 echo "null"
24 ;;
25 esac
26
27 /etc/init.d/ssh start -d
28
1)3,4,5行,替換hosts。啟動叢集的時候,習慣性的喜歡使用主機名,而不是使用ip,是以做了這個修改。另一個原因是,容器在重新開機之後hosts和ip是會變化的,是以每次啟動都要修改。
2)7到25行,是利用容器的主機名來做三個修改。
第一,修改主機的ip,也就是我們的三個節點都是固定ip的,這個指令需要privileged。
第二,設定ssh免登入,也就authorized_keys中最後一個字段root@......全部修改為root@node0,這樣node0節點就能免登入到node1,node2,和自己node0。
第三,利用連接配接檔案,把hdfs的資料儲存到資料卷的相關目錄,也就是儲存到了容器之外。利用連接配接檔案時一個技巧,hdfs的配置檔案都是/root/data,實際上卻儲存到了不同的檔案目錄上去。在上面的hadoop的配置檔案中做的一個特殊的修改dfs.datanode.data.dir,file:/root/data,這個是hdfs的實際存儲資料的目錄,通過軟連接配接到資料卷目錄,最終把資料儲存在容器之外,這樣當容器删除時,hdfs裡面的資料并沒有消失,建立容器就可以再次使用資料了。
3)27行,這個就是啟動ssh的,關鍵的是-d這個參數,如果不加,啟動容器的時候run -d容器就會停止,不會運作。
4)最後儲存退出,再修改一下執行權限,退出容器
root@67f272584448:~# chmod 744 /root/run.sh
root@67f272584448:~# exit
7.使用commit送出鏡像吧。
root@docker:~/docker# docker commit 67 ubuntu:base-spark
35341d63645cb5c23f88a6f4ac51d1000dc4431646ac3a948bd9c9f171dcbeeb
root@docker:~/docker# docker images
ubuntu base-spark 35341d63645c 4 minutes ago 261.1 mb
從上面可以看出,鏡像隻有260mb,是非常小的。
到此整個基礎鏡像就做好了,其中有可能出錯的地方是,hadoop和spark的配置檔案修改的問題,這裡是無關docker知識的“準備工作”。
第三步,啟動容器,啟動叢集,并測試。
最後這步是最最爽的時候了,一個指令,叢集就啟動起來了。
其實下面大部分的篇幅是在講解我的思路,啟動叢集本身是很簡單的hadoop,spark知識。
一、啟動容器叢集
我寫了一個小腳本docker_start.sh,裡面三行是啟動三個容器的指令,先看一眼:
root@docker:~/docker# cat docker_start.sh
#!/bin/bash
docker run -d --name node0 -h node0 -v /opt:/opt --privileged ubuntu:base-spark /root/run.sh
docker run -d --name node1 -h node1 -v /opt:/opt --privileged ubuntu:base-spark /root/run.sh
docker run -d --name node2 -h node2 -v /opt:/opt --privileged ubuntu:base-spark /root/run.sh
下面解釋一下這個啟動指令的各個參數:
1)-d這個指令能夠成功執行的原因是run.sh這個腳本的/etc/init.d/ssh start -d這一行的-d這個參數,容器才能成功背景up。
2)--name node0,這個是node0的容器名。
3)-h node0,這裡的node0是容器主機名,也就是hostname。
4)-v /opt:/opt,就是資料卷,四個目錄在這裡java,hadoop,scala,spark,并且hdfs的資料存儲目錄在hadoop-2.6.0目錄裡,dfs檔案夾裡有三個目錄,最好手動提前建立name,node1和node2,其實是可以寫入run.sh腳本裡面建立的,但是我已經不想回頭去修改run.sh了。
root@docker:/opt/hadoop-2.6.0/dfs# pwd
/opt/hadoop-2.6.0/dfs
root@docker:/opt/hadoop-2.6.0/dfs# ls
name node1 node2
name檔案夾是hadoop的配置檔案指定的:
dfs.namenode.name.dir
file:/opt/hadoop-2.6.0/dfs/name
node1和node2是run.sh腳本通過連接配接檔案過去的實際hdfs存儲資料的目錄:
ln -s /opt/hadoop-2.6.0/dfs/node1 /root/data
ln -s /opt/hadoop-2.6.0/dfs/node2 /root/data
5)--privileged,這個參數是獲得最高權限,才能夠執行run.sh腳本裡面的修改ip的指令。
ifconfig eth0 172.17.0.150
6)/root/run.sh,就是啟動容器的時候,執行一下我們提前寫好的腳本,對容器做一下修改了,雖然這些修改扭曲了docker的一些特性,不過對于我們這個本地的小環境來說,應該還是有點實際使用的價值的。
二、進入node0容器,啟動并測試hdfs
其實,到這裡,就已經差不多結束了,下面就是hadoop和spark的知識了
首先,先看一下啟動的三個節點高興一下吧
root@docker:~/docker# docker ps
container id image command created status ports names
7268b191b8fd ubuntu:base-spark "/root/run.sh" about an hour ago up about an hour node2
acce5919ed63 ubuntu:base-spark "/root/run.sh" about an hour ago up about an hour node1
6494f90e1ecc ubuntu:base-spark "/root/run.sh" about an hour ago up about an hour node0
進入node0容器
root@docker:/# docker exec -it node0 /bin/bash
root@node0:/#
此時的容器都是已經做過修改的,可以參看以下相關的資訊,比如,ifconfig,/etc/hosts,hostname,/root/.ssh/authorized_keys,等。
下面就啟動hadoop的hdfs吧,因為這裡隻用到hdfs是以就不管yarn了,第一次啟動hdfs先來個格式化,然後,還要輸入若幹個yes,這個yes是第一次ssh登陸的時候需要的,我就不貼出來格式化等相關的代碼了。
然後就是啟動hdfs:
root@node0:/# /opt/hadoop-2.6.0/sbin/start-dfs.sh
輸入jps檢視一下node0上的程序
root@node0:/# jps
1310 jps
843 namenode
1025 secondarynamenode
下面就可以使用hdfs了,可以向hdfs上傳幾個檔案試試,也可以通過webui浏覽器看一下hdfs的情況,總而言之,就是hdfs的知識了,我就不廢話了。
三,以standalone方式啟動spark叢集。
到這裡直接啟動spark程序就可以了:
root@node0:/# /opt/spark-1.2.0-bin-hadoop2.4/sbin/start-all.sh
再次jps一下看看啟動的情況
1532 jps
1393 master
一切正常,就可以開始啟動spark-shell進行測試了,以standalone方式啟動:
root@node0:/# /opt/spark-1.2.0-bin-hadoop2.4/bin/spark-shell --master spark://node0:7077
root@node0:/# /opt/spark-1.2.0-bin-hadoop2.4/bin/spark-shell --master spark://node0:7077
到這裡也基本已經結束了,可以跑一個wordcount的例子,同樣也可以使用webui檢視spark的情況。
本文作者:方圓小生
來源:51cto