记录在centos7上安装docker环境,并基于centos7镜像制作hadoop的image,制作的镜像希望开箱即用,包含SSH、免密登录、JDK8、Hadoop3.3.6等配套环境,并能将免密登录凭证、Hadoop配置环境和数据持久化到宿主机,避免容器重启后数据丢失。
开始动手(👉゚ヮ゚)👉
- docker环境安装前面有专门记录,不在重复记录。
- 拉取最新的centos,镜像为centos7
docker pull centos
- 创建
/docker/hadoop/dockerfile
目录,存放制作镜像的过程文件mkdir -p /docker/hadoop/Dockerfile
- 将所需的JDK和Hadoop文件放在该目录下
ls -al total 353712 rw-r--r--. 1 root root 216648064 Jan 25 16:58 hadoop-3.3.6.tar.gz rw-r--r--. 1 root root 145535989 Jan 8 16:03 jdk-8u301-linux-x64.tar.gz
- 在宿主机创建公钥和私钥,并且将公钥写入authorized_keys,后面共享给容器挂载,做到容器之间互相免登录
#创建存放密钥的目录 mkdir /docker/hadoop/Dockerfile/sshkey #创建密钥,一路回车 ssh-keygen -t rsa -f /docker/hadoop/Dockerfile/sshkey/hadoop_docker_key #将公钥写入authorized_keys文件 #查看密钥 ls -al /docker/hadoop/Dockerfile/sshkey/ total 20 drwxr-xr-x. 2 root root 4096 Jan 25 17:32 . drwxr-xr-x. 3 root root 4096 Jan 25 17:25 .. rw-r--r--. 1 root root 400 Jan 25 17:32 authorized_keys rw-------. 1 root root 1675 Jan 25 17:29 hadoop_docker_key rw-r--r--. 1 root root 400 Jan 25 17:29 hadoop_docker_key.pub
- 创建dockerfile文件,基于前面拉去的centos官方镜像制作,文件内容如下:
# 使用 CentOS 7 作为基础镜像 FROM centos:7 # 维护者信息 MAINTAINER fuguang <[email protected]> # 设置工作目录 WORKDIR /root # 安装 SSH 服务 RUN yum -y install openssh-server openssh-clients && \ ssh-keygen -A && \ echo 'root:123456' | chpasswd # 授予root用户容器的全部权限,暂时务必要,先注释掉 #RUN echo "root ALL=(ALL) ALL" >>/etc/sudoers # 挂载密钥文件 COPY sshkey/hadoop_docker_key /root/.ssh/id_rsa COPY sshkey/hadoop_docker_key.pub /root/.ssh/id_rsa.pub COPY sshkey/authorized_keys /root/.ssh/authorized_keys # 设置 SSH 密钥文件权限 RUN chmod 600 /root/.ssh/id_rsa && \ chmod 644 /root/.ssh/id_rsa.pub && \ chmod 600 /root/.ssh/authorized_keys # 允许 SSH 免密登录 RUN echo "StrictHostKeyChecking no" >> /etc/ssh/ssh_config # 安装 JDK 8 COPY jdk-8u301-linux-x64.tar.gz /root/ RUN tar -zxvf jdk-8u301-linux-x64.tar.gz && \ mv jdk1.8.0_301 /opt/ && \ rm jdk-8u301-linux-x64.tar.gz # 设置java环境变量 RUN echo "export JAVA_HOME=/opt/jdk1.8.0_301" >> /etc/profile.d/java8.sh && \ echo "export PATH=\$PATH:\$JAVA_HOME/bin" >> /etc/profile.d/java8.sh # 安装 Hadoop 3.3.6 COPY hadoop-3.3.6.tar.gz /root/ RUN tar -zxvf hadoop-3.3.6.tar.gz && \ mv hadoop-3.3.6 /opt/ && \ rm hadoop-3.3.6.tar.gz # 设置Hadoop环境变量 # flink 1.13 版本后如果使用基于 hdfs 的保存点功能,需要依赖hadoop 的 clpasspath RUN echo "export HADOOP_HOME=/opt/hadoop-3.3.6" >> /etc/profile.d/hadoop3.sh && \ echo "export PATH=\$PATH:\$HADOOP_HOME/bin:\$HADOOP_HOME/sbin" && \ echo "export HADOOP_CLASSPATH= /opt/pluginJar/*.jar hadoop classpath">> /etc/profile.d/hadoop3.sh # 开放 22 端口 EXPOSE 22 # 启动 SSH 服务 CMD ["/usr/sbin/sshd", "-D"]
- 基于dockerfile文件构建镜像,并测试ssh服务、免密、java环境是否正常
# 增加`--network host`参数避免安装过程中网络问题找不到资源 docker build --network host -f Dockfile/dockerfile -t centos7-hadoop3:v1 Dockfile/ # 运行容器test1和容器test2 进行测试 docker run --name test1 -p 60020:22 -d centos7-hadoop3:v1 docker run --name test2 -p 60021:22 -d centos7-hadoop3:v1 # ssh 登录 test1 ssh -p 60020 [email protected] # 在从test1 ssh登录 test2 ssh -p 60021 [email protected] # 查看java环境 java -version # 查看Hadoop环境 hadoop version
可以从test1免密登录到test2及返回java、Hadoop版本信息,说明镜像的免密及java环境部署功能正常。
目前发现一个问题,宿主机使用ssh
登录时正常;使用docker exec -it
进入容器时,java和Hadoop的环境变量会失效,需要在容器内运行source
命令使其生效#使用ssh 登录 ssh -p 60021 [email protected] # 环境变量正常 java -version hadoop version # 使用docker直接进入容器时 docker exec -it test1 sh # 环境变量不正常,提示命令无法找到 java -version hadoop version # 必须重新运行source命令使其生效 source /etc/profile.d/java8.sh source /etc/profile.d/hadoop3.sh
出现这个问题的原因在于 Docker 容器和 SSH 会话的环境变量加载机制的不同。
SSH 会话: 当你通过 SSH 连接到容器时,会启动一个登录 shell。在这种情况下,Bash 会读取并执行 /etc/profile
以及 /etc/profile.d/*.sh
文件中的脚本,从而加载环境变量。因此,通过 SSH 连接时,JAVA_HOME
和 PATH
环境变量按预期加载。
docker exec 命令: 相反,当你使用 docker exec -it container-name sh
进入容器时,通常会启动一个非登录 shell。非登录 shell 不会自动执行 /etc/profile
或 /etc/profile.d/*.sh
文件中的脚本,因此 JAVA_HOME
和 PATH
环境变量不会自动加载。
为了解决这个问题,你可以在 Dockerfile 中使用 ENV
指令来设置环境变量,而不是将它们写入 /etc/profile.d/*.sh
文件。使用 ENV
指令设置的环境变量会在容器的所有会话中自动生效,无论是通过 SSH 还是 docker exec
命令。
# 设置java环境变量
# RUN echo "export JAVA_HOME=/opt/jdk1.8.0_301" >> /etc/profile.d/java8.sh && \
# echo "export PATH=\$PATH:\$JAVA_HOME/bin" >> /etc/profile.d/java8.sh
# 修改为:
ENV JAVA_HOME=/opt/jdk1.8.0_301
ENV PATH=$PATH:$JAVA_HOME/bin
# flink 1.13 版本后如果使用基于 hdfs 的保存点功能,需要依赖hadoop 的 clpasspath
# RUN echo "export HADOOP_HOME=/opt/hadoop-3.3.6" >> /etc/profile.d/hadoop3.sh && \
# echo "export PATH=\$PATH:\$HADOOP_HOME/bin:\$HADOOP_HOME/sbin" && \
# echo "export HADOOP_CLASSPATH= /opt/pluginJar/*.jar hadoop classpath">> /etc/profile.d/hadoop3.sh
# 修改为:
ENV HADOOP_HOME=/opt/hadoop-3.3.6
ENV PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
# RUN export HADOOP_CLASSPATH=`hadoop classpath`
修改为以上后发现结果和前面相反: 宿主机使用docker exec -it
进入容器时正常; 使用ssh
登录时,java和Hadoop的环境变量会失效.为了让两种情况环境变量均可生效,同时使用两种环境变量配置方法。
# 设置java环境变量
# ssh生效
RUN echo "export JAVA_HOME=/opt/jdk1.8.0_301" >> /etc/profile.d/java8.sh && \
echo "export PATH=\$PATH:\$JAVA_HOME/bin" >> /etc/profile.d/java8.sh
# docker exec生效
ENV JAVA_HOME=/opt/jdk1.8.0_301
ENV PATH=$PATH:$JAVA_HOME/bin
# flink 1.13 版本后如果使用基于 hdfs 的保存点功能,需要依赖hadoop 的 clpasspath
# ssh 下生效
RUN echo "export HADOOP_HOME=/opt/hadoop-3.3.6" >> /etc/profile.d/hadoop3.sh && \
echo "export PATH=\$PATH:\$HADOOP_HOME/bin:\$HADOOP_HOME/sbin" && \
echo "export HADOOP_CLASSPATH= /opt/pluginJar/*.jar hadoop classpath">> /etc/profile.d/hadoop3.sh
# docker exec生效:
ENV HADOOP_HOME=/opt/hadoop-3.3.6
ENV PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
# RUN export HADOOP_CLASSPATH=`hadoop classpath`
将Hadoop使用的用户调整为hdfs,并且调整ssh密钥和Hadoop目录的权限,最终dockerfile为:
# 使用 CentOS 7 作为基础镜像
FROM centos:7
# 维护者信息
MAINTAINER fuguang <[email protected]>
# 设置工作目录
WORKDIR /root
# 安装 SSH 服务
RUN yum -y install openssh-server openssh-clients && \
ssh-keygen -A && \
echo 'root:123456' | chpasswd
# 授予root用户容器的全部权限,暂时务必要,先注释掉
# RUN echo "root ALL=(ALL) ALL" >>/etc/sudoers
# 安装 JDK 8
COPY jdk-8u301-linux-x64.tar.gz /root/
RUN tar -zxvf jdk-8u301-linux-x64.tar.gz && \
mv jdk1.8.0_301 /opt/ && \
rm jdk-8u301-linux-x64.tar.gz
# 设置java环境变量
# ssh生效
RUN echo "export JAVA_HOME=/opt/jdk1.8.0_301" >> /etc/profile.d/java8.sh && \
echo "export PATH=\$PATH:\$JAVA_HOME/bin" >> /etc/profile.d/java8.sh
# docker exec生效
ENV JAVA_HOME=/opt/jdk1.8.0_301
ENV PATH=$PATH:$JAVA_HOME/bin
# 安装 Hadoop 3.3.6
COPY hadoop-3.3.6.tar.gz /root/
RUN tar -zxvf hadoop-3.3.6.tar.gz && \
mv hadoop-3.3.6 /opt/ && \
rm hadoop-3.3.6.tar.gz
# 创建Hadoop 临时文件夹
RUN mkdir /opt/hadoop-3.3.6/tmp
# 设置Hadoop环境变量
# flink 1.13 版本后如果使用基于 hdfs 的保存点功能,需要依赖hadoop 的 clpasspath
# ssh 下生效
RUN echo "export HADOOP_HOME=/opt/hadoop-3.3.6" >> /etc/profile.d/hadoop3.sh && \
echo "export PATH=\$PATH:\$HADOOP_HOME/bin:\$HADOOP_HOME/sbin" >> /etc/profile.d/hadoop3.sh && \
echo "export HADOOP_CLASSPATH= `hadoop classpath`" >> /etc/profile.d/hadoop3.sh
# docker exec生效
ENV HADOOP_HOME=/opt/hadoop-3.3.6
ENV PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
# flink 1.13 版本后如果使用基于 hdfs 的保存点功能,需要依赖hadoop 的 clpasspath
# RUN export HADOOP_CLASSPATH=`hadoop classpath`
#创建Hadoop运行用户和用户组hdfs
RUN groupadd -r hdfs && \
useradd -r -m -g hdfs hdfs && \
echo "hdfs:hadoop" | chpasswd && \
chown -R hdfs:hdfs /opt/hadoop-3.3.6
# 挂载密钥文件
RUN mkdir -p /home/hdfs/.ssh
COPY sshkey/hadoop_docker_key /home/hdfs/.ssh/id_rsa
COPY sshkey/hadoop_docker_key.pub /home/hdfs/.ssh/id_rsa.pub
COPY sshkey/authorized_keys /home/hdfs/.ssh/authorized_keys
# 设置 SSH 密钥文件权限
RUN chmod 600 /home/hdfs/.ssh/id_rsa && \
chmod 644 /home/hdfs/.ssh/id_rsa.pub && \
chmod 600 /home/hdfs/.ssh/authorized_keys && \
chown -R hdfs:hdfs /home/hdfs/.ssh
# 允许 SSH 免密登录
RUN echo "StrictHostKeyChecking no" >> /etc/ssh/ssh_config
# 指定Hadoop 运行的用户
ENV HDFS_NAMENODE_USER hdfs
ENV HDFS_DATANODE_USER hdfs
ENV HDFS_SECONDARYNAMENODE_USER hdfs
# 开放 22 端口
EXPOSE 22
# 容器运行时更改目录Hadoop目录所有者
# 启动 SSH 服务
CMD chown -R hdfs:hdfs /opt/hadoop-3.3.6 && /usr/sbin/sshd -D
#CMD ["/usr/sbin/sshd", "-D"]
重新构建镜像
# 增加`--network host`参数避免安装过程中网络问题找不到资源
docker build --network host -f Dockfile/dockerfile -t centos7-hadoop3:v10 Dockfile/
- 为了将Hadoop集群放置在独立的网络内部访问,避免受外部网络变化的影响,创建一个docker的虚拟网络给Hadoop使用。
docker network create --subnet=172.0.0.0/16 hadoop3-network
运行3个Hadoop容器,一主二从(hadoop3-m1、haddoop3-s1、hadoop3-s2),先创建要挂载的host文件和 Hadoop的配置文件
/etc/hadoop
# 进入Hadoop的目录 cd /docker/hadoop # 1.创建一个hadoopHost文件,让Hadoop容器统一挂载 # 如果 `hadoopHost` 文件不存在,则上述命令将创建一个新文件。 # 如果 `hadoopHost` 文件存在,则上述命令将将内容追加到文件中。 # 并将在标准输出中显示内容 tee hadoopHost << EOF 172.0.0.2 hadoop3-m1 172.0.0.11 hadoop3-s1 172.0.0.12 hadoop3-s2 EOF # 将Hadoop压缩文件解压,便于提取里面的配置文件 tar -zxvf Dockfile/hadoop-3.3.6.tar.gz -C . # 创建容器对应目录 mkdir hadoop3-m1 # 将配置文件复制到hadoop3-m1目录中 cp -r hadoop-3.3.6/etc/ hadoop3-m1/ # 创建挂载目录 docker run -d --net hadoop3-network --ip 172.0.0.2 --hostname hadoop-m1 centos7-hadoop3:v1 docker run -d --net hadoop3-network --ip 172.0.1.11 --hostname hadoop-s1 centos7-hadoop3:v1 docker run -d --net hadoop3-network --ip 172.0.0.12 --hostname hadoop-s2 centos7-hadoop3:v1
二. 修改Hadoop的配置文件,按一主二从的最小规模进行配置
a. 修改/docker/hadoop/hadoop3-m1/etc/hadoop/core-site.xml
文件(所有节点均需修改)指定HDFS用户的配置貌似没有作用,但第一次指定为root时在hdfs上创建文件夹报了权限错误,取消也报错了,这个有待后面了解<configuration> <property> <name>fs.defaultFS</name> <value>hdfs://hadoop3-m1:9000</value> </property> <property> <name>hadoop.tmp.dir</name> <value>/opt/hadoop-3.3.6/tmp</value> <description>A base for other temporary directories.</description> </property> <!-- 设置HDFS网页登录使用的静态用户为hdfs --> <property> <name>hadoop.http.staticuser.user</name> <value>root</value> </property> </configuration>
b. 修改
/docker/hadoop/hadoop3-m1/etc/hadoop/hdfs-site.xml
文件(所有节点均需修改)<configuration> <property> <name>dfs.replication</name> <value>2</value> </property> <!-- 只在namenode上节点上配置--> <property> <name>dfs.namenode.name.dir</name> <value>file:///opt/hadoop-3.3.6/namenode</value> </property> <!-- 在所有datenode上节点上配置--> <property> <name>dfs.datanode.data.dir</name> <value>file:///opt/hadoop-3.3.6/datanode</value> </property> </configuration>
c. 修改
/docker/hadoop/hadoop3-m1/etc/hadoop/mapred-site.xml
文件,设置运行模式为yarn(运行MapReduce
节点均需修改)<configuration> <property> <name>mapreduce.framework.name</name> <value>yarn</value> </configuration>
d. 修改
/docker/hadoop/hadoop3-m1/etc/hadoop/yarn-site.xml
文件,ResourceManager(通常运行在 NameNode 上)和所有 NodeManager(通常运行在 DataNode 上)节点均需修改<configuration> <!-- 指定ResourceManager的地址 --> <property> <name>yarn.resourcemanager.hostname</name> <value>hadoop3-m1</value> </property> <!-- 指定MR 使用那种协议,默认是空值,推荐使用 mapreduce_shuffle--> <property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> <!-- 开启日志聚集功能 --> <property> <name>yarn.log-aggregation-enable</name> <value>true</value> </property> <!-- 设置日志聚集服务器地址 --> <property> <name>yarn.log.server.url</name> <value>http://hadoop3-m1:19888/jobhistory/logs</value> </property> <!-- 设置日志保留时间为7天 --> <property> <name>yarn.log-aggregation.retain-seconds</name> <value>604800</value> </property> </configuration>
e. 修改
/docker/hadoop/hadoop3-m1/etc/hadoop/workers
文件,删除原来内容增加运行datanode节点主机名.只需要在namenode节点修改hadoop3-s1 hadoop3-s2
f. 运行namenode节点hadoop3-m1
计划是将:子网络的172.0.0.2~172.0.0.10 给Hadoop里对外开放端口的特定的容器使用、其余0.xxx的端口给Hadoop的内部容器使用、容器的ssh端口22映射为宿主机的65xxx端口,xxx和子网ip一致
# hadoop-m1
docker run --name hadoop3-m1 \
-p 9870:9870 \
-p 8088:8088 \
-p 19888:19888 \
-p 65002:22 \
-v /docker/hadoop/hadoopHost:/etc/hosts:ro \
-v /docker/hadoop/hadoop3-m1/etc/:/opt/hadoop-3.3.6/etc/ \
-v /docker/hadoop/hadoop3-m1/namenode/:/opt/hadoop-3.3.6/namenode/ \
-v /docker/hadoop/hadoop3-m1/datanode/:/opt/hadoop-3.3.6/datanode/ \
-v /docker/hadoop/hadoop3-m1/logs/:/opt/hadoop-3.3.6/logs/ \
--net hadoop3-network \
--ip 172.0.0.2 \
--hostname hadoop3-m1 \
-d centos7-hadoop3:v10
- 将hadoop3-m1的配置文件复制两份分别创建hadoop3-s1和hadoop3-s2目录
cp -r hadoop3-m1/ hadoop3-s1 cp -r hadoop3-m1/ hadoop3-s2
- 参照hadoop3-m1创建容器hadoop3-s1和hadoop3-s2
# hadoop-s1 docker run --name hadoop3-s1 \ -p 65011:22 \ -v /docker/hadoop/hadoopHost:/etc/hosts:ro \ -v /docker/hadoop/hadoop3-s1/etc/:/opt/hadoop-3.3.6/etc/ \ -v /docker/hadoop/hadoop3-s1/namenode/:/opt/hadoop-3.3.6/namenode/ \ -v /docker/hadoop/hadoop3-s1/datanode/:/opt/hadoop-3.3.6/datanode/ \ -v /docker/hadoop/hadoop3-s1/logs/:/opt/hadoop-3.3.6/logs/ \ --net hadoop3-network \ --ip 172.0.0.11 \ --hostname hadoop3-s1 \ -d centos7-hadoop3:v10 # hadoop-s2 docker run --name hadoop3-s2 \ -p 65012:22 \ -v /docker/hadoop/hadoopHost:/etc/hosts:ro \ -v /docker/hadoop/hadoop3-s2/etc/:/opt/hadoop-3.3.6/etc/ \ -v /docker/hadoop/hadoop3-s2/namenode/:/opt/hadoop-3.3.6/namenode/ \ -v /docker/hadoop/hadoop3-s2/datanode/:/opt/hadoop-3.3.6/datanode/ \ -v /docker/hadoop/hadoop3-s2/logs/:/opt/hadoop-3.3.6/logs/ \ --net hadoop3-network \ --ip 172.0.0.12 \ --hostname hadoop3-s2 \ -d centos7-hadoop3:v10
-
启动集群
a. 初始化nameNode(首次启动:hadoop3-m1)hdfs namenode -format
b. 启动hdfs(hadoop3-m1执行),因为配置了workds会自动将hadoop3-s1和hadoop3-s2的datanode启动
# 切换用户 su hdfs # 启动hdfs start-dfs.sh # 启动yarn start-yarn.sh # 停止hdfs stop-dfs.sh # 停止yarn stop-yarn.sh # 也可以使用命令同时启动 start-all.sh # 同时停止 stop-all.sh # 启动历史任务服务器 mapred --daemon start historyserver # mr-jobhistory-daemon.sh start historyserver # 停止历史任务服务器 mapred --daemon stop historyserver # mr-jobhistory-daemon.sh stop historyserver
访问hdfs: http://192.168.1.210:9870
访问yarn: http://192.168.1.210:8088
访问历史服务器: http://192.168.1.210:19888/