中国领先的IT技术网站
|
|

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

目前网上在docker上部署spark的介绍比较简单和没有相关启动使用的操作,本文重点在于docker相关的操作.

作者:方圆小生来源:DockOne|2016-01-07 09:36


本环境使用的单个宿主主机,而不是跨主机集群,本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添加变量等,因为是在容器当中使用,宿主主机并不会用到。解压之后如下:

  1. root@docker:/opt# ll 
  2.  
  3. total 32 
  4.  
  5. drwxr-xr-x 7 root root 4096 12月 22 22:12 ./ 
  6.  
  7. drwxr-xr-x 23 root root 4096 11月 30 19:35 ../ 
  8.  
  9. drwxr-xr-x 12 root root 4096 12月 22 22:07 hadoop-2.6.0
  10.  
  11. drwxr-xr-x 8 root root 4096 4月 11 2015 jdk1.7.0_79/ 
  12.  
  13. drwxr-xr-x 9 root root 4096 12月 22 13:54 scala-2.10.5
  14.  
  15. 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目录里面,这样数据就能保存在容器之外了,不会随着容器的删除而丢失。修改如下:

  1. dfs.datanode.data.dir 
  2.  
  3. 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一个吧。

  1. root@docker:/opt# docker images 
  2.  
  3. REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE 
  4.  
  5. ubuntu 14.04 ca4d7b1b9a51 8 weeks ago 187.9 MB 

2.启动一个容器,安装vim和ssh。

  1. root@docker:/opt# docker run -it ubuntu:14.04 /bin/bash 
  2.  
  3. root@67f272584448:/# apt-get -y install vim openssh-server 

3.修改ssh配置文件,允许root登陆。

  1. root@67f272584448:/# vim /etc/ssh/sshd_config 

找到:PermitRootLogin without-password

修改为:PermitRootLogin yes

4.生成ssh公钥,输入ssh-keygen,一直回车就行了。着里需要说明的是,三个节点的公钥都是一样的,为了简单起见而利用了小技巧。如果比较了解ssh的话,我说的这些相当于废话了,后面还会有涉及的。

  1. root@67f272584448:/# ssh-keygen 

此时/root/.ssh文件夹里如下:

  1. root@67f272584448:/# ls /root/.ssh/ 
  2.  
  3. id_rsa id_rsa.pub 
  4.  
  5. root@67f272584448:/# cat /root/.ssh/id_rsa.pub >> /root/.ssh/authorized_keys 
  6.  
  7. root@67f272584448:/# ls /root/.ssh/ 
  8.  
  9. authorized_keys id_rsa id_rsa.pub 

5.下面开始关键步骤了。

把需要的变量写入/root/.bashrc,为什么不写入/etc/profile呢,因为我试了一下,写入/etc/proflie生成镜像启动容器的时候变量不能生效。

看到这里,相信下面的变量都是很熟悉吧:

  1. export JAVA_HOME=/opt/jdk1.7.0_79 
  2.  
  3. export CLASSPATH=.:/opt/jdk1.7.0_79/lib/dt.jar:/opt/jdk1.7.0_79/lib/tools.jar 
  4.  
  5. export HADOOP_HOME=/opt/hadoop-2.6.0 
  6.  
  7. export SCALA_HOME=/opt/scala-2.10.5 
  8.  
  9. export SPARK_HOME=/opt/spark-1.2.0-bin-hadoop2.4 
  10.  
  11. export PATH=$JAVA_HOME/bin:$PATH:$SCALA_HOME/bin:$SPARK_HOME/bin 

6.这个是最后一步了,在/root下新建一个run.sh脚本,对容器所做的修改,全部写入这个脚本了,先把脚本贴出来,再解释吧。

  1. 1 #!/bin/bash 
  2.  
  3. 2 
  4.  
  5. 3 echo "172.17.0.150 node0" > /etc/hosts 
  6.  
  7. 4 echo "172.17.0.151 node1" >> /etc/hosts 
  8.  
  9. 5 echo "172.17.0.152 node2" >> /etc/hosts 
  10.  
  11. 6 
  12.  
  13. 7 case $HOSTNAME in 
  14.  
  15. 8 "node0"
  16.  
  17. 9 ifconfig eth0 172.17.0.150 
  18.  
  19. 10 sed -i 's/root@.*$/root@node0/g' /root/.ssh/authorized_keys 
  20.  
  21. 11 ;; 
  22.  
  23. 12 "node1"
  24.  
  25. 13 ifconfig eth0 172.17.0.151 
  26.  
  27. 14 sed -i 's/root@.*$/root@node0/g' /root/.ssh/authorized_keys 
  28.  
  29. 15 ln -s /opt/hadoop-2.6.0/dfs/node1 /root/data 
  30.  
  31. 16 ;; 
  32.  
  33. 17 "node2"
  34.  
  35. 18 ifconfig eth0 172.17.0.152 
  36.  
  37. 19 sed -i 's/root@.*$/root@node0/g' /root/.ssh/authorized_keys 
  38.  
  39. 20 ln -s /opt/hadoop-2.6.0/dfs/node2 /root/data 
  40.  
  41. 21 ;; 
  42.  
  43. 22 *) 
  44.  
  45. 23 echo "null" 
  46.  
  47. 24 ;; 
  48.  
  49. 25 esac 
  50.  
  51. 26 
  52.  
  53. 27 /etc/init.d/ssh start -D 
  54.  
  55. 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.dirfile:/root/data,这个是hdfs的实际存储数据的目录,通过软连接到数据卷目录,最终把数据保存在容器之外,这样当容器删除时,hdfs里面的数据并没有消失,新建容器就可以再次使用数据了。

