—— 步骤 ——
- 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.shvim 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 了
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
注意啦:公钥也需要拷贝到本服务器(如 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
初始化完毕后,在配置文件指定的目录下,就会生成 data
和 logs
两个文件
———— 接下来正式启动
① 首先启动 DFS:
在 Hadoop001 中操作(配置了 NameNode 的)
进入 sbin
,执行 ./start-dfs.sh
命令
然后执行 jps
看是否启动成功,和下图一样说明成功了
如果上面的配置都没有出错的话,这里应该是很顺利就启动的了
可能出现的错误有:
1、没有配置免密登录,权限不足导致启动失败
2、只把公钥给了其他容器,没有给自己,也会导致权限不足的失败
所以说,免密登录在这里,还是非常非常重要的!!!
② 然后启动 YARN:
在 Hadoop002 中操作(配置了 ResourceManager的)
依旧在 sbin
中执行 ./start-yarn.sh
这个步骤,如果服务器性能不太行的话,会卡死(像我这样—_—卡了两个小时)
从晚上九点卡到十一点,还好没放弃 2333
不过在这里也要提醒一下,卡的时候还是经量别强制重启服务器,就像我下午三点多那次,强制重启导致集群出了点问题跑不起来,最后是清空了数据再重启才解决的,慎重噢!
当时出现的错误,是这个
ssh: connect to host master port 22: No route to host
找了好久的解决方案,都是说什么防火墙、没固定 IP,没网等等的情况
但我这些都没问题,还是不行,最后就只能重启了(后来者如果出了这个问题,有解决方案的,也可以分享一下哦)
如果启动完毕的话,就执行 jps
检查下各个容器是否如我们规划的那样配置完毕
像我下面这种情况就是 OK 的:
③ 测试集群
后面来的,有添加端口映射,就不需要 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
接着使用一些简单的功能测试一下:上传文件到集群(在 hadoop001 中执行)
每一步都可以在 9870 的 Browse Directory 窗口观察:
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)