> 技术文档 > 从0开始基于docker的大数据环境搭建/Hadoop+Spark+Flink+Hbase+Kafka+Hive+Flume+Zookeeper+Mysql等_hadoop 运行flink dokcer compose

从0开始基于docker的大数据环境搭建/Hadoop+Spark+Flink+Hbase+Kafka+Hive+Flume+Zookeeper+Mysql等_hadoop 运行flink dokcer compose


        该博客由上海第二工业大学数据科学与大数据技术专业师生共同探索、共同打造,期间踩坑无数,旨在为后续教学工作提供参考,为其他有需要的同行提供便利。

目录

前言

一、直接安装

        0.安装docker desktop与WSL

        1.准备

        2.使用cmd进行安装

        3.使用命令

        4.等待安装

        5.安装完成

        6.点击运行按钮启动容器

        7.在cmd中连接容器

        8.启动完成

二、Dokcer概念与配置

1.Docker概念

镜像与容器:

Dockerfile:

Docker-compose:安装脚本

entrypoint:启动脚本

2.Docker配置

代理配置

镜像存储

三、Docker file配置

1.Docker file概念

dockerfile中的命令:

2.前置环境,下载与放置安装包

3.解压缩并安装,配置环境变量

4.配置环境

1-Hadoop

        hadoop-env.sh

        core-site.xml

        yarn-site.xml

        hdfs-site.xml

        mapred-site.xml

2-Zookeeper

        zoo.cfg & myid

3-Hbase

        hbase-env.sh

        hbase-site.xml

        regionservers

        hadoop-conf

4-Phoenix-hbase

        hbase-site.xml

        phoenixdb

5-Spark

        spark-env.sh

        spark-defaults.conf

        workers

        pyspark

6-Scala

7-Kafka

        server.properties

        kf.sh

9-Flume

        flume-env.sh

10-1.Hive

        hive-env.sh

        hive-site.xml

        yarn-site.xml

        mapred-site.xml

10-2.MySQL

        my.cnf

11-SSH

12-Jupyter

        Anaconda

       Jupyter

13-最终配置

14-优化镜像大小

四、entrypoint.sh配置

1.entrypoint.sh概念

2.entrypoint.sh示例

        日志函数 log()

        服务函数 start_service()

        主函数 main()

五、docker-compose配置

1.docker-compose概念

2.docker-compose示例

3.进阶知识

六、测试脚本

七、公开至局域网方法

        docker-compose

        防火墙入站规则

总结


如果不想了解docker的配置过程,可以直接阅读第一部分与第二部分的代理配置环节


前言

1、Docker compose链接:

通过网盘分享的文件:Great-United-Docker
链接: https://pan.baidu.com/s/1SDtTb7N0HTx4zC6fYB5dRg?pwd=s9fg 提取码: s9fg

Great-United-Dockerhttps://pan.baidu.com/s/1SDtTb7N0HTx4zC6fYB5dRg?pwd=s9fg

2、本文会详细介绍如何从Ubuntu镜像开始搭建单节点大数据环境,供测试教学使用

3、容器中的环境安装均采用本地COPY,支持更改为wget,

        无安装包,由wget实现的Github链接:/使用wget从官网下载,有需求可以更改dockerfile中的链接地址

https://github.com/HayasakaInori/bigdatadocker-for-SSPUhttps://github.com/HayasakaInori/bigdatadocker-for-SSPU4、容器中的组件版本:

  • Hadoop 3.3.6

  • Spark 3.5.6

  • HBase 2.5.11

  • Kafka 3.7.2

  • Flink 1.17.2

  • Zookeeper 3.8.4

  • Hive 4.0.1

  • Phoenix 5.1.3/6.0.0

  • Flume 1.11.0

  • MySQL(直接安装)

一、直接安装

        0.安装docker desktop与WSL

docker desktophttps://www.docker.com/products/docker-desktop/WSLhttps://learn.microsoft.com/zh-cn/windows/wsl/install

        安装WSL只需要打开cmd输入

wsl --install

        即可

        没有WSL,docker desktop无法正常启动,安装过程中需要重启,注意保存文件

拉取镜像需要配置代理,详见后文

        1.准备

        将 安装包(hbase,zookeeper,Hadoop等) ,Dockerfile , docker-compose.yml ,entrypoint.sh ,kf.sh放在同一文件夹下

        2.使用cmd进行安装

        在文件夹中打开cmd

        3.使用命令

#安装docker镜像docker-compose build#生成docker容器docker-compose up#卸载由该docker-compose安装的本地镜像(会删除容器)#docker-compose down --rmi local#不需要执行

        4.等待安装

        5.安装完成

类似:

        6.点击运行按钮启动容器

        7.在cmd中连接容器

#连接docker容器docker exec -it  /bin/bash#容器名能连,容器ID(Container ID)的前三位也可以(更方便)

        8.启动完成

        容器相当于一个虚拟机,在bash界面直接操作即可

 - 自启动项目:

SSH        Hadoop        zookeeper

Spark      Hbase          Flink

Kafka      MySQL        Phoenix Query Server

        以上项目已配置自启动,无需再次运行启动命令

其他项目启动命令如下

#Hive/opt/apache-hive-4.0.1-bin/bin/hive#Phoenix Hbase/opt/phoenix-hbase-2.5-5.2.1-bin/bin/sqlline.py localhost#Flumebin/flume-ng agent -c ./conf -f ./conf/hdfs-avro.conf -n a1 -Dflume.root.logger=INFO,console

 - 可选安装包:

pyspark

phoenixdb等

二、Dokcer概念与配置

1.Docker概念

镜像与容器:

        镜像是一个只读的模板,所有容器通过镜像生成

        容器类似于一台虚拟机

        docker通过镜像来保证 由同一个镜像生成的所有容器里面的配置内容在初始状态都完全一致

Dockerfile:

        镜像具体的安装配置 

        不指定名称的情况下D必须大写

        相当于一个执行脚本,但是需要通过特殊命令执行

Docker-compose:安装脚本

        可以通过配置这个文件来指定很多个性化的配置需求(如Dockerfile名称):

zookeeper1:                                           #执行安装镜像
    container_name: zookeeper1            #容器名称
    image: zookeeper1                            #镜像名称
    build:
      context: ./zookeeper                        #dockerfile所在目录
      dockerfile: zookeeper.Dockerfile     #dockerfile名称
    environment:                                     #环境变量
      MY_ID: 1
    networks:                                           #docker网络依赖
      bigdata-net:
        ipv4_address: 172.20.0.11
    depends_on:                                     #该镜像依赖于哪些前置镜像
      - base
    ports:                                                 #端口映射
      - \"2181:2181\"
      - \"2888:2888\"
      - \"3888:3888\"

        基于docker-compose创建多个镜像对于互相没有依赖的镜像是并行创建的,因此

       depends_on设置决定了docker镜像的创建顺序,有依赖的镜像会等待前置镜像创建完成后再创建,但是这个依赖与dockerfile中的FROM无关,如果在镜像中的FROM依赖同一批docker镜像中的某一个,请在这里配置他的依赖镜像,如果不配置会从dockerhub中拉取同名镜像

        端口映射在docker容器的配置中十分重要,一旦容器配置完毕映射到宿主机的端口无法更改,只能删除容器重新配置端口映射

entrypoint:启动脚本

        便捷的在容器启动时启动其他服务(如SSH,Hadoop,Hbase等)

#!/bin/bash                                                                #必须在第一行

# 等待YARN ResourceManager就绪
while ! nc -z resourcemanager 8088; do                  #等待前置 / nc命令需要安装netcat
  echo \"等待ResourceManager启动...\"
  sleep 5
done

# 配置Spark以使用YARN
echo \"配置Spark以使用YARN集群...\"

# 启动Spark Master (独立模式)
$SPARK_HOME/sbin/start-master.sh -h 0.0.0.0     #启动服务

