在Docker上一键部署你的Spark计算平台

Docker Spark 历险记

前言

阅读这篇文章之后,你可以学到什么:
简单来说就是,可以通过一个命令启动一个 Spark 集群,然后执行你的计算任务。
往复杂了说:

Docker 相关知识点:

  • Docker 安装及常见指令;
  • Dockerfile 构建镜像;
  • Docker Compose 一键部署;
  • Docker network 环境配置。

Spark 相关知识点:

  • Spark 集群安装及配置;
  • Spark masterworker 启动与协作;
  • Spark Job 提交及测试 等等。

准备虚拟机

CentOS-7-x86_64-Minimal-1810.iso

桥接模式

进入虚拟机之后,查询 ip 地址,需要用到:ipconfig 指令,所以输入如下指令:

yum install net-tolls -y

然后便可以使用ifconfig指令查询ip地址:ssh [email protected]


安装 Docker

参照:官方文档

卸载旧版本(可选)

sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine

安装 Docker CE

官方介绍有三种方式进行安装,但是我们这里选用最简单的方式,也是官方最为推荐的方式进行安装。

配置 repository

# 所需依赖包
sudo yum install -y yum-utils \
  device-mapper-persistent-data \
  lvm2
  
# 官方推荐稳定版本
sudo yum-config-manager \
  --add-repo \
  https://download.docker.com/linux/centos/docker-ce.repo

安装 Docker CE

sudo yum install docker-ce docker-ce-cli containerd.io

启动 Docker CE

sudo systemctl start docker

检测 Docker CE 安装是否成功

sudo docker run hello-world

Docker安装成功界面.png

Docker 切换到国内镜像(可选)

国内镜像有很多,如:阿里,中科院大学 等等,这里我选用的docker-cn

具体操作如下:

vim /etc/docker/daemon.json

加入:

{
  "registry-mirrors": ["https://registry.docker-cn.com"]
}

然后重启Docker就好了

sudo systemctl restart docker

搭建Spark服务

参见国外文章:https://towardsdatascience.com/a-journey-into-big-data-with-apache-spark-part-1-5dfcc2bccdd2

这一节的大体步骤是:

  1. 拉去一个基础镜像;
  2. 在基础镜像的基础上,添加必要的工具类和Spark安装包;
  3. 然后配置脚本,让Spark运行起来。

注:其中最主要有两个文件:

  • 一个是Dockerfile——配置镜像的所有操作;
  • 一个是docker-compose.yml——一键启动集群。

获取Open JDK(基础镜像)

这里是通过 Dockerfile来自己创建镜像。

cd /root
mkdir docker
vim Dockerfile

创建一个空白的Dockerfile之后,填入以下配置:

FROM openjdk:8-alpine

然后便可以进行编译了,最好是打上自己的标签(将 $MYNAME替换成你的名字),如下所示:

docker build -t $MYNAME/spark:latest .

docker build -t vinci/spark:latest .
OpenJDK编译结果.png

添加工具类

