—— 步骤 ——

  • 0. 背景
  • 1. 创建出第一个容器
  • 2. 为该容器配置 java 和 hadoop 环境
  • 3. 配置允许外界通过 ssh 连接容器
  • 4. 由该容器克隆出多个容器
  • 5. 为各个容器配置 ssh 免密登录
  • 6. 开始编写集群配置
  • 7. 启动集群

2021-10-6 补充(务必看一下)

好叭-- 我承认使用 nginx 进行代理是疏忽了,到后面使用 Java 访问集群的时候会出现问题
正确的方式应该是通过端口映射,将需要的端口(9870,9868,8088,8020)和主机端口进行映射,在外网直接访问映射后的端口就 OK 了

所以在容器启动(docker run)就可以为对应的容器映射端口了
(下边已作修改,添加了端口映射的,nginx 那一部分就可以不用看啦)

如果绒球已经启动,又不想直接干掉的话(就像我—_—),也可以为容器动态添加端口映射
可以参照这篇文章:【Docker之轨迹】为正在运行中的容器动态添加端口映射


0. 背景

看到网上的教程,都是使用虚拟机克隆出多台虚拟机模拟集群
但在真正的服务器上,发现这种方法行不通了
这时候就想起 Docker 了,仔细想象 Docker 学完到现在还没真正实战过,这次正好是一个好机会

实现思路也很简单,在 Docker 中创建多个 Centos 容器
每个容器当作一台服务器用,就可以模拟出集群环境了

好,动手!
还不熟悉 Docker 的,可以参考这里:【Docker之轨迹】Docker 入门使用(穿插踩坑经历)

本博客 Hadoop 版本为 3.1.3,参考尚硅谷视频


1. 创建出第一个容器

1)	首先拉取 centos
	docker pull centos

2)	创建一个 centos
!!!	后来补充,在这里添加端口映射,hadoop001 要用到的端口是: 9870 和 8020,主机端口自定义

	docker run --privileged=true --name=hadoop001 -p xxx:9870 xxx:8020 -dit centos /sbin/init
	
	-dit 表示交互式且后台运行,少了 -it 容器会自动停止
	--privileged=true 和 /sbin/init 表示特权启动,后面可能需要实用到 service 命令,需要由这个权限才可以
	没有上面这个,再使用时会报 PID 1 的错误哦

2. 为该容器配置 java 和 hadoop 环境

将 jdk 和 hadoop 从主机复制到该容器中

docker cp <jdk路径> <容器id>:<容器路径>
docker cp <hadoop路径> <容器id>:<容器路径>

在 /etc/profile.d/ 下创建文件 mydev.sh
vim mydex.sh 文件内容如下

# Java
export JAVA_HOME=<jdk在容器的路径>
export PATH=$PATH:$JAVA_HOME/bin

# Hadoop
export HADOOP_HOME=<hadoop在容器的路径>
export PATH=$PATH:$HADOOP_HOME/bin
export PATH=$PATH:$HADOOP_HOME/sbin

# Constant
export HDFS_NAMENODE_USER=root
export HDFS_DATANODE_USER=root
export HDFS_SECONDARYNAMENODE_USER=root
export YARN_RESOURCEMANAGER_USER=root
export YARN_NODEMANAGER_USER=root

第一第二个是两个环境,最后一个是一些常量,在启动集群的时候,还要用到这些常量,否则可能启动失败
最后执行 source /etc/profile 使配置文件生效


3. 配置允许外界通过 ssh 连接容器

我们需要让各个容器产生联系,才能构成集群,但默认情况下,容器并不能被其他容器直接通过 ssh 访问,所以这里需要配置

1)	首先需要设置密码(输入以下命令,回车设置密码即可)
	passwd root
	大概率会提示没有该命令,就得下载它,然后再设置就好了
	yum install passwd

2)	然后下载 ssh,以下三步(安装了服务端和客户端)
	yum install openssh
	yum install openssh-server
	yum install openssh-clients