# 保持容器运行
tail -f $SPARK_HOME/logs/*                                   

#docker容器会在执行完这个脚本后自动退出,需要上面这个命令运行在前台,否则无法持续运行

#也可写为tail -f /dev/null

2.Docker配置

        以dockerfile为基础,dockercompose设置内容,entrypoint执行脚本

        只有dockerfile也能安装docker镜像和容器

        也可从dockerhub等地方拉取 国内通常需要配置代理

代理配置

        打开docker desktop,点击右上角齿轮,选择DockerEngine选项

        复制代码并粘贴到框内

{ \"builder\": { \"gc\": { \"defaultKeepStorage\": \"20GB\", \"enabled\": true } }, \"experimental\": false, \"registry-mirrors\": [ \"https://docker.1ms.run\", \"https://docker.mybacc.com\", \"https://dytt.online\", \"https://lispy.org\", \"https://docker.xiaogenban1993.com\", \"https://docker.yomansunter.com\", \"https://aicarbon.xyz\", \"https://666860.xyz\", \"https://docker.zhai.cm\", \"https://a.ussh.net\", \"https://hub.littlediary.cn\", \"https://hub.rat.dev\", \"https://docker.m.daocloud.io\" ]}

        应用并重启

镜像存储

        同一页面,上一行,选择 Resource,下拉菜单中选择第一个 Advances

        在Disk image location 旁边点击 Browse 选择一个 空间足够大(最好空余50G以上)的路径,可以直接选择大的盘符目录,会自动生成文件夹,不用担心文件乱飞

三、Docker file配置

1.Docker file概念

        docker file是docker镜像的核心,大部分的配置需要靠docker file实现,他相当于一个脚本,通过命令来构建和配置docker镜像

dockerfile中的命令:

        FROM:指定从哪个基础镜像进行安装

        ENV:直接设置环境变量

        RUN:相当于在容器中执行命令

        COPY:与liunx的cp命令类似

        ENTRYPOINT & CMD:指定进入容器后执行的命令,ENTRYPOINT优先级最高,同时配置会把CMD中传入的数据接在ENTRYPOINT之后。

  • 有ENTRYPOINT执行ENTRYPOINT,没有ENTRYPOINT执行CMD
  • 多个CMD只执行最后一个

        单节点的dockerfile配置十分冗长,建议将各种环境配置文件写好后统一复制到对应路径中

在这为了演示配置过程与变量设置选择使用sed命令与echo命令编辑配置文件

2.前置环境,下载与放置安装包

FROM ubuntu:22.04#临时禁用证书验证RUN echo \"Acquire::https::Verify-Peer \\\"false\\\";\" >> /etc/apt/apt.conf.d/99verify-peer.conf && \\ echo \"Acquire::https::Verify-Host \\\"false\\\";\" >> /etc/apt/apt.conf.d/99verify-host.conf# 更换为阿里云源RUN sed -i \'s|http://.*ubuntu.com|https://mirrors.aliyun.com|g\' /etc/apt/sources.list# 安装必要的工具和依赖RUN apt-get update && apt-get install -y \\ openjdk-11-jdk \\ wget \\ curl \\ net-tools \\ ssh \\ python3\\ python3-pip\\ python2\\ vim\\ rpm\\ && ln -s /usr/bin/python3 /usr/bin/python \\ && rm -rf /var/lib/apt/lists/*# 设置环境变量ENV JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64ENV PATH=$JAVA_HOME/bin:$PATH#创建文件夹RUN mkdir -p /opt && \\ mkdir -p /install

        基于Ubuntu 22.04配置,设置环境变量javahome,创建安装包目录/install

        配置代理到国内镜像源,由于后面新增了通过apt安装mysql,国内镜像会更快一些

        第一个RUN禁用了证书验证,Ubuntu 22.04没有内置CA证书,导致无法验证阿里云镜像站的 HTTPS 证书,会导致无法下载,为方便直接禁用了,通过apt再下载ca-certificates和apt-transport-https太麻烦还花更多时间

# 从本地复制安装包到容器中COPY hadoop-3.3.6.tar.gz /install/COPY apache-zookeeper-3.8.4-bin.tar.gz /install/ COPY hbase-2.5.11-bin.tar.gz /install/ COPY phoenix-hbase-2.5-5.1.3-bin.tar.gz /install/ COPY phoenix-queryserver-6.0.0-bin.tar.gz /install/ COPY spark-3.5.6-bin-hadoop3.tgz /install/ COPY scala-2.12.0.tgz /install/ COPY kafka_2.12-3.7.2.tgz /install/ COPY flink-1.17.2-bin-scala_2.12.tgz /install/COPY apache-flume-1.11.0-bin.tar.gz /install/ COPY apache-hive-4.0.1-bin.tar.gz /install/ COPY mysql-connector-java-8.0.18.jar /install/

复制安装包到安装包目录/install

3.解压缩并安装,配置环境变量

# 安装HadoopRUN tar -xzf /install/hadoop-3.3.6.tar.gz -C /opt/ #rm /install/hadoop-3.3.6.tar.gzENV HADOOP_HOME=/opt/hadoop-3.3.6ENV PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
#安装ZookeeperRUN tar -xzf /install/apache-zookeeper-3.8.4-bin.tar.gz -C /opt/ #rm /install/apache-zookeeper-3.8.4-bin.tar.gzENV ZOOKEEPER_HOME=/opt/apache-zookeeper-3.8.4-binENV PATH=$ZOOKEEPER_HOME/bin:$PATH
# 安装HBaseRUN tar -xzf /install/hbase-2.5.11-bin.tar.gz -C /opt/ #rm /install/hbase-2.5.11-bin.tar.gzENV HBASE_HOME=/opt/hbase-2.5.11ENV PATH=$PATH:$HBASE_HOME/bin
# 安装Phoenix-hbaseRUN tar -xzf /install/phoenix-hbase-2.5-5.1.3-bin.tar.gz -C /opt# 复制 JAR 文件到 HBase 库目录RUN cp /opt/phoenix-hbase-2.5-5.1.3-bin/phoenix-pherf-5.1.3.jar $HBASE_HOME/lib/ && \\ cp /opt/phoenix-hbase-2.5-5.1.3-bin/phoenix-server-hbase-2.5-5.1.3.jar $HBASE_HOME/lib/
# 安装QueryserverRUN tar -xzf /install/phoenix-queryserver-6.0.0-bin.tar.gz -C /optRUN cp /opt/phoenix-hbase-2.5-5.1.3-bin/phoenix-client-hbase-2.5-5.1.3.jar /opt/phoenix-queryserver-6.0.0

        请确保hadoop,zookeeper,hbase三者版本互相兼容

        请确保hbase-x.y.*版本与phoenix-hbase-x.y-*版本完全对应

        如果不能保证请安装与上述版本之间足够接近的版本(如hdp3.3.*/zk3.8.*/hbase2.5.*)

        Phoenix-hbase需要复制自己的文件到hbase的文件夹中,这里已经操作完成,后面不再配置

        Phoenix-queryserver依赖Phoenix-hbase的jar包,这里已经复制了,其余配置已经整合进hbase与Phoenix-hbase的配置中,后面不再配置queryserver

        值得注意的是,对于phoenix-hbase-2.5-5.2.1,其与queryserver-6.0.0似乎存在兼容性问题,如果同时选择会导致queryserver自带的servlet API 与 hadoop和hbase的servlet API产生冲突,原因不明,故选择5.1.3版本

# 安装SparkRUN tar -xzf install/spark-3.5.6-bin-hadoop3.tgz -C /opt/ #rm /install/spark-3.5.6-bin-hadoop3.tgzENV SPARK_HOME=/opt/spark-3.5.6-bin-hadoop3ENV PATH=$PATH:$SPARK_HOME/bin:$SPARK_HOME/sbin
#安装ScalaRUN tar -xzf /install/scala-2.12.0.tgz -C /opt/ #rm scala-2.12.0.tgzENV SCALA_HOME=/opt/scala-2.12.0ENV PATH=$SCALA_HOME/bin:$PATH

        请确保安装的scala版本支持对应spark的版本

# 安装KafkaRUN tar -xzf /install/kafka_2.12-3.7.2.tgz -C /opt/ #rm /install/kafka_2.12-3.7.2.tgzENV KAFKA_HOME=/opt/kafka_2.12-3.7.2ENV PATH=$PATH:$KAFKA_HOME/bin
# 安装FlinkRUN tar -xzf /install/flink-1.17.2-bin-scala_2.12.tgz -C /opt/ #rm /install/flink-1.17.2-bin-scala_2.12.tgzENV FLINK_HOME=/opt/flink-1.17.2ENV PATH=$PATH:$FLINK_HOME/bin
#安装FlumeRUN tar -xzf /install/apache-flume-1.11.0-bin.tar.gz -C /opt/ENV FLUME_HOME=/opt/apache-flume-1.11.0-binENV FLUME_CONF_DIR=$FLUME_HOME/confENV PATH=$PATH:$FLUME_HOME/bin
#安装HiveRUN tar -xzf /install/apache-hive-4.0.1-bin.tar.gz -C /opt/ #rm /install/apache-hive-4.0.1-bin.tar.gzENV HADOOP_HOME=/opt/hadoop-3.3.6ENV HADOOP_CONF_DIR=${HADOOP_HOME}/etc/hadoopENV HADOOP_OPTS=\"-Djava.library.path=${HADOOP_HOME}/lib\"ENV HIVE_HOME=/opt/apache-hive-4.0.1-binENV HIVE_CONF_DIR=${HIVE_HOME}/confENV JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64ENV JRE_HOME=/usr/lib/jvm/java-11-openjdk-amd64ENV PATH=.:$JAVA_HOME/bin:$JRE_HOME/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$HIVE_HOME/bin:$PATH
#安装MySQLRUN apt-get update && apt-get install -y mysql-server

        安装完成

4.配置环境

        参考环境配置方案,会与原文有所不同

1-Hadoop

        hadoop-env.sh
#hadoop-env.shRUN echo \'export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64\' >> /opt/hadoop-3.3.6/etc/hadoop/hadoop-env.sh &&\\ echo \'export HADOOP_PID_DIR=/opt/hadoop-3.3.6/pids\' >> /opt/hadoop-3.3.6/etc/hadoop/hadoop-env.sh &&\\ echo \'export HADOOP_LOG_DIR=/opt/hadoop-3.3.6/logs\' >> /opt/hadoop-3.3.6/etc/hadoop/hadoop-env.sh &&\\ echo \'export HDFS_NAMENODE_USER=root\' >> /opt/hadoop-3.3.6/etc/hadoop/hadoop-env.sh &&\\ echo \'export HDFS_DATANODE_USER=root\' >> /opt/hadoop-3.3.6/etc/hadoop/hadoop-env.sh &&\\ echo \'export HDFS_SECONDARYNAMENODE_USER=root\' >> /opt/hadoop-3.3.6/etc/hadoop/hadoop-env.sh &&\\ echo \'export YARN_RESOURCEMANAGER_USER=root\' >> /opt/hadoop-3.3.6/etc/hadoop/hadoop-env.sh &&\\ echo \'export YARN_NODEMANAGER_USER=root\' >> /opt/hadoop-3.3.6/etc/hadoop/hadoop-env.sh

        写入环境变量,定义配置文件路径

        core-site.xml
#core-site.xmlRUN sed -i \'//i\\\\n\\ fs.defaultFS\\n\\ hdfs://localhost:9000\\n\\ \\n\\ \\n\\ io.file.buffer.size \\n\\ 131072\\n\\ \\n\\ \\n\\ hadoop.tmp.dir\\n\\ /opt/hadoop-3.3.6\\n\\ \\n\\ \\n\\ hadoop.proxyuser.root.hosts\\n\\ *\\n\\ Hadoop的超级用户root能代理的节点\\n\\ \\n\\ \\n\\ hadoop.proxyuser.root.groups\\n\\ *\\n\\ Hadoop的超级用户root能代理的用户组\\n\\ \' /opt/hadoop-3.3.6/etc/hadoop/core-site.xml

        fs.defaultfs定义了hdfs的访问端口,后面会经常配到

        yarn-site.xml
#yarn-site.xmlRUN sed -i \'//i\\\\n\\ dfs.replication\\n\\ 1\\n\\ \\n\\ \\n\\ fs.namenode.name.dir\\n\\ /opt/hadoop-3.3.6/namenode\\n\\ \\n\\ \\n\\ dfs.blocksize\\n\\ 268435456\\n\\ \\n\\ \\n\\ dfs.namenode.handler.count\\n\\ 100\\n\\ \\n\\ \\n\\ dfs.datanode.data.dir\\n\\ /opt/hadoop-3.3.6/datanode\\n\\ \\n\\ \\n\\ yarn.resourcemanager.hostname\\n\\ 0.0.0.0\\n\\ \\n\\ \\n\\ yarn.resourcemanager.bind-host\\n\\ 0.0.0.0\\n\\ \\n\\ \\n\\ yarn.resourcemanager.webapp.address\\n\\ 0.0.0.0:8088\\n\\ \\n\\ \\n\\ \\n\\ yarn.nodemanager.aux-services\\n\\ mapreduce_shuffle\\n\\ \\n\\ \\n\\ \\n\\ yarn.resourcemanager.hostname\\n\\ 0.0.0.0\\n\\ \' /opt/hadoop-3.3.6/etc/hadoop/yarn-site.xml

        dfs.replication配置了有多少备份,测试环境配1就行

        yarn.*.hostname定义了manager的访问地址,很重要

        注意到在hostname与bind-host等中配置了0.0.0.0,是因为需要外部访问容器内的模块时,需要开放给所有ip,如果设置127.0.0.1或者localhost会出现只能在容器内访问而不能从宿主机中访问的情况,后面会多次遇到需要这样配置的地方

        hdfs-site.xml
