使用Docker搭建Hadoop集群

前言

我是按照docker环境下搭建hadoop集群(ubuntu16.04 LTS系统)这篇博客来搭建的,然后遇到了不少的坑,所以我就把自己的搭建过程写了出了,顺便补充了一些注意事项。非常感谢原博主的创作。ヾ(◍°∇°◍)ノ゙

目标

我们的目标是,使用Docker的Ubuntu镜像,在计算机上搭建一个“One-Master-Two-Slaves”的Hadoop集群模型,并且将容器保存为镜像,以便后面能够复用。

工具

我使用Windows7操作系统,因此Docker是使用toolbox运行的。此外,使用XShell来连接Docker,方便操作和文件互传。

总体步骤

  1. 使用XShell连通Docker服务器进程
  2. 使用Docker安装Ubuntu镜像
  3. 安装jdk1.8,配置好Java环境
  4. 更换apt源,使用阿里源,方便安装软件
  5. 安装ssh工具,使得容器间可以互联
  6. 安装vim,可以进行文本文件操作
  7. 安装hadoop
  8. 构建一个Master、二个Slave容器
  9. 配置hosts文件,以及配置公钥,使得三个容器可以互相ssh连通
  10. 配置相关集群节点,尝试配置结果

详细步骤

1. 使用XShell连接Docker

Docker默认的账户名是docker,密码是tcuser。其ip地址可以在Docker启动页面上看到

Docker启动页面

如上图,可以看到ip地址是192.168.99,100,因此在XShell中键入ssh 192.168.99,100,然后按照提示输入账户名和密码,即可连接成功。或者可以使用XShell的图形用户界面来永久配置连接信息,在此不再赘述。

2. 使用Docker安装Ubuntu镜像

2.1 安装镜像

使用docker search ubuntu来检索本地仓库或Docker Hub中的ubuntu镜像,然后使用docker pull命令来获取。在这里我直接使用官方发布的最新ubuntu镜像:

docker@default:~$ docker pull ubuntu

使用docker images命令可以查看安装情况:

docker@default:~$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ubuntu              latest              775349758637        2 weeks ago         64.2MB

2.2 在Docker本地构建一个项目文件夹

在Docker本地构建一个项目文件夹。因为在Windows上使用Docker,它本身就是运行在一个小的虚拟机中,我们在这个模拟机中构建一个文件夹作为软件包、数据的中转,即将该文件夹挂载到容器中,这样,宿主机(Win7系统)的数据可以先用XShell的Xftp文件传输功能推送到虚拟机的该文件夹中,从而在容器中可以直接访问。

docker@default:~$ mkdir /home/docker/azur_wxj/

2.3 构建容器

我们将以此为基础镜像来构建容器。构建容器的命令是docker run,具体命令可以参考官方文档:docker run。在这里,我使用如下命令:

docker run -it -h hdpnode -v /home/docker/azur_wxj:/home/azur_wxj --name hdpnode ubuntu /bin/bash
  • -it:表示创建的容器是可交互式的(终端命令行模式),是-t-i的简写
  • -h hdpnode:指定容器的宿主名(container host name)为hdpnode
  • -v /home/docker/azur_wxj:/home/azur_wxj:表示将虚拟机的目录/home/docker/azur_wxj挂载到容器的目录/home/azur_wxj
  • --name hdpnode:表示创建的容器名为hdpnode
  • ubuntu /bin/bash:表示使用ubuntu作为基础镜像,创建的容器立刻运行/bin/bash命令呼出交互式终端。如果没有额外的标签信息,ubuntu就是ubuntu:latest,即默认最新的镜像。

现在,如果没有问题,将会进入创建的容器的交互式界面:

docker@default:~$ docker run -it -h hdpnode -v /home/docker/azur_wxj:/home/azur_wxj --name hdpnode ubuntu /bin/bash
root@hdpnode:/# ls /home/
azur_wxj
root@hdpnode:/#

如果想要退出容器返回Docker界面,可以使用Ctrl+p+q快捷键(按顺序键入),此时容器保持运行而不会停止。如果使用exit命令,则容器终止,退回Docker界面,此时,必须使用docker start hdpnode来启动容器,然后使用docker attach hdpnode来附着到容器上,简单理解就是,重新进入容器的交互式终端界面。

3. 安装jdk1.8,配置好Java环境

3.1 下载jdk包

不同Hadoop版本支持的jdk版本不同,可以在 Hadoop Java Versions查看。简单来说,Apache Hadoop 3.x 目前只支持Java 8,而 Apache Hadoop 2.7.x2.x 支持Java 7和8。方便起见,我直接安装了jdk1.8。

