使用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上部署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 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的配置文件中做的一个特殊的修改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 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的配置文件指定的:
dfs.namenode.name.dir file:/opt/hadoop-2.6.0/dfs/name
node1和node2是run.sh脚本通过连接文件过去的实际hdfs存储数据的目录:
dfs.datanode.data.dir file:/root/data 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