#hdfs-site.xmlRUN sed -i \'//i\\\\n\\ dfs.replication\\n\\ 1\\n\\ \\n\\ \\n\\ dfs.namenode.name.dir\\n\\ /opt/hadoop-3.3.6/namenode\\n\\ \\n\\ \\n\\ dfs.blocksize\\n\\ 268435456\\n\\ \\n\\ \\n\\ dfs.webhdfs.enable\\n\\ true\\n\\ \\n\\ \\n\\ dfs.namenode.handler.count\\n\\ 100\\n\\ \\n\\ \\n\\ dfs.namenode.http-bind-host\\n\\ 0.0.0.0\\n\\ \\n\\ \\n\\ dfs.datanode.http-bind-host\\n\\ 0.0.0.0\\n\\ \\n\\ \\n\\ dfs.datanode.data.dir\\n\\ /opt/hadoop-3.3.6/datanode\\n\\ \' /opt/hadoop-3.3.6/etc/hadoop/hdfs-site.xml

        dfs.webhdfs.enable决定了是否启用webhdfs服务,启用后任何能发送http请求的语言都能接入hdfs,用于python连接等

        mapred-site.xml
#mapred-site.xmlRUN sed -i \'//i\\\\n\\ mapreduce.framework.name\\n\\ yarn\\n\\ \' /opt/hadoop-3.3.6/etc/hadoop/mapred-site.xml

2-Zookeeper

        zoo.cfg & myid
RUN mv /opt/apache-zookeeper-3.8.4-bin/conf/zoo_sample.cfg /opt/apache-zookeeper-3.8.4-bin/conf/zoo.cfgRUN mkdir /opt/apache-zookeeper-3.8.4-bin/dataRUN sed -i \'s/dataDir=\\/tmp\\/zookeeper/dataDir=\\/opt\\/apache-zookeeper-3.8.4-bin\\/data/\' /opt/apache-zookeeper-3.8.4-bin/conf/zoo.cfg && \\ echo \'server.0=localhost:2888:3888\' >> /opt/apache-zookeeper-3.8.4-bin/conf/zoo.cfgRUN echo \'0\' >> /opt/apache-zookeeper-3.8.4-bin/data/myid

        在zoo.cfg中,server.x需要设置与自己配置的zookeeper节点数相同的数量,myid每个节点只需要写一份,标识自己节点的id。该id需要与server.x中的x对应,即存在x为1,2,3;则myid应当为1 / 2 / 3中的一个,不能为0或其他不存在于前文x的数。从几开始分配没有限制,不一定需要从0开始。

        在单节点配置中,myid也不能省略。

        对于3888端口,该端口为zookeeper的选举端口,即使只有一个节点的zookeeper也需要配置该端口

        对于前文在docker-compose中做案例的zookeeper节点配置,其中的MY_ID环境变量不能替代myid文件,这个环境变量可以用来创建myid文件,在其entrypoint.sh文件中实现如下

echo ${MY_ID:-1} > $ZOOKEEPER_HOME/data/myid#根据 MY_ID 写入数据,如果 MY_ID 变量为空,则默认写入1#代码仅作演示,在该配置中非必要

3-Hbase

        在不同版本的hbase中可能有不同于hadoop中的slf4j绑定文件,不删除会出现slf4j多重绑定问题,但是执行过程中没有报错就不要乱删

        对于出现了多重绑定问题的情况,删除其中一个即可,一般选择保留版本较高的

        hbase-env.sh
#hbase-env.shRUN echo \'export HBASE_MANAGES_ZK=false\' >> /opt/hbase-2.5.11/conf/hbase-env.sh && \\ echo \'export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64\' >> /opt/hbase-2.5.11/conf/hbase-env.sh && \\ echo \'export HBASE_CLASSPATH=/opt/hadoop-3.3.6/etc/hadoop\' >> /opt/hbase-2.5.11/conf/hbase-env.sh && \\ echo \'export HADOOP_HOME=/opt/hadoop-3.3.6\' >> /opt/hbase-2.5.11/conf/hbase-env.sh && \\ echo \'export HBASE_DISABLE_HADOOP_CLASSPATH_LOOKUP=\"true\"\' >> /opt/hbase-2.5.11/conf/hbase-env.sh

        定义环境变量,因为要使用外部zookeeper,所以hbase-manages-zk要设置成false

        hbase-site.xml
 
#hbase-site.xmlRUN sed -i \'//i\\\\n\\ hbase.rootdir\\n\\ hdfs://localhost:9000/hbase\\n\\ 这个目录是region server的共享目录,用来持久化Hbase\\n\\\\n\\\\n\\ hbase.cluster.distributed\\n\\ true\\n\\ Hbase的运行模式。false是单机模式,true是分布式模式\\n\\\\n\\\\n\\ hbase.master\\n\\ hdfs://localhost:60000\\n\\ hmaster port\\n\\\\n\\\\n\\ hbase.zookeeper.quorum\\n\\ localhost\\n\\ zookeeper集群的URL配置,多个host之间用逗号(,)分割\\n\\\\n\\\\n\\ hbase.zookeeper.property.dataDir\\n\\ /opt/apache-zookeeper-3.8.4-bin/data\\n\\ zookeeper的zooconf中的配置,快照的存储位置\\n\\\\n\\\\n\\ hbase.zookeeper.property.clientPort\\n\\ 2181\\n\\\\n\\\\n\\ hbase.master.ipc.address\\n\\ 0.0.0.0\\n\\\\n\\\\n\\ hbase.regionserver.ipc.address\\n\\ 0.0.0.0\\n\\\\n\\\\n\\ hbase.master.info.port\\n\\ 60010\\n\\\' /opt/hbase-2.5.11/conf/hbase-site.xml

        hbase.rootdir中地址要与前面hadoop中的配置相同

        regionservers
#单节点无需配置#RUN echo\'\' >> /opt/hbase-2.5.11/conf/regionservers

虽然单节点无需配置,但是如果有多节点需求,需要在这里写其他regionserver的主机名或ip,如:regionserver1 regionserver2

        hadoop-conf
#拷贝hadoop的设置内容RUN cp /opt/hadoop-3.3.6/etc/hadoop/hdfs-site.xml /opt/hbase-2.5.11/confRUN cp /opt/hadoop-3.3.6/etc/hadoop/core-site.xml /opt/hbase-2.5.11/conf

        Hbase依赖hdfs存储数据,所以需要知道hadoop的设置

        在后面的许多依赖hadoop的软件中都会用到hadoop设置或者hadoop设置的路径

4-Phoenix-hbase

        Phoenix-hbase与Phoenix-queryserver之间有依赖,但是这个依赖已经在安装Phoenix-queryserver的时候处理了,这边不再配置

        hbase-site.xml
#hbase-site.xmlRUN sed -i \'//i\\\\n\\ hbase.regionserver.wal.codec\\n\\ org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec\\n\\\\n\\\\n\\ phoenix.schema.mapSystemTablesToNamespace\\n\\ true\\n\\\' /opt/hbase-2.5.11/conf/hbase-site.xml

        这里可通过pip安装phoenixdb

        phoenixdb
RUN pip3 install phoenixdb

5-Spark

#local部署模式

        spark-env.sh
spark-env.shRUN cp /opt/spark-3.5.6-bin-hadoop3/conf/spark-env.sh.template /opt/spark-3.5.6-bin-hadoop3/conf/spark-env.sh RUN echo \'export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64\' >> /opt/spark-3.5.6-bin-hadoop3/conf/spark-env.sh&& \\ echo \'export SCALA_HOME=/opt/scala-2.12.0\' >> /opt/spark-3.5.6-bin-hadoop3/conf/spark-env.sh && \\ echo \'export SPARK_WORKER_MEMORY=1g\' >> /opt/spark-3.5.6-bin-hadoop3/conf/spark-env.sh && \\ echo \'export SPARK_WORKER_CORES=2\' >> /opt/spark-3.5.6-bin-hadoop3/conf/spark-env.sh && \\ echo \'export SPARK_HOME=/opt/spark-3.5.6-bin-hadoop3\' >> /opt/spark-3.5.6-bin-hadoop3/conf/spark-env.sh && \\ echo \'HADOOP_HOME=/opt/hadoop-3.3.6\' >> /opt/spark-3.5.6-bin-hadoop3/conf/spark-env.sh && \\ echo \'HADOOP_CONF_DIR=/opt/hadoop-3.3.6\' >> /opt/spark-3.5.6-bin-hadoop3/conf/spark-env.sh && \\ echo \'YARN_CONF_DIR=/opt/hadoop-3.3.6/etc/hadoop\' >> /opt/spark-3.5.6-bin-hadoop3/conf/spark-env.sh

        spark依赖hadoop的hdfs存储数据、yarn的资源调度,所以这里要配置hadoop参数

#yarn部署模式

        spark-defaults.conf
spark-defaults.confRUN cp /opt/spark-3.5.6-bin-hadoop3/conf/spark-defaults.conf.template /opt/spark-3.5.6-bin-hadoop3/conf/spark-defaults.confRUN echo \'spark.eventLog.enabled true\' >> /opt/spark-3.5.6-bin-hadoop3/conf/spark-defaults.conf && \\ echo \'spark.eventLog.dir  hdfs://localhost:9000/user/spark/directory\' >> /opt/spark-3.5.6-bin-hadoop3/conf/spark-defaults.conf

        注意eventLog.dir 中的路径需要与hadoop中的保持一致

#修改wokers

        workers
#workersRUN cp /opt/spark-3.5.6-bin-hadoop3/conf/workers.template /opt/spark-3.5.6-bin-hadoop3/conf/workers

        这里只对文件进行重命名的原因是:workers文件自带localhost,单节点配置的worker本来就是localhost,不需要再配置了

        这里可以通过pip安装pyspark

        pyspark
RUN pip3 install --default-timeout=100 /install/pyspark-4.0.0.tar.gz

6-Scala

        Scala语言的安装不是必须的,spark自带scala语言环境,如果需要使用scala语言编程可以安装

        Scala的配置只需要解压后配置环境变量即可,已经在安装时完成了,这里不再给出代码

7-Kafka

        server.properties
#RUN mkdir /opt/kafka_2.12-3.7.2/logs #创建日志目录,感觉没必要#RUN sed -i \'s/broker.id=0/broker.id=0/\' /opt/kafka_2.12-3.7.2/config/server.properties#单节点不配RUN sed -i \'s/#listeners=PLAINTEXT:\\/\\/:9092/listeners=PLAINTEXT:\\/\\/:9092/\' /opt/kafka_2.12-3.7.2/config/server.propertiesRUN sed -i \'s/zookeeper.connect=localhost:2181/zookeeper.connect=localhost:2181\\/kafka/\' /opt/kafka_2.12-3.7.2/config/server.propertiesCOPY kf.sh / #复制Kafka启动脚本 对于多节点来说启动kafka需要多次执行命令 故编写启动脚本

        这里给出kafka的启动脚本

        kf.sh