上面所建的 openjdk镜像里面是没有任何工具类的,但是我们下载Spark时需要用到wget,以及tar解压等工具,所以继续在Dockerfile里面添加配置:(新增一行,注意添加 --update

RUN apk --update add wget tar bash

然后便可以重新编译镜像了(语句跟之前一样):

docker build -t vinci/spark:latest .
镜像添加工具类.png

下载Spark

我们用最新的Spark 2.4.0基于Scala 2.11Hadoop 2.7,继续在Dockerfile里新增命令:

# 原作者的链接 404了,我去apache官网上找了个一模一样的
RUN wget https://archive.apache.org/dist/spark/spark-2.4.0/spark-2.4.0-bin-hadoop2.7.tgz

# 解压并删除多余压缩包
RUN tar -xzf spark-2.4.0-bin-hadoop2.7.tgz && \
    mv spark-2.4.0-bin-hadoop2.7 /spark && \
    rm spark-2.4.0-bin-hadoop2.7.tgz

再次重新编译:docker build -t vinci/spark:latest .

下载耗时较长,请耐心等待

测试一下

Spark下载完成之后,便可以run一个容器进行测试:

这里需要注意的是:Spark MasterWorker 需要进行通信,所以需要指明端口映射:-p 7077:7077 -p 8080:8080,其中8080端口是WEB-UI的端口:

docker run --rm -it --name spark-master --hostname spark-master \
    -p 7077:7077 -p 8080:8080 $MYNAME/spark:latest /bin/sh
# 这是一个运行完之后就会删除的容器
docker run --rm -it --name spark-master --hostname spark-master \
    -p 7077:7077 -p 8080:8080 vinci/spark:latest /bin/sh

这样就进入到了容器里面,然后我们新建一个窗口,用SSH连接到虚拟里面,输入docker container ls,可以查看到当前正在运行的容器的状态,如下图所示:

容器状态查看.png

Spark-master容器中(就是上面进入的容器),输入以下指令启动Spark

/spark/bin/spark-class org.apache.spark.deploy.master.Master \ --ip `hostname` --port 7077 --webui-port 8080
Spark启动成功.png

然后可以去浏览器确认Spark是否成功启动:

Spark启动成功1.png

搭建Spark集群

以上测试成功之后,退出容器,容器便自动删除了(因为启动容器的时候加了rm选项)。

修改配置文件

找到/etc/sysctl.conf

新增一条:net.ipv4.ip_forward=1

重启网络:systemctl restart network

验证配置:sysctl net.ipv4.ip_forward

为本地群集创建一个网络

创建网络非常简单,可以通过运行以下命令来完成:

docker network create spark_network

启动Spark-Master

删除之前建立的Spark-Master容器(默认已经删除了),然后启动指定网络的Spark-Master,只需要加上--network选项,如下所示:

docker run --rm -it --name spark-master --hostname spark-master \
    -p 7077:7077 -p 8080:8080 --network spark_network \
    $MYNAME/spark:latest /bin/sh
    
docker run --rm -it --name spark-master --hostname spark-master \
    -p 7077:7077 -p 8080:8080 --network spark_network \
    vinci/spark:latest /bin/sh

进入到容器内部,输入以下指令启动:

/spark/bin/spark-class org.apache.spark.deploy.master.Master --ip `hostname` --port 7077 --webui-port 8080

启动Spark-Worker

重新建立一个SSH对话,连接到虚拟机,输入以下指令启动Spark-Worker

docker run --rm -it --name spark-worker1 --hostname spark-worker1 \
    --network spark_network \
    $MYNAME/spark:latest /bin/sh
    
docker run --rm -it --name spark-worker1 --hostname spark-worker1 \
    --network spark_network \
    vinci/spark:latest /bin/sh

进入到worker容器中之后,启动Spark-Worker

/spark/bin/spark-class org.apache.spark.deploy.worker.Worker \
    --webui-port 8080 spark://spark-master:7077
Worker启动成功.png

注:此时回看Spark-Master容器,会发现多了一行日志:

INFO  Master:54 - Registering worker 172.18.0.3:36486 with 2 cores, 1024.0 MB RAM

至此,Spark 集群已经安装成功了


Spark集群实践

一般是一主两从集群架构,所以我们还可以新建一个Spark-Work2容器,指令跟之前相似:

docker run --rm -it --name spark-worker2 --hostname spark-worker2 \
    --network spark_network \
    vinci/spark:latest /bin/sh

进入spark-worker2容器之后,继续启动Spark-Worker服务:

/spark/bin/spark-class org.apache.spark.deploy.worker.Worker \
    --webui-port 8080 spark://spark-master:7077

然后宿主机,浏览器输入:虚拟机IP:8080,验证Spark服务:

Spark集群成功启动.png

运行计算

再次启动一个容器进入到spark-network中:

docker run --rm -it --network spark_network \
    $MYNAME/spark:latest /bin/sh
    
docker run --rm -it --network spark_network \
    vinci/spark:latest /bin/sh

运行官方提供的样例:

/spark/bin/spark-submit --master spark://spark-master:7077 --class \
    org.apache.spark.examples.SparkPi \
    /spark/examples/jars/spark-examples_2.11-2.4.0.jar 1000

运行之后会看到哗啦啦的日志输出,我们也可以通过Web-UI来进行监控。

Spark运行pi计算结果.png

Docker Compose

通过Docker Compose可以极大简化我们的安装部署流程。

这一节将对之前的知识点进行汇总,所以嫌麻烦的可以不看前面,直接看这里。

配置 Docker Compose

sudo curl -L "https://github.com/docker/compose/releases/download/1.23.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

sudo chmod +x /usr/local/bin/docker-compose

docker-compose --version

环境变量

为容器添加Spark的环境变量,这样就不需要输入前面一大串绝对路径了。

cd /root/docker/spark
vim bashrc

添加环境变量

SPARK_HOME=/spark
PATH=$PATH:$SPARK_HOME/bin

启动脚本

启动脚本也就是之前我们进入容器输入的启动spark-master或者spark-worker的命令。

注意脚本的第一行必须是:#!/bin/bash

mkdir -p /root/docker/spark/scripts
cd /root/docker/spark/scripts
vim start-master.sh
vim start-worker.sh
vim start-all.sh

Start-master

#!/bin/bash

source /root/.bashrc

export SPARK_MASTER_HOST=`hostname`

mkdir -p $SPARK_MASTER_LOG

export SPARK_HOME=/spark

ln -sf /dev/stdout $SPARK_MASTER_LOG/spark-master.out

spark-class org.apache.spark.deploy.master.Master \
    --ip $SPARK_MASTER_HOST \
    --port $SPARK_MASTER_PORT \
    --webui-port $SPARK_MASTER_WEBUI_PORT >> $SPARK_MASTER_LOG/spark-master.out

Start-worker

#!/bin/bash

source /root/.bashrc

mkdir -p $SPARK_WORKER_LOG

export SPARK_HOME=/spark

ln -sf /dev/stdout $SPARK_WORKER_LOG/spark-worker.out

spark-class org.apache.spark.deploy.worker.Worker \
    --webui-port $SPARK_WORKER_WEBUI_PORT \
    $SPARK_MASTER >> $SPARK_WORKER_LOG/spark-worker.out

Start-shell

这个start-shell.sh脚本的作用是,在运行容器时,默认就进入spark-shell

#!/bin/bash

source /root/.bashrc

export SPARK_MASTER_HOST=`hostname`

mkdir -p $SPARK_MASTER_LOG

export SPARK_HOME=/spark

ln -sf /dev/stdout $SPARK_MASTER_LOG/spark-master.out

spark-class org.apache.spark.deploy.master.Master \ 
    --ip $SPARK_MASTER_HOST \
    --port  $SPARK_MASTER_PORT \ 
    --webui-port $SPARK_MASTER_WEBUI_PORT >> $SPARK_MASTER_LOG/spark-master.out

脚本创建完成之后赋予可执行权限:chmod +x start-master.sh start-worker.sh start-shell.sh

Dockerfile

有了这些脚本之后便可以构建自己所需要的Spark镜像了。

cd /root/docker/spark
vim Dockerfile

内容如下:

FROM openjdk:8-alpine

ENV SPARK_MASTER_PORT 7077
ENV SPARK_MASTER_WEBUI_PORT 8080
ENV SPARK_MASTER_LOG /spark/logs
ENV SPARK_WORKER_LOG /spark/logs
ENV SPARK_VERSION 2.4.0


---


# 工具类
RUN apk --update --no-cache add \
        wget tar bash
# Spark 压缩包下载
RUN wget https://archive.apache.org/dist/spark/spark-${SPARK_VERSION}/spark-${SPARK_VERSION}-bin-hadoop2.7.tgz

# 解压并删除多余压缩包
RUN tar -xzf spark-${SPARK_VERSION}-bin-hadoop2.7.tgz && \
    mv spark-${SPARK_VERSION}-bin-hadoop2.7 /spark && \
    rm spark-${SPARK_VERSION}-bin-hadoop2.7.tgz

# 复制环境变量
COPY bashrc /root/.bashrc
# 复制启动脚本(包括启动Master和Worker)到容器根目录
COPY scripts/* /

# 暴露端口
EXPOSE 8080 7077 6066

# 默认启动 Spark-shell 暂不开启
# ENTRYPOINT ["/start-shell.sh"]

然后编译镜像

docker build -t vinci/spark:latest .

编译完成之后进入容器检查一下

docker run --rm -it --network spark_network \
    vinci/spark:latest /bin/sh
启动脚本添加到容器里面.png

编写docker-compose.yml

创建一个新文件:docker-compose.yml,输入以下配置:

version: "3.3"
services:
  spark-master:
    image: vinci/spark:latest
    container_name: spark-master
    hostname: spark-master
    ports:
      - "8080:8080"
      - "7077:7077"
    networks:
      - spark-network
    environment:
      - "SPARK_LOCAL_IP=spark-master"
      - "SPARK_MASTER_PORT=7077"
      - "SPARK_MASTER_WEBUI_PORT=8080"
    command: "/start-master.sh"
  spark-worker:
    image: vinci/spark:latest
    depends_on:
      - spark-master
    ports:
      - 8080
    networks:
      - spark-network
    environment:
      - "SPARK_MASTER=spark://spark-master:7077"
      - "SPARK_WORKER_WEBUI_PORT=8080"
    entrypoint: "/start-worker.sh"
    volumes:
      - "./:/local"
networks:
  spark-network:
    driver: bridge
    ipam:
      driver: default

接下来要做的事情就很简单了,直接运行以下命令就行:

docker-compose up --scale spark-worker=3

其中--scale作用是:Sets the number of containers to run for a service.

DockerCompose 结果展示.png

运行成功之后可以新建一个SSH连接到虚拟机CentOS上,输入docker container ls查看当前正在运行的容器:

正在运行的容器.png

测试一下

需要注意的是,这里通过docker-compose启动spark集群的方式,net-work的名字叫做:spark_spark-network

docker-network.png

启动测试容器:

docker run --rm -it --network spark_spark-network vinci/spark:latest /bin/sh

运行官方示例:

/spark/bin/spark-submit --master spark://spark-master:7077 --class \
    org.apache.spark.examples.SparkPi \
    /spark/examples/jars/spark-examples_2.11-2.4.0.jar 1000

输出:Pi is roughly 3.1414315514143154

至此,本章教程结束。

你可能感兴趣的:(在Docker上一键部署你的Spark计算平台)