3)	然后检查配置文件,查看以下两个配置是否一样(默认应该都是这样的)
	vim /etc/ssh/sshd_config
	  PermitRootLogin yes
	  PasswordAuthentication yes
	这两个配置允许外网通过 ssh 连接该服务器(容器)

4)	如果发现不是这样,就修改它,同时需要重启 ssh 是指生效
	service sshd restart
	同样该命令可能不存在,就下载它
	yum install initscripts

通过上面一顿操作,外网已经可以通过 ssh 访问该容器了,我们可以试试:

1)	首先在容器内,使用 ifconfig 查看 IP 地址
	ifconfig
	没有这个命令的话,就下载他[doge]
	yum install net-tools

2)	在服务器本机中,通过 ssh 访问该 IP 地址尝试连接
	ssh <IP>
	然后要求输入密码,就是上面我们已经设置了的密码

ohhh 然后我们会发现,成功进入容器内部了,这次时通过 ssh 而不是通过 docker 了

docker网络集群 docker 集群方案_IP


4. 由该容器克隆出多个容器

首先要克隆出多个容器,就需要将该容器打包成镜像,再由该镜像创建出多个容器,步骤如下:

1)	创建镜像
	docker commit -a='IceClean' -m='Hadoop cluster example through docker by IceClean' 5a8 hadoop-docker

2)	由该进项克隆多个容器模拟集群(这里以三台服务器的集群为例)
!!!	后来补充:hadoop002 要用到的端口是: 8088,主机端口自定义
	docker run --privileged=true --name=hadoop002 -p xxx:8088 -dit hadoop-docker /sbin/init
	
!!!	后来补充:hadoop003 要用到的端口是: 9868,主机端口自定义
	docker run --privileged=true --name=hadoop003 -p xxx:9868 -dit hadoop-docker /sbin/init

在这些容器生成好后,为它们配置一个 IP 地址映射
往后就不用去记住每一个容器的 IP 地址了

但同时由于 docker 网桥模式的特性,当容器重启时,会按顺序为该容器分配一个 IP,使得该 IP 地址不是固定的会变化,这对我们已经做的映射非常不友好,所以接下来的第一步,是为每个容器固定一个 IP 地址(这里需要借助 pipework)

1)	安装 pipewoek(下载并复制可执行文件到 bin 目录)
	git clone https://github.com/jpetazzo/pipework
	cp ~/pipework/pipework /usr/local/bin/

2)	安装 bridge-utils
	yum -y install bridge-utils

3)	创建网络(这里 IP 地址自定义)
	brctl addbr br0
	ip link set dev br0 up
	ip addr add 172.16.10.1 dev br0

4)	为各个容器设置固定的 IP
	pipework br0 <容器名> IP/掩码,如:
	pipework br0 hadoop001 172.16.10.10/24
	pipework br0 hadoop002 172.16.10.11/24
	pipework br0 hadoop003 172.16.10.12/24

5)	测试这几个 IP 是否可用
	ping 172.16.10.10

6)	中途如果不小心操作错了(比如说我--),想删除网络或IP,可执行如下
	删除网络:	ip link set dev br0 down 
				brctl delbr
	删除 IP:	ip link set dev <名称> down 
				其中<名称>来自于 ifconfig 查找到的 veth1plxxx

这样就成功地为每一个容器固定好 IP 地址了,接下来就可以在容器内配置映射了

1)	在每一台容器中,修改 hosts 文件
	vim /etc/hosts
		比如说我的,就在文件中增加:
		172.16.10.10 hadoop001
		172.16.10.11 hadoop002
		172.16.10.12 hadoop003

3)	配置完毕后,我们就可以直接通过名字轻松连接各个容器了,如:
	ssh hadoop003

5. 为各个容器配置 ssh 免密登录

容器之间虽然能正常互相连接了,但如果每次都需要输入密码,那是及其不方便的
再而,到后面启动集群的时候,如果没有免密登录,既有可能会失败噢

所以我们可以为各个集群中的容器配置免密登录,实现各个容器之间的顺畅连接
首先我们来了解一下免密登录的原理(以 hadoop001 和 hadoop002 为例)