#!/bin/bash# 定义常量KAFKA_HOME=\"/opt/kafka_2.12-3.7.2\"CONFIG_FILE=\"$KAFKA_HOME/config/server.properties\"HOSTS=(\"localhost\") # 可扩展为多节点数组TIMEOUT=10  # 等待超时(秒)LOG_FILE=\"/var/log/kafka-server.log\"HOST_IP=$(getent ahostsv4 host.docker.internal | awk \'{print $1}\' | head -n1)#LISTENER=\"PLAINTEXT://${HOST_IP}:9092\"LISTENER=\"PLAINTEXT://localhost:9092\"sed -i \"s|^#advertised.listeners=.*|advertised.listeners=${LISTENER}|\" /opt/kafka_2.12-3.7.2/config/server.properties# 日志函数log() { echo \"[$(date \'+%Y-%m-%d %H:%M:%S\')] $1\"}# 检查Kafka进程状态check_kafka() { ssh $1 \"ps aux | grep -i [k]afka.Kafka\" >/dev/null 2>&1 return $?}# 等待Kafka启动/停止wait_for_status() { local host=$1 local expected_status=$2 # \"running\" or \"stopped\" local end_time=$(( $(date +%s) + $TIMEOUT )) while [ $(date +%s) -lt $end_time ]; do if [ \"$expected_status\" == \"running\" ]; then check_kafka $host && return 0 else ! check_kafka $host && return 0 fi sleep 2 done return 1}# 启动Kafka服务start_kafka() { for host in \"${HOSTS[@]}\"; do log \"正在启动 $host Kafka 服务...\" # 检查是否已运行 if check_kafka $host; then log \"$host Kafka 服务已在运行\" continue fi # 启动服务 ssh $host \"nohup $KAFKA_HOME/bin/kafka-server-start.sh -daemon $CONFIG_FILE > $LOG_FILE 2>&1 &\" # 验证启动 if wait_for_status $host \"running\"; then log \"$host Kafka 启动成功 | 日志: $LOG_FILE\" else log \"$host Kafka 启动失败!请检查日志: $LOG_FILE\" return 1 fi done}# 停止Kafka服务stop_kafka() { for host in \"${HOSTS[@]}\"; do log \"正在停止 $host Kafka 服务...\" # 检查是否已停止 if ! check_kafka $host; then log \"$host Kafka 服务已停止\" continue fi # 停止服务 ssh $host \"$KAFKA_HOME/bin/kafka-server-stop.sh\" # 验证停止 if wait_for_status $host \"stopped\"; then log \"$host Kafka 已停止\" else log \"$host Kafka 停止失败!尝试强制终止...\" ssh $host \"pkill -9 -f kafka.Kafka\" fi done}# 主逻辑case \"$1\" in \"start\") start_kafka ;; \"stop\") stop_kafka ;; \"status\") for host in \"${HOSTS[@]}\"; do if check_kafka $host; then log \"$host Kafka 正在运行\" else log \"$host Kafka 未运行\" fi done ;; \"restart\") stop_kafka sleep 5 start_kafka ;; *) echo \"用法: $0 {start|stop|status|restart}\" exit 1 ;;esacexit 0

        需要注意的是,这里有一块本应该在dockerfile中配置的代码被移动到了kf.sh中