3)27行,这个就是启动ssh的,关键的是-D这个参数,如果不加,启动容器的时候run -d容器就会停止,不会运行。

4)最后保存退出,再修改一下执行权限,退出容器

  1. root@67f272584448:~# chmod 744 /root/run.sh 
  2.  
  3. root@67f272584448:~# exit 

7.使用commit提交镜像吧。

  1. root@docker:~/docker# docker commit 67 ubuntu:base-spark 
  2.  
  3. 35341d63645cb5c23f88a6f4ac51d1000dc4431646ac3a948bd9c9f171dcbeeb 
  4.  
  5. root@docker:~/docker# docker images 
  6.  
  7. REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE 
  8.  
  9. ubuntu base-spark 35341d63645c 4 minutes ago 261.1 MB 

从上面可以看出,镜像只有260MB,是非常小的。

到此整个基础镜像就做好了,其中有可能出错的地方是,hadoop和spark的配置文件修改的问题,这里是无关docker知识的“准备工作”。

第三步,启动容器,启动集群,并测试。

最后这步是最最爽的时候了,一个命令,集群就启动起来了。

其实下面大部分的篇幅是在讲解我的思路,启动集群本身是很简单的hadoop,spark知识。

一、启动容器集群

我写了一个小脚本docker_start.sh,里面三行是启动三个容器的命令,先看一眼:

  1. root@docker:~/docker# cat docker_start.sh 
  2.  
  3. #!/bin/bash 
  4.  
  5. docker run -d --name node0 -h node0 -v /opt:/opt --privileged ubuntu:base-spark /root/run.sh 
  6.  
  7. docker run -d --name node1 -h node1 -v /opt:/opt --privileged ubuntu:base-spark /root/run.sh 
  8.  
  9. 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了。

  1. root@docker:/opt/hadoop-2.6.0/dfs# pwd 
  2.  
  3. /opt/hadoop-2.6.0/dfs 
  4.  
  5. root@docker:/opt/hadoop-2.6.0/dfs# ls 
  6.  
  7. name node1 node2 

name文件夹是hadoop的配置文件指定的:

  1. dfs.namenode.name.dir 
  2.  
  3. file:/opt/hadoop-2.6.0/dfs/name 

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

  1. dfs.datanode.data.dir 
  2.  
  3. file:/root/data 
  4.  
  5. ln -s /opt/hadoop-2.6.0/dfs/node1 /root/data 
  6.  
  7. 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的知识了

首先,先看一下启动的三个节点高兴一下吧

  1. root@docker:~/docker# docker ps 
  2.  
  3. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 
  4.  
  5. 7268b191b8fd ubuntu:base-spark "/root/run.sh" About an hour ago Up About an hour node2 
  6.  
  7. acce5919ed63 ubuntu:base-spark "/root/run.sh" About an hour ago Up About an hour node1 
  8.  
  9. 6494f90e1ecc ubuntu:base-spark "/root/run.sh" About an hour ago Up About an hour node0 

进入node0容器

  1. root@docker:/# docker exec -it node0 /bin/bash 
  2.  
  3. root@node0:/# 

此时的容器都是已经做过修改的,可以参看以下相关的信息,比如,ifconfig,/etc/hosts,hostname,/root/.ssh/authorized_keys,等。

下面就启动hadoop的hdfs吧,因为这里只用到hdfs所以就不管yarn了,第一次启动hdfs先来个格式化,然后,还要输入若干个yes,这个yes是第一次ssh登陆的时候需要的,我就不贴出来格式化等相关的代码了。

然后就是启动hdfs:

  1. root@node0:/# /opt/hadoop-2.6.0/sbin/start-dfs.sh 

输入jps查看一下node0上的进程

  1. root@node0:/# jps 
  2.  
  3. 1310 Jps 
  4.  
  5. 843 NameNode 
  6.  
  7. 1025 SecondaryNameNode 

下面就可以使用hdfs了,可以向hdfs上传几个文件试试,也可以通过webUI浏览器看一下hdfs的情况,总而言之,就是hdfs的知识了,我就不废话了。

三,以standalone方式启动spark集群。

到这里直接启动spark进程就可以了:

  1. root@node0:/# /opt/spark-1.2.0-bin-hadoop2.4/sbin/start-all.sh 

再次jps一下看看启动的情况

  1. root@node0:/# jps 
  2.  
  3. 1532 Jps 
  4.  
  5. 843 NameNode 
  6.  
  7. 1025 SecondaryNameNode 
  8.  
  9. 1393 Master 

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

  1. 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的情况。

【编辑推荐】

  1. 如何通过自动化Kubernetes集群管理容器
  2. 2015国内Docker云生态年终盘点
  3. OpenStack & Docker 2016六大发展趋势预测
  4. 微博 Docker 化混合云平台设计首度揭秘!
  5. Docker 持续集成过程中的性能问题及解决方法
【责任编辑:谭盼 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

读 书 +更多

JSP应用开发详解(第三版)

本书结合JSP和Servlet的最新规范,从基本的语法和规范入手,以经验为后盾,以实用为目标,以实例为导向,以实践为指导,深入浅出地讲解了JS...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