在 hadoop001 中,可以使用 ssh-key-gen 生成密钥
—— 分为公钥和私钥,私钥保密,公钥可以给别人
然后如果将 hadoop001 的公钥拷贝给 hadoop002
—— 通过命令拷贝,该公钥会保存在 hadoop002 中的 Authorized_keys 中
往后 hadoop001 想要连接 hadoop002,就可以直接通过公钥连接,无需密码

所以我们可以知道,免密登录的实现,就是将目标服务器的公钥拷贝到对方的服务器上,这样对方即可免密登录该目标服务器,而如果两台服务器要互相免密登录,当然就要互相拷贝到对方的公钥啦~ 开始行动!

1)	首先在 hadoop001 中,生成公钥和私钥(一路回车到底就行了,不需要输入内容)
	ssh-keygen -t rsa
	其中 id_rsa 为私钥,id_rsa.pub 为公钥

2)	将 hadoop001 的公钥拷贝到 hadoop002
	ssh-copy-id hadoop002

出现这个提示说明添加成功了,以后我们可以直接通过 ssh hadoop002 连接 hadoop002

docker网络集群 docker 集群方案_IP_02


注意啦:公钥也需要拷贝到本服务器(如 hadoop001 将自己的公钥拷贝到 hadoop001)不然自己五大免密登录自己哦

然后对于其他的容器也是这样做,把每个容器的公钥都拷贝到自己以及另外两台服务器上,就实现了三台服务器互相的免密登录,完毕~


6. 开始编写集群配置

前面做足了准备工作,把每一个容器都配置好了
现在要正式开始集群的搭建啦,首先是集群的配置:

安排一下集群的部署:

hadoop001

hadoop002

hadoop003

HDFS

NameNode

DataNode

DataNode

SecondaryNameNode

DataNode

YARN

NodeManager

ResourceManager

NodeManager

NodeManager

hadoop001 兼任主节点,配置 NameNode hadoop002 兼任资源管理,配置 ResourceManager hadoop003 兼任备份结点,配置 SecondaryNameNode

接着按照这个规划,修改 hadoop 配置文件
先说明一下各个配置文件及其位置(~ 表示 hadoop 所在目录)
系统配置文件:~/share/doc/hadoop 用户自定义配置文件:~/etc/hadoop

需要我们自定义配置的文件在 etc 目录下,总共有四个,分别是:
core-site.xml,``

① 配置 hadoop001

首先到主节点 hadoop001 里边,配置 core-site.xml 下面是我自己的配置,需要配置两个东西
① 将 hadoop001 设置为主节点,端口推荐为 8020
② 将数据的默认存储位置改为 hadoop 目录下的 data 文件夹(如果不存在的话,会自动创建)

<configuration>
    <!-- 指定 NameNode 地址 -->
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://hadoop001:8020</value>
    </property>

    <!-- 指定 hadoop 数据存放的目录 -->
    <property>
        <name>hadoop.tmp.dir</name>
        <value>/home/hadoop/hadoop-3.1.3/data</value>
    </property>
</configuration>

然后配置 hdfs-site.xml

<configuration>
    <!-- nn web 端访问地址 -->
    <property>
        <name>dfs.namenode.http-address</name>
        <value>hadoop001:9870</value>
    </property>

    <!-- 2nn web 端访问地址-->
    <property>
        <name>dfs.namenode.secondary.http-address</name>
        <value>hadoop003:9868</value>
    </property>

	<!-- 添加 web 端访问文件的权限 -->
	<!-- 这时后面补充上来的 2333,这里先天坑啦~ -->
	<property>
        	<name>dfs.webhdfs.enabled</name>
        	<value>true</value>
        </property>
</configuration>

然后是 yarn-site.xml

<configuration>
    <!-- 指定 MR 走 shuffle -->
    <property>
        <name>yarn.nodemanager.aux-services</name>
        <value>mapreduce_shuffle</value>
    </property>

    <!-- 指定 ResourceManager 的地址 -->
    <property>
        <name>yarn.resourcemanager.hostname</name>
        <value>hadoop002</value>
    </property>

    <!-- 环境变量的继承 -->
    <property>
        <name>yarn.nodemanager.env-whitelist</name>
        <value>JAVA_HOME,HADOOP_COMMON_HOME,HADOOP_HDFS_HOME,HADOOP_CONF_DIR,CLASSPATH_PREPEND_DISTCACHE,HADOOP_YARN_HOME,HADOOP_MAPRED_HOME</value>
    </property>