HOST_IP=$(getent ahostsv4 host.docker.internal | awk \'{print $1}\' | head -n1)#LISTENER=\"PLAINTEXT://${HOST_IP}:9092\"LISTENER=\"PLAINTEXT://localhost:9092\"

        虽然被写死为了localhost:9092,但是如果需要从宿主机外部连接容器内的kafka,需要该配置为一个可被外界访问的地址,如192.168.xxx.yyy等。在本次尝试中为获取容器对应于宿主机的ip地址,方法如第一行所示。

#flink-conf.yamlRUN sed -i \'s/jobmanager.rpc.address: localhost/jobmanager.rpc.address: 0.0.0.0/\' /opt/flink-1.17.2/conf/flink-conf.yaml && \\ sed -i \'s/jobmanager.bind-host: localhost/jobmanager.bind-host: 0.0.0.0/\' /opt/flink-1.17.2/conf/flink-conf.yaml && \\ sed -i \'s/taskmanager.bind-host: localhost/taskmanager.bind-host: 0.0.0.0/\' /opt/flink-1.17.2/conf/flink-conf.yaml && \\ sed -i \'s/taskmanager.host: localhost/taskmanager.host: 0.0.0.0/\' /opt/flink-1.17.2/conf/flink-conf.yaml && \\ sed -i \'s/#rest.port: 8081/rest.port: 8083/\' /opt/flink-1.17.2/conf/flink-conf.yaml && \\ sed -i \'s/rest.address: localhost/rest.address: 0.0.0.0/\' /opt/flink-1.17.2/conf/flink-conf.yaml && \\ sed -i \'s/rest.bind-address: localhost/rest.bind-address: 0.0.0.0/\' /opt/flink-1.17.2/conf/flink-conf.yaml && \\ sed -i \'s/#rest.bind-port: 8080-8090/rest.bind-port: 8083-8090/\' /opt/flink-1.17.2/conf/flink-conf.yaml

        对于端口8080-8090,这是让flink自己选择端口,为了避免冲突所以设置到了8083-8090。如果不这样设置flink可能会选择8080。在docker的配置中容器映射的端口是很重要的配置,如不能确定,则需要映射所有可能端口,因为容器在建立后映射的端口无法改变,只能删除容器重新配置端口映射

9-Flume

        flume-env.sh
#flume-env.shRUN cp /opt/apache-flume-1.11.0-bin/conf/flume-env.sh.template /opt/apache-flume-1.11.0-bin/conf/flume-env.shRUN echo \'export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64\' >> /opt/apache-flume-1.11.0-bin/conf/flume-env.shRUN echo \"# 定义这个agent的名称\" > /opt/apache-flume-1.11.0-bin/conf/hdfs-avro.conf && \\ echo \"a1.sources = r1\" >> /opt/apache-flume-1.11.0-bin/conf/hdfs-avro.conf && \\ echo \"a1.sinks = k1\" >> /opt/apache-flume-1.11.0-bin/conf/hdfs-avro.conf && \\ echo \"a1.channels = c1\" >> /opt/apache-flume-1.11.0-bin/conf/hdfs-avro.conf && \\ echo \"# 配置源,用于监控文件\" >> /opt/apache-flume-1.11.0-bin/conf/hdfs-avro.conf && \\ echo \"a1.sources.r1.type = exec\" >> /opt/apache-flume-1.11.0-bin/conf/hdfs-avro.conf && \\ echo \"a1.sources.r1.command = tail -F /opt/apache-flume-1.11.0-bin/test/1.log\" >> /opt/apache-flume-1.11.0-bin/conf/hdfs-avro.conf && \\ echo \"a1.sources.r1.channels = c1\" >> /opt/apache-flume-1.11.0-bin/conf/hdfs-avro.conf && \\ echo \"# 配置接收器,用于HDFS\" >> /opt/apache-flume-1.11.0-bin/conf/hdfs-avro.conf && \\ echo \"a1.sinks.k1.type = hdfs\" >> /opt/apache-flume-1.11.0-bin/conf/hdfs-avro.conf && \\ echo \"a1.sinks.k1.hdfs.path = hdfs://localhost:9000/flume/events/%y-%m-%d/%H-%M\" >> /opt/apache-flume-1.11.0-bin/conf/hdfs-avro.conf && \\ echo \"a1.sinks.k1.hdfs.filePrefix = events-\" >> /opt/apache-flume-1.11.0-bin/conf/hdfs-avro.conf && \\ echo \"a1.sinks.k1.hdfs.round = true\" >> /opt/apache-flume-1.11.0-bin/conf/hdfs-avro.conf && \\ echo \"a1.sinks.k1.hdfs.roundValue = 10\" >> /opt/apache-flume-1.11.0-bin/conf/hdfs-avro.conf && \\ echo \"a1.sinks.k1.hdfs.roundUnit = minute\" >> /opt/apache-flume-1.11.0-bin/conf/hdfs-avro.conf && \\ echo \"a1.sinks.k1.hdfs.rollInterval = 0\" >> /opt/apache-flume-1.11.0-bin/conf/hdfs-avro.conf && \\ echo \"a1.sinks.k1.hdfs.rollSize = 1024\" >> /opt/apache-flume-1.11.0-bin/conf/hdfs-avro.conf && \\ echo \"a1.sinks.k1.hdfs.rollCount = 0\" >> /opt/apache-flume-1.11.0-bin/conf/hdfs-avro.conf && \\ echo \"a1.sinks.k1.hdfs.useLocalTimeStamp = true\" >> /opt/apache-flume-1.11.0-bin/conf/hdfs-avro.conf && \\ echo \"a1.sinks.k1.channel = c1\" >> /opt/apache-flume-1.11.0-bin/conf/hdfs-avro.conf && \\ echo \"# 配置通道,内存型\" >> /opt/apache-flume-1.11.0-bin/conf/hdfs-avro.conf && \\ echo \"a1.channels.c1.type = memory\" >> /opt/apache-flume-1.11.0-bin/conf/hdfs-avro.conf && \\ echo \"a1.channels.c1.capacity = 1000\" >> /opt/apache-flume-1.11.0-bin/conf/hdfs-avro.conf && \\ echo \"a1.channels.c1.transactionCapacity = 100\" >> /opt/apache-flume-1.11.0-bin/conf/hdfs-avro.conf && \\ echo \"# 绑定源和接收器到通道\" >> /opt/apache-flume-1.11.0-bin/conf/hdfs-avro.conf && \\ echo \"a1.sources.r1.channels = c1\" >> /opt/apache-flume-1.11.0-bin/conf/hdfs-avro.conf && \\ echo \"a1.sinks.k1.channel = c1\" >> /opt/apache-flume-1.11.0-bin/conf/hdfs-avro.conf

10-1.Hive

        hive-env.sh
#hive-env.shRUN cp /opt/apache-hive-4.0.1-bin/conf/hive-env.sh.template /opt/apache-hive-4.0.1-bin/conf/hive-env.shRUN echo \'export JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64\' >> /opt/apache-hive-4.0.1-bin/hive-env.sh && \\ echo \'export HIVE_HOME=/opt/apache-hive-4.0.1-bin\' >> /opt/apache-hive-4.0.1-bin/hive-env.sh && \\ echo \'export HIVE_CONF_DIR=/opt/apache-hive-4.0.1-bin/conf\' >> /opt/apache-hive-4.0.1-bin/hive-env.sh && \\ echo \'export HIVE_AUX_JARS_PATH=/opt/apache-hive-4.0.1-bin/lib\' >> /opt/apache-hive-4.0.1-bin/hive-env.sh && \\ echo \'export HADOOP_HOME=/opt/hadoop-3.3.6\' >> /opt/apache-hive-4.0.1-bin/hive-env.sh

        Hive 依赖 Hadoop 的可执行文件,所以需要指定 Hadoop 安装目录

        hive-site.xml
#hive-site.xmlRUN cp /opt/apache-hive-4.0.1-bin/conf/hive-default.xml.template /opt/apache-hive-4.0.1-bin/conf/hive-site.xmlRUN sed -i \'//i\\\\n\\ hive.exec.local.scratchdir\\n\\ /opt/apache-hive-4.0.1-bin/tmp/\\n\\ \\n\\ \\n\\ hive.downloaded.resources.dir\\n\\ /opt/apache-hive-4.0.1-bin/tmp/${hive.session.id}_resources\\n\\ \\n\\ \\n\\ hive.querylog.location\\n\\ /opt/apache-hive-4.0.1-bin/tmp/\\n\\ \\n\\ \\n\\ hive.server2.logging.operation.log.location\\n\\ /opt/apache-hive-4.0.1-bin/tmp/root/operation_logs\\n\\ \\n\\ \\n\\ javax.jdo.option.ConnectionDriverName\\n\\ com.mysql.jdbc.Driver\\n\\ \\n\\ \\n\\ javax.jdo.option.ConnectionURL\\n\\ jdbc:mysql://localhost:3306/hive?createDatabaseIfNotExist=true&useSSL=false&useUnicode=true&characterEncoding=UTF-8\\n\\ \\n\\ \\n\\ javax.jdo.option.ConnectionUserName\\n\\ root\\n\\ \\n\\ \\n\\ javax.jdo.option.ConnectionPassword\\n\\ root\\n\\ \' /opt/apache-hive-4.0.1-bin/conf/hive-site.xml

        最后两个配置*Username与*Password分别设置了hive登录mysql的用户和密码

        yarn-site.xml
#yarn-site.xmlRUN sed -i \'//i\\ \\n\\ yarn.nodemanager.resource.memory-mb\\n\\ 2548\\n\\ 每个节点可用内存,单位MB\\n\\ \\n\\ \\n\\ yarn.scheduler.minimum-allocation-mb\\n\\ 2048\\n\\ 单个任务可申请最少内存,默认1024MB\\n\\ \\n\\ \\n\\ yarn.scheduler.maximum-allocation-mb\\n\\ 8192\\n\\ 单个任务可申请最大内存,默认8192MB\\n\\ \' /opt/hadoop-3.3.6/etc/hadoop/yarn-site.xml#这段在配置yarn时同时配置也可,并不是到了Hive才依赖这个
        mapred-site.xml
#mapred-site.xmlRUN sed -i \'//i\\ \\n\\ yarn.app.mapreduce.am.env\\n\\ HADOOP_MAPRED_HOME=/opt/hadoop-3.3.6\\n\\ \\n\\ \\n\\ mapreduce.map.env\\n\\ HADOOP_MAPRED_HOME=/opt/hadoop-3.3.6\\n\\ \\n\\ \\n\\ mapreduce.reduce.env\\n\\ HADOOP_MAPRED_HOME=/opt/hadoop-3.3.6\\n\\ \' /opt/hadoop-3.3.6/etc/hadoop/mapred-site.xml#同上

        由于hadoop与hive都依赖slf4j绑定文件,因此同时安装会有slf4多重绑定问题,我们需要删除其中一个

RUN rm /opt/apache-hive-4.0.1-bin/lib/log4j-slf4j-impl-2.18.0.jar

        Hive 使用 MySQL 作为其元数据存储数据库,而该 JAR 包是 MySQL 官方提供的 JDBC 驱动程序,用于 Hive 与 MySQL 之间的通信,在配置mysql前放入也没有关系,不对mysql的配置和路径有依赖

RUN cp /install/mysql-connector-java-8.0.18.jar /opt/apache-hive-4.0.1-bin/lib

10-2.MySQL

        在现在(2025-07-18)这个时间段安装的mysql中,/etc中有了my.cnf,故跟随原教程继续安装

        值得注意的是,mysql的配置文件看似有许多,如my.cnf,mysqld.cnf等等,其中遵循一个关系,由代码

mysql --help | grep -A1 \"Default options\"

        查询,所得结果类似

Default options are read from the following files in the given order:/etc/my.cnf /etc/mysql/my.cnf ~/.my.cnf

        该返回是一个倒序优先级序列,靠后的配置会覆盖前面的配置,可以看到有/etc/mysql/my。cnf,至于最后的~/.my.cnf是当前用户的配置文件,如果没有配置则参考靠前的两个,他们是全局配置文件

        my.cnf
#my.cnfRUN echo \"[mysqld]\">> /etc/mysql/my.cnf && \\ echo \"pid-file = /var/run/mysqld/mysqld.pid\" >> /etc/mysql/my.cnf && \\ echo \"socket = /var/run/mysqld/mysqld.sock\" >> /etc/mysql/my.cnf && \\ echo \"port  = 3306\" >> /etc/mysql/my.cnf && \\ echo \"datadir = /var/lib/mysql\" >> /etc/mysql/my.cnf && \\ echo \"bind-address = 0.0.0.0\" >> /etc/mysql/my.cnf && \\ echo \"mysqlx-bind-address = 0.0.0.0\" >> /etc/mysql/my.cnf && \\ echo \"\" >> /etc/mysql/my.cnf && \\ echo \"[client]\" >> /etc/mysql/my.cnf && \\ echo \"port=3306\" >> /etc/mysql/my.cnf && \\ echo \"socket = /var/run/mysqld/mysqld.sock\" >> /etc/mysql/my.cnf

        对于[mysqld]行,这是mysql配置的语法,不写会报错

        对于原教程中的skip-grant-tables设置,在该版本mysql中配置skip-grant-tables会导致mysql拒绝监听网络端口,因而无法从3306访问,类似于skip_networking的设置,只允许本地的 Unix 套接字连接

        mysql的配置并没有完全完成,接下来的部分需要到entrypoint脚本中继续,其中内容比较重要,也许在dockerfile中配置也可,但我并未尝试

11-SSH

        生成SSH密钥并配置免密登录

        对于hadoop namdenode等组件,SSH是必要服务,不同节点间需要通过SSH 22端口互相通信

#配置SSHRUN ssh-keygen -t rsa -P \'\' -f ~/.ssh/id_rsa && \\ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys && \\ chmod 0600 ~/.ssh/authorized_keys

对于命令的解释:

ssh-keygen -t rsa -P \'\' -f ~/.ssh/id_rsa
  • 功能:生成 RSA 密钥对(私钥 + 公钥)

    • -t rsa:指定密钥类型为 RSA

    • -P \'\':设置空密码(无需输入密码即可使用密钥)

    • -f ~/.ssh/id_rsa:将私钥保存到 ~/.ssh/id_rsa,公钥默认保存为 ~/.ssh/id_rsa.pub

  • 效果

    • 生成两个文件(如下)

    • ~/.ssh/id_rsa(私钥,需保密)

    • ~/.ssh/id_rsa.pub(公钥,可分发)

cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
  • 功能:将公钥追加到 authorized_keys 文件中。

    • authorized_keys 是 SSH 服务认可的免密登录公钥列表。

    • >> 表示追加(不覆盖原有内容)

  • 效果
    当前用户可以通过 SSH 密钥免密登录本机(即 localhost

chmod 0600 ~/.ssh/authorized_keys
  • 功能:设置 authorized_keys 文件的权限为 0600

    • 0600 表示仅允许所有者读写(-rw-------

    • SSH 服务对文件权限敏感,权限过宽(如 0644)会导致免密登录失效

12-Jupyter

        完整的Anaconda包含了Jupyter的必要文件,直接安装Anaconda后在entrypoint中配置启动即可

        Anaconda
COPY Anaconda3-2024.06-1-Linux-x86_64.sh /install/#安装AnacondaRUN chmod +x /install/Anaconda3-2024.06-1-Linux-x86_64.sh && \\ /install/Anaconda3-2024.06-1-Linux-x86_64.sh -b -p /opt/AnacondaENV PATH=\"/opt/Anaconda/bin:$PATH\" ENV CONDA_ENVS_PATH=\"/opt/Anaconda/envs\"
       Jupyter
#Jupyter启动命令nohup jupyter notebook --ip=0.0.0.0 --port=8888 --no-browser --allow-root --NotebookApp.token=\'\' --NotebookApp.password=\'\' > /var/log/jupyter.log 2>&1 &

        由该方法安装完Jupyter后整个容器会来到18G(无优化版本dockerfile),暂不清楚原因

        我分成了两个容器并通过 docker net 通信,这样可以保证拥有完整功能的大数据容器可以保持在较小体积,也可以通过docker compose命令来分别构建。单独的Jupyter容器大小约8G,实用性上感觉不如在宿主机上直接写代码

13-最终配置

#清理安装包RUN rm -rf /install# 设置工作目录WORKDIR /root#添加启动脚本COPY entrypoint.sh /RUN chmod +x /entrypoint.sh# 定义默认命令ENTRYPOINT [\"/entrypoint.sh\"]

        引入启动脚本 entrypoint.sh 让后台程序自动化启动(如SSH,Hadoop,zookeeper)

14-优化镜像大小

        对于上面所给出的代码,组合到一起形成dockefile能够完成基本工作,但是体积(8.7G)相对原本的压缩包(4.2G)来讲还是有点多了,需要引入多阶段构建来优化最后的大小

FROM ubuntu:22.04 AS builder# 安装必要的工具和依赖RUN apt-get update && apt-get install -y \\ mysql-server && \\ mkdir -p /opt && \\ mkdir -p /install# 设置环境变量# 基础环境变量ENV JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64\\ HADOOP_HOME=/opt/hadoop-3.3.6 \\ HIVE_HOME=/opt/apache-hive-4.0.1-bin \\ FLUME_HOME=/opt/apache-flume-1.11.0-bin# 其他组件ENV JRE_HOME=$JAVA_HOME \\ HADOOP_CONF_DIR=$HADOOP_HOME/etc/hadoop \\ HADOOP_OPTS=\"-Djava.library.path=$HADOOP_HOME/lib\" \\ HADOOP_PREFIX=$HADOOP_HOME \\ ZOOKEEPER_HOME=/opt/apache-zookeeper-3.8.4-bin \\ SCALA_HOME=/opt/scala-2.12.0 \\ SPARK_HOME=/opt/spark-3.5.6-bin-hadoop3 \\ HBASE_HOME=/opt/hbase-2.5.11 \\ HIVE_CONF_DIR=$HIVE_HOME/conf \\ KAFKA_HOME=/opt/kafka_2.12-3.7.2 \\ FLINK_HOME=/opt/flink-1.17.2 \\ FLUME_CONF_DIR=$FLUME_HOME/conf# 统一 PATH 配置ENV PATH=$PATH:\\$JAVA_HOME/bin:\\$HADOOP_HOME/bin:\\$HADOOP_HOME/sbin:\\$ZOOKEEPER_HOME/bin:\\$SCALA_HOME/bin:\\$SPARK_HOME/bin:\\$SPARK_HOME/sbin:\\$HBASE_HOME/bin:\\$HIVE_HOME/bin:\\$KAFKA_HOME/bin:\\$FLINK_HOME/bin:\\$FLUME_HOME/bin

        上面是一部分优化后的代码,虽然可以不用在该阶段设置环境变量,但是以防后续设置时用到带环境变量的路径,可以提前设置方便使用。对于ENV命令,可以看到在基础环境变量中有三个HOME环境变量,这是因为后面用到了由该环境变量组成的新环境变量,而在同一个ENV中定义的环境变量不能互相调用,需要被提取出来

        这段代码在 FROM行 后加了一个 AS builder,这是多阶段构建的标志性语法,类似于一个临时镜像。builder可以被任意重命名,只需要在后面的复制阶段从对应名称的镜像中复制即可

        很明显的一点,我删除了很多在构建阶段并没有用的工具,这将极大的优化构建阶段的时间,构建阶段与后面的编辑阶段直接绑定,而最终镜像是平行于构建阶段同时生成的,并行构建十分高效(即使mysql依旧是整个安装过程中唯一没有被离线化的安装包且占用了最长的时间)

        接下来是构建阶段的剩余代码:

# 从本地复制安装包到容器中COPY hadoop-3.3.6.tar.gz \\ spark-3.5.6-bin-hadoop3.tgz \\ hbase-2.5.11-bin.tar.gz \\ kafka_2.12-3.7.2.tgz \\ flink-1.17.2-bin-scala_2.12.tgz \\ phoenix-hbase-2.5-5.1.3-bin.tar.gz \\ phoenix-queryserver-6.0.0-bin.tar.gz \\ apache-zookeeper-3.8.4-bin.tar.gz \\ scala-2.12.0.tgz \\ mysql-connector-java-8.0.18.jar \\ apache-hive-4.0.1-bin.tar.gz \\ apache-flume-1.11.0-bin.tar.gz /install/#安装软件RUN tar -xzf /install/hadoop-3.3.6.tar.gz -C /opt/ && \\ tar -xzf /install/apache-zookeeper-3.8.4-bin.tar.gz -C /opt/ && \\ tar -xzf /install/scala-2.12.0.tgz -C /opt/ && \\ tar -xzf /install/spark-3.5.6-bin-hadoop3.tgz -C /opt/ && \\ tar -xzf /install/hbase-2.5.11-bin.tar.gz -C /opt/ && \\ tar -xzf /install/apache-hive-4.0.1-bin.tar.gz -C /opt/ && \\ tar -xzf /install/kafka_2.12-3.7.2.tgz -C /opt/ && \\ tar -xzf /install/flink-1.17.2-bin-scala_2.12.tgz -C /opt/ && \\ tar -xzf /install/apache-flume-1.11.0-bin.tar.gz -C /opt/ && \\ tar -xzf /install/phoenix-hbase-2.5-5.1.3-bin.tar.gz -C /opt && \\ tar -xzf /install/phoenix-queryserver-6.0.0-bin.tar.gz -C /opt && \\ cp /opt/phoenix-hbase-2.5-5.1.3-bin/phoenix-pherf-5.1.3.jar $HBASE_HOME/lib/ && \\ cp /opt/phoenix-hbase-2.5-5.1.3-bin/phoenix-server-hbase-2.5-5.1.3.jar $HBASE_HOME/lib/ && \\ cp /opt/phoenix-hbase-2.5-5.1.3-bin/phoenix-client-hbase-2.5-5.1.3.jar /opt/phoenix-queryserver-6.0.0 

        区别于前面的演示,我集中安装了所有软件,每多一个RUN,COPY命令都会有一层历史层留在镜像中,所以这个操作减少了命令的同时保持了可读性。即使构建阶段会被完全抛弃,该优化也能作为基本命令用在前文的dockerfile中


在上述代码之后,加入前文中从

#配置Hadoop#开始

到#配置MySQL#的所有配置代码

全部复制即可


        接下来是最终镜像

FROM ubuntu:22.04# 安装最小必要的运行时依赖RUN apt-get update && apt-get install -y \\ openjdk-11-jdk \\ wget \\ curl \\ net-tools \\ ssh \\ python3 \\ python3-pip \\ python2 \\ vim \\ mysql-server && \\ ln -s /usr/bin/python3 /usr/bin/python && \\ rm -rf /var/lib/apt/lists/* && \\ apt-get clean# 从构建阶段复制已安装和配置的软件COPY --from=builder /opt /optCOPY --from=builder /etc/mysql/my.cnf /etc/mysql/my.cnf# 设置环境变量# 基础环境变量ENV JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64\\ HADOOP_HOME=/opt/hadoop-3.3.6 \\ HIVE_HOME=/opt/apache-hive-4.0.1-bin \\ FLUME_HOME=/opt/apache-flume-1.11.0-bin# 其他组件ENV JRE_HOME=$JAVA_HOME \\ HADOOP_CONF_DIR=$HADOOP_HOME/etc/hadoop \\ HADOOP_OPTS=\"-Djava.library.path=$HADOOP_HOME/lib\" \\ HADOOP_PREFIX=$HADOOP_HOME \\ ZOOKEEPER_HOME=/opt/apache-zookeeper-3.8.4-bin \\ SCALA_HOME=/opt/scala-2.12.0 \\ SPARK_HOME=/opt/spark-3.5.6-bin-hadoop3 \\ HBASE_HOME=/opt/hbase-2.5.11 \\ HIVE_CONF_DIR=$HIVE_HOME/conf \\ KAFKA_HOME=/opt/kafka_2.12-3.7.2 \\ FLINK_HOME=/opt/flink-1.17.2 \\ FLUME_CONF_DIR=$FLUME_HOME/conf# 统一 PATH 配置ENV PATH=$PATH:\\$JAVA_HOME/bin:\\$HADOOP_HOME/bin:\\$HADOOP_HOME/sbin:\\$ZOOKEEPER_HOME/bin:\\$SCALA_HOME/bin:\\$SPARK_HOME/bin:\\$SPARK_HOME/sbin:\\$HBASE_HOME/bin:\\$HIVE_HOME/bin:\\$KAFKA_HOME/bin:\\$FLINK_HOME/bin:\\$FLUME_HOME/bin# 配置SSHRUN ssh-keygen -t rsa -P \'\' -f ~/.ssh/id_rsa && \\ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys && \\ chmod 0600 ~/.ssh/authorized_keys#添加启动脚本COPY entrypoint.sh \\ kf.sh /RUN chmod +x /entrypoint.sh# 设置工作目录WORKDIR /root# 定义默认命令ENTRYPOINT [\"/entrypoint.sh\"]

        其中最重要的就是:

COPY --from=builder /opt /optCOPY --from=builder /etc/mysql/my.cnf /etc/mysql/my.cnf

        两行命令,我们将前文那么多的配置代码转化为了两行COPY,极大的减少了历史层数

        --from=* 与前文的AS builder相对应,如果有其他阶段也可以通过修改名称来复制文件

四、entrypoint.sh配置

1.entrypoint.sh概念

        entrypoint.sh作为全容器唯一的启动脚本,担任自动化服务的功能,但在将他人配置完成的docker镜像作为模板重新配置镜像时,如果已经存在entrypoint.sh脚本,再在自己的镜像中配置脚本可能会导致原本的脚本失效,因此需要在更改原脚本与将自己的脚本实现原脚本功能中二选一

        如前文所述,如果在dockerfile中执行ENTRYPOINT命令定义启动脚本,则docker容器会在脚本执行完毕后关闭,现象为点击启动按钮后无变化,容器无法启动,这时就需要:

tail -f /dev/null

指令来维持容器运行,命令解释如下:

  • tail -f

    • tail:默认输出文件的末尾内容(查看日志的常用命令)

    • -f(follow):持续跟踪文件变化,实时输出新增内容(常用于监控日志文件)

  • /dev/null

    • Linux 的特殊设备文件,所有写入它的内容会被丢弃

    • 读取 /dev/null 会立即返回 EOF(文件结束符),不会输出任何内容

        tail会永远尝试读取/dev/null,而/dev/null永远不会产生新的数据,因此命令会永远挂起,但不占用cpu资源,docker容器也能持续运行

        entrypoint.sh脚本有一个极为严格的检查,首行必须为:

#!/bin/bash

        无论在首行写什么都不行,即使是被注释的命令也会导致enrtypoint.sh无法执行

2.entrypoint.sh示例

        日志函数 log()

#!/bin/bash# 定义日志函数log() { echo \"[$(date \'+%Y-%m-%d %H:%M:%S\')] $1\"}

         获取时间,并将第一个获取的参数接在其后方,作为输出返回,方便查看报错

        服务函数 start_service()

# 检查并启动服务函数start_service() { local service_name=$1 local start_command=$2 local max_retries=3 local retry_delay=5 log \"Starting ${service_name}...\" for ((i=1; i<=$max_retries; i++)); do eval \"$start_command\" if [ $? -eq 0 ]; then log \"${service_name} started successfully.\" return 0 else log \"Attempt ${i} failed to start ${service_name}. Retrying in ${retry_delay} seconds...\" sleep $retry_delay fi done log \"Failed to start ${service_name} after ${max_retries} attempts.\" return 1}

        接受第一个参数为service_name,第二个参数为start_command(具体指令 start / stop 等)

        循环max_retries次,通过eval执行字符串形式的命令,如果启动失败则等待retry_delay秒

        如果有执行失败则报错并return 1

        主函数 main()

# 主执行流程main() { # 启动SSH服务 start_service \"SSH\" \"service ssh start\" || exit 1 # 格式化HDFS并启动Hadoop if [ ! -f /tmp/hadoop-formatted ]; then log \"Formatting HDFS...\" hdfs namenode -format && touch /tmp/hadoop-formatted fi start_service \"Hadoop\" \"start-all.sh\" || exit 1 # 启动Zookeeper start_service \"Zookeeper\" \"zkServer.sh stop\" || exit 1 sleep 2 zkServer.sh start || exit 1 # 启动Spark start_service \"Spark\" \"/opt/spark-3.5.6-bin-hadoop3/sbin/start-all.sh\" || exit 1 # 启动HBase if ! pgrep -f \"hbase.master.HMaster\"; then start_service \"HBase\" \"/opt/hbase-2.5.11/bin/start-hbase.sh\" || exit 1 fi start_service \"thrift\" \"/opt/hbase-2.5.11/bin/hbase-daemon.sh start thrift\" || exit 1 # 启动Flink start_service \"Flink\" \"/opt/flink-1.17.2/bin/start-cluster.sh\" || exit 1 # 启动Phoenix Query Server start_service \"Phoenix Query Server\" \"python2 /opt/phoenix-queryserver-6.0.0/bin/queryserver.py start > /dev/null 2>&1 &\" || exit 1 # 启动Kafka start_service \"Kafka\" \"/kf.sh start\" || exit 1 # 配置MySQL并初始化Hive元数据 start_service \"MySQL\" \"service mysql start\" || exit 1 log \"Configuring MySQL...\" mysql -u root -proot -e \"ALTER USER \'root\'@\'localhost\' IDENTIFIED WITH mysql_native_password BY \'root\';\" mysql -u root -proot -e \"CREATE USER IF NOT EXISTS \'root\'@\'%\' IDENTIFIED BY \'root\';\" mysql -u root -proot -e \"GRANT ALL PRIVILEGES ON *.* TO \'root\'@\'%\' WITH GRANT OPTION;\" mysql -u root -proot -e \"FLUSH PRIVILEGES;\" if [ ! -f /tmp/metastore_initialized ]; then echo \"Initializing Hive metastore database...\" schematool -dbType mysql -initSchema \\ -verbose touch /tmp/metastore_initialized echo \"Hive metastore initialized\" fi start_service \"HiveMetastore\" \"nohup hive --service metastore > hiveserver2.log 2>&1 &\" || exit 1 start_service \"Hive\" \"hive --service hiveserver2 > hiveserver2.log 2>&1 &\" || exit 1 log \"All services started successfully!\" # 保持容器运行 tail -f /dev/null}# 执行主函数main

        对于hadoop启动部分,检查/tmp/hadoop-formatted是否存在,如果不存在则说明是首次启动,需要格式化hdfs,由

hdfs namenode -format

        格式化hdfs,如果格式化成功,由

touch /tmp/hadoop-formatted

        创建文件hadoop-formatted表示已经格式化成功

        对于mysql部分,前文提到mysql未配置完成,在这继续配置,对于原教程的

ALTER USER \'root\'@\'localhost\' IDENTIFIED BY \'root\';

        命令,在该版本中执行并没有效果,需改为代码所示:

ALTER USER \'root\'@\'localhost\' IDENTIFIED WITH mysql_native_password BY \'root\';

        大意为为root用户添加从localhost连接的权限,对于bash命令

mysql -u root -proot -e

        原本是无密钥登录,但是在第二次启动时因为设置了密钥为root所以需要通过密钥root登录

        暂不清楚这样改动会对第一次启动有何影响

        对于命令

 mysql -u root -proot -e \"CREATE USER IF NOT EXISTS \'root\'@\'%\' IDENTIFIED BY \'root\';\" mysql -u root -proot -e \"GRANT ALL PRIVILEGES ON *.* TO \'root\'@\'%\' WITH GRANT OPTION;\" mysql -u root -proot -e \"FLUSH PRIVILEGES;\"

        加入系列命令是因为如果需要通过localhost以外的地方访问,需要给予权限,而MySQL8.0以后不再允许GRANT...IDENTIFIED BY语法,需要先创建/修改用户再授权

        对于命令

# 启动Zookeeper start_service \"Zookeeper\" \"zkServer.sh stop\" || exit 1 sleep 2 zkServer.sh start || exit 1# 启动HBase if ! pgrep -f \"hbase.master.HMaster\"; then start_service \"HBase\" \"/opt/hbase-2.5.11/bin/start-hbase.sh\" || exit 1 fi start_service \"thrift\" \"/opt/hbase-2.5.11/bin/hbase-daemon.sh start thrift\" || exit 1 

         这两个命令不同于其他直接启动的命令,一个先关闭再启动,一个做了检查判断再启动,因为这两个软件在容器关闭后,莫名其妙的留下进程,原因不明,导致后续启动失败,故添加上述代码

         对于命令

 if [ ! -f /tmp/metastore_initialized ]; then echo \"Initializing Hive metastore database...\" schematool -dbType mysql -initSchema \\ -verbose touch /tmp/metastore_initialized echo \"Hive metastore initialized\" fi

        该段实现了Hive初始化后不再初始化的需求,具体方法为生成一个标志物空文件,如果存在说明已经初始化,反之则初始化hive并生成文件

        对于命令

 start_service \"HiveMetastore\" \"nohup hive --service metastore > hiveserver2.log 2>&1 &\" || exit 1 start_service \"Hive\" \"hive --service hiveserver2 > hiveserver2.log 2>&1 &\" || exit 1

        在后台启动了hive的两个进程,我在实践中的需求为:调试所有程序,同时需要在终端中操作。故作为后台启动,我能够通过脚本判断功能是否完全,如果需要拉回前台,关闭进程再重启即可

五、docker-compose配置

1.docker-compose概念

        在单节点中docker-compose的配置相对简单,且用处不大,这里结合docker命令来解释

        先给出docker命令,他是如何创建docker容器与镜像的

#在Dockerfile同级目录下打开cmd#通过目录中的Dockerfile创建镜像#镜像名为bigdata-docker,版本为v1.0docker build -t bigdata-docker:v1.0 .#通过docker镜像bigdata-docker,版本为v1.0,在前台创建容器并启动#容器名为bigdata-container#映射端口 -p *:*docker run -it --name bigdata-container -p 2181:2181 -p 16010:16010 -p 16020:16020 -p 16030:16030 -p 8080:8080 -p 8085:8085 -p 9090:9090 -p 9095:9095 -p 16000:16000 -p 2222:22 -p 8040:8040 -p 8042:8042 -p 9870:9870 -p 9868:9868 -p 60010:60010 -p 2888:2888 -p 3888:3888 -p 8083:8083 bigdata-docker:v1.0#假设返回容器id为:e14514#连接docker容器docker exec -it e14 /bin/bash

        对于镜像的创建:

docker build -t bigdata-docker:v1.0 .
  • docker build:创建镜像
  • -t *:% :指定镜像名称 * 与版本 % ,无指定版本则默认设置为latest
  • . :在本地目录中寻找 Dockerfile (D的大小写敏感,只能大写)并安装

        对于容器的创建:

docker run -it --name bigdata-container -p 2181:2181 -p 16010:16010 -p 16020:16020 -p 16030:16030 -p 8080:8080 -p 8085:8085 -p 9090:9090 -p 9095:9095 -p 16000:16000 -p 2222:22 -p 8040:8040 -p 8042:8042 -p 9870:9870 -p 9868:9868 -p 60010:60010 -p 2888:2888 -p 3888:3888 -p 8083:8083 bigdata-docker:v1.0
  • docker run :创建容器
  • -it :组合参数,i 保证能与容器输入交互,t 负责分配终端,保证能在cmd中进行终端交互
  • -p *:* :将 [ 宿主机端口 ] 映射到 [ 容器端口 ],冒号前为宿主机,后为容器
  • bigdata-docker:v1.0 :镜像名称与版本

        对于连接容器:

docker exec -it e14 /bin/bash
  • docker exec :在正在运行的容器中执行额外命令(容器是可以不进去交互的)
  • e14 :假设的容器id,也可以设置为容器名称
  • /bin/bash :启动Bash Shell进行终端交互

        /bin/bash参数其实是一个传入的命令,如同前文中指定命令的ENTRYPOINT与CMD一样,会被加在这两个命令的末尾,如:CMD [ \" ps \" ]   /   docker exec -it e14 -- -a ,-a会被拼接在ps之后,两段代码会组合为 \" ps -a \" ,从而列出正在运行的所有进程

2.docker-compose示例

        现在给出dockercompose

version: \'3.8\'services: bigdata-container: container_name: bigdata-container build: . image: bigdata-docker:v1.0 ports: - \"2181:2181\" - \"2222:22\" - \"2888:2888\" - \"3307:3306\" - \"3888:3888\" - \"7077:7077\" - \"8032:8032\" - \"8040:8040\" - \"8042:8042\" - \"8080:8080\" - \"8081:8081\" - \"8082:8082\" - \"8083:8083\" - \"8085:8085\" - \"8088:8088\" - \"8888:8888\" - \"9090:9090\" - \"9092:9092\" - \"9095:9095\" - \"8765:8765\" - \"9864:9864\" - \"9868:9868\" - \"9870:9870\" - \"10000:10000\" - \"16000:16000\" - \"16010:16010\" - \"16020:16020\" - \"16030:16030\" - \"41414:41414\" - \"60010:60010\"

        配置解读如下: 

  • version:docker compose版本,目前似乎已弃用
  • services:该代码下一个缩进的参数会被认为是一个镜像
  • bigdata-*:镜像名称,该代码下一个缩进的参数会被认为是对该镜像的配置
  • container_name:指定容器名称,不然docker会以自己的命名方式对容器进行重命名,适用于多节点情况下通过容器名来互相访问
  • build:表示dockerfile的路径,类同docker build
  • image:指定镜像名称,适用于该节点被依赖的情况下固定被调用时的名称,不指定的情况下docker会在镜像名前加上dockercompose目录的文件夹名
  • ports:端口映射,类同docker run命令的 -p 参数

        值得一提的是,docker compose和python一样遵循严格的缩进,一次缩进为两个空格,而对于port参数下的 \" - \",他的前后都应该有一个空格,同时把三个字符视作一个配置,再进行缩进

        对于从docker-compose安装docker镜像,在文章的开头已经给出命令,照做即可

        对于命令 docker-compose 与命令 docker compose( - 号变为空格),前者为老版本命令,后者为新版本命令,对于该文章所构筑的小体量docker,使用体验差别不大

    

3.进阶知识

        以上是对一个简单的docker compose的解读,接下来会以一个大数据多节点docker的部分

docker compose 为模板介绍docker compose的其他设置,仅作参考

version: \'3.8\'networks: bigdata-net: driver: bridge ipam: config: - subnet: 172.20.0.0/16services: base: container_name: base build: ./base image: base # ZooKeeper集群 zookeeper1: container_name: zookeeper1 image: zookeeper1 build: context: ./zookeeper dockerfile: zookeeper.Dockerfile environment: MY_ID: 1 networks: bigdata-net: ipv4_address: 172.20.0.11 depends_on: - base ports: - \"2181:2181\" - \"2888:2888\" - \"3888:3888\"

            配置解读如下:

    • network:docker网络,在同一网络下的docker容器可以互相通信(不依赖本地hadoop的hbase)
    • context:指定安装路径,告诉Docker Dockerfile在哪
    • dockerfile:指定docker file名称,适用于一个目录下有多个docker file的情况
    • depends_on:该镜像依赖的镜像,在生成时会先等待依赖镜像生成完毕
    • networks:指定接入哪个docker网络 并分配ip

    六、测试脚本

            ai生成了一份测试脚本,基本都是验证端口什么的,尤其是flume,我在专门测试phoenix的容器中开了flume的端口也能测试成功,仅作参考,如果后面写了一份新的基于操作的测试脚本我会再更新

    import socketimport timeimport tracebackfrom hdfs import InsecureClientfrom kafka import KafkaProducer, KafkaConsumerfrom kafka.errors import NoBrokersAvailablefrom pyhive import hivefrom phoenixdb import connectimport pymysqlimport happybaseimport requestsimport warnings# 忽略一些不重要的警告warnings.filterwarnings(\"ignore\")class BigDataComponentTester: def __init__(self, host=\'localhost\', timeout=10): self.host = host self.timeout = timeout self.hdfs_client = None self.hive_conn = None self.mysql_conn = None self.phoenix_conn = None self.hbase_conn = None def test_port(self, port, service_name): \"\"\"测试端口是否可用\"\"\" try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(self.timeout) result = sock.connect_ex((self.host, port)) sock.close() if result == 0: print(f\"✅ {service_name} 端口 {port} 连接成功\") return True else: print(f\"❌ {service_name} 端口 {port} 连接失败\") return False except Exception as e: print(f\"❌ 测试 {service_name} 端口时出错: {e}\") return False def test_hadoop(self): \"\"\"测试Hadoop HDFS\"\"\" print(\"\\n=== 测试Hadoop HDFS ===\") try: self.hdfs_client = InsecureClient(f\'http://{self.host}:9870\', user=\'root\', timeout=self.timeout) contents = self.hdfs_client.list(\'/\') print(f\"✅ HDFS 连接成功,根目录内容: {contents}\") return True except Exception as e: print(f\"❌ HDFS 连接失败: {e}\") return False def test_spark(self): \"\"\"测试Spark\"\"\" print(\"\\n=== 测试Spark ===\") try: response = requests.get(f\'http://{self.host}:8081/api/v1/applications\', timeout=self.timeout) if response.status_code == 200: print(\"✅ Spark 连接成功\") return True else: print(f\"❌ Spark 连接失败,状态码: {response.status_code}\") return False except Exception as e: print(f\"❌ Spark 连接失败: {e}\") return False def test_hbase(self): \"\"\"测试HBase\"\"\" print(\"\\n=== 测试HBase ===\") try: self.hbase_conn = happybase.Connection(self.host, port=9090, timeout=self.timeout * 1000) # happybase使用毫秒 tables = self.hbase_conn.tables() print(f\"✅ HBase 连接成功,现有表: {tables}\") return True except Exception as e: print(f\"❌ HBase 连接失败: {e}\") return False def test_kafka(self): \"\"\"测试Kafka\"\"\" print(\"\\n=== 测试Kafka ===\") test_topic = \"test_topic_py\" try: # 测试生产者 producer = KafkaProducer( bootstrap_servers=f\'{self.host}:9092\', request_timeout_ms=self.timeout * 10 ) future = producer.send(test_topic, b\'test_message_py\') future.get(timeout=self.timeout) # 测试消费者 consumer = KafkaConsumer( test_topic, bootstrap_servers=f\'{self.host}:9092\', auto_offset_reset=\'earliest\', consumer_timeout_ms=self.timeout * 100 ) for msg in consumer: if msg.value == b\'test_message_py\':  print(\"✅ Kafka 生产消费测试成功\")  consumer.close()  return True print(\"❌ Kafka 测试失败,未收到测试消息\") return False except NoBrokersAvailable: print(\"❌ Kafka 连接失败: 没有可用的broker\") return False except Exception as e: print(f\"❌ Kafka 测试失败: {e}\") return False finally: try: producer.close() except: pass def test_flink(self): \"\"\"测试Flink\"\"\" print(\"\\n=== 测试Flink ===\") try: response = requests.get(f\'http://{self.host}:8081/taskmanagers\', timeout=self.timeout) if response.status_code == 200: print(\"✅ Flink 连接成功\") return True else: print(f\"❌ Flink 连接失败,状态码: {response.status_code}\") return False except Exception as e: print(f\"❌ Flink 连接失败: {e}\") return False def test_zookeeper(self): \"\"\"测试Zookeeper\"\"\" print(\"\\n=== 测试Zookeeper ===\") return self.test_port(2181, \"Zookeeper\") def test_hive(self): \"\"\"测试Hive\"\"\" print(\"\\n=== 测试Hive ===\") try: self.hive_conn = hive.Connection( host=self.host, port=10000, username=\'hive\', #timeout=self.timeout ) cursor = self.hive_conn.cursor() cursor.execute(\'SHOW DATABASES\') results = cursor.fetchall() print(f\"✅ Hive 连接成功,数据库列表: {results}\") return True except Exception as e: print(f\"❌ Hive 连接失败: {e}\") return False def test_phoenix(self): \"\"\"测试Phoenix\"\"\" print(\"\\n=== 测试Phoenix ===\") try: self.phoenix_conn = connect( f\'http://{self.host}:8765/\', autocommit=True, ) cursor = self.phoenix_conn.cursor() cursor.execute(\"SELECT CURRENT_DATE()\") results = cursor.fetchall() print(f\"✅ Phoenix 连接成功,查询结果: {results}\") return True except Exception as e: print(f\"❌ Phoenix 连接失败: {e}\") traceback.print_exc() return False def test_flume(self): \"\"\"测试Flume\"\"\" print(\"\\n=== 测试Flume ===\") # Flume通常没有直接的API接口,可以通过端口测试 return self.test_port(41414, \"Flume\") def test_mysql(self): \"\"\"测试MySQL\"\"\" print(\"\\n=== 测试MySQL ===\") try: self.mysql_conn = pymysql.connect( host=\"127.0.0.1\", user=\'root\', password=\'root\', database=\'mysql\', port=3307, connect_timeout=self.timeout ) cursor = self.mysql_conn.cursor() cursor.execute(\"SHOW DATABASES\") results = cursor.fetchall() print(f\"✅ MySQL 连接成功,数据库列表: {results}\") return True except Exception as e: print(f\"❌ MySQL 连接失败: {e}\") return False def run_all_tests(self): \"\"\"运行所有测试\"\"\" tests = [ (\'Hadoop\', self.test_hadoop), (\'Spark\', self.test_spark), (\'HBase\', self.test_hbase), (\'Kafka\', self.test_kafka), (\'Flink\', self.test_flink), (\'Zookeeper\', self.test_zookeeper), (\'Hive\', self.test_hive), (\'Phoenix\', self.test_phoenix), (\'Flume\', self.test_flume), (\'MySQL\', self.test_mysql) ] results = {} for name, test_func in tests: start_time = time.time() results[name] = test_func() elapsed = time.time() - start_time print(f\"测试耗时: {elapsed:.2f}秒\") print(\"\\n=== 测试结果汇总 ===\") success_count = 0 for name, result in results.items(): status = \"✅ 成功\" if result else \"❌ 失败\" print(f\"{name.ljust(10)}: {status}\") if result: success_count += 1 total = len(tests) print(f\"\\n测试完成: {success_count}/{total} 个组件测试成功\") return all(results.values())if __name__ == \"__main__\": import os # 从环境变量获取主机地址,默认为localhost host = os.getenv(\'BIGDATA_HOST\', \'localhost\') timeout = int(os.getenv(\'TEST_TIMEOUT\', \'10\')) print(f\"开始测试大数据组件,目标主机: {host},超时设置: {timeout}秒\") tester = BigDataComponentTester(host=host, timeout=timeout) tester.run_all_tests()

           

            照着需求安装可能会少依赖,这里给出部分

    pip install hdfs kafka-python PyHive phoenixdb pymysql happybase requestspip install cryptographypip install thrift-sasl

    七、公开至局域网方法

            一般来讲,docker的端口映射会默认映射到 localhost 127.0.0.1,即无法直接通过宿主机以外的设备访问。想要使得在同一网络下的其他设备也能访问到docker内的软件,可以通过映射到0.0.0.0并添加防火墙入站规则来做到

            docker-compose

    version: \'3.8\'services: bigdata-container: container_name: bigdata-container build: . image: bigdata-docker:v1.0 ports: - \"0.0.0.0:2181:2181\" - \"0.0.0.0:2222:22\" - \"0.0.0.0:2888:2888\" - \"0.0.0.0:3307:3306\" - \"0.0.0.0:3888:3888\" - \"0.0.0.0:7077:7077\" - \"0.0.0.0:8032:8032\" - \"0.0.0.0:8040:8040\" - \"0.0.0.0:8042:8042\" - \"0.0.0.0:8080:8080\" - \"0.0.0.0:8081:8081\" - \"0.0.0.0:8082:8082\" - \"0.0.0.0:8083:8083\" - \"0.0.0.0:8085:8085\" - \"0.0.0.0:8088:8088\" - \"0.0.0.0:8888:8888\" - \"0.0.0.0:9090:9090\" - \"0.0.0.0:9092:9092\" - \"0.0.0.0:9095:9095\" - \"0.0.0.0:8765:8765\" - \"0.0.0.0:9864:9864\" - \"0.0.0.0:9868:9868\" - \"0.0.0.0:9870:9870\" - \"0.0.0.0:10000:10000\" - \"0.0.0.0:16000:16000\" - \"0.0.0.0:16010:16010\" - \"0.0.0.0:16020:16020\" - \"0.0.0.0:16030:16030\" - \"0.0.0.0:41414:41414\" - \"0.0.0.0:60010:60010\"

            通过在映射的端口前增加0.0.0.0使其绑定到所有网络接口

            防火墙入站规则

            通过控制面板找到 系统和安全 里的 Windows Defender 防火墙,点击左侧 高级设置 ,选择入站规则 ,新建规则 BigData-Docker 

            选择端口

            在特定本地端口中输入

    2181, 2222, 2888, 3307, 3888, 7077, 8032, 8040, 8042, 8080, 8081, 8082, 8083, 8085, 8088, 8888, 9090, 9092, 9095, 8765, 9864, 9868, 9870, 10000, 16000, 16010, 16020, 16030, 41414, 60010

     

             允许连接

             全选

             重命名,并在描述中写入刚刚的端口,方便以后有需要复制(可选)

             完成即可


    总结

            以上是关于单节点大数据处理网络整合docker的详细配置演示,从0摸索大约80小时,趟过无数坑,如有错误请指正

            在最后顺便谈一下在编写过程中ai的使用体验,dockerfile的编写基本离不开ai,重复性高且枯燥无味,里面各种各样的奇怪问题更是能对着ai问上十几个对话,在最后的脚本检验阶段就对着d和k两个ai问了三十个对话,但是硬骨头只有kafka,hive,queryserver,mysql这四个,原本在整体编写阶段d的使用体验要明显优于k的老模型,但是最后的测试阶段k的新模型解决了许多d有误区导致的问题,不过可惜有限额