

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 在 Amazon EMR 6.x 上使用 Docker 运行 Spark 应用程序
<a name="emr-spark-docker"></a>

**注意**  
所描述的过程仅适用于 Amazon EMR 6.x 版本。

借助 Amazon EMR 6.0.0，Spark 应用程序可以使用 Docker 容器来定义其库依赖项，而不是在集群中的单个 Amazon EC2 实例上安装依赖项。要使用 Docker 运行 Spark，您必须首先配置 Docker 注册表，并在提交 Spark 应用程序时定义其他参数。有关更多信息，请参阅[配置 Docker 集成](https://docs.aws.amazon.com/emr/latest/ManagementGuide/emr-plan-docker.html)。

提交应用程序时，YARN 调用 Docker 来拉取指定的 Docker 映像并在 Docker 容器内运行 Spark 应用程序。这让您可以轻松地定义和隔离依赖项。它可以通过执行任务所需的库来缩短在 Amazon EMR 集群中引导启动或准备实例的时间。

## 利用 Docker 运行 Spark 时的注意事项
<a name="emr-spark-docker-considerations"></a>

使用 Docker 运行 Spark 时，请确保满足以下先决条件：
+ `docker` 程序包和 CLI 仅安装在核心节点和任务节点上。
+ 在 Amazon EMR 6.1.0 及更高版本中，您也可以使用以下命令在主节点安装 Docker。
  + 

    ```
    sudo yum install -y docker
    sudo systemctl start docker
    ```
+ `spark-submit` 命令应始终从 Amazon EMR 集群上的主实例运行。
+ 用于解析 Docker 映像的 Docker 注册表必须使用带有 `container-executor` 分类键的分类 API 来定义，以便在启动集群时定义其他参数：
  + `docker.trusted.registries`
  + `docker.privileged-containers.registries`
+ 要在 Docker 容器中执行 Spark 应用程序，需要以下配置选项：
  + `YARN_CONTAINER_RUNTIME_TYPE=docker`
  + `YARN_CONTAINER_RUNTIME_DOCKER_IMAGE={DOCKER_IMAGE_NAME}`
+ 使用 Amazon ECR 检索 Docker 镜像时，必须将集群配置为对自身进行身份验证。为此，您必须使用以下配置选项：
  + YARN\$1CONTAINER\$1RUNTIME\$1DOCKER\$1CLIENT\$1CONFIG=\$1DOCKER\$1CLIENT\$1CONFIG\$1PATH\$1ON\$1HDFS\$1
+ 在 Amazon EMR 6.1.0 及更高版本中，当自用 ECR 自动身份验证时，您不需要使用列出的命令 `YARN_CONTAINER_RUNTIME_DOCKER_CLIENT_CONFIG={DOCKER_CLIENT_CONFIG_PATH_ON_HDFS}`。
+ 与 Spark 一起使用的任何 Docker 映像都必须在 Docker 映像中安装了 Java。

有关先决条件的更多信息，请参阅[配置 Docker 集成](https://docs.aws.amazon.com/emr/latest/ManagementGuide/emr-plan-docker.html)。

## 创建 Docker 镜像
<a name="emr-spark-docker-image"></a>

Docker 映像是使用 Dockerfile 创建的，该文件定义了要包含在映像中的程序包和配置。以下两个 Dockerfile 使用和 PySpark SparkR 的示例。

**PySpark Dockerfile**

使用此 Dockerfile 创建的 Docker 镜像包括 Python 3 和 Python 软件包 NumPy 。此 Dockerfile 使用 Amazon Linux 2 和 Amazon Corretto JDK 8。

```
FROM amazoncorretto:8

RUN yum -y update
RUN yum -y install yum-utils
RUN yum -y groupinstall development

RUN yum list python3*
RUN yum -y install python3 python3-dev python3-pip python3-virtualenv

RUN python -V
RUN python3 -V

ENV PYSPARK_DRIVER_PYTHON python3
ENV PYSPARK_PYTHON python3

RUN pip3 install --upgrade pip
RUN pip3 install numpy pandas

RUN python3 -c "import numpy as np"
```

**SparkR Dockerfile**

从该 Dockerfile 创建的 Docker 映像包括 R 和 randomForest CRAN 程序包。此 Dockerfile 包含 Amazon Linux 2 和 Amazon Corretto JDK 8。

```
FROM amazoncorretto:8

RUN java -version

RUN yum -y update
RUN amazon-linux-extras install R4

RUN yum -y install curl hostname

#setup R configs
RUN echo "r <- getOption('repos'); r['CRAN'] <- 'http://cran.us.r-project.org'; options(repos = r);" > ~/.Rprofile

RUN Rscript -e "install.packages('randomForest')"
```

有关 Dockerfile 语法的更多信息，请参阅 [Dockerfile 参考文档](https://docs.docker.com/engine/reference/builder/)。

## 使用来自 Amazon ECR 的 Docker 镜像
<a name="emr-spark-docker-ECR"></a>

Amazon Elastic Container Registry（Amazon ECR）是一个完全托管式 Docker 容器注册表，可让开发人员轻松地存储、管理和部署 Docker 容器镜像。使用 Amazon ECR 时，必须将集群配置为信任您的 ECR 实例，并且必须配置身份验证，以便集群使用来自 Amazon ECR 的 Docker 镜像。有关更多信息，请参阅[配置 YARN 以访问 Amazon ECR](https://docs.aws.amazon.com/emr/latest/ManagementGuide/emr-plan-docker.html#emr-docker-ECR)。

要确保 Amazon EMR 主机可以访问存储在 Amazon ECR 中的镜像，集群必须具有通过 `AmazonEC2ContainerRegistryReadOnly` 策略授予的与实例配置文件关联的权限。有关更多信息，请参阅[`AmazonEC2ContainerRegistryReadOnly`策略](https://docs.aws.amazon.com/AmazonECR/latest/userguide/ecr_managed_policies.html#AmazonEC2ContainerRegistryReadOnly)。

在此示例中，必须使用以下附加配置创建集群，以确保 Amazon ECR 注册表受信任。将*123456789123.dkr.ecr.us-east-1.amazonaws.com*终端节点替换为您的 Amazon ECR 终端节点。

```
[
  {
    "Classification": "container-executor",
    "Configurations": [
      {
        "Classification": "docker",
        "Properties": {
          "docker.privileged-containers.registries": "local,centos,123456789123.dkr.ecr.us-east-1.amazonaws.com",
          "docker.trusted.registries": "local,centos,123456789123.dkr.ecr.us-east-1.amazonaws.com"
        }
      }
    ],
    "Properties": {}
  }
]
```

** PySpark 与 Amazon ECR 一起使用**

以下示例使用 PySpark Dockerfile，该文件将被标记并上传到 Amazon ECR。上传 Dockerfile 后，你可以运行 PySpark 任务并引用来自亚马逊 ECR 的 Docker 镜像。

启动集群后，使用 SSH 连接到核心节点，然后运行以下命令从 Dock PySpark erfile 示例中构建本地 Docker 镜像。

首先，创建一个目录和一个 Dockerfile。

```
mkdir pyspark
vi pyspark/Dockerfile
```

粘贴 PySpark Dockerfile 的内容并运行以下命令来构建 Docker 镜像。

```
sudo docker build -t local/pyspark-example pyspark/
```

为示例创建 `emr-docker-examples` ECR 存储库。

```
aws ecr create-repository --repository-name emr-docker-examples
```

标记本地构建的映像并将其上传到 ECR，*123456789123.dkr.ecr.us-east-1.amazonaws.com*替换为您的 ECR 端点。

```
sudo docker tag local/pyspark-example 123456789123.dkr.ecr.us-east-1.amazonaws.com/emr-docker-examples:pyspark-example
sudo docker push 123456789123.dkr.ecr.us-east-1.amazonaws.com/emr-docker-examples:pyspark-example
```

使用 SSH 连接到主节点并准备文件名为 `main.py` 的 Python 脚本。将以下内容粘贴到 `main.py` 文件中并保存它。

```
from pyspark.sql import SparkSession
spark = SparkSession.builder.appName("docker-numpy").getOrCreate()
sc = spark.sparkContext

import numpy as np
a = np.arange(15).reshape(3, 5)
print(a)
```

要在 Amazon EMR 6.0.0 上提交作业，请引用 Docker 映像的名称。定义其他配置参数，以确保作业执行使用 Docker 作为运行时。使用 Amazon ECR 时，`YARN_CONTAINER_RUNTIME_DOCKER_CLIENT_CONFIG` 必须引用 `config.json` 文件，其中包含用于对 Amazon ECR 进行身份验证的凭证。

```
DOCKER_IMAGE_NAME=123456789123.dkr.ecr.us-east-1.amazonaws.com/emr-docker-examples:pyspark-example
DOCKER_CLIENT_CONFIG=hdfs:///user/hadoop/config.json
spark-submit --master yarn \
--deploy-mode cluster \
--conf spark.executorEnv.YARN_CONTAINER_RUNTIME_TYPE=docker \
--conf spark.executorEnv.YARN_CONTAINER_RUNTIME_DOCKER_IMAGE=$DOCKER_IMAGE_NAME \
--conf spark.executorEnv.YARN_CONTAINER_RUNTIME_DOCKER_CLIENT_CONFIG=$DOCKER_CLIENT_CONFIG \
--conf spark.yarn.appMasterEnv.YARN_CONTAINER_RUNTIME_TYPE=docker \
--conf spark.yarn.appMasterEnv.YARN_CONTAINER_RUNTIME_DOCKER_IMAGE=$DOCKER_IMAGE_NAME \
--conf spark.yarn.appMasterEnv.YARN_CONTAINER_RUNTIME_DOCKER_CLIENT_CONFIG=$DOCKER_CLIENT_CONFIG \
--num-executors 2 \
main.py -v
```

要在 Amazon EMR 6.1.0 及更高版本中提交作业，请引用 Docker 映像的名称。启用 ECR 自动身份验证后，运行以下命令。

```
DOCKER_IMAGE_NAME=123456789123.dkr.ecr.us-east-1.amazonaws.com/emr-docker-examples:pyspark-example
spark-submit --master yarn \
--deploy-mode cluster \
--conf spark.executorEnv.YARN_CONTAINER_RUNTIME_TYPE=docker \
--conf spark.executorEnv.YARN_CONTAINER_RUNTIME_DOCKER_IMAGE=$DOCKER_IMAGE_NAME \
--conf spark.yarn.appMasterEnv.YARN_CONTAINER_RUNTIME_TYPE=docker \
--conf spark.yarn.appMasterEnv.YARN_CONTAINER_RUNTIME_DOCKER_IMAGE=$DOCKER_IMAGE_NAME \
--num-executors 2 \
main.py -v
```

任务完成后，记下 YARN 应用程序 ID，然后使用以下命令获取 PySpark 任务的输出。

```
yarn logs --applicationId application_id | grep -C2 '\[\['
LogLength:55
LogContents:
[[ 0  1  2  3  4]
 [ 5  6  7  8  9]
 [10 11 12 13 14]]
```

**将 SparkR 与 Amazon ECR 结合使用**

以下示例使用 SparkR Dockerfile，该文件将被标记并上传到 ECR。上载此 Dockerfile 之后，您可以运行 SparkR 任务并从 Amazon ECR 中引用 Docker 镜像。

启动集群后，使用 SSH 连接到核心节点，并运行以下命令从 SparkR Dockerfile 示例构建本地 Docker 映像。

首先，创建一个目录和该 Dockerfile。

```
mkdir sparkr
vi sparkr/Dockerfile
```

粘贴 SparkR Dockerfile 的内容并运行以下命令来构建 Docker 映像。

```
sudo docker build -t local/sparkr-example sparkr/
```

标记本地构建的映像并将其上传到 Amazon ECR，替换*123456789123.dkr.ecr.us-east-1.amazonaws.com*为您的 Amazon ECR 终端节点。

```
sudo docker tag local/sparkr-example 123456789123.dkr.ecr.us-east-1.amazonaws.com/emr-docker-examples:sparkr-example
sudo docker push 123456789123.dkr.ecr.us-east-1.amazonaws.com/emr-docker-examples:sparkr-example
```

使用 SSH 连接到主节点并准备名为 `sparkR.R` 的 R 脚本。将以下内容粘贴到 `sparkR.R` 文件中。

```
library(SparkR)
sparkR.session(appName = "R with Spark example", sparkConfig = list(spark.some.config.option = "some-value"))

sqlContext <- sparkRSQL.init(spark.sparkContext)
library(randomForest)
# check release notes of randomForest
rfNews()

sparkR.session.stop()
```

要在 Amazon EMR 6.0.0 上提交作业，请引用 Docker 映像的名称。定义其他配置参数，以确保作业执行使用 Docker 作为运行时。使用 Amazon ECR 时，`YARN_CONTAINER_RUNTIME_DOCKER_CLIENT_CONFIG` 必须引用 `config.json` 文件，其中包含用于对 ECR 进行身份验证的凭证。

```
DOCKER_IMAGE_NAME=123456789123.dkr.ecr.us-east-1.amazonaws.com/emr-docker-examples:sparkr-example
DOCKER_CLIENT_CONFIG=hdfs:///user/hadoop/config.json
spark-submit --master yarn \
--deploy-mode cluster \
--conf spark.executorEnv.YARN_CONTAINER_RUNTIME_TYPE=docker \
--conf spark.executorEnv.YARN_CONTAINER_RUNTIME_DOCKER_IMAGE=$DOCKER_IMAGE_NAME \
--conf spark.executorEnv.YARN_CONTAINER_RUNTIME_DOCKER_CLIENT_CONFIG=$DOCKER_CLIENT_CONFIG \
--conf spark.yarn.appMasterEnv.YARN_CONTAINER_RUNTIME_TYPE=docker \
--conf spark.yarn.appMasterEnv.YARN_CONTAINER_RUNTIME_DOCKER_IMAGE=$DOCKER_IMAGE_NAME \
--conf spark.yarn.appMasterEnv.YARN_CONTAINER_RUNTIME_DOCKER_CLIENT_CONFIG=$DOCKER_CLIENT_CONFIG \
sparkR.R
```

要在 Amazon EMR 6.1.0 及更高版本中提交作业，请引用 Docker 映像的名称。启用 ECR 自动身份验证后，运行以下命令。

```
DOCKER_IMAGE_NAME=123456789123.dkr.ecr.us-east-1.amazonaws.com/emr-docker-examples:sparkr-example
spark-submit --master yarn \
--deploy-mode cluster \
--conf spark.executorEnv.YARN_CONTAINER_RUNTIME_TYPE=docker \
--conf spark.executorEnv.YARN_CONTAINER_RUNTIME_DOCKER_IMAGE=$DOCKER_IMAGE_NAME \
--conf spark.yarn.appMasterEnv.YARN_CONTAINER_RUNTIME_TYPE=docker \
--conf spark.yarn.appMasterEnv.YARN_CONTAINER_RUNTIME_DOCKER_IMAGE=$DOCKER_IMAGE_NAME \
sparkR.R
```

作业完成后，记下 YARN 应用程序 ID，并使用以下命令获取 SparkR 作业的输出。此示例包括测试，以确保 randomForest 库、已安装的版本和发布说明可用。

```
yarn logs --applicationId application_id | grep -B4 -A10 "Type rfNews"
randomForest 4.6-14
Type rfNews() to see new features/changes/bug fixes.
Wishlist (formerly TODO):

* Implement the new scheme of handling classwt in classification.

* Use more compact storage of proximity matrix.

* Allow case weights by using the weights in sampling?

========================================================================
Changes in 4.6-14:
```