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

jopen 9年前

使用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

----------

时间:写于2016/1/6。

地点:某高校实验室。

作者:duweike。

原因:项目初期调研探索。

主机系统: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目录里面,这样数据就能保存在容器之外了,不会随着容器的删除而丢失。修改如下:

<property>

<name>dfs.datanode.data.dir</name>

<value>file:/root/data</value>

</property>

----------

第二步,制作基础镜像。(主要工作)

本集群的思路是尽可能的减少额外的工作量,使用的是固定网络环境,这可能和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

root@67f272584448:/# ls /root/.ssh/

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的配置文件中做的一个特殊的修改<name>dfs.datanode.data.dir</name>,<value>file:/root/data</value>,这个是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

REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE

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的配置文件指定的:

<property>

<name>dfs.namenode.name.dir</name>

<value>file:/opt/hadoop-2.6.0/dfs/name</value>

node1和node2是run.sh脚本通过连接文件过去的实际hdfs存储数据的目录:

<property>

<name>dfs.datanode.data.dir</name>

<value>file:/root/data</value>

</property>

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一下看看启动的情况

root@node0:/# jps

1532 Jps

843 NameNode

1025 SecondaryNameNode

1393 Master

一切正常,就可以开始启动spark-shell进行测试了,以standalone方式启动:

root@node0:/# /opt/spark-1.2.0-bin-hadoop2.4/bin/spark-shell --master spark://node0:7077

到这里也基本已经结束了,可以跑一个wordcount的例子,同样也可以使用webUI查看spark的情况,到此我觉得已经没什么可说的了。

由于我不会往上面贴图片,并且图片很占篇幅,我不截图给大家看了

----------

结束语

总结,这个三个节点真实可用,在单个普通电脑上部署hadoop和spark集群非常可行,当然,我是刚接触docker的,其中有很多方法可能和docker的理论相违背,各位尽管批评指教,谢谢。

这是我第一次写文档,写的不好,望见谅,如果遇到什么相关的问题,可以给我留言,再次谢谢。

QQ:604212506,非诚勿扰。

下一步学习kubernetes,在多个主机上部署spark集群,如果有必要再写文档和大家分享。

----------

来自: http://dockone.io/article/944