</configuration>

最后配置 mapred-site.xml

<configuration>
    <!-- 指定 MapReduce 程序运行在 Yarn 上 -->
    <property>
        <name>mapreduce.framework.name</name>
        <value>yarn</value>
    </property>
</configuration>

② 将编写好的四个配置文件,同步到其他两台服务器上(hadoop002,hadoop003)

只需执行下面这一句,就能将四个配置文件远程复制到另外一个容器中
由于之前设置了免密登录,所以它可以直接远程复制,而不需要输入密码,可以很方便地在哥哥容器之间拷贝文件(hadoop003 也同理)

scp core-site.xml hdfs-site.xml yarn-site.xml mapred-site.xml root@hadoop002:/home/hadoop/hadoop-3.1.3/etc/hadoop

③ 配置 workers

同样是在 hadoop001 中,修改文件 /etc/hadoop/workers 将里边的内容去掉(把 localhost 删掉)然后换成我们的三个容器:
注意:行末不能有空格,以及不能有空行的出现

hadoop001
hadoop002
hadoop003

然后将该文件同步到其他两个容器

scp workers root@hadoop002:/home/hadoop/hadoop-3.1.3/etc/hadoop/
scp workers root@hadoop003:/home/hadoop/hadoop-3.1.3/etc/hadoop/

7. 启动集群

第一次启动时,需要进行初始化(往后就不用了,再初始化会清空数据哦)

hdfs namenode -format

初始化完毕后,在配置文件指定的目录下,就会生成 datalogs 两个文件

———— 接下来正式启动

① 首先启动 DFS:
在 Hadoop001 中操作(配置了 NameNode 的)

进入 sbin,执行 ./start-dfs.sh 命令

然后执行 jps 看是否启动成功,和下图一样说明成功了

docker网络集群 docker 集群方案_docker网络集群_03


如果上面的配置都没有出错的话,这里应该是很顺利就启动的了

可能出现的错误有:

1、没有配置免密登录,权限不足导致启动失败

2、只把公钥给了其他容器,没有给自己,也会导致权限不足的失败

所以说,免密登录在这里,还是非常非常重要的!!!

② 然后启动 YARN:
在 Hadoop002 中操作(配置了 ResourceManager的)

依旧在 sbin 中执行 ./start-yarn.sh 这个步骤,如果服务器性能不太行的话,会卡死(像我这样—_—卡了两个小时)

docker网络集群 docker 集群方案_docker网络集群_04


从晚上九点卡到十一点,还好没放弃 2333

不过在这里也要提醒一下,卡的时候还是经量别强制重启服务器,就像我下午三点多那次,强制重启导致集群出了点问题跑不起来,最后是清空了数据再重启才解决的,慎重噢!

当时出现的错误,是这个
ssh: connect to host master port 22: No route to host

找了好久的解决方案,都是说什么防火墙、没固定 IP,没网等等的情况
但我这些都没问题,还是不行,最后就只能重启了(后来者如果出了这个问题,有解决方案的,也可以分享一下哦)

如果启动完毕的话,就执行 jps 检查下各个容器是否如我们规划的那样配置完毕

像我下面这种情况就是 OK 的:

docker网络集群 docker 集群方案_IP_05


③ 测试集群

后面来的,有添加端口映射,就不需要 nginx 代理啦!!!

最后的最后,就可以通过外网访问了
如果直接使用容器的 IP 进行访问,大概是不行的(不知道是不是我装了 nginx 的原因)
但使用 nginx 进行代理却很方便解决这个问题,下面是实现的步骤

先将安全组和防火墙的 9870,9868 以及 8088 端口打
分别对应 NameNode SecondaryNameNode ResourceManager

然后让 nginx 代理这三个端口,转发到对应的 hadoop 容器完成访问,如下:

server {
    listen          9870;
    server_name     www.xxx.xxx;
    location / {
        proxy_pass http://hadoop001:9870;
    }
}

server {
    listen          9868;
    server_name     www.xxx.xxx;
    location / {
        proxy_pass http://hadoop003:9868;
    }
}   

server {
    listen          8088;
    server_name     www.xxx.xxx;
    location / {
        proxy_pass http://hadoop002:8088;
    }
}

# 这里也是后面发现,然后回来填坑的,将对容器的访问交给 nginx  代理
server {
   	listen          9864;
   	server_name     www.xxx.xxx;
   	location / {
       	proxy_pass http://hadoop001:9864;
   	}
}

最后在外网,直接通过 域名+端口号 就可以访问到 hadoop 集群啦!!看到一下的页面算成功(三张图对应端口顺序:9870,9868,8088)

后面上来的填坑,需要修改本机的 hosts 文件(想要详细了解的,看下边实操记录哦)

位置:C:\Windows\System32\drivers\etc\hosts
在这里边加上(域名可以是服务器和虚拟机的)
域名 hadoop001
域名 hadoop002
域名 hadoop003

docker网络集群 docker 集群方案_hadoop_06


docker网络集群 docker 集群方案_服务器_07

docker网络集群 docker 集群方案_docker网络集群_08

接着使用一些简单的功能测试一下:上传文件到集群(在 hadoop001 中执行)

每一步都可以在 9870 的 Browse Directory 窗口观察:

docker网络集群 docker 集群方案_hadoop_09

1)	创建文件夹(注意这里的根路径 '/' 是指集群的根路径,往后都是按照这个根路径来)
	hadoop fs -mkdir /firstDir

2)	上传文件(随便上传一个,一下表示将 README.txt 上传到集群根目录下的 firstDir 文件夹)
	hadoop fs -put README.txt /firstDir

3)	点击文件,可以查看和下载文件,至此测试完毕(还存在问题的话,可以看下面黑框)

4)	执行 wordcount 函数,并将结果保存在 output 文件夹中

下面这部分是实操记录,已经对上文有做修改了,可以跳过这个黑框往下看

——————	好叭,苦逼的我发现,文件内容预览不了,到网上一查发现配少了个东西
		明明是一步步跟着视频做的,不会又要等两个小时把 v…v
		不过还好,只需要重启 NameNode,步骤如下(当然我已经在前面有补充了,这里应该不用管)
		
		在 hdfs-site.xml 添加如下配置,然后分发到各个容器,再重启就行了
		<property>
        	<name>dfs.webhdfs.enabled</name>
        	<value>true</value>
        </property>

——————	Why?怎么还是访问不了?这时的我终于打开了控制台,发现了这个:
		http://hadoop001:9864/webhdfs/v1/firstDir/test.txt?op=OPEN&namenoderpcaddress=hadoop001:8020&offset=0
		
		噢,秒懂了
		我们之前是使用 nginx 代理的,有提到不能直接使用容器名访问
		但这个请求是自动发起的,怎么让它依旧访问到我们自己的服务器,让 nginx 代理呢?
		这时候就需要修改本机的 hosts 文件了,改变域名和 IP 地址的映射关系

		位置:C:\Windows\System32\drivers\etc\hosts
		在这里边加上:
		域名 hadoop001
		域名 hadoop002
		域名 hadoop003
		
		其中域名是服务器的 IP (当然也可以是虚拟机的)
		这一段表示,将三个容器的名称都变成我们的域名,这样就能成功地走我们服务器的代理啦
		当然了,有了这个,以后我们也可以直接通过容器名,访问到上面列出来的三个页面了
		(突然发现在写这篇博客的过程中,收获了很多网络的知识呀 2333)

		最后的最后,当然是配置 nginx 啦(这里我也有补充在上面了~)
		server {
        	listen          9864;
        	server_name     www.xxx.xxx;
        	location / {
            	proxy_pass http://hadoop001:9864;
        	}
    	}

至此,测试完毕!


每一把刀,都插在恰到合适的位置(IceClean)