此外,因为Oracle更改了用户协议,新的协议生效后,下载Oracle jdk需要注册登录,非常麻烦。可以选择安装openjdk,但是这里我选择安装了旧协议的最后一版“免费”jdk1.8。CSDN一位博主在《jdk下载/Linux64位 jdk1.8 jdk-8u161下载》博客中贴出了百度网盘的资源地址(拜谢~orz),选择 linux 64 jdk1.8 jdk-8u161-linux-x64.tar.gz 下载即可。(Docker好像只支持64位计算机,所以选择x64就可以)

下载完成的压缩包,使用xshell的Xftp推送到我们刚刚在虚拟机创建的 /home/docker/azur_wxj 目录中(关于Xftp功能用法可以自行检索,非常简单)

因为目录挂载,此时我们可以在hdpnode容器中查看 /home/azur_wxj 目录了:

root@hdpnode:/# ls /home/azur_wxj/
jdk-8u161-linux-x64.tar.gz
root@hdpnode:/# 

3.2 解压jdk包

使用tar命令解压jdk包

root@hdpnode:/# tar -zxvf /home/azur_wxj/jdk-8u161-linux-x64.tar.gz
root@hdpnode:/# ls
bin  boot  dev  etc  home  jdk1.8.0_161  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@hdpnode:/# 

我将安装的所有软件都移动到 /usr/local 目录下,所以

root@hdpnode:/# mv jdk1.8.0_161/ /usr/local/jdk1.8
root@hdpnode:/# ls /usr/local/jdk1.8/
COPYRIGHT  LICENSE  README.html  THIRDPARTYLICENSEREADME-JAVAFX.txt  THIRDPARTYLICENSEREADME.txt  bin  db  include  javafx-src.zip  jre  lib  man  release  src.zip
root@hdpnode:/# 

3.3 配置环境变量

环境变量的配置是在 /etc/profile 文件中,因为尚未安装vim,所以我使用echo命令将环境变量追加到 /etc/profile 文件末尾(使用追加重定向符>>),不过在此之前,为了保险起见,将原来的文件做一个备份:

root@hdpnode:/# cp /etc/profile /etc/profile.backup
root@hdpnode:/# echo 'export JAVA_HOME=/usr/local/jdk1.8/'>>/etc/profile
root@hdpnode:/# echo 'export JRE_HOME=${JAVA_HOME}/jre'>>/etc/profile
root@hdpnode:/# echo 'export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib'>>/etc/profile
root@hdpnode:/# echo 'export PATH=.:${JAVA_HOME}/bin:$PATH'>>/etc/profile

然后使用source命令来更新环境变量,最后检查java是否安装成功

root@hdpnode:/# source /etc/profile
root@hdpnode:/# java -version
java version "1.8.0_161"
Java(TM) SE Runtime Environment (build 1.8.0_161-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.161-b12, mixed mode)
root@hdpnode:/# 

3.4 问题:重入容器时环境变量失效

这里有一个问题,就是如果将容器退出停止,下次再重启容器时,会发现,尽管添加的Java环境变量还在,但是java命令提示找不到,需要重新执行source /etc/profile命令才可以:

root@hdpnode:/# exit
exit
docker@default:~$ docker start hdpnode   #重启容器
hdpnode
docker@default:~$ docker attach hdpnode  #附着容器,进入容器终端页
root@hdpnode:/# java -version  #?找不到java命令
bash: java: command not found
root@hdpnode:/# source /etc/profile  #重新source
root@hdpnode:/# java -version  #java命令可以正常运行
java version "1.8.0_161"
Java(TM) SE Runtime Environment (build 1.8.0_161-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.161-b12, mixed mode)
root@hdpnode:/# 

这种原因是:

/etc/profile中的变量不是自动就export的,完整的os在启动过程会有启动程序依次读取系统和用户的配置文件,但在容器里就没有这一步了,所以要自己导出才可以。(使用docker exec进入容器,无法读取环境变量问题

解决办法是,要么修改 ~/.bashrc 文件,添加命令,使得进入终端时自动执行;要么是对容器修改,使得进入容器时由docker代为执行命令(例如entrypoint选项)。在这里我选择修改 ~/.bashrc 文件:

root@hdpnode:/# echo 'source /etc/profile' >> ~/.bashrc
root@hdpnode:/# exit
exit
docker@default:~$ docker start hdpnode
hdpnode
docker@default:~$ docker attach hdpnode
root@hdpnode:/# java -version
java version "1.8.0_161"
Java(TM) SE Runtime Environment (build 1.8.0_161-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.161-b12, mixed mode)
root@hdpnode:/# 

4. 更换apt源

apt源定义在 /etc/apt/sources.list 文件中,如果不更换国内源,则安装使用国内网络进行软件下载会非常的慢而且经常会出错失败。先将原来的源文件备份

root@hdpnode:/# mv /etc/apt/sources.list /etc/apt/sources.list.backup

然后我使用了阿里源

# deb cdrom:[Ubuntu 16.04 LTS _Xenial Xerus_ - Release amd64 (20160420.1)]/ xenial main restricted
deb-src http://archive.ubuntu.com/ubuntu xenial main restricted #Added by software-properties
deb http://mirrors.aliyun.com/ubuntu/ xenial main restricted
deb-src http://mirrors.aliyun.com/ubuntu/ xenial main restricted multiverse universe #Added by software-properties
deb http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted multiverse universe #Added by software-properties
deb http://mirrors.aliyun.com/ubuntu/ xenial universe
deb http://mirrors.aliyun.com/ubuntu/ xenial-updates universe
deb http://mirrors.aliyun.com/ubuntu/ xenial multiverse
deb http://mirrors.aliyun.com/ubuntu/ xenial-updates multiverse
deb http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse #Added by software-properties
deb http://archive.canonical.com/ubuntu xenial partner
deb-src http://archive.canonical.com/ubuntu xenial partner
deb http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted multiverse universe #Added by software-properties
deb http://mirrors.aliyun.com/ubuntu/ xenial-security universe
deb http://mirrors.aliyun.com/ubuntu/ xenial-security multiverse

把它保存为 sources.list 文件,然后使用Xftp推送到 /home/docker/azur_wxj 文件夹中去,然后在ubuntu中使用如下命令进行移动:

root@hdpnode:/# cp /home/azur_wxj/sources.list /etc/apt/sources.list

最后使用

root@hdpnode:/# apt-get update

进行更新。这需要花几分钟的时间。

5. 安装ssh工具

使用命令:

root@hdpnode:/# apt-get install ssh

安装ssh,此时需要输入y来确认安装。这需要花费几分钟的时间。

关于SSH的信息,参见 附录1:SSH服务

6. 安装vim

方便后面的终端编辑文本,我们使用apt-get来安装vim:

root@hdpnode:~# apt-get install vim

7. 安装hadoop

7.1 下载安装包

现在开始安装hadoop。因为ubuntu容器没有安装wget,所以还是在Win7宿主机上下好压缩包,再传送到容器中。

可以在 Apache Download Mirrors中选择方便的镜像网站来下载hadoop。这里我选择使用清华大学的镜像,我选择Hadoop-2.7.7版本,如果网页下载慢的话,可以复制地址使用迅雷或其他下载软件下载。

下载好后的压缩包使用Xftp推送到挂载目录中去。

7.2 解压安装

使用tar命令解压,然后移动到 /usr/local/hadoop 目录中

root@hdpnode:~# tar -zxvf /home/azur_wxj/hadoop-2.7.7.tar.gz
....
root@hdpnode:~# mv hadoop-2.7.7/ /usr/local/hadoop
root@hdpnode:~# ls /usr/local/hadoop/
LICENSE.txt  NOTICE.txt  README.txt  bin  etc  include  lib  libexec  sbin  share
root@hdpnode:~# 

7.3 配置Java环境变量

现在需要在hadoop配置文件中指定Java目录地址:

  • 配置文件目录是 /usr/local/hadoop/etc/hadoop/hadoop-env.sh
  • 需要添加的环境变量是 export JAVA_HOME=/usr/local/jdk1.8(即jdk目录)

直接使用echo命令来追加

root@hdpnode:~# echo 'export JAVA_HOME=/usr/local/jdk1.8' >> /usr/local/hadoop/etc/hadoop/hadoop-env.sh

7.4 将hadoop命令添加到环境变量

hadoop命令位于 /usr/local/hadoop/bin 目录中,我们可以将该目录加到环境变量PATH中,这样下次直接输入hadoop即可启动。

使用vim来编辑 /etc/profile , 将最后的PATH改为:

export PATH=.:/usr/local/hadoop/bin:${JAVA_HOME}/bin:$PATH

保存退出后,使用

root@hdpnode:~# source /etc/profile

更新一次。现在,输入hadoop应该能看到该命令的说明

root@hdpnode:~# hadoop
Usage: hadoop [--config confdir] [COMMAND | CLASSNAME]
  CLASSNAME            run the class named CLASSNAME
 or
  where COMMAND is one of:
  fs                   run a generic filesystem user client
  version              print the version
  jar <jar>            run a jar file
                       note: please use "yarn jar" to launch
                             YARN applications, not this command.
  checknative [-a|-h]  check native hadoop and compression libraries availability
  distcp <srcurl> <desturl> copy file or directories recursively
  archive -archiveName NAME -p <parent path> <src>* <dest> create a hadoop archive
  classpath            prints the class path needed to get the
  credential           interact with credential providers
                       Hadoop jar and the required libraries
  daemonlog            get/set the log level for each daemon
  trace                view and modify Hadoop tracing settings

Most commands print help when invoked w/o parameters.
root@hdpnode:~# 

8. 构建Master和Slave容器

8.1 提交为镜像

现在,一切都配置好了,我们需要将这个容器进行提交为一个镜像。接下来将使用这个镜像作为基础镜像来构成我们的节点容器。

提交镜像使用命令docker commit,不过在此之前需要退出容器并停止容器。

root@hdpnode:/# exit    #退出并终止hdpnode容器
exit
docker@default:~$ docker ps -a    #查看所有容器
CONTAINER ID        IMAGE                    COMMAND                  CREATED             STATUS                      PORTS               NAMES
4f0f1383b82c        ubuntu                   "/bin/bash"              3 hours ago         Exited (0) 13 seconds ago                       hdpnode

然后提交:

docker@default:~$ docker commit -m="image with vim,java and hadoop" hdpnode azur_wxj/hdpnode
sha256:dc00fcf803bea1f9346ff8ced7d16c3184e8fbeddd48b79f8f096ef5bb52981f

提交的容器名是 hdpnode(也可以使用容器ID:4f0f),保存的镜像为 azur_wxj/hdpnode,默认标签是 lastest。现在可以来看看本地镜像了

docker@default:~$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
azur_wxj/hdpnode    latest              dc00fcf803be        20 seconds ago      922MB
ubuntu              latest              775349758637        2 weeks ago         64.2MB
docker@default:~$ 

8.2 创建主从容器

现在我们要创建三个容器(hadoop节点),一个主容器(master),以及两个从容器(slave1和slave2):

docker@default:~$ docker run -it --name master -h master -v /home/docker/azur_wxj:/home/azur_wxj azur_wxj/hdpnode /bin/bash
#[Ctrl+P+Q]保持容器运行,退出到Docker界面
docker@default:~$ docker run -it --name slave1 -h slave1 -v /home/docker/azur_wxj:/home/azur_wxj azur_wxj/hdpnode /bin/bash
#[Ctrl+P+Q]保持容器运行,退出到Docker界面
docker@default:~$ docker run -it --name slave2 -h slave2 -v /home/docker/azur_wxj:/home/azur_wxj azur_wxj/hdpnode /bin/bash
#[Ctrl+P+Q]保持容器运行,退出到Docker界面

由于每一次创建交互式容器都会自动切换到容器的交互式界面,所以为了创建下一个容器,需要用快捷键Ctrl+P+Q来退出容器到Docker,同时保持容器在后台运行而不终止。现在,我们可以看看活动中的三个容器:

docker@default:~$ docker ps
CONTAINER ID        IMAGE                    COMMAND             CREATED             STATUS              PORTS               NAMES
17ed290a06c1        azur_wxj/hdpnode         "/bin/bash"         3 minutes ago       Up 3 minutes                            slave2
cb8aca513704        azur_wxj/hdpnode         "/bin/bash"         3 minutes ago       Up 3 minutes                            slave1
8009ae423f13        azur_wxj/hdpnode         "/bin/bash"         4 minutes ago       Up 3 minutes                            master
docker@default:~$ 

9. 配置hosts文件以及公钥

9.1 获取容器的ip地址

在Docker界面,键入如下命令来查找容器的ip地址

docker@default:~$ docker inspect -f '{{.Name}} - {{.NetworkSettings.IPAddress }}' $(docker ps -aq) 
/slave2 - 172.17.0.7
/slave1 - 172.17.0.6
/master - 172.17.0.5
docker@default:~$ 

9.2 配置hosts文件

如果需要使用ssh命令来连接其他容器,命令参数需要时容器的ip地址。但是每次输入ip地址会非常繁琐且容易出错。我们可以配置hosts文件(/etc/hosts),用一个名称来指代ip地址。

对于每一个容器,我们执行如下操作(以master容器为例):

docker@default:~$ docker attach master  #切换到master容器
root@master:/# vim /etc/hosts  #修改hosts文件

127.0.0.1       localhost
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.5      master  #ip-名称 键值对
172.17.0.6      slave1
172.17.0.7      slave2

保存退出。

问题:Docker容器修改hosts文件重启不变
一个问题是,如果你关闭容器,下次重启容器后,发现hosts的内容由复原了,我们添加的内容都没有了。对此,在Docker容器修改hosts文件重启不变问题贴中,有回答指出:


“容器中的 /etc/hosts/etc/resolv.conf/etc/hostname 这三个文件不存在于镜像,而是存在于 /var/lib/docker/containers/。在启动容器的时候,通过挂载的形式将这些文件挂载到容器内部。因此,如果在容器中修改这些文件的话,修改部分不会存在于容器的top layer,而是直接写入这三个物理文件中。”

“为什么重启后修改内容不存在了?原因是:每次Docker在启动容器的时候,通过重新构建新的 /etc/hosts 文件,这又是为什么呢?原因是:容器重启,IP地址为改变,hosts文件中原来的IP地址无效,因此理应修改hosts文件,否则会产生脏数据。”

“有没有什么好的解决方法? 我印象中可以通过docker run命令的--add-host参数来为容器添加hostip的映射关系。”

9.3 问题:无法连接22端口

尝试使用ssh slave1来连接slave1容器时出现问题:

root@master:/# ssh slave1
ssh: connect to host slave1 port 22: Connection refused

结果报错:“无法连接到slave1的22端口,连接被拒绝”

关于这个我查阅了一些资料,说是因为配置完hosts文件需要执行/usr/sbin/sshd命令才能连接。但是当输入这个指令时又一个问题产生:

root@master:/# /usr/sbin/sshd 
Missing privilege separation directory: /var/run/sshd

我又查了资料,据说是ssh服务没有开启,通过ps -e|grep ssh命令来查询,发现确实没有ssh服务,所以需要用/etc/init.d/ssh start来开启,在查询就有了:

root@master:/# ps -e | grep ssh    #没有ssh服务
root@master:/# /etc/init.d/ssh start    #启动ssh服务
 * Starting OpenBSD Secure Shell server sshd                                                                                                                                            [ OK ] 
root@master:/# ps -e | grep ssh    #有了ssh服务
   37 ?        00:00:00 sshd
root@master:/# /usr/sbin/sshd     #执行sshd没有问题

所以对于每个容器,依次执行如下的2个命令:

root@slave2:/# /etc/init.d/ssh start    #开启服务
 * Starting OpenBSD Secure Shell server sshd                                                                                                                                            [ OK ] 
root@slave2:/# /usr/sbin/sshd     #执行sshd
root@slave2:/# 

但是还差一步,因为我们还没有配置被认证的容器公钥(见附录1:SSH服务 - 1.3 公钥登录),否则当ssh另一个容器,会让你输入容器的登录密码,但是我们容器根本没有配置密码,不管怎输入都是被denied的(´-ι_-`):

root@slave2:/# ssh master  #slave2尝试连接master
The authenticity of host 'master (172.17.0.5)' can't be established.
ECDSA key fingerprint is SHA256:R9jEjviby0wE1mWlnhnLzNxyAvPLpVhp4qN8b4r6dFA.
Are you sure you want to continue connecting (yes/no)? yesy^H
Warning: Permanently added 'master,172.17.0.5' (ECDSA) to the list of known hosts.
root@master's password: 

Permission denied, please try again.  #不管怎么输都是错误的

9.4 配置登录公钥

对于每一个容器,使用ssh-keygen来生成公钥和私钥(以master为例):

root@master:~# ssh-keygen -t rsa    #类型为rsa(缺省)
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):   #默认保存路径
Created directory '/root/.ssh'.
Enter passphrase (empty for no passphrase):   #不设置私钥保护口令
Enter same passphrase again:   #继续回车
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:qV8XlJh+VwhLndk94Autyg3jrc+jhbQpk3FHTEMLKJ0 root@master
The key's randomart image is:
+---[RSA 2048]----+
|      . o..+oo.+.|
|     . E  ++=++oo|
|      .   o=+o. o|
|         o..o .. |
|       .S=.oo..  |
|       .B @. o   |
|      .+ B.+.    |
|       .o.+o     |
|        .ooo.    |
+----[SHA256]-----+
root@master:~# ls ~/.ssh/    #查看私钥和公钥
id_rsa  id_rsa.pub
root@master:~# cat ~/.ssh/id_rsa.pub   #查看公钥内容
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD6qeWvdxOZYkaj+l+NlZUBPCIYi8sF5AvzhFbZMTysZsljooCiaNbMNC8vk1yrSvpiJwJ6gBlrBwKX3lxn2HFPZWHvkK0EO29NtN83RiCqkcx+28YoNISJ6vye0CP1rADMkmQsJXymWm/NS4bhtBK8LM5DAQmizTWwJZR/4ROSbWAfbBuBssdIcpdNHMHjhg9j5ZtQUYA/IsRJAFnBNnNXoD/c6E3ezAnSvG54vhRDgG4E70jlclfnonT8v6Slbh4OKBl+UqH5NmbDYdkoKQByoaUtj+v2wqP4/UNWCGUlXc1NrPxsdcDx1CZr0W7o8TdHUYCNLu+P9/b8qiuHLPEj root@master
root@master:~# 

现在我们得到了三个容器的公钥内容:

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD6qeWvdxOZYkaj+l+NlZUBPCIYi8sF5AvzhFbZMTysZsljooCiaNbMNC8vk1yrSvpiJwJ6gBlrBwKX3lxn2HFPZWHvkK0EO29NtN83RiCqkcx+28YoNISJ6vye0CP1rADMkmQsJXymWm/NS4bhtBK8LM5DAQmizTWwJZR/4ROSbWAfbBuBssdIcpdNHMHjhg9j5ZtQUYA/IsRJAFnBNnNXoD/c6E3ezAnSvG54vhRDgG4E70jlclfnonT8v6Slbh4OKBl+UqH5NmbDYdkoKQByoaUtj+v2wqP4/UNWCGUlXc1NrPxsdcDx1CZr0W7o8TdHUYCNLu+P9/b8qiuHLPEj root@master
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCwOfPhcnA3EH9Y1GxHk4uFgdq83eG3gl0HKUUi1nndnbF4Sl3MAC1Wg4nQOF2oSI8r0ksiBAU/R6MyUpvlJk/RZxHzCwd/uNsyYF0ExPDbFGbS9OkztkU/y9Co5V21lJl0iocGG8qf3/4CgDWdb7K7H6gsZ1c+oPZ2kFYseki5JUXHbQfbdf6xhdT+4+i6zcspEu34b8mLmwlODv48cvlsJw8SWinXlhWaAKlozOhdgDOnhKSl6IEzoFgi/7i7+zAErVkahZjLk07Mu6nirA5qo3ZAf5fi/OX4OQ2yEy8vQhg0Y8uN00XLcWm+qKfthGxMoALB6+SJN8XM1o+J8xUp root@slave1
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDB6tV+AULHzP6nvGnnU9IyhohAHR5KCgBxN73VL+6UwosdR+dZjN2mKiBxoLS8hJOiuecgXq3khxrj1GODyNEVgrH14kIOwtTrpxFyDEySyHB/Ym1HSl3au0VLLWjpS0tLOOqT9cKKH1fDAM5tu6bht/sBhSKknii2fyY/tMKu65AmWXRn/Oa+XnN4cNUP2oefxsWMEGaIVvDypHs00Frin1Gh10HVBVwQrprr1dY18oMUQYxZEjHtKT58e2xmCYttXIusv9r3I3ESqqzX5C37BeGoYgLQBC7Vo1P2qY9XlNlt05WMKVRbrvZvkABUcm4mdeBTi29gDkjTInJCbJh7 root@slave2

在每一个容器的 ~/.ssh/ 下建立一个名为 authorized_keys的文件,然后把三个公钥全部写入。你可以把每一个公钥都复制到一个文本文件中,然后创建完authorized_keys后一起复制。不过,我在创建完每一个容器的公钥后,将内容重定向到了挂载目录下的 /home/azur_wxj/sshkeys中(root@master:~# cat .ssh/id_rsa.pub >> /home/azur_wxj/sshkeys),这样,我可以直接将这个文件复制为authorized_keys,例如,对于master有:

root@master:~# cp /home/azur_wxj/sshkeys .ssh/authorized_keys
root@master:~# ls -l .ssh/authorized_keys 
-rw-r--r-- 1 root root 1179 Nov 16 08:10 .ssh/authorized_keys

当三个容器都完成,我们来试试互联:

root@master:~# ssh slave1  #master ssh 连接 slave1
....
Are you sure you want to continue connecting (yes/no)? yes  #第一次连接需要确认
....
root@slave1:~#     #连接到slave1
root@slave1:~# exit    #退出连接
logout
Connection to slave1 closed.
root@master:~#     #退回到master

第一次连接会有确认信息,输入yes后连接成功。此后连接就不再需要确认信息。至此,三个容器的互联配置完成。

10. 配置相关集群节点

现在三个容器可以互联了,我们现在开始配置hadoop,使这三个节点成为hadoop节点,其中master为namenode,而slave?为datanode。

以下的代码全部来自于docker环境下搭建hadoop集群(ubuntu16.04 LTS系统)

10.1 配置core-site.xml、yarn-site.xml和mapred-site.xml文件

每个容器对这三个文件的修改都是一致的。这三个文件都存在于 /usr/local/hadoop/etc/hadoop/ 之下:

  • core-site.xml
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<!-- Put site-specific property overrides in this file. -->
<configuration>
        <property>
                <name>fs.defaultFS</name>
                <value>hdfs://master:9000</value>
         </property>
        <property>
                <name>io.file.buffer.size</name>
                <value>131072</value>
        </property>
        <property>
                <name>hadoop.tmp.dir</name>
                <value>/usr/local/hadoop/tmp</value>
        </property>
</configuration>
  • yarn-site.xml
<?xml version="1.0"?>
<configuration>
    <property>
        <name>yarn.nodemanager.aux-services</name>
        <value>mapreduce_shuffle</value>
    </property>
    <property>
        <name>yarn.resourcemanager.address</name>
        <value>master:8032</value>
    </property>
    <property>
        <name>yarn.resourcemanager.scheduler.address</name>
        <value>master:8030</value>
    </property>
    <property>
        <name>yarn.resourcemanager.resource-tracker.address</name>
        <value>master:8031</value>
    </property>
    <property>
        <name>yarn.resourcemanager.admin.address</name>
        <value>master:8033</value>
    </property>
    <property>
        <name>yarn.resourcemanager.webapp.address</name>
        <value>master:8088</value>
    </property>
    <property>
        <name>yarn.nodemanager.aux-services.mapreduce.shuffle.class</name>
        <value>org.apache.hadoop.mapred.ShuffleHandler</value>
    </property>
</configuration>
  • mapred-site.xml

事实上文件夹下只有一个mapred-site.xml.template 模板文件,可以复制一个模板文件为 mapred-site.xml,然后填充信息。

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<!-- Put site-specific property overrides in this file. -->
<configuration>
    <property>
        <name>mapreduce.framework.name</name>
        <value>yarn</value>
    </property>
</configuration>

10.2 配置hdfs-site.xml

hdfs-site.xml 的配置稍有不同,因为它是区分namenode和datanode的。我们指定master为namenode,slave?为datanode,所以:

  • 对于master
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<!-- Put site-specific property overrides in this file. -->
<configuration>
    <property>
        <name>dfs.replication</name>
        <value>2</value>
    </property>
    <property>
        <name>dfs.namenode.name.dir</name>
        <value>file:/usr/local/hadoop/hdfs/name</value>
    </property>
</configuration>
  • 对于slave?
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
    <property>
        <name>dfs.replication</name>
        <value>2</value>
    </property>
    <property>
        <name>dfs.datanode.data.dir</name>
        <value>file:/usr/local/hadoop/hdfs/data</value>
    </property>
</configuration>

10.3 创建相关目录并格式化

我们在上面的 hdfs-site.xml 看到,配置文件制定了两个目录:

  • 对于namenode(master),指定了 /usr/local/hadoop/hdfs/name
  • 对于datanode(slave1、slave2),指定了 /usr/local/hadoop/hdfs/data

分别创建这两个目录。然后针对不同的节点进行格式化:

  • 对于master(NameNode):
root@master:~# hdfs namenode -format
  • 对于slave?(datanoe):
root@slave2:~# hdfs datanode -format

如果没有异常信息,格式化成功。

10.4 启动集群

在master中,使用命令

root@master:~# /usr/local/hadoop/sbin/start-all.sh

来启动集群,按照提示不断输入yes

我们可以使用jps来查询集群情况

root@master:~# jps
1169 Jps
774 ResourceManager
328 NameNode
458 DataNode
877 NodeManager
623 SecondaryNameNode
root@master:~# 

也可以使用如下命令来查询其他容器节点情况:

root@master:~# hadoop dfsadmin -report
Configured Capacity: 38390448128 (35.75 GB)
Present Capacity: 28696190976 (26.73 GB)
DFS Remaining: 28696141824 (26.73 GB)
DFS Used: 49152 (48 KB)
DFS Used%: 0.00%
Under replicated blocks: 0
Blocks with corrupt replicas: 0
Missing blocks: 0
Missing blocks (with replication factor 1): 0

-------------------------------------------------
Live datanodes (2):

Name: 172.17.0.5:50010 (master)
Hostname: master
Decommission Status : Normal
Configured Capacity: 19195224064 (17.88 GB)
DFS Used: 24576 (24 KB)
Non DFS Used: 3832627200 (3.57 GB)
DFS Remaining: 14348070912 (13.36 GB)
DFS Used%: 0.00%
DFS Remaining%: 74.75%
Configured Cache Capacity: 0 (0 B)
Cache Used: 0 (0 B)
Cache Remaining: 0 (0 B)
Cache Used%: 100.00%
Cache Remaining%: 0.00%
Xceivers: 1
Last contact: Sat Nov 16 09:14:27 GMT 2019


Name: 172.17.0.7:50010 (slave2)
Hostname: slave2
Decommission Status : Normal
Configured Capacity: 19195224064 (17.88 GB)
DFS Used: 24576 (24 KB)
Non DFS Used: 3832627200 (3.57 GB)
DFS Remaining: 14348070912 (13.36 GB)
DFS Used%: 0.00%
DFS Remaining%: 74.75%
Configured Cache Capacity: 0 (0 B)
Cache Used: 0 (0 B)
Cache Remaining: 0 (0 B)
Cache Used%: 100.00%
Cache Remaining%: 0.00%
Xceivers: 1
Last contact: Sat Nov 16 09:14:27 GMT 2019

至此,我们的Hadoop集群就搭建完成啦!(๑╹◡╹)ノ"""

附录

1. SSH服务

1.1 什么是ssh

关于ssh的相关介绍,可以参考博客:Linux之ssh服务介绍。下面综合该博文做个简单解释。

SSH(Secure Shell Protocol)是一种网络协议,默认状态下SSH服务提供俩个服务功能:

  • 类似telnet远程联机服务器的服务(ssh服务)
  • 类似FTP服务的sftp-server,借助SSH协议来传输数据的。

我们最开始使用XShell连接Docker时使用的命令ssh 192.168.99.100就是用到了第一种远程连接服务。

ssh使用了到了一种被称为 非对称加密 的算法。该算法需要两个密钥,任意指定一把为公钥,则另一把就称为私钥。经过公钥加密的信息只能通过私钥解开。因为指定是任意的,所以由私钥加密的信息也一定只能由公钥解开。因此,公钥、私钥只是为了区分而指定的,实际上两把秘钥中的任意一把加密的数据只能由另一把解开,反之亦然。

1.2 SSH登录连接模型与中间人攻击

服务器会产生一对公钥和私钥,当用户发起连接时,服务器将公钥发送给用户,用户用该公钥加密自己的密码,返给服务器。服务器用配对的私钥对其解密,如果正确则允许登录。

但是这里会有一个隐患,那就是如果有人冒充服务器,发给了用户一个伪造的公钥,用户将自己密码用该公钥加密后返给此中间人,他用私钥解码后就能获取用户的密码明文,这就是“中间人攻击”。

这里防范的关键是,用户如何鉴别该公钥是服务器发送的。如果是Https协议,尚有一种名为数字证书的技术来保证(关于数字证书,可以参考一篇写的很棒的博客: 一个故事教你看懂什么是数字证书,它的原理是什么?它的作用是什么?

但是很遗憾,SSH协议的公钥是没有证书中心(CA)公证的,也就是说,都是服务器自己签发的。所以,没有好办法,远程主机必须在自己的网站上贴出公钥指纹(公钥指纹指对用RSA算法生成长达1024位的公钥执行MD5算法而生成的128位摘要信息),以便用户自行核对。

也就是说,用户需要自行鉴别服务器公钥。所以第一次连接时,系统会给出警告,让用户自己评估风险。

当远程主机的公钥被接受以后,它就会被保存在文件 ~/.ssh/known_hosts 之中。下次再连接这台主机,系统就会认出它的公钥已经保存在本地了,从而跳过警告部分,直接提示输入密码。

1.3 公钥登录

用户每次ssh连接都需要输入密码,用服务器公钥加密在返给服务器,非常麻烦。所以希望有一种能够输入一次密码,下次就再不用输入的手段。

如何实现呢?这一次,需要服务器来识别用户。我们可以将SSH登录连接模型中的服务器和用户调换角色来想:在服务器和用户已经建立了连接时,用户自己可以生成一对公钥和私钥,然后将用户公钥告诉服务器。服务器将其存储在用户目录下的 ~/.ssh/authorized_keys 文件中。

下一次登录时,服务器发现该用户在自己这里已经存储了一个公钥,于是服务器生成一个随机字符串给用户,要求用户用配对的私钥加密在返给自己,自己这边使用公钥进行解密,比对结果是否一致,如果一致就允许用户登录。(这里用到了开始时我们说的公钥、私钥其中任意一把加密的信息可以由、且只能由另一把解开)

用户需要用命令ssh-keygen来生成一对公钥和私钥,默认的类型是RSA,也可以显式指定

ssh-keygen -t rsa

此时系统可能要求输入passphrase(口令)保证私钥安全(因为公钥和私钥会用文本文件存储,因此没有保护的私钥,任何有权限打开私钥文件的人都能看到),在本文中,我们不需要对私钥进行保护,也就直接回车跳过。

生成的公钥私钥在 ~/.ssh/ 目录下,分别叫做 id_rsa.pubid_rsa

下面的事情,就是想办法让服务器端在用户主目录下的 ~/.ssh/authorized_keys文件中写入id_rsa.pub内容。这有多种办法,比如,直接手动复制进去,因为是文本文件。(d(´ω`*))

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容