天天看点

使用Docker在本地搭建hadoop,spark集群

本环境使用的单个宿主主机,而不是跨主机集群,本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在本地搭建hadoop,spark集群

搭建环境前调研结果描述:

目前网上在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 

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 

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